Spring AOP
AOP概述
AOP为Aspect Oriented Programming
的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 是一种新的模块化机制,用来描述分散在对象,类,或函数中的横切关注点,分离关注点使解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑代码中不在含义针对特定领域的代码调用,业务逻辑同特定领域问题的关系通过切面封装,维护,这样原本分散在整个应用程序中的变动可以很好地管理起来。
用人话说就是,通过切面完成特定逻辑(事务,入参出参日志等) 可以和业务逻辑(CRUD)抽离开,便于维护
- Advice 通知:定义在连接点做什么,为切面增强提供植入接口。描述Spring AOP围绕方法调而注入的切面行为
- Pointcut切入点:切点决定Advice通知应该作用在哪个连接点,也就是通过
Poincut
来定义需要增强的方法集合,这些集合可以按照一定规则来完成,这种情况下,Pointcut
意味着标识方法(比如事务切面定义了事务注解方法上生效)切入点是一些列织入逻辑代码的连接点集合 - Advisor通知器:整合Advice 和 Pointcut,定义应该在哪个关注点使用什么通知进行增强。
AOP重要接口和编程体验
我们先抛弃Spring框架,利用Spring AOP
中存在的工具实现AOP增强。
基于Advice
Advice
接口的实现有AfterAdvice后置通知
,Beforeadvice前置通知
,MethodInterceptor方法拦截器
可以看做是环绕通知。
服务类
1
2
3
4
5public class AopService {
public void doSomething(){
System.out.println("service doSomething");
}
}自定义advice 环绕通知
1
2
3
4
5
6
7
8
9
10public class MyAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("my advice before");
// 方法执行
Object res = invocation.proceed();
System.out.println("my advice after");
return res;
}
}客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class Client {
public static void main(String[] args) {
// 代理工程
ProxyFactory proxyFactory = new ProxyFactory();
// 目标对象
AopService aopService = new AopService();
// 设置需要代理的对象
proxyFactory.setTarget(aopService);
proxyFactory.addAdvice(new MyAdvice());
// 生成代理对象
AopService proxy = (AopService)proxyFactory.getProxy();
proxy.doSomething();
}
}
/*
my advice before
service doSomething
my advice after
*/
基于Advisor
1 | public class Client { |
只有名称匹配的方法才生效。
Advisor
接口具备方法Advice getAdvice()
来获取通知。PointcutAdvisor
实现了Advisor
并且新增方法Pointcut getPointcut()
来获取切入点的定义。Pointcut
接口定义了两个方法ClassFilter getClassFilter()
,MethodMatcher getMethodMatcher()
分别是对类和方法的筛选,来决定Advise是不是应该作用于当前类。
ProxyFactory 和 ProxyFactoryBean
ProxyFactory
类图
TargetSource
用于获取 AOP 调用的当前“目标”
getTargetClass
可以获取被代理对象的类型,getTarget
可以获取被代理对象,HotSwappableTargetSource
中的swap方法可以替换掉代理对象,Spring Aop
常用的是SingletonTargetSource
它持有了原始的被代理对象。
ProxyFactory是如何创建代理对象的
在ProxyFactory
类中有个getProxy()
方法
1 | public Object getProxy() { |
createAopProxy()
方法通过getAopProxyFactory()
来创建代理对象,一般默认是AopProxyFactory
的实现类DefaultAopProxyFactory
。
DefaultAopProxyFactory # createAopProxy
1 |
|
这里生成的AopProxy
才是负责生成代理对象的,其中spring内置了两种策略——JDK动态代理和CGLIB动态代理。
只有设置了需要代理目标类,或者说没有指定代理的接口,且代理目标类不是接口,不是已经被JDK动态代理后的类,那么才会使用CGLIB进行动态代理。
AopProxy
AopPorxy
有2个实现类CglibAopProxy
和JdkDynamicAopProxy
JdkDynamicAopProxy
JdkDynamicAopProxy
还实现了InvocationHandler
生成代理对象
getProxy()
1
2
3
4
5
6
7
8
9
public Object getProxy( ClassLoader classLoader){
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); //this只自己
}invoke()
首先是对equals
,hashCode
的处理,目标对象声明了让目标对象执行,反之调用JdkDynamicAopProxy
对应的方法,其次是如果配置中设置了暴露代理对象,那么将其放入到AopContext
中的ThreadLocal
中1
2
3
4
5if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}然后获取当前对象的拦截器链,如果拦截器链为空 那么直接反射调用目标对象的方法。如果存在拦截器链那么new 一个
ReflectiveMethodInvocation
利用反射依次执行。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}Spring只支持方法拦截器
MethodInterceptor
进行代理增强,对于Advise都会适配成MethodInterceptor
,Spring采用适配器模式,具体的适配器如下1
2
3
4
5
6
7public interface AdvisorAdapter {
//支持什么Advise被适配
boolean supportsAdvice(Advice advice);
//适配
MethodInterceptor getInterceptor(Advisor advisor);
}Spring遍历每一个Advise责任链模式依次找到
AdvisorAdapter
然后调用适配方法得到一个MethodInterceptor
,下面是适配成的MethodInterceptor
。
CGLIB动态代理
设置CallBack
首先new
一个Enhancer
设置父类为被代理对象的类型,这里会将Aop
的逻辑转变为一个DynamicAdvisedInterceptor
,equals和hashCode方法也有对应的callBack
注意这里的MethodInterceptor
是org.springframework.cglib.proxy.MethodInterceptor
,其中的intercept 方法的逻辑和JDK动态代理的invoke类似,都是链式调用。
ProxyFactoryBean
ProxyFactoryBean
创建代理对象的逻辑和ProxyFactory
类似,但是ProxyFactoryBean
是一个FactoryBean
,我们可以利用这一点在bean初始化的时候生成一个代理对象
使用ProxyFactoryBean
或其他IOC类来创建AOP代理的最重要好处之一是,这意味着advice和切入点也可以由IoC管理。ProxyFactoryBean
的getObject()
将创建一个包装目标对象的AOP代理
1 |
|
getSingletonInstance()
1 | private synchronized Object getSingletonInstance() { |
Spring AOP 和IOC是如何结合起来的
我们在使用Spring AOP
的时候会在启动类上加一个@EnableAspectJAutoProxy
注解,这个注解@Import(AspectJAutoProxyRegistrar.class)
导入了AspectJAutoProxyRegistrar
,这个类实现了ImportBeanDefinitionRegistrar
,Spring容器会调用其registerBeanDefinitions
方法为我们注入BeanDefinition
,后续会实例化一个AnnotationAwareAspectJAutoProxyCreator
类型的bean,它是Spring IOC和AOP结合的关键
1 | class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { |
AnnotationAwareAspectJAutoProxyCreator
类结构图
这其中最为关键的必然是AnnotationAwareAspectJAutoProxyCreator
,它是一个BeanPostProcessor
,从而在Spring 回调postProcessAfterInitialization
对bean进行代理的增强,并且它实现了SmartInstantiationAwareBeanPostProcessor
Spring容器创建bean的时候如果出现了循环依赖那么会调用到getEarlyBeanReference
,在这个方法里面同样也会进行aop
的增强
AbstractAutoProxyCreator
实现了SmartInstantiationAwareBeanPostProcessor
是一个bean后置处理器,使用 AOP 代理包装每个符合条件的 bean,在调用 bean 本身之前委托给指定的拦截器,AOP代理发生的地方。AbstractAdvisorAutoProxyCreator
为了每一个Bean找到合适的
Advisor
并且进行,如果Advisor
标注了@Order
或者说实现了Ordered
接口那么会进行排序。AspectJAwareAdvisorAutoProxyCreator
AbstractAdvisorAutoProxyCreator
子类,对一个切面中的多个Advisor进行优先级排序AnnotationAwareAspectJAutoProxyCreator
AspectJAwareAdvisorAutoProxyCreator
的子类,会将容器中标注了@AspectJ
注解的类解析成Advisor
(整合Advice
和Pointcut
,定义应该使用哪个通知器并在哪个关注点使用它)
AbstractAutoProxyCreator
的Aop增强
getEarlyBeanReference
在Spring进行bean的实例化后, 在bean创建完成后会向三级缓存提添加单例工厂,而此时三级缓存的出现特意为了解决有AOP代理时循环依赖问题的,getEarlyBeanReference
会对bean使用SmartInstantiationAwareBeanPostProcessor
的实现类进行处理。
1 | // AbstractAutowireCapableBeanFactory |
而在bean实例化、填充属性后,初始bean时会调用BeanPostProcessor
的postProcessAfterInitialization
的方法,而对此时的AOP来说就进行了重要的一环wrapIfNecessary
1 | public Object postProcessAfterInitialization( Object bean, String beanName){ |
对于有AOP的对象,会在实例化后,就直接创建出代理对象并返回,但是这时,代理对象是没有初始化过的,是不可用的,所以Spring在创建代理时,持有了目标对象bean的引用
wrapIfNecessary
1 | protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { |
其中shouldSkip
被AspectJAwareAdvisorAutoProxyCreator
重写,如果Advisor
是AspectJPointcutAdvisor
并且切面名称和bean名称相同那么会跳过,这应该是我们标注@Aspect
的时候需要保证这个类会被Spring加入到容器,所有需要加@Componet
1 |
找到所有合适的advice 和advisor
1 | protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { |
findCandidateAdvisors
方法会找到容器中所以的Advisor
类型的bean,AnnotationAwareAspectJAutoProxyCreator
进行了重写,它还会把所以标注了@Aspect
注解的bean中的增强逻辑封装成Advisor
findAdvisorsThatCanApply
这个方法内部逻辑基本上就是调用PointcutAdvisor
获取类过滤器,方法匹配器进行匹配。sortAdvisors
这里默认是通过@Order注解,或者Ordered接口进行排序,但是AspectJAwareAdvisorAutoProxyCreator
进行了重写,因为它需要对同一个标注@Aspect切面里面的前置后置等进行排序
创建代理对象
1 | protected Object createProxy(Class<?> beanClass, String beanName, |
这里其实和我们上面的Aop
编程体验中基于Advisor
类似,最后都是AopProxy
创建代理对象
AnnotationAwareAspectJAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator
会将@Aspect注解类解析成Advisor
,下面我们重点看下AnnotationAwareAspectJAutoProxyCreator
是怎么将@Aspect注解类解析成Advisor
的
1 |
|
BeanFactoryAspectJAdvisorsBuilder
,它会遍历所有bean,并调用isAspect
方法
1 |
|
然后调用ReflectiveAspectJAdvisorFactory
的getAdvisors
方法将其适配成多个Advisor
,会遍历每一个没有标注@Pointcut
的方法,然后获取@Around, @Before, @After, @AfterReturning, @AfterThrowing
(如果没有那么直接返回)然后获取value
中的内容包装成AspectJExpressionPointcut
(AspectJ表达式pointcut),然后包装成InstantiationModelAwarePointcutAdvisorImpl
在这个类中会把对应注解的方法封装成对应的AbstractAspectJAdvice
的子类
调用对应方法依旧采用反射,其子类在合适的实际进行调用。
参考: