Spring AOP

什么是 AOP

AOP (Aspect-Oriented Programming,面向切面编程) 能够将那些与业务无关,却被业务共同调用的逻辑 (如:事务处理、日志管理、权限控制等) 封装起来,降低模块间的耦合程度,利于扩展和维护

AOP 是基于 动态代理 实现,说白了就是在原有代码基础上进行一定的包装,如:在方法执行前、方法返回后、方法抛出异常后等地方进行一定的拦截处理或者叫增强处理,和动态代理的思想一模一样

更具体点,AOP 的实现是有一个代理类,实际执行的其实是生成的代理类对象,并非真正的目标类对象,由代理类对象真正的调用目标类对象的方法,在调用前后可以进行一些特殊处理,称之为增强处理

在动态代理中介绍有两种实现方式,JDK 动态代理适用于实现了接口的目标类,CGLIB 动态代理适用于没有实现接口的目标类,所以 AOP 也是如此,因为它底层的实现就是动态代理

AOP 应用场景

日志记录:日志可以用于系统审计、调试、性能检测等,在方法执行前后或异常抛出时自动记录日志,而不需要在每个方法中都编写记录日志的逻辑

事务管理:在方法执行前开始事务,在方法执行后根据执行结果提交或回滚事务

安全控制:在方法执行前进行权限检查,验证用户是否具有执行该方法的权限

性能监控:在方法执行前后计算方法执行时间,统计方法调用次数,用于性能监测和优化

缓存管理:在方法执行前检查缓存中是否存在所需数据,如果存在直接返回,不需要执行方法

异常处理:在方法抛出异常后统一处理,进行异常的记录、转换、重试等操作

总结:这些应用场景无非就是将多个方法中重复的代码逻辑抽离出来,放到代理类中统一处理,这样就可以不需要在每个方法中都编写相同逻辑的代码,减少冗余,增强扩展性

AOP Demo

下面给五个从不同角度实现 Spring AOP 的小 Demo,都是基于 XML 配置文件实现滴~~

先给出这五个例子中使用到的依赖包:

再给出这五个例子都会用到的类:

Advice 实现

先给出 XML 配置文件spring_1.2_advice.xml

然后给出测试类:

输出结果如下:

从这个例子中可以看出 Advice 实现的两点局限性:

Advisor 实现

为了解决 Advice 实现的第一点局限性,增加了 Advisor 实现,它可以匹配到方法,也就是可以只拦截指定方法,而非目标类的所有方法

先给出 XML 配置文件spring_1.2_advisor.xml

然后给出测试类:

输出结果如下:

从结果中可以看出,它只拦截了createUser()方法,且只进行了方法执行前的处理,因为我们在 XML 配置文件中只配置了logArgsAdvice

Interceptor 实现

先给出 XML 配置文件spring_1.2_interceptor.xml

然后实现MethodInterceptor接口:

最后给出测试类:

输出结果如下:

可以看到当我们调用目标对象的方法时,其实调用的是invoke()方法

BeanNameAutoProxyCreator 实现

上面三种方式都是基于 ProxyFactoryBean 实现,需要在 XML 配置文件中为每个目标类设置一个ProxyFactoryBean,十分的不灵活;而 BeanNameAutoProxyCreator 实现就对这一局限进行了改进

先给出 XML 配置文件spring_1.2_BeanNameAutoProxy.xml

然后给出测试类:

输出结果如下:

从结果中可以看出,BeanNameAutoProxyCreator 实现和 Advice 实现很像,拦截粒度都是类级别,不同之处在于它会自动生成符合正则表达式规则的所有代理类

DefaultAdvisorAutoProxyCreator 实现

DefaultAdvisorAutoProxyCreator 实现可以匹配到方法,也就是可以只拦截指定方法,而非目标类的所有方法

先给出 XML 配置文件spring_1.2_DefaultAdvisorAutoProxy.xml

然后给出测试类:

输出结果如下:

从结果中可以看出,只拦截了create*()方法,并没有拦截query*()方法

Spring AOP 源码剖析

下面主要分析 BeanNameAutoProxyCreator 实现的源码,关注点在于 Spring 如何为目标对象自动创建代理对象~~

在 XML 配置文件中会配置BeanNameAutoProxyCreator,它继承AbstractAutoProxyCreator

AbstractAutoProxyCreator类实现了BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法

在 Spring IoC 容器启动自动注入对象时,会在初始化阶段前执行postProcessBeforeInitialization方法,初始化后执行postProcessAfterInitialization方法。关于 Spring IoC 相关内容可见 Spring IoC

为目标对象自动创建代理对象就在postProcessAfterInitialization方法中完成,这也是 AOP 基于后处理器实现的原因

先来看看AbstractAutoProxyCreator类:

继续看ProxyFactory类:

最后看DefaultAopProxyFactory类:

总结:Spring AOP 的实现基于 动态代理。如果目标类实现了接口,Spring 会使用 JDK 动态代理;如果目标类没有实现接口,Spring 会使用 CGLIB 动态代理

这里给出个人的一个猜想,不知对错,有懂的可以在首页联系作者微信!!

在写 Spring 项目时,为什么建议在 Service 层先定义接口,然后再写对应实现类??

我认为有一个原因可能是,目标类实现了接口就可以使用 JDK 动态代理,而 JDK 动态代理效率相比于 CGLIB 动态代理更加优秀,而且随着 JDK 版本的升级,这个优势更加明显

参考文章