AOP简单使用
配置类
package jane.config;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* AOP:动态代理
* 指在程序运行期间动态将某段代码切入到指定方法指定位置进行运行的编程方式
* 步骤是:
* 1.导入AOP模块,spring-aspects
* 2.定义一个业务逻辑类(MathCalculator),在业务逻辑运行的时候将日志打印
* (方法之前,方法运行之后,方法异常....)
* 3.定义一个日志切面类(LogAspect):切面类里面的方法需要
* 动态感知MathCalculator的方法(div)运行到哪里而执行
* 通知方法:
* 前置通知(@Before):longStart:目标方法(div)运行之前运行
* 后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论正常结束还是异常结束)
* 返回通知(@AfterReturning):logReturn:在目标方法正常返回之后运行
* 异常通知(@AfterThrowing):logException:在目标方法出现异常之后运行
* 环绕通知(@Around):动态代理,是手动推进目标方法的运行(joinPoint.procced())
* 4.给切面类的目标方法标注何时何地运行(通知注解)
* 5.将切面类和业务逻辑类(就是目标方法所在的类)都加入到容器中
* 6.必须告诉spring哪个类是切面类,就是给切面类加上一个注解
* @Aspect:告诉spring当前类是一个切面类
* 7.按照之前,我们写xml配置文件,会写一个
* <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
* 来开启基于注解版的切面功能
* 现在我们变成给配置类中加上@EnableAspectJAutoProxy来开启基于注解的aop模式
* 在spring中有很多的@Enablexxx的注解,就是用来开启某一项的功能
*/
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import jane.aop.LogAspect;
import jane.aop.MathCalculator;
@EnableAspectJAutoProxy
@Configuration
public class MyConfigOfAOP
{
@Bean
public MathCalculator mathCalculator()
{
return new MathCalculator();
}
@Bean
public LogAspect logAspect()
{
return new LogAspect();
}
}
业务逻辑类
package jane.aop;
public class MathCalculator
{
public int div(int a,int b)
{
System.out.println("div正在进行中");
return a/b;
}
}
日志类
package jane.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class LogAspect
{
/*
* 抽取公共的切入点表达式
* 1.本类引用,,直接方法名加()就行
* 其他的切面引用,就需要写全类名,比如jane.aop.LogAspect.pointCut()
*/
@Pointcut("execution(public int jane.aop.MathCalculator.*(..))")
public void pointCut(){};
/*
* @Before在目标方法之前切入,后面的是切入点表达式(指定哪个方法切入)
*/
//不抽取的例子
//如果有多个参数,JoinPoint joinPoint一定要出现在参数列表的第一位
@Before("execution(public int jane.aop.MathCalculator.*(..))")
public void logStart(JoinPoint joinPoint)
{
Object[] args = joinPoint.getArgs();
System.out.println(joinPoint.getSignature().getName()+"计算运行,参数是"+Arrays.toString(args));
}
//内部引用的例子
@After("pointCut()")
public void logEnd()
{
System.out.println("计算结束");
}
//外部引用的例子
@AfterReturning(value = "jane.aop.LogAspect.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);
}
}
测试类
@org.junit.Test
public void testAOP()
{
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAOP.class);
MathCalculator calculator = applicationContext.getBean(MathCalculator.class);
calculator.div(1, 1);
}
AOP原理
主要是探究这个注解给容器注册了什么组件,这个组件什么时候工作,和它的功能是什么
/*
* AOP原理:
* 我们首要认识的是@EnableAspectJAutoProxy,要有这个注解AOP才起作用
* 进入到@EnableAspectJAutoProxy,发现里面最主要的是
* @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar组件
*
* 进入到AspectJAutoProxyRegistrar里面发现它是实现了ImportBeanDefinitionRegistrar接口
* 这样就可以自定义给容器中注册bean
* 下面来看它注册了什么bean
* 注册了一个AnnotationAwareAspectJAutoProxyCreator组件
*/
进入AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
(由名知意:如果需要的话,注册一个AspectJAnnotationAutoProxyCreator组件)
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
判断注册信息中是否包含internalAutoProxyCreator
这个bean
我们这里没有,直接到后面,创建一个AnnotationAwareAspectJAutoProxyCreator
这样的bean
然后加入一些信息,
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
最后将这个bean的信息注册到容器里面,id是internalAutoProxyCreator
原码探究AnnotationAwareAspectJAutoProxyCreator的创建
/*
* 探究AnnotationAwareAspectJAutoProxyCreator
* 继承关系:
* AnnotationAwareAspectJAutoProxyCreator
* 父类:AspectJAwareAdvisorAutoProxyCreator
* 父类:AbstractAdvisorAutoProxyCreator
* 父类:AbstractAutoProxyCreator
* implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
* 现在我们关注这两个接口:一个是后置处理器,另外一个能装配BeanFactory
*
* 现在我们想断点分析它的工作原理,但是这么多的继承关系,我们应该去哪里找?
* 应该从最开始的父类来找,子类对应的方法要么重写,要么调用父类的
* 看那些方法是要打断点的
* 先是AbstractAutoProxyCreator开始
* AbstractAutoProxyCreator.setBeanFactory()
* AbstractAutoProxyCreator中有写后置处理器逻辑的方法
*
* 到AbstractAdvisorAutoProxyCreator类
* 重写setBeanFactory()方法,里面调用了initBeanFactory()方法
*
* 到AspectJAwareAdvisorAutoProxyCreator类
* 没发现
*
* 到AnnotationAwareAspectJAutoProxyCreator
* 重写了initBeanFactory()
*
* 然后debug调试:
* 流程:
* 1)传入配置类,创建IOC容器
* 2)注册配置类,调用refresh();方法刷新容器
* 3)如何刷新:
* // Register bean processors that intercept bean creation.
* registerBeanPostProcessors(beanFactory);
* 注册bean的后置处理器来方便拦截bean的创建
* registerBeanPostProcessors方法里面调用registerBeanPostProcessors
* registerBeanPostProcessors里面步骤:
* 1.获取IOC容器已经定义好的需要创建对象的所有的BeanPostProcessor
* 比如 @EnableAspectJAutoProxy注解就会定义一个AnnotationAwareAspectJAutoProxyCreator后置处理器
* 只是还没创建对象
* 对应的代码:String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
* 2.给容器添加一些别的BeanPostProcessor
* 对应代码:beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
* 3.优先注册实现了PriorityOrdered接口的BeanPostProcessor
* 4.再给容器中注册实现了Ordered接口的BeanPostProcessor
* 5.注册没实现优先级接口的BeanPostProcessor
* 3-5步的原码解释:
* // Separate between BeanPostProcessors that implement PriorityOrdered,
* // Ordered, and the rest.
*
* // First, register the BeanPostProcessors that implement PriorityOrdered.
* // Next, register the BeanPostProcessors that implement Ordered.
* // Now, register all regular BeanPostProcessors.
* 6.怎么注册呢?
* 先getBean-->doGetBean-->getSingleton-->singletonFactory.getObject();
* 一直想获取,但是获取不到,所以直接跳到异常里面创建,调用this.singletonObjects.get(beanName);
* 跳到createBean(beanName, mbd, args);创建对象
*
* 注册BeanPostProcessor就是创建BeanPostProcessor对象,并且保存在容器中
* 就是创建internalAutoProxyCreator的后置处理器,类型是AnnotationAwareAspectJAutoProxyCreator
* 创建bean的过程:
* 1 创建Bean实例
* 2 populateBean:给bean的各种属性赋值
* 3 initializeBean:初始化bean:
* 过程:
* 1.invokeAwareMethods(),处理Aware接口的方法回调
* 2.applyBeanPostProcessorsBeforeInitialization():后置处理器的PostProcessorsBeforeInitialization
* 3.invokeInitMethods():执行自定义的初始化方法
* 4.applyBeanPostProcessorsAfterInitialization():后置处理器的PostProcessorsAfterInitialization
* 4 BeanPostProcessor(以AnnotationAwareAspectJAutoProxyCreator为例)创建成功
* 7.把BeanPostProcessor注册到BeanFactory中:
* 调用:beanFactory.addBeanPostProcessor(postProcessor);
* =====上面就是创建和注册AnnotationAwareAspectJAutoProxyCreator的全过程====
*/
原码探究AnnotationAwareAspectJAutoProxyCreator的作用
package jane.config;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.Interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import jane.aop.LogAspect;
import jane.aop.MathCalculator;
/*
* AOP原理:
* 我们首要认识的是@EnableAspectJAutoProxy,要有这个注解AOP才起作用
* 进入到@EnableAspectJAutoProxy,发现里面最主要的是
* @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar组件
*
* 进入到AspectJAutoProxyRegistrar里面发现它是实现了ImportBeanDefinitionRegistrar接口
* 这样就可以自定义给容器中注册bean
* 下面来看它注册了什么bean
* 注册了一个AnnotationAwareAspectJAutoProxyCreator组件
*
* 探究AnnotationAwareAspectJAutoProxyCreator
* 继承关系:
* AnnotationAwareAspectJAutoProxyCreator
* 父类:AspectJAwareAdvisorAutoProxyCreator
* 父类:AbstractAdvisorAutoProxyCreator
* 父类:AbstractAutoProxyCreator
* implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
* 现在我们关注这两个接口:一个是后置处理器,另外一个能装配BeanFactory
*
* 现在我们想断点分析它的工作原理,但是这么多的继承关系,我们应该去哪里找?
* 应该从最开始的父类来找,子类对应的方法要么重写,要么调用父类的
* 看那些方法是要打断点的
* 先是AbstractAutoProxyCreator开始
* AbstractAutoProxyCreator.setBeanFactory()
* AbstractAutoProxyCreator中有写后置处理器逻辑的方法
*
* 到AbstractAdvisorAutoProxyCreator类
* 重写setBeanFactory()方法,里面调用了initBeanFactory()方法
*
* 到AspectJAwareAdvisorAutoProxyCreator类
* 没发现
*
* 到AnnotationAwareAspectJAutoProxyCreator
* 重写了initBeanFactory()
*
* 然后debug调试:
* 流程:
* 1)传入配置类,创建IOC容器
* 2)注册配置类,调用refresh();方法刷新容器
* 3)如何刷新:
* // Register bean processors that intercept bean creation.
* registerBeanPostProcessors(beanFactory);
* 注册bean的后置处理器来方便拦截bean的创建
* registerBeanPostProcessors方法里面调用registerBeanPostProcessors
* registerBeanPostProcessors里面步骤:
* 1.获取IOC容器已经定义好的需要创建对象的所有的BeanPostProcessor
* 比如 @EnableAspectJAutoProxy注解就会定义一个AnnotationAwareAspectJAutoProxyCreator后置处理器
* 只是还没创建对象
* 对应的代码:String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
* 2.给容器添加一些别的BeanPostProcessor
* 对应代码:beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
* 3.优先注册实现了PriorityOrdered接口的BeanPostProcessor
* 4.再给容器中注册实现了Ordered接口的BeanPostProcessor
* 5.注册没实现优先级接口的BeanPostProcessor
* 3-5步的原码解释:
* // Separate between BeanPostProcessors that implement PriorityOrdered,
* // Ordered, and the rest.
*
* // First, register the BeanPostProcessors that implement PriorityOrdered.
* // Next, register the BeanPostProcessors that implement Ordered.
* // Now, register all regular BeanPostProcessors.
* 6.怎么注册呢?
* 先getBean-->doGetBean-->getSingleton-->singletonFactory.getObject();
* 一直想获取,但是获取不到,所以直接跳到异常里面创建,调用this.singletonObjects.get(beanName);
* 跳到createBean(beanName, mbd, args);创建对象
*
* 注册BeanPostProcessor就是创建BeanPostProcessor对象,并且保存在容器中
* 就是创建internalAutoProxyCreator的后置处理器,类型是AnnotationAwareAspectJAutoProxyCreator
* 创建bean的过程:
* 1 创建Bean实例
* 2 populateBean:给bean的各种属性赋值
* 3 initializeBean:初始化bean:
* 过程:
* 1.invokeAwareMethods(),处理Aware接口的方法回调
* 2.applyBeanPostProcessorsBeforeInitialization():后置处理器的PostProcessorsBeforeInitialization
* 3.invokeInitMethods():执行自定义的初始化方法
* 4.applyBeanPostProcessorsAfterInitialization():后置处理器的PostProcessorsAfterInitialization
* 4 BeanPostProcessor(以AnnotationAwareAspectJAutoProxyCreator为例)创建成功
* 7.把BeanPostProcessor注册到BeanFactory中:
* 调用:beanFactory.addBeanPostProcessor(postProcessor);
* =====上面就是创建和注册AnnotationAwareAspectJAutoProxyCreator的全过程==
* 4)// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
完成BeanFactory的初始化工作,就是创建剩下的单实例bean
1.遍历获取容器中所有的Bean,依次创建对象getBean(beanName);
getBean(beanName);->doGetBean()->getSingleton()->singletonFactory.getObject();->createBean
2.注意第一步的createBean,它是有if条件的
首先是从缓存中获取当前bean,如果能获取到证明bean是之前被创建过的,直接使用
如果获取不到进入else语句,创建bean
这样就保证单实例的bean只会被创建一次,创建好的bean会被缓存起来
3.createBean();创建bean
[AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,
因为它是InstantiationAwareBeanPostProcessor类,会调用postProcessBeforeInstantiation()方法]
AnnotationAwareAspectJAutoProxyCreator会在任何bean创建之前先尝试返回bean的实例
[BeanPostProcessor是在bean对象创建完成初始化前后调用的]
[InstantiationAwareBeanPostProcessor是在创建Bean实例之前尝试用后置处理器返回对象的]
1.resolveBeforeInstantiation(beanName, mbdToUse);解析resolveBeforeInstantiation(beanName, mbdToUse);
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
希望后置处理器在此返回一个代理对象,如果能返回就使用,如果不能就第二步
2.doCreateBean(beanName, mbdToUse, args);这个是真正创建bean
和上面的创建AnnotationAwareAspectJAutoProxyCreator的bean的过程一样
接下来看postProcessBeforeInstantiation干了什么
AnnotationAwareAspectJAutoProxyCreator[InstantiationAwareBeanPostProcessor类]的作用:
1)每一个bean创建之前,都会调用postProcessBeforeInstantiation();
1)判断当前bean是否在advisedBean中(这里面保存了所有需要增强的bean,就是需要切面来切的bean)
对应代码:if (this.advisedBeans.containsKey(cacheKey))
2)判断当前bean是否是基础类型的Advice ||Pointcut||Advisor ||AopInfrastructureBean
或者是否是切面(@Aspect),我们现在是返回false,
对应代码:isInfrastructureClass(beanClass)
3)下一个判断是shouldSkip(beanClass, beanName),是否需要跳过
1)首先是获取候选的增强器,增强器就是切面里面的通知方法,
现在将他们包装成List<Advisor> candidateAdvisors,
每一个增强器的类型是:InstantiationModelAwarePointcutAdvisor
判断每一个增强器是否是AspectJPointcutAdvisor类型的
然后我们发现上面返回的都是false
2)创建对应的对象
3)调用postProcessAfterInitialization
return wrapIfNecessary(bean, beanName, cacheKey);//如果需要的话就包装
1)// Create proxy if we have advice.创建代理的对象,如果我们有advice
1.找到候选的所有的增强器,就是找那些通知方法是需要切入当前bean方法的
List<Advisor> candidateAdvisors = findCandidateAdvisors();
2.获取能在当前bean使用的增强器
对应的代码是:
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
3.给增强器排序:eligibleAdvisors = sortAdvisors(eligibleAdvisors);
2)保存当前bean在advisedBean中:this.advisedBeans.put(cacheKey, Boolean.TRUE);
3)如果当前bean需要增强,创建当前bean的代理对象
1.获取所有增强器(通知方法)
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
2.保存到proxyFactory
proxyFactory.addAdvisors(advisors);
3.创建代理对象:(spring自己决定创建哪一种对象,好像有接口的就创建JDK的,不然创建cglib的)
new JdkDynamicAopProxy(config);
new ObjenesisCglibAopProxy(config);
4)给容器中返回当前组件使用cglib增强了的代理对象
5)以后容器中获取到的就是这个组件的代理对象,
执行目标方法的时候,代理对象就会执行通知方法的流程
============================================================
下面我们想看的是目标方法是如何执行的,就是这个代理对象如何执行通知方法
目标方法的执行:
容器中保存了组件的代理对象(cglib增强后的对象)
这个对象里面保存了详细信息(比如增强器,目标对象等等)
进入目标方法,
1)一直进入到CglibAopProxy.intercept();方法,这是拦截目标方法的执行
2)根据ProxyFactory对象获取将要执行的目标方法的拦截去链;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
3)如果没有拦截器链,直接执行目标方法
4)如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个CglibMethodInvocation对象
并且调用Object retVal = CglibMethodInvocation.proceed();
5)拦截器链触发的过程:
进入到CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
首先是
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
这里就是currentInterceptorIndex来标记,如果没有拦截器,直接执行目标方法,
或者拦截器的索引和拦截器数组大小-1大小一样(指的是最后一个拦截器也执行完了)执行目标方法
链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行
完成返回以后再来执行,拦截器链的机制,保证通知方法与目标方法的执行顺序
顺序可以看后面的顺序图
根据上面的流程,可以看出最重要的是哪个拦截器链是个什么的,感觉就是通知方法的执行
下面我们就来看下它干嘛的:
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
1)首先List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
保存了所有拦截器,五个拦截器,一个是默认的ExposeInvocationInterceptor和四个增强器
2)遍历所有的增强器,将其转化为Interceptor;
Interceptor[] interceptors = registry.getInterceptors(advisor);
3)将增强器转化成List<MethodInterceptor>
如果是MethodInterceptor,就直接加入到集合中
如果不是,使用AdvisorAdapter适配器将增强器转化为MethodInterceptor
转换完成后返回MethodInterceptor数组
所以总结来说,拦截器链就是每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制触发使用)
总结:
1)@EnableAspectJAutoProxy开启AOP功能
2)@EnableAspectJAutoProxy会给容器中注册一个组件AnnotationAwareAspectJAutoProxyCreator
3)AnnotationAwareAspectJAutoProxyCreator是一个后置处理器
4)容器的创建流程:
1)registerBeanPostProcessors()注册后置处理器,会创建AnnotationAwareAspectJAutoProxyCreator
2)finishBeanFactoryInitialization()初始化剩下的单实例bean
1)创建业务逻辑组件和切面组件
2)AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程
3)组件创建完成后,判断组件是否需要增强
是:将切面的通知方法包装成增强器(Advisor),给业务逻辑组件创建一个代理对象
5)执行目标方法:
1)代理对象执行目标方法
2)CglibAopProxy.intercept();
1)得到目标方法的拦截器链(这是增强器包装成拦截器MethodInterceptor)
2)利用拦截器的链式机制,依次进入每一个拦截器进行执行
效果就是:前置通知->目标方法->后置通知->正常就返回通知,出现异常就异常通知
*/
@EnableAspectJAutoProxy
@Configuration
public class MyConfigOfAOP
{
@Bean
public MathCalculator mathCalculator()
{
return new MathCalculator();
}
@Bean
public LogAspect logAspect()
{
return new LogAspect();
}
}