Spring的AOP
AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,
却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,
便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。
Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;
而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。
也可以使用AspectJ,Spring AOP已经集成了AspectJ,AspectJ是Java生态系统中最完整的AOP框架了。
AOP核心概念
- 切面(
aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象 - 横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
- 连接点(
joinpoint):被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。 - 切入点(
pointcut):对连接点进行拦截的定义 - 通知(
advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。 - 目标对象:代理的目标对象
- 织入(
weave):将切面应用到目标对象并导致代理对象创建的过程 - 引入(
introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。
AOP代理方式
Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由
AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,
则使用JDK动态代理技术,否则使用Cglib来生成代理。
JDK动态接口代理JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
CGLib动态代理CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库, 可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。- 和
JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。
AOP实现
@Aspect
public class TransactionDemo {
@Pointcut(value="execution(* com.yangxin.core.service.*.*.*(..))")
public void point(){
}
@Before(value="point()")
public void before(){
System.out.println("transaction begin");
}
@AfterReturning(value = "point()")
public void after(){
System.out.println("transaction commit");
}
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("transaction begin");
joinPoint.proceed();
System.out.println("transaction commit");
}
}
AspectJ
Spring AOP和AspectJ AOP
Spring AOP是属于运行时增强,而AspectJ是编译时增强。
Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。
Spring AOP已经集成了AspectJ,AspectJ算得上是Java生态系统中最完整的AOP框架。
AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。
如果我们的切面比较少,那么两者性能差异不大。当切面太多的话,最好选择AspectJ,它比Spring AOP快很多。
AspectJ定义的通知类型
Before(前置通知):⽬标对象的方法调用之前触发After(后置通知):⽬标对象的方法调用之后触发AfterReturning(返回通知):⽬标对象的方法调用完成,在返回结果值之后触发AfterThrowing(异常通知) :⽬标对象的方法运行中抛出或触发异常后触发。AfterReturning和AfterThrowing两者互斥。- 如果方法调用成功⽆异常,则会有返回值;如果方法抛出了异常,则不会有返回值。
Around:(环绕通知)编程式控制⽬标对象的方法调用。- 环绕通知是所有通知类型中可操作范围最⼤的一种,因为它可以直接拿到⽬标对象,以及要执行的方法
- 所以环绕通知可以任意的在⽬标对象的方法调用前后搞事,甚⾄不调用⽬标对象的方法
多个切面的执行顺序
通常使用@Order注解直接定义切面顺序
// 值越小优先级越⾼
@Order(3)
@Aspect
@Component
public class AClassAspect implements Ordered {
}
实现Ordered接⼝重写getOrder方法。
@Aspect
@Component
public class AClassAspect implements Ordered {
@Override
public int getOrder() {
// 返回值越小优先级越⾼
return 1;
}
}