一、AOP基本使用
三步:
- 将业务逻辑组件和切面类都加入到容器中,告诉Spring哪个是切面类(@Aspect)
- 在切面类上的每一个通知方法上标注通知注解,告诉Spring何时(@Before、@After、@Around……)何地运行(切入点表达式)
- 开启基于注解的aop模式,@EnableAspectJAutoProxy
定义业务逻辑类、切面类:
public class MathCalculator {
public int div(int i, int j){
return i / j;
}
}
@Aspect //需要标注此注解,spring才知道这是切面类
public class LogAspects {
@Pointcut("execution(public int org.example.MathCalculator.*(..))")
public void pointCut(){};
//@Before(value = "public int org.example.MathCalculator.div(int, int)")
//@Before("public int org.example.MathCalculator.*(..)")
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println("除法方法:" + joinPoint.getSignature().getName() + "开始运行,参数列表是:" + Arrays.asList(args));
}
@After("pointCut()")
public void logEnd(){
System.out.println("除法方法结束运行");
}
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(Object result){
System.out.println("除法正常运行并返回结果:" + result);
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(Exception exception){
System.out.println("除法异常运行,异常信息:" + exception);
}
}
将业务逻辑类和切面类注册到spring容器中(这里我用的方式是配置类方式):
@EnableAspectJAutoProxy
@Configuration
public class AopConfiguration {
// 业务逻辑类加入容器中
@Bean
public MathCalculator mathCalculator(){
return new MathCalculator();
}
// 切面类加入容器中
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
测试:
public class AopTest {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AopConfiguration.class);
//MathCalculator mathCalculator = new MathCalculator();不是自己创建对象,而是从spring拿对象,这样切面才会生效
MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
mathCalculator.div(4,2); //测试目标方法是否被增强
applicationContext.close();
}
}
二、AOP原理
总结:
1)、@EnableAspectJAutoProxy 开启AOP功能
2)、@EnableAspectJAutoProxy 会给容器中注册一个组件:AnnotationAwareAspectJAutoProxyCreator
3)、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器;
4)、容器的创建流程:
1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator后置处理器
2)、 finishBeanFactoryInitialization () 初始化剩下的单实例bean
1)、如创建业务逻辑类组件、切面类组件
2)、AnnotationAwareAspectJAutoProxyCreator会拦截以上组件的创建过程:组件创建完之后,判断组件是否需要增强?
是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib)
5)、执行目标方法
1)代理对象执行目标方法
1)CglibAopProxy.intercept()来拦截目标方法
2)得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
3)利用拦截器的链式机制,依次进入每一个拦截器进行执行
4)效果:
正常执行:前置通知->目标方法->后置通知->返回通知
出现异常:前置通知->目标方法->后置通知->异常通知
分析AOP原理时,我们从这个注解开始分析:
启示,如果想研究xx原理,就可以从@EnableXXX注解分析,分析是否给容器注入了什么组件,我们只需要搞清楚这个组件的作用是什么,就可以分析出原理。
一、从@EnableAspectJAutoProxy注解开始分析
1、@EnableAspectJAutoProxy
- AspectJAutoProxyRegistrar.class
- 跟进registerAspectJAnnotationAutoProxyCreatorIfNecessary方法中,一路跟进,会进入以下方法:
也就是说@EnableAspectJAutoProxy注解的作用就是给容器中注入bean定义,为后边往容器中注入组件做准备。
internalAutoProxyCreator(组件名),AnnotationAwareAspectJAutoProxyCreator(组件类型)
所以接下来我们就是探究AnnotationAwareAspectJAutoProxyCreator组件的是功能是什么,何时工作?
- AnnotationAwareAspectJAutoProxyCreator.class
- AspectJAwareAdvisorAutoProxyCreator.class
- AbstractAdvisorAutoProxyCreator.class
- AbstractAutoProxyCreator.class
可以得出结论,AbstractAutoProxyCreator是一个后置处理器,还实现了aware接口,以下是重写的方法:
在AbstractAutoProxyCreator类中重写了后置处理器的两个方法:
在AbstractAdvisorAutoProxyCreator类中重写了aware接口的方法:
- SmartInstantiationAwareBeanPostProcessor相较于普通的后置处理器,额外还有以下两个需要重写的方法XXXInstantiation,这两个方法的执行时机也与XXXInitialization方法执行时机(bean初始化前后执行)不同。我们之后会分析。
以上是与AOP有关的组件的大致介绍。接下来我们启动AOP的测试demo,从容器创建、创建单实例bean、调用目标方法来分析AOP是怎么起作用的。
这里启动的demo就是本文介绍AOP基本使用的那个demo。
二、启动容器
1)registerBeanPostProcessors():注册所有后置处理器到容器中
1、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor的名字。其中一个就是AbstractAutoProxyCreator后置处理器的,这个定义就是在通过@EnableAspectJAutoProxy创建的
2、注册后置处理器
2.1、优先注册实现了PriorityOrdered接口的BeanPostProcessor;
2.2、再给容器中注册实现了Ordered接口的BeanPostProcessor;这里注册的就是我们的AbstractAutoProxyCreator。因为AbstractAutoProxyCreator后置处理器实现了@order接口。
2.3、注册没实现优先级接口的BeanPostProcessor;
后置处理器注册到容器中的过程:
1、创建bean实例:createBeanInstance()
2、属性赋值:populateBean()
3、初始化bean:initializeBean(),进入initializeBean()
3.1.invokeAwareMethods() -> 如果该bean实现aware接口,则执行重写aware接口的方法
3.2.applyBeanPostProcessorsBeforeInitialization()->执行该bean实现后置处理器接口,重写的postProcessBeforeInitialization()
3.3.invokeInitMethods()->执行初始化方法
3.4.applyBeanPostProcessorsAfterInitialization()->执行该bean实现后置处理器接口,重写的postProcessAfterInitialization()
3、将后置处理器注册到beanFactory中。
以上,AbstractAutoProxyCreator后置处理器已经注册到容器中了,这个后置处理器会在注册单实例bean(MathCalculator)时起作用。
2)finishBeanFactoryInitialization:注册所有单实例bean到容器中
1、先尝试用后置处理器获取代理对象【只有当后置处理是InstantiationAwareBeanPostProcessor类型时才有效】
resolveBeforeInstantiation()
由于AbstractAutoProxyCreator后置处理器就是InstantiationAwareBeanPostProcessor类型的,因此就会执行AbstractAutoProxyCreator里重写的postProcessBeforeInstantiation方法。【我们主要关心MathCalculator、LogAspects】
1.1、applyBeanPostProcessorsBeforeInstantiation()->postProcessBeforeInstantiation()
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = this.getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
return null;
}
}
===========================
最后直接返回null
1.2、applyBeanPostProcessorsAfterInitialization()->postProcessAfterInitialization()
因为postProcessBeforeInstantiation返回null,因此这里这个方法不执行。
2、真正创建bean实例
doCreateBean()
createBeanInstance()
populateBean()
initializeBean()
applyBeanPostProcessorsAfterInitialization()->postProcessAfterInitialization()
wrapIfNecessary():如果需要则包装成代理对象
1)获取当前bean的且已排好序的增强方法:Object[] specificInterceptors
- 获取所有增强器(通知方法)
- 找哪些通知方法是需要切入当前bean方法的
- 给增强器排序
2)保存当前bean到advisedBeans中:this.advisedBeans.put(cacheKey, Boolean.TRUE);
3)创建当前bean的代理对象:this.createProxy()
-
获取所有增强器(通知方法)
-
保存到proxyFactory
-
创建代理对象:Spring自动决定
- JdkDynamicAopProxy(config)->jdk动态代理
- ObjenesisCglibAopProxy(config)->cglib的动态代理
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (NativeDetector.inNativeImage() || !config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) { return new JdkDynamicAopProxy(config); } else { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation."); } else { return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) && !ClassUtils.isLambdaClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config)); } } }
4)最后返回代理对象给容器:return proxy
注意,以下情况则返回的是原始对象:
- 核心基础设施类:
Advisor
,Advice
,Pointcut
的实现 - 切面类:被
@Aspect
注解的类(通过shouldSkip()
显式排除) - 无匹配的
Advisor
的类
解释:核心基础设施类是怎么得到的?
假设有以下切面:
@Aspect
public class SecurityAspect {
@Around("@annotation(RequireAuth)")
public Object checkAuth(ProceedingJoinPoint pjp) {
// 权限检查逻辑
}
}
Spring会创建以下基础设施对象:
// 1. 创建Pointcut
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("@annotation(RequireAuth)");
// 2. 创建Advice
Advice advice = new MethodInterceptor() {
public Object invoke(MethodInvocation mi) {
// 包装checkAuth方法的逻辑
}
};
// 3. 创建Advisor
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
那么,Advice与Advisor的区别在哪?
-
Advisor:封装了
Pointcut
(切点) +Advice
(增强)。定义"在哪里增强"和"如何增强"的完整规则 -
Advice:只包含增强行为。定义"具体增强什么"
- @Around:AspectJAroundAdvice
- @Before:AspectJMethodBeforeAdvice
- @After:AspectJAfterAdvice
- @AfterReturning:AspectJAfterReturningAdvice
- @AfterThrowing:AspectJAfterThrowingAdvice
@Aspect
public class LogAspect {
// 👇 被拆解为 Pointcut + Advice
@Before("execution(* com.service.*.*(..))")
public void logBefore() {
System.out.println("Before method");
}
}
转换过程:
@Before
注解 → 创建AspectJExpressionPointcut
(Pointcut)logBefore()
方法 → 创建MethodBeforeAdviceInterceptor
(Advice)- 组合两者 → 创建
InstantiationModelAwarePointcutAdvisor
(Advisor)
三、调用目标方法
前面我们分析完启动容器后,容器会生成代理对象。当代理对象调用目标方法时,就会走增强的逻辑,具体是怎样的过程,我们现在来分析。
👆容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx)
1)CglibAopProxy.intercept()
1、根据ProxyFactory对象获取将要执行的目标方法拦截器链:List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
getInterceptorsAndDynamicInterceptionAdvice()
-
List interceptorList = new ArrayList(advisors.length);interceptorList保存所有拦截器5:一个默认的ExposeInvocationInterceptor 和4个增强器;
-
遍历所有的增强器,将其转为Interceptor;
Interceptor[] interceptors = registry.getInterceptors(advisor)
interceptorList.addAll(Arrays.asList(interceptors));
-
将增强器转为List; 如果是MethodInterceptor,直接加入到集合中。如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor; 转换完成返回MethodInterceptor数组。(统一转换为
MethodInterceptor
目的是标准化执行接口,实现调用链的通用处理机制)MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException { List<MethodInterceptor> interceptors = new ArrayList(3); Advice advice = advisor.getAdvice(); if (advice instanceof MethodInterceptor) { // 如果增强器是MethodInterceptor类型,那么直接添加 interceptors.add((MethodInterceptor)advice); } Iterator var4 = this.adapters.iterator(); while(var4.hasNext()) { AdvisorAdapter adapter = (AdvisorAdapter)var4.next(); if (adapter.supportsAdvice(advice)) { // 否则使用适配器进行转换 interceptors.add(adapter.getInterceptor(advisor)); } } if (interceptors.isEmpty()) { throw new UnknownAdviceTypeException(advisor.getAdvice()); } else { return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[0]); } } =================================== 1、public class AspectJAfterReturningAdvice extends AbstractAspectJAdvice implements AfterReturningAdvice, AfterAdvice, Serializable {} --->未实现MethodInterceptor接口,因此需要使用对应适配器转换。 1.1、转换方法:adapter.getInterceptor(advisor) : public MethodInterceptor getInterceptor(Advisor advisor) { AfterReturningAdvice advice = (AfterReturningAdvice)advisor.getAdvice(); return new AfterReturningAdviceInterceptor(advice); } 也就是说,将AspectJAfterReturningAdvice-->AfterReturningAdviceInterceptor。 1.2、还有AspectJMethodBeforeAdvice-->MethodBeforeAdviceInterceptor 2、其他的advice本身就实现了MethodInterceptor接口,因此无需转变: public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable {} public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {} public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable {} ================================= public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable { private final AfterReturningAdvice advice; public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } @Nullable public Object invoke(MethodInvocation mi) throws Throwable { Object retVal = mi.proceed(); this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; } } ================================= @FunctionalInterface public interface MethodInterceptor extends Interceptor { @Nullable Object invoke(@Nonnull MethodInvocation invocation) throws Throwable; } 可以看到,MethodInterceptor接口里只包含一个invoke抽象方法,将advice都转换为MethodInterceptor类型,就是为了之后统一调用invoke方法。
-
得到的拦截器链如下:
2、如果没有拦截器链,直接执行目标方法。
3、如果有拦截器链,把需要执行的目标对象、目标方法、拦截器链等信息传入创建一个CglibMethodInvocation对象,
并调用 Object retVal = mi.proceed();
retVal = (new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();
有了拦截器链后,我们现在来分析proceed方法,探究是怎么个拦截法。
递归过程就是:
1、依次按顺序0-4遍历每个拦截器,然后执行invoke方法。
2、0-3号的拦截器中的invoke方法中,第一步都是直接递归调用proceed方法。
3、直到遍历到4号拦截器,也就是MethodBeforeAdviceInterceptor拦截器。
4、接着再一次进入proceed方法时,由于满足出递归的条件判断,因此执行目标方法。
5、目标方法执行完则返回到上一个拦截器的invoke方法中。3号拦截器就是AspectJAfterAdvice。
6、接着继续返回到上一层拦截器的invoke方法中。2号拦截器是AfterReturningAdviceInterceptor。执行对应的增强方法。(只有没有异常才会执行。)
7、接着继续返回到上一层拦截器的invoke方法中。1号拦截器是AspectJAfterThrowingAdvice。执行对应的增强方法。(有异常才会执行。)
以上,就是整个拦截器调用过程。