SpringAOP源码解析
本文使用SpringBoot版本(2.2.x版本),在该版本中AOP的代理方式默认全是CGLIB代理
简单的AOP程序示例
下面我讲结合一个简单的AOP案例来讲解AOP的源码。
假设我们现在有一个类CService
,要对CService
的hello
开头的方法进行日志跟踪,需要在这些方法执行前入参信息,执行完毕后打印方法耗时。该怎么实现呢?
@Component
public class CService {
public void helloC1(String name) {
System.out.println("hello " + name);
}
public void helloC2(String name) {
System.out.println("hello " + name);
}
public void method(String name) {
System.out.println("hello " + name);
}
}
AOP是最好的选择!对代码无侵入性,且方便扩展,下面就来实现以下这个需求,编写切面MyLogAspect
@Component
@Aspect
public class MyLogAspect {
// 使用execution表达式,只拦截CService的hello开头的方法
@Pointcut("execution(public * com.sjx.service.CService.hello*(..))")
public void pointcut() {
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pj) throws Throwable {
StopWatch watch = new StopWatch();
Object result = null;
Object[] args = pj.getArgs(); // 获取源方法的参数
System.out.printf("方法%s入参 %s ...%n", pj.getSignature(), CollectionUtils.arrayToList(args));
watch.start();
result = pj.proceed();
watch.stop();
System.out.println("方法耗时:\n" + watch.prettyPrint());
return result;
}
}
需要注意的是 使用@Aspect
注解需要额外引入依赖org.aspectj:aspectjweaver:1.9.19
(版本号自己决定)
启动应用执调用helloC1
方法可以看到如下输出
AOP源码分析
概念
众所周知,AOP中有这么几个概念:连接点,切入点,切面。 下面我使用前文的示例稍微解释一下AOP要用到的这几个名词
连接点:连接点其实就是每个方法再AOP中的抽象概念,即每个方法都是连接点。CService
中每个方法都是一个连接点
切入点:就是一些连接点的集合。在上例中它使用execution
表达式来定义:execution(public * com.sjx.service.CService.hello*(..))
表示CService中所有hello开头且方法修饰符为public的方法。
切面:切面定义了增强方法的逻辑。对应MyLogAspect
中的around
方法
源码解析
我们知道,Spring是通过IOC容器管理Bean的,在Spring启动时,会实例化所有标记@Service @Component
注解的类。阅读本文假设你已经熟悉了SpringIOC的知识,明白bean是怎么创建的。
Spring提供了BeanPostProcessor
这个接口可以插手Bean的初始化逻辑。
而AOP的逻辑正是通过实现BeanPostProcessor
,重写postProcessAfterInitialization
方法来完成在Bean的初始化之后对Bean进行代理。代理后的Bean会具备增强逻辑。这个实现类就是AbstractAutoProxyCreator
,接下来我们就来看下AbstractAutoProxyCreator
是如何生成代理Bean的。postProcessAfterInitialization
逻辑如下
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 获取缓存key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 生成代理bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
}
其中wrapIfNecessary
方法的作用就是:如果bean有必要被代理则返回代理对象,否则不进行代理。接下来开一下wrapIfNecessary
方法
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 省略一些判断逻辑
// 这里获取被代理的bean的所有相关拦截器,比如自定义的切面,Transactional注解的拦截器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 使用CGLIB创建代理对象,很重要
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;
}
在wrapIfNecessary
方法中,getAdvicesAndAdvisorsForBean
方法是获取拦截器,这里是很关键的逻辑,bean的增强逻辑实际上都是通过一个个拦截器(advisor)来实现的。如果没有获取到拦截器则说明bean不需要被代理——就会返回源对象。否则就会试图通过createProxy
方法创建代理对象。
也就是说wrapIfNecessary
方法做了两件重要的事情
- 获取拦截器specificInterceptors
- 如果存在拦截器,则使用拦截器创建代理对象createProxy
获取拦截器
获取拦截器是getAdvicesAndAdvisorsForBean方法实现的。我们来看一下这个方法是如何获取到拦截器的。
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 获取合适的拦截器 Advisor就是拦截器
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;
}
getAdvicesAndAdvisorsForBean方法就是要给壳子方法,它通过findEligibleAdvisors方法完成拦截器的查询获取。在findEligibleAdvisors方法中
- 通过findCandidateAdvisors获取所有的拦截器
- 通过findAdvisorsThatCanApply对第一步的拦截器进行过滤,只留下合适的拦截器。比如方法上没有
Transactional
注解的时候,事务拦截器就会在次被过滤掉
至此,就获取到执行切面必须的拦截器了,通过debug可以发现它是一个InstantiationModelAwarePointcutAdvisorImpl
类型的Advisor,这里补充一下,在SpringAOP中Interceptor就是Advisor,源码中Interceptor是Advisor的子接口。
补充:Advisor、Advice、Advised、Interceptor、PointCut
Advice:具体的建议,即拦截器的逻辑
Advisor:顾问,它包含了Advice、PointCut。既然是顾问,它总得知道自己要对谁进行建议,顾问就是通过PointCut来定位对象
Advised:过去式,已建议;它可以包揽一个顾问列表。
他们之间的关系如下
如果把Bean比作一个人,那么增强逻辑+切入点就相当于是顾问,顾问在指定地方建议你做指定的事。也就符合了Spring中这些命名含义。
生成代理对象
还记得获取拦截器之后,要做什么吗?当然是把拦截器们和bean绑定起来。这个绑定的动作就是为源bean生成代理bean,由代理bean执行拦截器里的增强逻辑。我们接着上文来看创建代理的方法
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 它是一个ProxyCreatorSupport--AdvisedSupport--Advised
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 把所有的连接器都转成了Advisor
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// ProxyFactory代理工厂中要保留拦截器对象advisors和源对象targetSource
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
return proxyFactory.getProxy(classLoader);
}
这个方法的逻辑相对简单,创建代理工厂,再把拦截器数组转换成为顾问列表,然后把顾问列表交给工厂,由工厂来完成创建代理对象的行为。重要的就是最后一行代码。proxyFactory.getProxy(classLoader);
具体创建代理的行为交给工厂来做。工厂只需要具备原材料就行了,这里的原材料就是代理顾问列表advisors
来看一下ProxyFactory#getProxy
方法
public Object getProxy(@Nullable ClassLoader classLoader) {
// createAopProxy()就是CglibAopProxy
return createAopProxy().getProxy(classLoader);
}
// org.springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)
public Object getProxy(@Nullable ClassLoader classLoader) {
Class<?> rootClass = this.advised.getTargetClass();
Class<?> proxySuperClass = rootClass;
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
validateClassIfNecessary(proxySuperClass, classLoader);
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
// 代理对象的方法调用最后都是回调函数在起作用DynamicAdvisedInterceptor
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
return createProxyClassAndInstance(enhancer, callbacks);
}
}
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
enhancer.setInterceptDuringConstruction(false);
enhancer.setCallbacks(callbacks);
return (this.constructorArgs != null && this.constructorArgTypes != null ?
enhancer.create(this.constructorArgTypes, this.constructorArgs) :
// 创建代理对象
enhancer.create());
}
createAopProxy()方法简单说一下,它的内部是判断使用JDK动态代理,还是使用CGLIB动态代理,再SpringBoot2.2.x版本中,AOP已全是CGLIB代理,所以就不再详细介绍了。也就是说createAopProxy()方法最终返回的是CGLIB代理工厂。
而CGLIB最后就是通过enhancer.create()
来创建代理对象的。我们知道CGLIB的代理对象回调逻辑的关键再Enhancer的setCallbacks
方法。这里的callback就是把包装了顾问列表的callback。最终执行逻辑在DynamicAdvisedInterceptor
的invoke中。通过下图的继承体系,可以看出DynamicAdvisedInterceptor
就是一个Callback。回调逻辑就在DynamicAdvisedInterceptor
中了。至此Spring就为AOP产生了一个代理对象,此时容器中的bean不再是CService类型,而是要给CGLIB代理过后的代理类了。
代理对象回调
前一小节说到,SpringAOP在BeanPostProcessor
的postProcessAfterInitialization
方法中生成的代理对象。那代理对象生成以后,回调逻辑是如何执行的呢?换句话说,CService的hello*
方法是如何执行的呢?
由于Spring容器中存在的已经不是CService对象,而是CService的代理对象,所以在调用hello*
方法的时候,实际上会走进DynamicAdvisedInterceptor
的intercept方法中
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
private final AdvisedSupport advised;
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
// 获取拦截器链,使用了责任链模式+递归
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 非public方法、toString方法、hashCode方法、equals方法会走该逻辑
if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = invokeMethod(target, method, argsToUse, methodProxy);
}
// 其他方法执行回调逻辑
else {
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
// 省略其他代码
}
}
public Object proceed() throws Throwable {
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 执行源方法
return invokeJoinpoint();
}
// 获取拦截器链中的拦截器
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// 递归执行
return proceed();
}
}
else {
// 拦截器执行拦截逻辑
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
转载自:https://juejin.cn/post/7294468438391947315