1、AOP概述
AOP技术即Aspect Oriented Programming的缩写,译为面向切面编程。AOP是OOP的一种延续,利用AOP技术可以对业务逻辑的各个部分进行隔离,从使得业务逻辑各部分之间的耦合性降低,提高程序的可重用性,同时提高了开发的效率。
AOP采用横向抽取机制,取代了传统纵向继承体系重复性代码,AOP可以在不修改源代码的前提下,对程序进行增强。
2、AOP技术的底层实现
- 基于jdk的动态代理:必须是面向接口的,只有实现了具体接口的类才能生成代理对象
- 基于CGLIB动态代理:对于没有实现接口的类,也可以产生代理,产生这个类的子类的方式
Spring的传统AOP中根据类是否实现接口而采用不同的代理方式,如果实现类接口,则使用jdk动态代理完成AOP,如果没有实现接口,采用CGLIB动态代理完成AOP。
JDK动态代理演示:
接口UserDao、实现类UserDaoImpl、动态代理类MyProxyUtils
11 package com.alphajuns.demo1; 22 33 public interface UserDao { 44 55 public void save(); 66 77 public void update(); 88 99 } 10 11 1 package com.alphajuns.demo1; 12 2 13 3 public class UserDaoImpl implements UserDao { 14 4 15 5 @Override 16 6 public void save() { 17 7 System.out.println("保存用户..."); 18 8 } 19 9 2010 @Override 2111 public void update() { 2212 System.out.println("修改用户..."); 2313 } 2414 2515 } 26 27 1 package com.alphajuns.demo1; 28 2 29 3 import java.lang.reflect.InvocationHandler; 30 4 import java.lang.reflect.Method; 31 5 import java.lang.reflect.Proxy; 32 6 33 7 /* 34 8 * 使用JDK的方式生成动态代理 35 9 */ 3610 public class MyProxyUtils { 3711 3812 public static UserDao getProxy(final UserDao dao) { 3913 // 使用Proxy类生成代理对象 4014 UserDao proxy = (UserDao) Proxy.newProxyInstance( 4115 dao.getClass().getClassLoader(), 4216 dao.getClass().getInterfaces(), 4317 new InvocationHandler() { 4418 4519 @Override 4620 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 4721 if ("save".equals(method.getName())) { 4822 System.out.println("记录日志..."); 4923 } 5024 // 执行dao类中的方法 5125 return method.invoke(dao, args); 5226 } 5327 }); 5428 5529 // 返回代理对象 5630 return proxy; 5731 } 5832 5933 }
CGLIB代理:
1 1 package com.alphajuns.demo2; 2 2 3 3 import java.lang.reflect.Method; 4 4 5 5 import org.springframework.cglib.proxy.Enhancer; 6 6 import org.springframework.cglib.proxy.MethodInterceptor; 7 7 import org.springframework.cglib.proxy.MethodProxy; 8 8 9 9 public class MyCglibUtils { 1010 1111 /* 1212 * 使用Cglib方法生成代理对象 1313 */ 1414 public static BookDaoImpl getProxy() { 1515 Enhancer enhancer = new Enhancer(); 1616 // 设置父类 1717 enhancer.setSuperclass(BookDaoImpl.class); 1818 // 设置回调函数 1919 enhancer.setCallback(new MethodInterceptor() { 2020 // 代理对象的方法执行,回调函数的方法就会执行 2121 @Override 2222 public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 2323 if ("save".equals(method.getName())) { 2424 System.out.println("记录日志..."); 2525 } 2626 // 正常执行 2727 return methodProxy.invokeSuper(obj, args); 2828 } 2929 }); 3030 3131 // 生成代理对象 3232 BookDaoImpl proxy = (BookDaoImpl) enhancer.create(); 3333 return proxy; 3434 } 3535 3636 }
3、AOP相关术语
- JoinPoint(连接点):被拦截的点。Spring中,这些点是指方法,Spring只支持方法类型的连接点。
- Pointcut(切入点):对需要被拦截的JoinPoint的定义。
- Advice(通知/增强):拦截到JointPoint之后所要做的事情就是通知。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知
- Introduction(引介):引介是一种特殊的通知,在不修改代码的前提下,Introduction可以在运行期为类动态地添加一些方法或field
- Target(目标对象):代理的目标对象
- Weaving(织入):是指增强应用到目标对象来创建新的代理对象的过程
- Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
- Aspect(切面):切入点与通知的结合
4、XML方式AOP开发步骤
- 创建WEB项目,引入jar包
- 创建Spring配置文件,引入AOP的schema约束
- 创建包结构,编写具体的接口和实现类
- 将目标类配置到Spring配置文件中
- 定义切面类
- 在配置文件中定义切面类
- 在配置文件中完成AOP配置
- 测试
5、切入点表达式
切入点表达式在下面applicationContext2中以注释形式进行了介绍。
6、AOP通知类型
- 前置通知:在目标类的方法执行之前执行
- 后置通知:在目标类方法执行之后执行
- 异常抛出通知:在抛出异常后通知
- 环绕通知:方法执行前后都执行
7、AOP应用举例
接口CustomerDao、实现类CustomerDaoImpl、切面类MyAspectXml、Spring配置文件applicationContext、applicationContext2、applicationContext3
11 package com.alphajuns.demo3; 22 33 public interface CustomerDao { 44 55 public void save(); 66 77 public void update(); 88 99 } 10 11 1 package com.alphajuns.demo3; 12 2 13 3 public class CustomerDaoImpl implements CustomerDao { 14 4 15 5 @Override 16 6 public void save() { 17 7 System.out.println("保存客户..."); 18 8 } 19 9 2010 @Override 2111 public void update() { 2212 System.out.println("更新客户..."); 2313 } 2414 2515 } 26 27 1 package com.alphajuns.demo3; 28 2 29 3 import org.aspectj.lang.ProceedingJoinPoint; 30 4 31 5 /* 32 6 * 切面类:切入点+通知 33 7 */ 34 8 public class MyAspectXml { 35 9 3610 /* 3711 * 通知(具体的增强) 3812 */ 3913 public void log() { 4014 System.out.println("记录日志..."); 4115 } 4216 4317 /* 4418 * 最终通知:方法执行成功或出现异常,都会执行 4519 */ 4620 public void after() { 4721 System.out.println("最终通知..."); 4822 } 4923 5024 /* 5125 * 后置通知:方法执行之后,执行后置通知,出现异常则不执行 5226 */ 5327 public void afterReturn() { 5428 System.out.println("后置通知..."); 5529 } 5630 5731 /* 5832 * 环绕通知:方法执行之前和方法执行之后进行通知,默认情况下,目标对象的方法不执行。需要手动让目标对象的方法执行 5933 */ 6034 public void around(ProceedingJoinPoint joinPoint) { 6135 System.out.println("环绕通知1..."); 6236 try { 6337 // 手动让目标对象的方法执行 6438 joinPoint.proceed(); 6539 } catch (Throwable e) { 6640 e.printStackTrace(); 6741 } 6842 System.out.println("环绕通知2..."); 6943 } 7044 7145 } 72 73 1 <?xml version="1.0" encoding="UTF-8"?> 74 2 <beans xmlns="http://www.springframework.org/schema/beans" 75 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 76 4 xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" 77 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 78 6 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> 79 7 80 8 <!-- 配置客户的dao --> 81 9 <bean id="customerDao" class="com.alphajuns.demo3.CustomerDaoImpl"/> 8210 <!-- 配置切面类 --> 8311 <bean id="myAspectXml" class="com.alphajuns.demo3.MyAspectXml"/> 8412 <!-- 配置AOP --> 8513 <aop:config> 8614 <!-- 配置切面类:切入点+通知类型 --> 8715 <aop:aspect ref="myAspectXml"> 8816 <!-- 配置前置通知 --> 8917 <aop:before method="log" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> 9018 </aop:aspect> 9119 </aop:config> 9220 9321 </beans> 94 95 1 <?xml version="1.0" encoding="UTF-8"?> 96 2 <beans xmlns="http://www.springframework.org/schema/beans" 97 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 98 4 xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" 99 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 100 6 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> 101 7 102 8 <!-- 配置客户的dao --> 103 9 <bean id="customerDao" class="com.alphajuns.demo3.CustomerDaoImpl"/> 10410 <!-- 配置切面类 --> 10511 <bean id="myAspectXml" class="com.alphajuns.demo3.MyAspectXml"/> 10612 <!-- 配置AOP --> 10713 <aop:config> 10814 <!-- 配置切面类:切入点+通知类型 --> 10915 <aop:aspect ref="myAspectXml"> 11016 <!-- 配置前置通知 --> 11117 <!-- 切入点的表达式 11218 1、execution() 固定的,不能不写 11319 2、public 可以省略不写 11420 3、void出可以用*替代,表示返回值类型任意,不能省略不写 11521 4、包的简写方式 11622 5、类的写法 11723 6、方法的写法 11824 7、方法的参数 11925 --> 12026 <!-- 完整写法 --> 12127 <!-- <aop:before method="log" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 12228 <!-- public可以省略不写 --> 12329 <!-- <aop:before method="log" pointcut="execution(void com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 12430 <!-- void出用*替代 --> 12531 <!-- <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 12632 <!-- 包的写法 --> 12733 <!-- <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 12834 <!-- 包的写法 --> 12935 <!-- <aop:before method="log" pointcut="execution(* *..*.CustomerDaoImpl.save())"/> --> 13036 <!-- 类的写法 --> 13137 <!-- <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.*DaoImpl.save())"/> --> 13238 <!-- 方法写法 --> 13339 <!-- <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.CustomerDaoImpl.*save())"/> --> 13440 <!-- 方法的参数 --> 13541 <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.CustomerDaoImpl.*save(..))"/> 13642 </aop:aspect> 13743 </aop:config> 13844 13945 </beans> 140 141 1 <?xml version="1.0" encoding="UTF-8"?> 142 2 <beans xmlns="http://www.springframework.org/schema/beans" 143 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 144 4 xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" 145 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 146 6 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> 147 7 148 8 <!-- 配置客户的dao --> 149 9 <bean id="customerDao" class="com.alphajuns.demo3.CustomerDaoImpl"/> 15010 <!-- 配置切面类 --> 15111 <bean id="myAspectXml" class="com.alphajuns.demo3.MyAspectXml"/> 15212 <!-- 配置AOP --> 15313 <aop:config> 15414 <!-- 配置切面类:切入点+通知类型 --> 15515 <aop:aspect ref="myAspectXml"> 15616 <!-- 配置前置通知 --> 15717 <!-- <aop:before method="log" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 15818 <!-- 配置后置通知 --> 15919 <!-- <aop:after method="log" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 16020 <!-- 环绕通知 --> 16121 <aop:around method="around" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> 16222 </aop:aspect> 16323 </aop:config> 16424 16525 </beans>