什么是AOP?
在运行时,动态的将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。AOP即面向切面编程。使用切面编程,可以将一些系统性的代码提取出来,独立实现,与核心业务代码剥离,比如权限管理、事务管理、日志记录等等。AOP是spring提供的关键特性之一。
AOP的实现原理
AOP分为静态AOP和动态AOP,静态AOP是由AspectJ实现的AOP,动态AOP是指将切面代码进行动态织入实现的AOP,Spring的AOP为动态AOP。实现的技术为:JDK提供的动态代理技术和CGLIB(动态字节码增强技术)两种。尽管实现技术不一样,但 都是基于代理模式 , 都是生成一个代理对象 。
JDK动态代理实现:查看我前面的博文-动态代理,我们看到该方式有一个要求, 被代理的对象必须实现接口,而且只有接口中的方法才能被代理 。也就是jdk代理方式是基于接口的一种代理方式。
CGLIB:字节码生成技术实现AOP,其实就是继承被代理对象,然后Override需要被代理的方法,在覆盖该方法时,自然是可以插入我们自己的代码的。
因为需要Override被代理对象的方法,所以自然CGLIB技术实现AOP时,就 必须要求需要被代理的方法不能是final方法,因为final方法不能被子类覆盖 。
我们使用CGLIB实现上面的例子:
package net.aazj.aop; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CGProxy implements MethodInterceptor{ private Object target; // 被代理对象 public CGProxy(Object target){ this.target = target; } public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy proxy) throws Throwable { System.out.println("do sth before...."); Object result = proxy.invokeSuper(arg0, arg2); System.out.println("do sth after...."); return result; } public Object getProxyObject() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 设置父类 // 设置回调 enhancer.setCallback(this); // 在调用父类方法时,回调 this.intercept() // 创建代理对象 return enhancer.create(); } }
public class CGProxyTest { public static void main(String[] args){ Object proxyedObject = new UserServiceImpl(); // 被代理的对象 CGProxy cgProxy = new CGProxy(proxyedObject); UserService proxyObject = (UserService) cgProxy.getProxyObject(); proxyObject.getUser(1); proxyObject.addUser(new User()); } }
观察spring中AOP相关源码可知,如果被代理对象实现了接口,那么就使用JDK的动态代理技术,反之则使用CGLIB来实现AOP,所以 Spring默认是使用JDK的动态代理技术实现AOP的 。
AOP在spring中的使用
Spring中AOP的配置一般有两种方法,一种是使用 <aop:config> 标签在xml中进行配置,一种是使用注解以及@Aspect风格的配置。
1) 基于<aop:config>的AOP配置
<tx:advice id="transactionAdvice" transaction-manager="transactionManager"?> <tx:attributes > <tx:method name="add*" propagation="REQUIRED" /> <tx:method name="append*" propagation="REQUIRED" /> <tx:method name="insert*" propagation="REQUIRED" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="update*" propagation="REQUIRED" /> <tx:method name="get*" propagation="SUPPORTS" /> <tx:method name="find*" propagation="SUPPORTS" /> <tx:method name="load*" propagation="SUPPORTS" /> <tx:method name="search*" propagation="SUPPORTS" /> <tx:method name="*" propagation="SUPPORTS" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="transactionPointcut" expression="execution(* net.aazj.service..*Impl.*(..))" /> <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice" /> </aop:config>
<bean id="aspectBean" class="net.aazj.aop.DataSourceInterceptor"/> <aop:config> <aop:aspect id="dataSourceAspect" ref="aspectBean"> <aop:pointcut id="dataSourcePoint" expression="execution(public * net.aazj.service..*.getUser(..))" /> <aop:pointcut expression="" id=""/> <aop:before method="before" pointcut-ref="dataSourcePoint"/> <aop:after method=""/> <aop:around method=""/> </aop:aspect> <aop:aspect></aop:aspect> </aop:config>
<aop:aspect> 配置一个切面;
<aop:pointcut>配置一个切点,基于切点表达式;
<aop:before>,<aop:after>,<aop:around>是定义不同类型的advise. a
spectBean 是切面的处理bean
2) 基于注解和@Aspect风格的AOP配置