AOP 的使用分为三步走:
@Aspect
@Pointcut
、@Before
~~~AOP
模式;@EnableAspectJAutoProxy
使用 aop
相关的注解必须先导入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.2.RELEASE</version>
</dependency>
下面以一个计算器的例子来介绍 AOP 的基本使用:
这是一个简单的计算器类,为了能够演示异常,所以创建了一个有除法的方法。
public class MathCalculator {
/**
* 除法
*
* @param i 被除数
* @param j 除数
* @return 返回运算结果
*/
public int div(int i, int j) {
return i / j;
}
}
我们想通过 AOP 实现记录除法运行的日志信息,所以新建一个 Log 类。
@Aspect
public class LogAspect {
/**
* 抽取出来的切入点表达式
*/
@Pointcut("execution(* top.wsuo.aop.MathCalculator.*(..))")
public void pointCut() {
}
/**
* 前置通知
*
* @param joinPoint 连接点
*/
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName() + "运行...参数列表是:{" + Arrays.toString(joinPoint.getArgs()) + "}");
}
/**
* 后置通知
*
* @param joinPoint 连接点
*/
@After("pointCut()")
public void logEnd(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName() + "结束...");
}
/**
* 返回通知
*
* @param joinPoint 连接点
* @param result 执行结果
*/
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println(joinPoint.getSignature().getName() + "正常返回...运行结果:{" + result + "}");
}
/**
* 异常通知
*
* @param joinPoint 连接点
* @param exception 异常信息
*/
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(JoinPoint joinPoint, Exception exception) {
System.out.println(joinPoint.getSignature().getName() + "出现异常...异常信息:{" + exception.getMessage() + "}");
}
}
最后在配置类上开启注解版 AOP,同时注册组件到容器中。
@Configuration
// Spring 中有很多 EnableXXX 代表开启某一项功能: 取代了配置
@EnableAspectJAutoProxy
public class MainConfigOfAOP {
@Bean
public MathCalculator mathCalculator() {
return new MathCalculator();
}
@Bean
public LogAspect logAspect() {
return new LogAspect();
}
}
测试及运行结果。
@Test
public void test12() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
MathCalculator calculator = context.getBean(MathCalculator.class);
System.out.println(calculator.div(4, 2));
}
整个 AOP 要想起作用,必须加上 @EnableAspectJAutoProxy
注解,这个注解的作用是什么呢?
点进去该注解:
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
看到了要导入一个 AspectJAutoProxyRegistrar
类组件,它继承自一个接口 ImportBeanDefinitionRegistrar
,这个接口我们之前讲过,他是添加自定义组件的接口,在这里:https://blog.csdn.net/weixin_43941364/article/details/107243459。
这说明 @EnableAspectJAutoProxy
注解的作用就是给容器中添加组件, 追踪 AspectJAutoProxyRegistrar
类的方法,发现有这么一段代码:
这段代码的作用就是先看一下容器中有没有
public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
"org.springframework.aop.config.internalAutoProxyCreator";
internalAutoProxyCreator
这个类,同时我们看到在调用上述方法的时候,传入了一个类型:
该类型是 AspectJAwareAdvisorAutoProxyCreator
实体类,看一下该类的继承结构。
可以看到该类实现了一个接口,就是 BeanPostProcessor
接口,他是一个 后置处理器 。这个接口是 Bean 生命周期相关的接口。
所以我们要重点分析一下该类的执行顺序,接下来 打断点调试 之前举的计算器的例子。
从容器启动开始分析:
@Test
public void test12() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
MathCalculator calculator = context.getBean(MathCalculator.class);
System.out.println(calculator.div(4, 2));
}
首先传入配置类,创建 IOC 容器;然后注册配置类,调用 refresh
方法刷新容器;
使用 registerBeanPostProcessors(beanFactory)
注册 Bean 的后置处理器,来拦截 Bean 的创建
先获取 IOC 容器中已经定义了的需要创建对象的所有 BeanPostProcessor
给容器中加别的 BeanPostProcessor
优先注册实现了 PriorityOrdered
接口的 BeanPostProcessor
再注册实现了 Ordered
接口的 BeanPostProcessor
之后注册没实现 Ordered
接口的 BeanPostProcessor
最后 registerBeanPostProcessors
执行,注册 BeanPostProcessor
,实际上就是创建 BeanPostProcessor
对象,保存在容器中。
创建 org.springframework.aop.config.internalAutoProxyCreator
的 BeanPostProcessor
,它的类型是 AnnotationAwareAspectJAutoProxyCreator
。
首先创建 Bean 的实例 instanceWrapper = createBeanInstance(beanName, mbd, args)
然后给属性赋值 populateBean(beanName, mbd, instanceWrapper)
最后初始化 Bean exposedObject = initializeBean(beanName, exposedObject, mbd)
invokeAwareMethods(beanName, bean)
初始化 Aware 接口的方法回调;applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
执行后置处理器的 postProcessBeforeInitialization
方法;invokeInitMethods(beanName, wrappedBean, mbd)
执行自定义初始化方法;applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
执行后置处理器的 postProcessAfterInitialization
方法;到此为止 AnnotationAwareAspectJAutoProxyCreator
类型的 BeanPostProcessor 创建成功;
创建完 BeanPostProcessor
对象之后,注册到 beanFactory
中
registerBeanPostProcessors(beanFactory, internalPostProcessors)
注册方法的实现:
for (BeanPostProcessor postProcessor : postProcessors) {
beanFactory.addBeanPostProcessor(postProcessor);
}
到此为止 AnnotationAwareAspectJAutoProxyCreator
就算是创建成功了,而它作为一个后置处理器,肯定有作用,下面分析一下他作为后置处理器做了什么事情。
注意 AnnotationAwareAspectJAutoProxyCreator
是 InstantiationAwareBeanPostProcessor
类型的后置处理器。
finishBeanFactoryInitialization(beanFactory)
完成 BeanFactory 的初始化工作
getBean、doGetBean、getSingleton
;先从缓存中获取当前 Bean,如果能获取到,说明 Bean 是之前创建过的,直接使用,否则再创建;
先从缓存中检查有没有这个 Bean
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
如果 他等于 null,才会继续执行下面的方法
sharedInstance = getSingleton(beanName, () -> {
只要创建好的 Bean 都会被缓存起来,这也是 Spring 保证单实例 Bean 的实现原理。
createBean
创建 Bean:AnnotationAwareAspectJAutoProxyCreator
会在任何 Bean 创建完成之前先尝试返回 Bean 的实例,其实就是拦截;
BeanPostProcessor 是在对象创建Bean完成初始化前后调用的,而 InstantiationAwareBeanPostProcessor 是在创建Bean实例之前先尝试用后置处理器返回对象的。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse)
,这句话的意思是希望后置处理器返回一个代理对象,如果能返回代理对象就使用,如果不能就继续;这个方法的实现就是拿到所有后置处理器,如果是 InstantiationAwareBeanPostProcessor,就执行 postProcessBeforeInstantiation 方法
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
Object beanInstance = doCreateBean(beanName, mbdToUse, args)
,真正的去创建一个 Bean,和之前 3.6
的流程是一样的。所以
AnnotationAwareAspectJAutoProxyCreator
会在任何 Bean 创建完成之前先尝试返回 Bean 的实例,因为他实现了InstantiationAwareBeanPostProcessor
接口,这个接口有两个方法,一个是postProcessBeforeInstantiation
,另一个是postProcessAfterInstantiation
,这两个方法是在 Bean 创建完成前后执行的,而BeanPostProcessor
接口的两个方法是在创建完成并且初始化前后调用的。
在每一个 Bean 创建之前调用 postProcessBeforeInstantiation
方法,在这一步找出需要增强的 Bean;
判断当前 Bean 是否在 advisedBeans
中(它保存了所有需要增强的 Bean )
判断当前 Bean 是否是基础类型 isInfrastructureClass
或者是切面。
判断是否该跳过 shouldSkip
:源码如下
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
首先获取所有候选的增强器,增强器就是切面里面的通知方法;
0 = {InstantiationModelAwarePointcutAdvisorImpl@2180} "InstantiationModelAwarePointcutAdvisor: expression [pointCut()]; advice method [public void top.wsuo.aop.LogAspect.logStart(org.aspectj.lang.JoinPoint)]; perClauseKind=SINGLETON"
1 = {InstantiationModelAwarePointcutAdvisorImpl@2181} "InstantiationModelAwarePointcutAdvisor: expression [pointCut()]; advice method [public void top.wsuo.aop.LogAspect.logEnd(org.aspectj.lang.JoinPoint)]; perClauseKind=SINGLETON"
2 = {InstantiationModelAwarePointcutAdvisorImpl@2182} "InstantiationModelAwarePointcutAdvisor: expression [pointCut()]; advice method [public void top.wsuo.aop.LogAspect.logReturn(org.aspectj.lang.JoinPoint,java.lang.Object)]; perClauseKind=SINGLETON"
3 = {InstantiationModelAwarePointcutAdvisorImpl@2183} "InstantiationModelAwarePointcutAdvisor: expression [pointCut()]; advice method [public void top.wsuo.aop.LogAspect.logException(org.aspectj.lang.JoinPoint,java.lang.Exception)]; perClauseKind=SINGLETON"
可以看到这就是我们的那几个通知方法。
只不过他把这些通知方法包装成为了一个 List<Advisor> candidateAdvisors
集合,每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor
。
这段代码的逻辑就是判断每一个增强器是否是 AspectJPointcutAdvisor
类型的,如果是返回 true ,如果不是就返回 false ;
在 Bean 创建之后调用 postProcessAfterInitialization
方法,在这一步增强需要增强的 Bean:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
在 wrapIfNecessary
方法中,获取当前 Bean 的所有增强器(通知方法),判断是否需要包装(增强)。
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
那么他是 怎么找的增强器 呢 ?我们继续查看方法调用。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors();
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
这一步是找到候选的所有增强器,即哪些通知方法是需要切入当前 Bean 方法的。
然后下面的方法 findAdvisorsThatCanApply
是获取到能在当前 Bean 使用的增强器,它使用了 canApply
方法判断。
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
最后是给这些增强器排序。
advisedBeans
中;获取所有的增强器(通知方法)
保存到 proxyFactory
中;
proxyFactory.getProxy(getProxyClassLoader());
创建代理对象,Spring 自动决定使用哪一种动态代理
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(
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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
可以看到有两种自动代理,分别是 JdkDynamicAopProxy
和 ObjenesisCglibAopProxy
。
此时创建完成之后,代理对象为 top.wsuo.aop.MathCalculator,通过 Spring 增强的类型。
4.所以最后 wrapIfNecessary(bean, beanName, cacheKey)
方法就是返回了当前组件使用的 cglib
增强了的代理对象。
5.以后容器中获取到的就是这个组件的 代理对象 ,执行目标方法的时候,代理对象就会执行通知方法的流程。
我们在测试方法上面打断点,看看除法运行的时候都有啥:
观察到此时的对象已经是 cglib 代理之后的对象了,这个对象中保存了详细信息,比如所有的增强器和目标对象。
下面进入到 org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor
的 intercept
方法中。
本来是想执行目标的,但是代理之后就要先被拦截一下。
然后根据 ProxyFactory
对象获取将要执行的目标拦截器链;
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)
拦截器链是如何获取的?
主要是在 getInterceptorsAndDynamicInterceptionAdvice
方法中。
List<Object> interceptorList = new ArrayList<>(advisors.length);
这 5 个包括一个默认的 org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
和 4 个增强器。Interceptor
。for (Advisor advisor : advisors)
registry.getInterceptors(advisor)
List<MethodInterceptor>
:
如果本来就是 MethodInterceptor
,则直接加到集合中;
如果不是,则使用 AdvisorAdapter
适配器转为 MethodInterceptor
。
怎么转的呢,其实这里就是强转然后包装了一下,源码如下。
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
可以看到这就是 最终通知 。
转化完成返回 MethodInterceptor
数组。
MethodInterceptor
的机制控制执行顺序。如果没有拦截器链,直接执行目标方法
retVal = methodProxy.invoke(target, argsToUse);
如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建一个 CglibMethodInvocation
对象,并调用它的 proceed
方法。
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
拦截器链的触发过程,触发方法就是 proceed
,所以只需要分析一下这个方法即可。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
invoke
方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行。这里的返回值是还是拦截器,传入的是这个拦截器本身,每次调用都会减少一个长度,并且改变当前的拦截器,所以执行顺序是栈式的结构。
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
首先执行到 interceptorOrInterceptionAdvice
的实现类 ExposeInvocationInterceptor
,就是方法本身;
跟进去执行的是 org.springframework.aop.interceptor.ExposeInvocationInterceptor
的 invoke
方法,该方法的实现如下:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
这一块代码的核心业务是放在 finally
中的,所以肯定会执行,下面接着跟进去 proceed
方法:
这个时候再次来到 proceed 方法,此时的下标变为 0,执行到 AspectJAfterThrowingAdvice
,即异常通知;
跟进去执行 org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
的 invoke
方法,该方法的实现如下:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
catch (Throwable ex) {
if (shouldInvokeOnThrowing(ex)) {
invokeAdviceMethod(getJoinPointMatch(), null, ex);
}
throw ex;
}
}
注意到这里有异常的捕捉,所以异常发生时是在这里处理的,没有异常则不会执行,继续跟进 proceed
方法。
这个时候再次来到 proceed 方法,此时的下标变为 1,执行到 AfterReturningAdviceInterceptor
,即返回(最终)通知;
跟进去执行 org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor
的 invoke
方法,该方法的实现如下:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
就是先执行其他的,然后执行返回通知的内容,继续跟进 proceed
方法。
这个时候再次来到 proceed 方法,此时的下标变为 2,执行到 AspectJAfterAdvice
,即后置通知;
跟进去执行 org.springframework.aop.aspectj.AspectJAfterAdvice
的 invoke
方法,该方法的实现如下:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
就是先执行后面的前置通知,然后执行后置通知的内容,继续跟进 proceed
方法。
这个时候再次来到 proceed 方法,此时的下标变为 3,执行到 MethodBeforeAdviceInterceptor
,即前置通知;
跟进去执行 org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor
的 invoke
方法,该方法的实现如下:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
这块代码是先执行自己的业务,再往下传递,我们继续跟进 proceed
方法:
这个时候再次来到 proceed 方法,此时的下标变为 4,还是执行 MethodBeforeAdviceInterceptor
但是现在已经开始回溯了,因为方法都已经入栈了,此时执行 前置通知 中的方法,控制台输出如下:
然后执行 后置通知 :
执行 返回通知 :
最后所有的通知执行完毕,由于没有异常产生,所以没有执行异常通知:
本文地址:https://blog.csdn.net/weixin_43941364/article/details/107322784
如对本文有疑问, 点击进行留言回复!!
[JVM学习之路]一、初识JVM,了解其结构、模型及生命周期
【JAVA并发编程】LinkedBlockingQueue原理
网友评论