前言
Spring中的AOP用于在程序运行期间,不修改源码对已有方法进行增强,其优势在于减少重复代码,提高开发效率,方便维护,比如说通常用在日志、事务等场景下,AOP的实现是基于动态代理技术,本章会先从AOP的概念出发, 到AOP的使用,最终涉及到相关的源码解析部分。
一、简单的aop术语
通知(advice):就是想要的方法,比如之前提到的事务、日志等等。
连接点(JoinPoint):spring中允许使用通知的地方,通俗点讲就是那些需要被增强的方法。比如下面例子中的save、update、delete方法。
切入点(Pointcut):比如一个类中有几个方法(也可以叫连接点),但是并不是每一个方法都需使用通知,那些想要被增强的方法就叫做切入点。
切面(Aspect):就是通知和切入点的结合。
引入(introduction):允许我们向现有的类添加新方法属性。就是把切面(也就是新方法属性:通知定义的)用到目标类中。
目标(target):引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。
代理(proxy):aop的实现机制。
织入(waving):把切面应用到目标对象来创建新的代理对象的过程。
二、几个入门案例与简单的aop使用方式
基于XML的AOP配置
首先定义这么一个接口
1 | package hdu.service; |
实现类
1 | package hdu.service.impl; |
要执行的公共代码,也就是要增强的部分
1 | package hdu.utils; |
在xml文件中我们可以这么配置:
1 |
|
除了前置通知,我们还能在其他地方进行增强
1 |
|
另外可以配置环绕通知
1 |
|
此时logger类中添加如下的方法
1 | /** |
基于AOP的注解配置
首先xml中需要先配置
1 |
|
logger类中只需要改为
1 | package hdu.utils; |
三、源码解析
回忆一下IOC部分的代码,在位置:AbstractAutowireCapableBeanFactory.java中
1 | protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { |
在initializeBean方法中涉及到了BeanPostProcessor的回调,也就是
1 | protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { |
AOP的关键就是在后置方法中进行实现的,下面详细来看。
由继承关系,BeanPostProcessor(接口)中的postProcessAfterInitialization方法被AbstractAutoProxyCreator中进行重写,如下。
1 | public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { |
进入类AbstractAutoProxyCreator中
1 | protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { |

这边可以很清除的看到通知printLog和我之前在XML文件中定义的切入点表达式。
继续来看创建代理的过程,仍然在类AbstractAutoProxyCreator中
1 | protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) { |
我们知道,如果被代理的目标类实现了一个或多个自定义的接口,那么就会使用 JDK 动态代理,如果没有实现任何接口,会使用 CGLIB 实现代理,如果设置了 proxy-target-class=”true”,那么都会使用 CGLIB。JDK 动态代理基于接口,所以只有接口中的方法会被增强,而 CGLIB 基于类继承,需要注意就是如果方法使用了 final 修饰,或者是 private 方法,是不能被增强的。
在此之前就会用以上原则判断是用jdk代理还是cglib代理,代码就省略掉了。
1 | public Object getProxy(@Nullable ClassLoader classLoader) { |
来看jdk动态代理中重写的invoke方法。
1 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
另外,代理的方法还有CGLib,这个是基于字节码ASM框架来实现的,这边暂时不讨论了。