目标:
1. 什么是AOP, 什么是AspectJ
2. 什么是Spring AOP
3. Spring AOP注解版实现原理
4. Spring AOP切面原理解析
一. 认识AOP及其使用
详见博文1: 5.1 Spring5源码--Spring AOP源码分析一
二. AOP的特点
2.1 Spring AOP
2.1.1 他是基于动态代理实现的
Spring 提供了很多的实现AOP的方式:Spring 接口方式,schema配置方式和注解的方式.
如果使用接口方式引入AOP, 就是用JDK提供的动态代理来实现.
如果没有使用接口的方式引入. 那么就是使用CGLIB来实现的.
Spring使用接口方式实现AOP, 下面有详细说明.
研究使用接口方式实现AOP, 目的是为了更好地理解spring使用动态代理实现AOP的两种方式
2.1.2 spring3.2以后, spring-core直接把CGLIB和ASM的源码引入进来了, 所以, 后面我们就不需要再显示的引入这两个依赖了.
2.1.3 Spring AOP依赖于Spring ioc容器来管理
2.1.4 Spring AOP只能作用于bean的方法.
如果某个类, 没有注入到ioc容器中, 那么是不能被增强的
2.1.5 Spring提供了对AspectJ的支持, 但只提供了部分功能的支持: 即AspectJ的切点解析(表达式)和匹配
我们在写切面的时候,经常使用到的@Aspect, @Before, @Pointcut, @After, @AfterReturning, @AfterThrowing等就是AspectJ提供的.
我们知道AspectJ很好用, 效率也很高. 那么为什么Spring不使用AspectJ全套的东西呢? 尤其是AspectJ的静态织入.
先来看看AspectJ有哪些特点
AspectJ的特点
1. AspectJ属于静态织入. 他是通过修改代码实现的. 它的织入时机有三种
1) Compile-time weaving: 编译期织入. 例如: 类A使用AspectJ增加了一个属性. 类B引用了类A, 这个场景就需要在编译期的时候进行织入, 否则类B就没有办法编译, 会报错.
2) Post-compile weaving: 编译后织入.也就是已经生成了.class文件了, 或者是都已经达成jar包了. 这个时候, 如果我们需要增强, 就要使用到编译后织入
3) Loading-time weaving: 指的是在加载类的时候进行织入. 2. AspectJ实现了对AOP变成完全的解决方案. 他提供了很多Spring AOP所不能实现的功能
3. 由于AspectJ是在实际代码运行前就完成了织入, 因此可以认为他生成的类是没有额外运行开销的.
扩展: 这里为什么没有使用到AspectJ的静态织入呢? 因为如果引入静态织入, 需要使用AspectJ自己的解析器. AspectJ文件是以aj后缀结尾的文件, 这个文件Spring是没有办法, 因此要使用AspectJ自己的解析器进行解析. 这样就增加了Spring的成本.
2.1.6 Spring AOP和AspectJ的比较。由于,Spring AOP基于代理实现. 容器启动时会生成代理对象, 方法调用时会增加栈的深度。使得Spring AOP的性能不如AspectJ好。
三. AOP的配置方式
上面说了Spring AOP和AspectJ. 也说道了AspectJ定义了很多注解, 比如: @Aspect, @Pointcut, @Before, @After等等. 但是, 我们使用Spring AOP是使用纯java代码写的. 也就是说他完全属于Spring, 和AspectJ没有什么关系. Spring只是沿用了AspectJ中的概念. 包括AspectJ提供的jar报的注解. 但是, 并不依赖于AspectJ的功能.
我们使用的@Aspect, @Pointcut, @Before, @After等注解都是来自于AspectJ, 但是其功能的实现是纯Spring AOP自己实现的.
Spring AOP有三种配置方式.
第一种: 基于接口方式的配置. 在Spring1.2版本, 提供的是完全基于接口方式实现的
第二种: 基于schema-based配置. 在spring2.0以后使用了xml的方式来配置.
第三种: 基于注解@Aspect的方式. 这种方式是最简单, 方便的. 这里虽然叫做AspectJ, 但实际上和AspectJ一点关系也没有.
因为我们在平时工作中主要使用的是注解的方式配置AOP, 而注解的方式主要是基于第一种接口的方式实现的. 所以, 我们会重点研究第一种和第三种配置方式.
3.1 基于接口方式的配置. 在Spring1.2版本, 提供的是完全基于接口方式实现的
这种方式是最古老的方式, 但由于spring做了很好的向后兼容, 所以, 现在还是会有很多代码使用这种方式, 比如:声明式事务.
我们要了解这种配置方式还有另一个原因, 就是我们要看源码. 源码里对接口方式的配置进行了兼容处理. 同时, 看源码的入口是从接口方式的配置开始的.
那么, 在没有引入AspectJ的时候, Spring是如何实现AOP的呢? 我们来看一个例子:
1. 定义一个业务逻辑接口类
package com.lxl.www.aop.interfaceAop; /**
* 使用接口方式实现AOP, 默认通过JDK的动态代理来实现. 非接口方式, 使用的是cglib实现动态代理
*
* 业务接口类-- 计算器接口类
*
* 定义三个业务逻辑方法
*/
public interface IBaseCalculate { int add(int numA, int numB); int sub(int numA, int numB); int div(int numA, int numB); int multi(int numA, int numB); int mod(int numA, int numB); }
2.定义业务逻辑类
package com.lxl.www.aop.interfaceAop;//业务类,也是目标对象 import com.lxl.www.aop.Calculate; import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service; /**
* 业务实现类 -- 基础计算器
*/ public class BaseCalculate implements IBaseCalculate { @Override
public int add(int numA, int numB) {
System.out.println("执行目标方法: add");
return numA + numB;
} @Override
public int sub(int numA, int numB) {
System.out.println("执行目标方法: sub");
return numA - numB;
} @Override
public int multi(int numA, int numB) {
System.out.println("执行目标方法: multi");
return numA * numB;
} @Override
public int div(int numA, int numB) {
System.out.println("执行目标方法: div");
return numA / numB;
} @Override
public int mod(int numA, int numB) {
System.out.println("执行目标方法: mod"); int retVal = ((Calculate) AopContext.currentProxy()).add(numA, numB);
return retVal % numA;
}
}
3. 定义通知类
前置通知
package com.lxl.www.aop.interfaceAop; import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; /**
* 定义前置通知
* 实现MethodBeforeAdvice接口
*/
public class BaseBeforeAdvice implements MethodBeforeAdvice { /**
*
* @param method 切入的方法
* @param args 切入方法的参数
* @param target 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("===========进入beforeAdvice()============");
System.out.println("前置通知--即将进入切入点方法");
System.out.println("===========进入beforeAdvice() 结束============\n");
} }
后置通知
package com.lxl.www.aop.interfaceAop; import org.aspectj.lang.annotation.AfterReturning;
import org.springframework.aop.AfterAdvice;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; /**
* 后置通知
* 实现AfterReturningAdvice接口
*/
public class BaseAfterReturnAdvice implements AfterReturningAdvice { /**
*
* @param returnValue 切入点执行完方法的返回值,但不能修改
* @param method 切入点方法
* @param args 切入点方法的参数数组
* @param target 目标对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("\n==========进入afterReturning()===========");
System.out.println("后置通知--切入点方法执行完成");
System.out.println("==========进入afterReturning() 结束=========== ");
} }
环绕通知
package com.lxl.www.aop.interfaceAop; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; /**
* 环绕通知
* 实现MethodInterceptor接口
*/
public class BaseAroundAdvice implements MethodInterceptor { /**
* invocation :连接点
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("===========around环绕通知方法 开始===========");
// 调用目标方法之前执行的动作
System.out.println("环绕通知--调用方法之前: 执行");
// 执行完方法的返回值:调用proceed()方法,就会触发切入点方法执行
Object returnValue = invocation.proceed();
System.out.println("环绕通知--调用方法之后: 执行");
System.out.println("===========around环绕通知方法 结束===========");
return returnValue;
} }
配置类
package com.lxl.www.aop.interfaceAop; import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.annotation.Bean; /**
* 配置类
*/
public class MainConfig { /**
* 被代理的对象
* @return
*/
@Bean
public IBaseCalculate baseCalculate() {
return new BaseCalculate();
} /**
* 前置通知
* @return
*/
@Bean
public BaseBeforeAdvice baseBeforeAdvice() {
return new BaseBeforeAdvice();
} /**
* 后置通知
* @return
*/
@Bean
public BaseAfterReturnAdvice baseAfterReturnAdvice() {
return new BaseAfterReturnAdvice();
} /**
* 环绕通知
* @return
*/
@Bean
public BaseAroundAdvice baseAroundAdvice() {
return new BaseAroundAdvice();
} /**
* 使用接口方式, 一次只能给一个类增强, 如果想给多个类增强, 需要定义多个ProxyFactoryBean
* 而且, 曾增强类的粒度是到类级别的. 不能指定对某一个方法增强
* @return
*/
@Bean
public ProxyFactoryBean calculateProxy() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setInterceptorNames("baseAfterReturnAdvice", "baseBeforeAdvice", "baseAroundAdvice");
proxyFactoryBean.setTarget(baseCalculate());
return proxyFactoryBean;
} }
之前说过, AOP是依赖ioc的, 必须将其注册为bean才能实现AOP功能
方法入口
package com.lxl.www.aop.interfaceAop; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class InterfaceMainClass{ public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
IBaseCalculate calculate = context.getBean("calculateProxy", IBaseCalculate.class);
System.out.println(calculate.getClass());
calculate.add(1, 3);
} }
执行结果:
===========进入beforeAdvice()============
前置通知--即将进入切入点方法
===========进入beforeAdvice() 结束============ ===========around环绕通知方法 开始===========
环绕通知--调用方法之前: 执行
执行目标方法: add
环绕通知--调用方法之后: 执行
===========around环绕通知方法 结束=========== ==========进入afterReturning()===========
后置通知--切入点方法执行完成
==========进入afterReturning() 结束===========
通过观察, 我们发现, 执行的顺序是: 前置通知-->环绕通知的前置方法 --> 目标逻辑 --> 环绕通知的后置方法 --> 后置通知.
那么到底是先执行前置通知, 还是先执行环绕通知的前置方法呢? 这取决于配置文件的配置顺序
这里,我们将环绕通知放在最后面, 所以, 环绕通知在前置通知之后执行.
@Bean
public ProxyFactoryBean calculateProxy() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setInterceptorNames( "baseAfterReturnAdvice", "baseBeforeAdvice", "baseAroundAdvice");
proxyFactoryBean.setTarget(baseCalculate());
return proxyFactoryBean;
}
那么, 如果我们将环绕通知放在前置通知之前. 就会先执行环绕通知
@Bean
public ProxyFactoryBean calculateProxy() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setInterceptorNames("baseAroundAdvice", "baseAfterReturnAdvice", "baseBeforeAdvice");
proxyFactoryBean.setTarget(baseCalculate());
return proxyFactoryBean;
}
运行结果
===========around环绕通知方法 开始===========
环绕通知--调用方法之前: 执行
===========进入beforeAdvice()============
前置通知--即将进入切入点方法
===========进入beforeAdvice() 结束============ 执行目标方法: add ==========进入afterReturning()===========
后置通知--切入点方法执行完成
==========进入afterReturning() 结束===========
环绕通知--调用方法之后: 执行
===========around环绕通知方法 结束===========
思考: 使用ProxyFactoryBean实现AOP的方式有什么问题?
1. 通知加在类级别上, 而不是方法上. 一旦使用这种方式, 那么所有类都会被织入前置通知, 后置通知, 环绕通知. 可有时候我们可能并不想这么做
2. 每次只能指定一个类. 也就是类A要实现加日志, 那么创建一个A的ProxyFactoryBean, 类B也要实现同样逻辑的加日志. 但是需要再写一个ProxyFactoryBean.
基于以上两点原因. 我们需要对其进行改善.
下面, 我们来看看, ProxyFactoryBean是如何实现动态代理的?
ProxyFactoryBean是一个工厂bean, 我们知道工厂bean在创建类的时候调用的是getObject(). 下面看一下源码
public class ProxyFactoryBean extends ProxyCreatorSupport
implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware {
......
@Override
@Nullable
public Object getObject() throws BeansException {
/**
* 初始化通知链: 将通知放入链中
* 后面初始化的时候, 是通过责任链的方式调用这些通知链的的.
* 那么什么是责任链呢?
*/
initializeAdvisorChain();
if (isSingleton()) {
/**
* 创建动态代理
*/
return getSingletonInstance();
}
else {
if (this.targetName == null) {
logger.info("Using non-singleton proxies with singleton targets is often undesirable. " +
"Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}
......
}
发送到initializeAdvisorChain是初始化各类型的Advisor通知, 比如, 我们上面定义的通知有三类: "baseAroundAdvice", "baseAfterReturnAdvice", "baseBeforeAdvice". 这里采用的是责任链调用的方式.
然后调用getSingletonInstance()创建动态代理.
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
Class<?> targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
// Initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
/**
* 创建动态代理
*/
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}
调用getProxy(CreateAopProxy())调用代理创建动态代理. 我们这里使用接口的方式, 这里调用的是JDKDynamicAopProxy动态代理.
@Override
public Object getProxy(@Nullable 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);
}
最终, 动态代理创建, 就是在JDKDynamicAopProxy了. 通过执行Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);创建动态代理实例.
其实我们通过ctx.getBean("calculateProxy")获得的类, 就是通过JDKDynamicAopProxy创建的动态代理类.
这里也看出, 为什么每次只能给一个类创建动态代理了.
上面提到了责任链, 那么什么是责任链呢? 如下图所示:
有一条流水线. 比如生产流水线. 里面有许多道工序. 完成工序1 ,才能进行工序2, 依此类推.
流水线上的工人也是各司其职. 工人1做工序1, 工人2做工序2, 工人3做工序3.....这就是一个简单的流水线模型.
工人的责任就是完成每一道工序, 那么所有工人的责任就是完成这条流水线. 这就是工人的责任链.
其实, 我们的通知也是一类责任链. 比如, 前置通知, 环绕通知, 后置通知, 异常通知. 他们的执行都是有顺序的. 一个工序完成, 做另一个工序.各司其职. 这就是责任链.
为了能工统一调度, 我们需要保证, 所有工人使用的都是同一个抽象. 这样, 就可以通过抽象类的调用方式. 各个工人有具体的工作实现.
通知也是如此. 需要有一个抽象的通知类Advicor. 进行统一调用.
结合上面的demo, 来看一个责任链调用的demo.
上面我们定义了两个方法. 一个是前置通知BaseBeforeAdvice 实现了MethodBeforeAdvice, 另一个是环绕通知BaseAroundAdvice 实现了MethodInterceptor. 如果想把这两个通知放在一个链上. 那么他们必须实现相同的接口. 但是, 现在不同.
我们知道环绕通知, 由两部分, 一部分是环绕通知的前置通知, 一部分是环绕通知的后置通知. 所以, 我们可以将前置通知看作是环绕通知的前置通知部分.
package com.lxl.www.aop.interfaceAop.chainDemo; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice; /**
* 为什么 我们可以让MethodBeforeAdvice 前置通知继承自环绕通知的接口呢?
* 主要原因是, 环绕通知的前半部分, 就是前置通知
*/
public class BeforeAdviceInterceptor implements MethodInterceptor { // 前置通知
MethodBeforeAdvice methodBeforeAdvice; public BeforeAdviceInterceptor(MethodBeforeAdvice methodBeforeAdvice) {
this.methodBeforeAdvice = methodBeforeAdvice;
} /**
* 使用了环绕通知的前半部分. 就是一个前置通知
* @param invocation the method invocation joinpoint
* @return
* @throws Throwable
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
methodBeforeAdvice.before(invocation.getMethod(), invocation.getArguments(), invocation.getClass());
return invocation.proceed();
}
}
这段代码包装了前置通知, 让其扩展为实现MethodInterceptor接口. 这是一个扩展接口的方法.
接下来我们要创建一条链. 这条链就可以理解为流水线上各个工人. 每个工人处理一个工序. 为了能够统一调用. 所有的工人都要实现同一个接口. 责任链的定义如下:
/**
* 把一条链上的都初始化
*
* 有一条链, 这条链上都有一个父类接口 MethodInterceptor.
* 也就是说, 链上都已一种类型的工人. 但每种工人的具体实现是不同的. 不同的工人做不同的工作
*
* 这里定义了一个责任链. 连上有两个工人. 一个是前置通知. 一个是环绕通知.
* 前置通知和环绕通知实现的接口是不同的. 为了让他们能够在一条链上工作. 我们自定义了一个MethodBeforeAdviceInterceptor
* 相当于为BaseBeforeAdvice()包装了一层MethodInterceptor
*/ List<MethodInterceptor> list = new ArrayList<>();
list.add(new BeforeAdviceInterceptor(new BaseBeforeAdvice()));
list.add(new BaseAroundAdvice());
这里定义了一个责任链. 连上有两个工人. 一个是前置通知. 一个是环绕通知.
前置通知和环绕通知实现的接口是不同的. 为了让他们能够在一条链上工作. 我们自定义了一个MethodBeforeAdviceInterceptor
相当于为BaseBeforAdvice()包装了一层MethodInterceptor
接下来是责任链的调用.
/**
* 责任链调用
*/
public static class MyMethodInvocation implements MethodInvocation { // 这是责任链
protected List<MethodInterceptor> list;
// 目标类
protected final BaseCalculate target; public MyMethodInvocation(List<MethodInterceptor> list) {
this.list = list;
this.target = new BaseCalculate();
} int i = 0; public Object proceed() throws Throwable {
if (i == list.size()) {
/**
* 执行到责任链的最后一环, 执行目标方法
*/
return target.add(2, 2);
}
MethodInterceptor interceptor = list.get(i);
i++;
/**
* 执行责任链调用
* 这个调用链第一环是: 包装后的前置通知
* 调用链的第二环是: 环绕通知.
* 都执行完以后, 执行目标方法.
*/
return interceptor.invoke(this);
} @Override
public Object getThis() {
return target;
} @Override
public AccessibleObject getStaticPart() {
return null;
} @Override
public Method getMethod() {
try {
return target.getClass().getMethod("add", int.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
} @Override
public Object[] getArguments() {
return new Object[0];
}
} }
这里重点看proceed() 方法. 我们循环获取了list责任链的通知, 然后执行invoke()方法
proceed() 方法是一个链式循环. 刚开始i=0, list(0)是前置通知, 当调用到前置通知的时候, BeforeAdviceInterceptor.invoke()方法, 又调用了invocation.proceed()方法, 回到了MyMethodInvocation.proceed()方法.
然后i=1, list(1)是环绕通知, 当调用环绕通知的时候, 又调用了invocation.proceed(); 有回到了MyMethodInvocation.proceed()方法.
这是已经是list的最后一环了, 后面不会在调用invoke()方法了. 而是执行目标方法. 执行结束以后, 整个调用结束.
这就是一个调用链.
对于责任链有两点:
1. 要有一个统一的调用, 也就是一个共同的抽象类.
2. 使用循环或者递归, 完成责任链的调用
总结:
上面这种方式, 使用的是ProxyFactoryBean 代理bean工厂的方式. 他有两个限制:
/**
* 使用接口方式, 一次只能给一个类增强, 如果想给多个类增强, 需要定义多个ProxyFactoryBean
* 而且, 曾增强类的粒度是到类级别的. 不能指定对某一个方法增强
* @return
*/
@Bean
public ProxyFactoryBean calculateProxy() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setInterceptorNames("baseAfterReturnAdvice", "baseBeforeAdvice", "baseAroundAdvice");
proxyFactoryBean.setTarget(baseCalculate());
return proxyFactoryBean;
}
1. 一次只能给1个类增强, 如果给多个类增强就需要定义多个ProxyFactoryBean
2. 增强的粒度只能到类级别上, 不能指定给某个方法增强.
这样还是有一定的限制.
为了解决能够在类级别上进行增强, Spring引入了Advisor和Pointcut.
Advisor的种类有很多
RegexpMethodPointcutAdvisor 按正则匹配类
NameMatchMethodPointcutAdvisor 按方法名匹配
DefaultBeanFactoryPointcutAdvisor xml解析的Advisor
InstantiationModelAwarePointcutAdvisorImpl 注解解析的advisor(@Before @After....)
我们使用按方法名的粒度来增强, 所示使用的是NameMatchMethodPointcutAdvisor
/**
* Advisor 种类很多:
* RegexpMethodPointcutAdvisor 按正则匹配类
* NameMatchMethodPointcutAdvisor 按方法名匹配
* DefaultBeanFactoryPointcutAdvisor xml解析的Advisor
* InstantiationModelAwarePointcutAdvisorImpl 注解解析的advisor(@Before @After....)
*/
@Bean
public NameMatchMethodPointcutAdvisor aspectAdvisor() {
/**
* 通知和通知者的区别:
* 通知(Advice) :是我们的通知类 没有带切点
* 通知者(Advisor):是经过包装后的细粒度控制方式。 带了切点
*/
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
// 切入点增强的通知类型--前置通知
advisor.setAdvice(baseBeforeAdvice());
// 指定切入点方法名--div
advisor.setMappedNames("div");
return advisor;
}
这里设置了切入点需要增强的通知, 和需要切入的方法名.
这样就可以对类中的某个方法进行增强了. 我们依然需要使用ProxyFactoryBean()代理工厂类来进行增强
@Bean
public ProxyFactoryBean calculateProxy() {
ProxyFactoryBean userService = new ProxyFactoryBean();
userService.setInterceptorNames("aspectAdvisor");
userService.setTarget(baseCalculate());
return userService;
}
注意, 这里增强的拦截器名称要写刚刚定义的 NameMatchMethodPointcutAdvisor 类型的拦截器.目标类还是我们的基础业务类baseCalculate()
这只是解决了可以对指定方法进行增强. 那么, 如何能够一次对多个类增强呢? Spring又引入了ProxyCreator.
/**
* Advisor 种类很多:
* RegexpMethodPointcutAdvisor 按正则匹配类
* NameMatchMethodPointcutAdvisor 按方法名匹配
* DefaultBeanFactoryPointcutAdvisor xml解析的Advisor
* InstantiationModelAwarePointcutAdvisorImpl 注解解析的advisor(@Before @After....)
*/
@Bean
public NameMatchMethodPointcutAdvisor aspectAdvisor() {
/*
* 通知和通知者的区别:
* 通知(Advice) :是我们的通知类 没有带切点
* 通知者(Advisor):是经过包装后的细粒度控制方式。 带了切点
*/
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
// 切入点增强的通知类型--前置通知
advisor.setAdvice(baseBeforeAdvice());
// 指定切入点方法名--div
advisor.setMappedNames("div");
return advisor;
} /**
* autoProxy: BeanPostProcessor 手动指定Advice方式,
* @return
*/
@Bean
public BeanNameAutoProxyCreator autoProxyCreator() {
// 使用bean名字进行匹配
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setBeanNames("base*");
// 设置拦截链的名字
beanNameAutoProxyCreator.setInterceptorNames("aspectAdvisor");
return beanNameAutoProxyCreator;
}
如上代码, beanNameAutoProxyCreator.setBeanNames("base*"); 表示按照名字匹配以base开头的类, 对其使用的拦截器的名称是aspectAdvisor
而aspectAdvisor使用的是按照方法的细粒度进行增强. 这样就实现了, 对以base开头的类, 对其中的某一个或某几个方法进行增强. 使用的增强类是前置通知.
下面修改main方法, 看看运行效果
public class InterfaceMainClass{ public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
IBaseCalculate calculate = context.getBean("baseCalculate", IBaseCalculate.class);
calculate.add(1, 3);
System.out.println("******************");
calculate.div(1, 3);
}
}
这里面执行了两个方法, 一个是add(), 另一个是div(). 看运行结果
执行目标方法: add
******************
===========进入beforeAdvice()============
前置通知--即将进入切入点方法
===========进入beforeAdvice() 结束============
执行目标方法: div
我们看到, add方法没有被增强, 而div方法被增强了, 增加了前置通知.
以上就是使用接口方式实现AOP. 到最后增加了proxyCreator, 能够根据正则表达式匹配相关的类, 还能够为某一个指定的方法增强. 这其实就是我们现在使用的注解类型AOP的原型了.
3.2 基于注解@Aspect的方式. 这种方式是最简单, 方便的. 这里虽然叫做AspectJ, 但实际上和AspectJ一点关系也没有.
3.2.1 @Aspect切面的解析原理
上面第一种方式详细研究了接口方式AOP的实现原理. 注解方式的AOP, 最后就是将@Aspect 切面类中的@Befor, @After等注解解析成Advisor. 带有@Before类会被解析成一个Advisor, 带有@After方法的类也会被解析成一个Advisor.....其他通知的方法也会被解析成Advisor 在Advisor中定义了增强的逻辑, 也就是@Befor和@After等的逻辑, 以及需要增强的方法, 比如div方法.
下面来分析一下使用注解@Aspect , @Before, @After的实现原理. 上面已经说了, 就是将@Before, @After生成Advisor
这里一共有三个部分.
- 第一部分: 解析@Aspect下带有@Before等的通知方法, 将其解析为Advisor
- 第二部分: 在createBean的时候, 创建动态代理
- 第三部分: 调用. 调用的时候, 执行责任链, 循环里面所有的通知. 最后输出结果.
下面我们按照这三个部分来分析.
第一步: 解析@Aspect下带有@Before等的通知方法, 将其解析为Advisor. 如下图:
第一步是在什么时候执行的呢?
在createBean的时候, 会调用很多PostProcessor后置处理器, 在调用第一个后置处理器的时候执行.
执行的流程大致是: 拿到所有的BeanDefinition,判断类上是不是带有@Aspect注解. 然后去带有@Aspect注解的方法中找@Before, @After, @AfterReturning, @AfterThrowing, 每一个通知都会生成一个Advisor
Advisor包含了增强逻辑, 定义了需要增强的方法. 只不过这里是通过AspectJ的execution的方式进行匹配的.
第二步: 在createBean的时候, 创建动态代理
createBean一共有三个阶段, 具体在哪一个阶段创建的动态代理呢?
其实, 是在最后一个阶段初始化之后, 调用了一个PostProcessor后置处理器, 在这里生成的动态代理
整体流程是:
在createBean的时候, 在初始化完成以后调用bean的后置处理器. 拿到所有的Advisor, 循环遍历Advisor, 然后根据execution中的表达式进行matchs匹配. 和当前创建的这个bean进行匹配, 匹配上了, 就创建动态代理.
pointcut的种类有很多. 上面代码提到过的有:
/**
* Advisor 种类很多:
* RegexpMethodPointcutAdvisor 按正则表达式的方式匹配类
* NameMatchMethodPointcutAdvisor 按方法名匹配
* DefaultBeanFactoryPointcutAdvisor xml解析的Advisor
* InstantiationModelAwarePointcutAdvisorImpl 注解解析的advisor(@Before @After....)
*/
第三步: 调用. 调用的时候, 执行责任链, 循环里面所有的通知. 最后输出结果.
调用的类,如果已经生成了动态代理. 那么调用的方法, 就是动态代理生成的方法了.然后拿到所有的advisor, 作为一个责任链调用. 执行各类通知, 最后返回执行结果
3.2.2 AOP切面源码分析
源码分析也分为三部分
1. 解析切面
2. 创建动态代理
3. 调用
源码分析的入口, 从注解开始:
组件的入口是一个注解, 比如启用AOP的注解@EnableAspectJAutoProxy. 在注解的实现类里面, 会有一个@Import(""). 这个@Import("")就是引入的源码实现类. 比如AOP的@Import(AspectJAutoProxyRegistrar.class)
通常, Spring要开启某一个功能, 都会增加一个注解, 如果我们再想要看某一个功能的源码, 那么就可以从他的注解跟进去看,在找到@Import("")就找到源码的入口了
源码分析的入口, AOP注解:
package com.lxl.www.aop; import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configurable
// 使用注解的方式引入AOP
@EnableAspectJAutoProxy
@ComponentScan("com.lxl.www.aop")
public class MainConfig { }
引入AOP, 我们需要在配置文件中增加@EnableAspectJAutoProxy代理. 那么想要去掉AOP的引入, 只需要将这个注解注释掉就可以了. 这个注解解释整个AOP的入口.
提示: 其他组件的引入也是类似的, 通常引入组件, 需要增加一个注解, 而整个功能的入口就在这个主机上面.
接下来, 进入到注解类
package org.springframework.context.annotation; import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }
这是, 我们看到EnableAspectJAutoProxy类增加了一个@Import注解类, 我们知道Import注解可以向IoC容器中增加一个bean.
下面进入到AspectJAutoProxyRegistrar类
package org.springframework.context.annotation; import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata; class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { /**
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
* {@code @Configuration} class.
*/
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
} }
我们看到, 使用ImportBeanDefinitionRegistrar注册了一个BeanDefinition.
需要记住的是, 通常使用ImportBeanDefinitionRegistrar结合@Import可以向容器中注册一个BeanDefinition.
如何注册的呢? 看具体实现.
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
注册名字是internalAutoProxyCreator的AnnotationAwareAspectJAutoProxyCreator
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) { /**
* 注册一个AnnotationAwareAspectJAutoProxyCreator类型的bean定义
*/
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
如上结构梳理如下:
我们看到, 注册了类AnnotationAwareAspectJAutoProxyCreator类型的bean. 这是一个什么样的类呢? 我们来看一下类的结构. 这个类的继承结构很庞大, 我们只看和本次内容相关的继承结构
解析切面, 创建动态代理, 都是在bean的后置处理器中进行的, 下面对照着AOP的实现原理以及createBean(创建bean)的过程来看
上图是bean加载过程中调用的9次后置处理器. 在创建bean之前调用了InstantiationAwareBeanPostProcessor后置处理器判断是否需要为这个类创建AOP, 也就是解析切面的过程. 所以在AnnotationAwareAspectJAutoProxyCreator里面实现了InstantiationAwareBeanPostProcessor后置处理器的接口. 重写了postProcessBeforeInstantiation方法.
在createBean的第三阶段初始化之后, 要创建AOP的动态代理, 调用了BeanPostProcess后置处理器, AnnotationAwareAspectJAutoProxyCreator也实现了BeanPostProcess接口. 重写了postProcessAfterInitialization.
同时也需要处理AOP的循环依赖的问题, 处理循环依赖是在属性赋值之前调用SmartInstantiationAwareBeanPostProcessor后置处理器, 然后重写getEarlyBeanReference方法. 我们看到AnnotationAwareAspectJAutoProxyCreator也实现了SmartInstantiationAwareBeanPostProcessor接口. 并重写getEarlyBeanReference方法.
1) AOP解析切面
通过上面的分析,我们知道了, 解析切面是在重写了InstantiationAwareBeanPostProcessor后置处理器的postProcessBeforeInstantiation方法. 所以,我们要找到AnnotationAwareAspectJAutoProxyCreator重写的postProcessBeforeInstantiation方法.
小贴士 如何找到呢? 在idea中使用快捷键ctrl + o, 找到当前类重写的所有方法. 在搜索postProcessBeforeInstantiation, 就可以找到了
进入创建动态代理的bean的后置处理器, 这是解析切面的第一个入口
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
......
}
我们在postProcessBeforeInstantiation方法的入口处打一个断点, 接下来看一下这个接口的调用链
如上图, 可以看出我们的入口是main方法, 然后调用了refresh()方法, 执行的是refresh()方法的finishBeanFactoryInitialization()方法, 然胡调用了doGetBean()下的createBean().然后调用的是resolveBeforeInstantiation的applyBeanPostProcessorsBeforeInstantiation方法,在这里获取到所有的bean的后置处理器, 判断这个bean的后置处理器是否是InstantiationAwareBeanPostProcessor的一个实例. 如果是, 那么就调用postProcessBeforeInstantiation()方法.
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
/**
* 获取容器中所有的后置处理器
* 这之前有一个注册bean定义的方法, 已经注册过了. 所以在这里可以获取到列表
*
* 9次bean的后置处理器, 都是一个类实现InstantiationAwareBeanPostProcessor类, 重写postProcessBeforeInstantiation方法
*/
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
下面就来分析postProcessBeforeInstantiation()方法
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
/**
* 在第一个bean创建的时候, 就会去调用所有的bean的后置处理器, 并且解析所有的切面.
* 这一步是非常消耗性能的. 所以, 会放到缓存当中
*/
// 构建缓存的key
Object cacheKey = getCacheKey(beanClass, beanName); // 没有beanName或者不包含在targetSourcedBeans
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
// 判断是否已经被解析过?
if (this.advisedBeans.containsKey(cacheKey)) {
// 解析过, 则直接返回
return null;
}
/*
* 判断当前这个类是不是需要跳过的类.如果是基础类或者是应该跳过里的类, 则返回null, 表示这个类不需要被解析
*
* 判断是不是基础bean(是不是切面类, 通知, 切点). 因为如果类本身是一个通知, 切面, 那我们不需要解析它
* 跳过的类: 默认是false. 在shouldSkip里面拿到所有的bean定义, 标记是不是@Aspect, 然后将每一个通知生成一个advisor
*/
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
/**
* advisedBean是一个集合, 用来保存类是否是一个advise
*/
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
} // Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); // 创建了代理
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} return null;
}
第一步: 构建缓存
构建缓存的key
Object cacheKey = getCacheKey(beanClass, beanName);
在第一个bean创建的时候, 就会去调用所有的bean的后置处理器, 并且解析所有的切面.
这一步是非常消耗性能的. 所以, 会放到缓存当中. 已经创建过的,后面将不再创建
第二步: 校验bean是否被解析过. 如果已经解析过, 则不再解析
// 判断是否已经被解析过
if (this.advisedBeans.containsKey(cacheKey)) {
// 解析过, 则直接返回
return null;
}
第三步: 判断类是否是需要跳过的类
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
/**
* advisedBean是一个集合, 用来保存类是否是一个advise
*/
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
如果是基础类或者是应该跳过的类, 则返回null, 表示这个类不需要被解析.
这里有两个判断.
isInfrastructureClass(beanClass) 判断当前这个类是不是基础类, 这里的基础类的含义如下: Advice、Pointcut、Advisor、AopInfrastructureBean。如果本身就是基础类,那么不用在解析了
protected boolean isInfrastructureClass(Class<?> beanClass) {
// 如果这个类是一个Advice类型的类, 或者 Pointcut类型的类, 或者Adivsor类型的类, 或者AOPInsfrastructureBean类型的类.
boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
Pointcut.class.isAssignableFrom(beanClass) ||
Advisor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass);
if (retVal && logger.isTraceEnabled()) {
logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
}
return retVal;
}
shouldSkip(beanClass, beanName)判断当前是否是需要跳过的类 .
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// 找到候选的Advisors(前置通知, 后置通知等)
List<Advisor> candidateAdvisors = findCandidateAdvisors();
for (Advisor advisor : candidateAdvisors) {
if (advisor instanceof AspectJPointcutAdvisor &&
((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
return true;
}
}
return super.shouldSkip(beanClass, beanName);
}
findCandidateAdvisors(); 找到候选的类, 然后将候选类构造成Advisor对象. 进到方法里看看是如何筛选出候选对象的.
AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
// 找到xml方式配置的Advisor和原生接口的AOP的advisor 以及找到事务相关的advisor
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// 将找到的aspect, 封装为一个Advisor
if (this.aspectJAdvisorsBuilder != null) {
//buildAspectJAdvisors()方法就是用来解析切面类, 判断是否含有@Aspect注解, 然后将每一个通知生成一个advisor
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
// 返回所有的通知
return advisors;
}
这里做了两件事
第一步: 解析xml方式配置的Advisor (包括原生接口方式配置的advisor 以及找到事务相关的advisor)
第二步: 解析注解方式的切面. buildAspectJAdvisors()方法是用来解析切面类的. 解析每一个切面类中的通知方法, 并为每个方法匹配切点表达式.
public List<Advisor> buildAspectJAdvisors() {
/*
* aspectNames: 用于保存切面名称的集合
* aspectNames是缓存的类级别的切面, 缓存的是已经解析出来的切面信息
*/
List<String> aspectNames = this.aspectBeanNames; // 如果aspectNames值为空, 那么就在第一个单例bean执行的时候调用后置处理器(AnnotationAwareAspectJAutoProxy)
if (aspectNames == null) {
// 加锁, 防止多个线程, 同时加载 Aspect
synchronized (this) {
aspectNames = this.aspectBeanNames;
// 双重检查
if (aspectNames == null) {
// 保存所有从切面中解析出来的通知
List<Advisor> advisors = new ArrayList<>();
// 保存切面名称的集合
aspectNames = new ArrayList<>();
/*
* 扫描Object的子类. 那就是扫描所有的类
*
* 这里传入要扫描的对象是Object.class. 也就是说去容器中扫描所有的类.
* 循环遍历. 这个过程是非常耗性能的, 所以spring增加了缓存来保存切面
*
* 但事务功能除外, 事务模块是直接去容器中找到Advisor类型的类 选择范围小
* spring 没有给事务模块加缓存
*/
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false); // 循环遍历beanNames
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
// 通过beanName去容器中获取到对应class对象
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// 判断bean是否是一个切面, 也就是脑袋上是否有@Aspect注解
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
// 将beanName和class对象构建成一个AspectMetadata对象
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 解析切面类中所有的通知--一个通知生成一个Advisor.
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); // 加入到缓存中
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
} else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
} else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
} if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
} else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
我们来看看如何生成List<Advisor>的
// 解析切面类中所有的通知--一个通知生成一个Advisor.
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
// 获取标记了@Aspect的类
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
// 获取切面类的名称
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
// 验证切面类
validate(aspectClass); // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
// 使用包装的模式来包装 aspectInstanceFactory, 构建成MetadataAwareAspectInstanceFactory类
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); // 通知的集合, 按照排序后
List<Advisor> advisors = new ArrayList<>();
// 获取切面类中所有的通知方法, 除了带有@Pointcut注解的方法
for (Method method : getAdvisorMethods(aspectClass)) {
// 将候选方法解析为Advisor. Advisor中包含advise和pointcut. 注意: getAdvisor()方法中定义了切面解析的顺序
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
} // If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
} // Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
} return advisors;
}
这里主要有两点, 第一个是getAdvisorMethods(aspectClass)获取当前切面类的所有的AdvisorMethod , 第二个是封装成的Advisor对象
- 第一步: 解析切面类中所有的通知方法.getAdvisorMethods(aspectClass)
/**
* 获取切面类中所有的方法, 且方法中有@Pointcut注解
* @param aspectClass
* @return
*/
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new ArrayList<>();
// 调用doWithMethods. 第二个参数是一个匿名函数, 重写了doWith方法
ReflectionUtils.doWithMethods(aspectClass, method -> {
// 解析切面类中所有的方法, 除了Pointcut
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
}, ReflectionUtils.USER_DECLARED_METHODS);
if (methods.size() > 1) {
// 对方法进行排序
methods.sort(METHOD_COMPARATOR);
}
return methods;
}
这个方法是, 扫描切面类的所有方法, 将其添加到methods中, 除了Pointcut注解的方法
然后对methods进行排序, 如何排序呢?
private static final Comparator<Method> METHOD_COMPARATOR; static {
Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
new InstanceComparator<>(
Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
(Converter<Method, Annotation>) method -> {
AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
return (ann != null ? ann.getAnnotation() : null);
});
Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
}
按照Aroud, Before, After, AferReturning, AfterThrowing的顺序对通知方法进行排序
- 第二步: 将候选的方法解析为Advisor. 这里也是有两步.具体如下:
/**
* 解析切面类中的方法
* @param candidateAdviceMethod 候选的方法
*/
@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) { validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 获取切面中候选方法的切点表达式
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
} // 将切点表达式和通知封装到InstantiationModelAwarePointcutAdvisorImpl对象中, 这是一个Advisor通知
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
在getPointcut中解析了method,以及切点表达式pointcut
/**
* 找到候选方法method属于哪一种类型的Aspectj通知
* @param candidateAdviceMethod 候选的通知方法
* @param candidateAspectClass 候选的切面类
* @return
*/
@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
// 第一步: 解析候选方法上的注解,类似@Before(value="pointcut()")
// 找到Aspectj注解: @Pointcut, @Around, @Before, @After, @AfterReturning, @AfterThrowing
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
} // 第二步: 解析aspect切面中的切点表达式
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
// 解析切点表达式
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
如上代码, 可知, 这里也是有两个操作. 分别是将method解析为Advise, 另一个是解析切面类中的pointcut切点表达式. 返回返回切点表达式.
接下来, 就是将候选方法和切点表达式封装成Advisor. 在getAdvisor(...)方法中:
// 将切点表达式和通知封装到InstantiationModelAwarePointcutAdvisorImpl对象中, 这是一个Advisor通知
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
expressionPointcut: 即切点表达式; candidateAdviceMethod: 即候选方法
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { // 当前的切点
this.declaredPointcut = declaredPointcut;
// 切面类
this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
// 切面方法名
this.methodName = aspectJAdviceMethod.getName();
//切面方法参数的类型
this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
//切面方法对象
this.aspectJAdviceMethod = aspectJAdviceMethod;
// aspectJ的通知工厂
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
// aspectJ的实例工厂
this.aspectInstanceFactory = aspectInstanceFactory;
// advisor的顺序
/**
* 前面我们看到, Advisor会进行排序, Around, Before, After, AfterReturning, AfterThrowing, 按照这个顺序.
* 那么order值是什么呢?是advisors的size. 如果size是0, 那么就是第一个方法. 这里第一个不一定是Around, 他可能没有Around通知, 也没有Before通知.
*/
this.declarationOrder = declarationOrder;
// 切面名
this.aspectName = aspectName; if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut = Pointcuts.union(
aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut); this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
this.lazy = true;
}
else {
// A singleton aspect.
this.pointcut = this.declaredPointcut;
this.lazy = false;
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
前面已经得到了切入点表达式, 这里会进行初始化Advice, 初始化的时候, 根据通知的类型进行初始化.
- 环绕通知, 构建一个环绕通知的对象
- 前置通知, 构建一个前置通知的对象
- 后置通知, 构建一个后置通知的对象
- 异常通知, 构建一个异常通知的对象
- 返回通知, 构建一个返回通知的对象
具体代码如下:
@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// 候选的切面类
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
// 通知方法上的注解内容
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
} // If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
} if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
} AbstractAspectJAdvice springAdvice; switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
case AtAround:
// 封装成环绕通知的对象
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
// 封装成前置通知对象
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
// 封装成后置通知对象
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
// 封装成返回通知对象
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
// 封装异常通知对象
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
} // Now to configure the advice...
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings(); return springAdvice;
}
这就是我们在之前的结构中说过的, 在解析切面的时候, 会解析切面中的每一个方法, 将其解析成一个Advisor, 而每一个Advisor都包含两个部分:Advise和pointcut.
最后, 将所有的切面类都解析完, 将所有的Advisor放入到集合advisors中返回.
这样就完成了切面的解析.
2) 调用动态代理
在ioc解析的过程中, 是在什么时候创建动态代理的呢?
通常是在创建bean初始化之后创建动态代理. 如果有循环依赖, 会在实例化之后创建动态代理, 再来感受一下创建bean过程中的操作.
下面我们来看正常的流程, 在初始化之后创建AOP动态代理 .
在创建bean的过程中,一共有三步, 来看看AbstractAutowireCpableBeanFactory.doCreateBean()
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException { // Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
//第一步: 实例化
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 这里使用了装饰器的设计模式
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
......
try {
// 第二步:填充属性, 给属性赋值(调用set方法) 这里也是调用的后置处理器
populateBean(beanName, mbd, instanceWrapper);
// 第三步: 初始化.
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
......
}
在第三步初始化的时候, 要处理很多bean的后置处理器.
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException { Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
postProcessAfterInitialization(result, beanName);就是处理初始化之后的后置处理器, 下面就从这个方法作为入口分析.
AnnotationAwareAspectJAutoProxyCreator也实现了postProcessAfterInitialization(result, beanName);接口
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
/**
* 每一个bean在解析的时候都会解析一遍切面.
* 为什么每次都要解析一遍呢? 因为还有另外一种方式-实现Advisor接口的方式实现AOP, 在加载过程中, 可能随时有新的bean被解析出来. 所以, 需要每次都重新解析一遍,.
* 我们在第一次解析的Advisor都已经放入到缓存, 在这里会先从缓存中取, 也就是已经解析过的不会重复解析. 也就是不 消耗性能
*/
if (bean != null) {
// 获取缓存key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
/**
* 因为有可能在循环依赖处理的时候已经创建国一遍, 如果是那么现在就不再创建了,并且删除
* 在这里, 我们要处理的是普通类的动态代理, 所以, 需要将循环以来创建的动态代理删掉
*/
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 该方法将返回动态代理的实例
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
接下来处理的流程如下:
这里,第三步:删除循环依赖创建的动态代理对象, 为什么要这样处理呢?
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 已经被处理过(解析切面的时候, targetSourcedBeans用来存储自己实现创建动态代理的逻辑)
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 判断bean是否是需要增强的bean
/**
* 哪些类是不需要增强的呢?
* 在解析切面的时候, 基础类和应该跳过的类是不需要增强的.
*/
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 判断是否是基础类, 或者是否是需要跳过的类
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
// 将其标记为不需要增强的类
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
} // 匹配Advisor. 根据类匹配advisors, 至少匹配上一个, 才创建动态代理, 否则不创建动态代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); // 匹配了至少一个advisor, 创建动态代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
首先判断是否是需要跳过的类. 哪些类是需要跳过的类呢?
第一类:基础类. Advice, Pointcut, Advisor, AopInfrastructureBean.
第二类: 原始的接口类, 以.ORIGINAL开头的类
接下来, 匹配Advisor.
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 第一步: 拿到已经解析出来的advisors(这次是从缓存中获取)
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 第二步:循环判断advisor能否作用于当前bean(原理: 切点是否命中bean)
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 第三步: 对匹配bean的advisor进行增强
extendAdvisors(eligibleAdvisors);
// 第四步: 对匹配bean的advisor进行排序
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
// 返回匹配到的advisors
return eligibleAdvisors;
}
这里经过了四步, 具体详见上述代码及注释.
- 第一步: 从缓存中拿到已经解析出来的advisors
- 第二步:循环判断advisor能否作用于当前bean
- 第三步: 对匹配bean的advisor进行增强
- 第四步: 对匹配bean的advisor进行排序
这里面的第一步: 从缓存中取出了已经解析出来的advisors集合. 解析方式是从缓存中取出已经解析的advisors
接下来,循环遍历获得到的advisors, 得到每一个advisor. 判断advisor是否是目标bean需要增强的通知.
这里在筛选的时候, 根据切点表达式进行了两次筛选. 第一次粗筛, 第二次是精筛. 整个目标类, 只要有一个类命中切点表达式, 那么这个类就是需要被创建动态代理的类, 返回true.
接下来就是要创建动态代理了. 然后,返回创建的动态代理对象.
下面来看看是如何创建动态代理的.
创建动态代理对象有两种方式: 一种是jdk代理, 一种是cglib代理.
无论是使用xml配置的方式, 还是使用注解的方式, 都有一个参数proxy-target-class, 如果将这个参数设置为true, 表示强制使用cglib代理. 如下所示设置:
使用注解的方式
@EnableAspectJAutoProxy(proxyTargetClass=true) 使用xml配置的方式
<aop: sapectj-autoproxy proxy-target-class="true"></aop:>
所以在创建动态代理之前, 先解析注解或者配置, 看是否配置了proxy-target-class参数. 如果配置了这个参数,且其值为true, 那么就创建一个cglib代理对象. 否则创建一个JDK代理对象.通常, 我们使用的更多的是spring自己定义的JDK代理对象. 通过Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);创建动态代理
在JDKDynamicAopProxy代理类中有一个invoke()方法. 这个invoke方法, 就是执行代理对象的方法时调用的方法.
该方法是通过反射的方法执行目标类中定义的方法的.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { }
3. 调用动态代理.
public class LxlMainClass {
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); Calculate calculate = (Calculate) ctx.getBean("lxlCalculate");
/**
* 上面的calculate, 就是返回的动态代理的类
* 当调用下面的div方法时, 实际上调用的是JdkDynamicAopProxy.invoke(...)方法
*/
calculate.div(2, 4); ProgramCalculate programCalculate = (ProgramCalculate) ctx.getBean("lxlCalculate");
String s = programCalculate.toBinary(5);
System.out.println(s);
}
}
如上代码, 我们在main方法中, 获取的Calculate对象, 其实是动态代理生成的对象. 当调用calculate.div(2, 4)方法时, 其实调用的是动态代理的invoke()方法.
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource;
Object target = null; try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
} Object retVal; if (this.advised.exposeProxy) {
// 把代理对象暴露在线程变量中.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
} // Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
// 获取目标对象
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null); // 把aop的advisor全部转化为拦截器, 通过责任链模式依次调用
/**
* 将advisor对象转换为interceptor对象.
*
* 为什么要将advisor都转化为interceptor拦截器呢?
* 主要还是因为要进行责任链调用. 之前说过, 要想进行责任链调用, 他们要有一个共同的方法.
* 转化为interceptor以后, 这里共同的方法就是invoke().
* beforeAdivsor, afterAdvisor, returningAdvisor, throwingAdvisor. 这几种类型. 只有returningAdvisor和throwingAdvisor会转化为Interceptor.
* 因为beforeAdvisor和adgerAdvisor本身就实现了interceptor接口
*/
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // 拦截器链为空
if (chain.isEmpty()) {
// 通过反射直接调用执行
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();
} // Massage return value if necessary.
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
调用这里有一个非常经典的调用逻辑--调用链.
在这里会将匹配方法的advisor对象转换为interceptor拦截器对象. 为什么要将advisor转换为interceptor拦截器呢?
因为要进行责任链调用. 前面说过, 要想进行责任链调用, 他们要有一个共同的方法.
转化为interceptor以后, 这里共同的方法就是invoke().
beforeAdivsor, afterAdvisor, returningAdvisor, throwingAdvisor. 这几种类型. 只有returningAdvisor和throwingAdvisor会转化为Interceptor.
因为beforeAdvisor和adgerAdvisor本身就实现了interceptor接口
如上图, 调用链的逻辑是, 调用动态代理方法,比如说div(arg1, arg2), 然后执行调用链中第一个通知advisor1, 然后第一个通知调用第二个通知, 在执行第二个, 以此类推, 当所有的通知执行完, 调用目标方法div(arg1, arg2), 然后返回执行结果. 我们来看看代码的逻辑实现
5.2 Spring5源码--Spring AOP源码分析二的更多相关文章
-
5.2 spring5源码--spring AOP源码分析二--切面的配置方式
目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...
-
5.1 Spring5源码--Spring AOP源码分析一
目标: 1.什么是AOP, 什么是AspectJ, 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP 1.1 什么是 ...
-
5.2 spring5源码--spring AOP源码分析三---切面源码分析
一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...
-
spring AOP源码分析(三)
在上一篇文章 spring AOP源码分析(二)中,我们已经知道如何生成一个代理对象了,那么当代理对象调用代理方法时,增强行为也就是拦截器是如何发挥作用的呢?接下来我们将介绍JDK动态代理和cglib ...
-
Spring AOP 源码分析 - 拦截器链的执行过程
1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...
-
框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
-
Spring AOP 源码分析 - 创建代理对象
1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...
-
Spring AOP 源码分析 - 筛选合适的通知器
1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...
-
Spring AOP 源码分析系列文章导读
1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解.在写完 Spring IOC 容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了3天时间阅 ...
随机推荐
-
Android WebView 302斗争之旅
一.背景 越来越多的业务接入,项目内多多少少会出现几个H5页面,只是单纯的提供WebView容器接入H5页面根本满足不了需求,他们需要登录态,需要制定协议控制Native的导航栏,或者需要JsBrid ...
-
1034: [ZJOI2008]泡泡堂BNB - BZOJ
Description 第XXXX届NOI期间,为了加强各省选手之间的交流,组委会决定组织一场省际电子竞技大赛,每一个省的代表队由n名选手组成,比赛的项目是老少咸宜的网络游戏泡泡堂.每一场比赛前,对阵 ...
-
Let&#39;s go! (Ubuntu下搭建Go语言环境)
自2009年Go语言发布以来,我一直在关注Go语言,如今Go语言已经发展到1.2版本,而且也收到越来越多的人关注这门语言.Go语言设计的目的就是为了解决执行数度快但是编译数度并不理想(如C++)以及编 ...
-
Effective C++ 沉思录
1.视C++是一个联邦语言.由C,Object-Oriented C++,Templete C++,和STL组成.其中面对不同的语言,采用不同的规约这样编程效率会提高很多.例如C和STL 都是有C-S ...
-
VirtualBox single usermode boot
VirtualBox single usermode boot 当系统因为某些原因无法通过图形界面登录VirtualBox内的系统时,可以通过Grub进入命令行模式/单一用户界面模式. 参考: 1.R ...
-
编译boost python模块遇到的错误:../../libraries/boost_1_44_0/boost/python/detail/wrap_python.hpp:75:24: fatal error: patchlevel.h: No such file or directory
就是遇到类似标题上面的错误. 原因是没有安装对应python的python-dev依赖,不然编译到boost python模块的时候就会出错. 所以解决方案是sudo apt-get install ...
-
Hierarchical Storage structure
1.hierarchical storage structure This notion of inserting a smaller, faster storage device (e.g ...
-
postgresql 登录查看表定义
su - postgres psql \connect database_name; \d table_name
-
第六十七天 js动画
1.事件总结 鼠标事件 var box = document.querySelect('.box') // 1.点击事件 box.onclick = function(){ console.log(' ...
-
【Shell】带颜色输出(白底x色)
echo -e "\033[31mHello World.\033[0m" 红色31m 绿色32m 黄色33m 蓝色34m 黑色30m 白色37m 紫色35m 深绿色36m