Spring AOP源码解析
一、源码环境准备
1.1 jar包
新建maven工程,在pom.xml文件中加入下面内容:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.12.RELEASE</version>
</dependency>
1.2 代码准备
因为主要是分析spring中源码,所以演示的代码很简单。主要包括下面四个文件
-
测试类
测试类代码如下:
public class App { public static void main( String[] args ) { AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MainAop.class); MathCalculator calculator = ioc.getBean(MathCalculator.class); calculator.div(2, 2); } }即主要是新建IOC容器,获取业务类bean,然后调用业务方法
-
配置类
@Configuration @EnableAspectJAutoProxy public class MainAop { @Bean public MathCalculator calculator() { return new MathCalculator(); } @Bean public LogAspect logAspect() { return new LogAspect(); } }注意配置类中要加入
@EnableAspectJAutoProxy注解,这样子才能开启AOP代理,如果是在Spring boot中,可以不用加入此注解,因为Spring boot自动配置会加入此注解。然后给容器中加入了两个bean
-
业务类
public class MathCalculator { public int div(int i, int j) { return i/j; } }就是一个除法运算
-
切面类
@Aspect public class LogAspect { @Pointcut("execution(public int com.leon.aop.MathCalculator.*(..))") public void pointCut() { } @Before("pointCut()") public void logStart(JoinPoint joinPoint) { System.out.println("除法运行.....参数列表:{}"); } @After("pointCut()") public void logEnd() { System.out.println("logEnd"); } @AfterReturning("pointCut()") public void logReturn() { System.out.println("logReturn,return value:{}"); } @AfterThrowing("pointCut()") public void logThrow() { System.out.println("logThrow"); } }即添加对应的注解,进行切入点的切入。
二、源码分析
源码分析主要从下面4个方面来进行讲解
2.1 @EnableAspectJAutoProxy注解作用
1. 注解作用
进入注解源码,可以看到下面内容
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
可以看到采用Import注解加载了AspectJAutoProxyRegistrar类,然后进入AspectJAutoProxyRegistrar类,内容如下:
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.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
2. AnnotationAwareAspectJAutoProxyCreator继承关系
可以看到AspectJAutoProxyRegistrar是一个BeanDefinitionRegistrar,即可以将BeanDefinition信息加载到容器中(BeanDefinition是bean对象的一种静态描述,spring会根据BeanDefinition来创建bean对象)。在代码第一行,是调用registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,此方法就是将AnnotationAwareAspectJAutoProxyCreator这个类的BeanDefinition放置到BeanDefinitionRegistry中。AnnotationAwareAspectJAutoProxyCreator类的继承关系如下:
AnnotationAwareAspectJAutoProxyCreator
--> AspectJAwareAdvisorAutoProxyCreator
-->AbstractAdvisorAutoProxyCreator
-->AbstractAutoProxyCreator
-->ProxyProcessorSupport
-->ProxyConfig
-->Ordered(接口)
-->BeanClassLoaderAware(接口)
-->AopInfrastructureBean(接口)
-->SmartInstantiationAwareBeanPostProcessor(接口)
-->InstantiationAwareBeanPostProcessor(接口)
-->BeanPostProcessor(接口)
-->BeanFactoryAware(接口)
-->Aware(接口)
通过分析类的继承路径,当前AnnotationAwareAspectJAutoProxyCreator具有以下几种能力
- InstantiationAwareBeanPostProcessor,即可以对bean创建前进行处理
- BeanPostProcessor,即bean初始化前后进行处理
- BeanFactoryAware,可以获取到当前IOC容器
- BeanClassLoaderAware,可以获取到当前类加载器
- Ordered,排序需要
3. 总结
@EnableAspectJAutoProxy注解给容器中添加了AnnotationAwareAspectJAutoProxyCreator类的定义,此类具有bean后置处理器的作用,所以猜想AOP应该是在bean创建前后来产生代理对象。AnnotationAwareAspectJAutoProxyCreator当前在容器中仅仅是BeanDefinition,真正创建实例加载到容器中是在AbstractApplicationContext中的refresh(注:此方法是spring中核心方法,后置处理器,事件监听注册,bean实例化都是在这个模板方法中进行的)中的registerBeanPostProcessors(beanFactory)方法中。这里就不具体分析了。refresh方法如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
2.2 切面类bean实例的创建过程(即LogAspect)
bean实例的创建是在refresh中的finishBeanFactoryInitialization(beanFactory);方法中
bean实例创建步骤, 简单来说,依次会执行以下方法
调用getBean(AbstractBeanFactory)获取logAspect对象
-
doGetBean,获取bean对象
记录当前bean在已经创建成功的集合中
-
createBean
-
调用resolveBeforeInstantiation方法,方法内容如下:
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); } return bean; }applyBeanPostProcessorsBeforeInstantiation中会依次调用每个后置处理器的postProcessBeforeInstantiation方法(注:此方法与BeanPostProcessor中的postProcessBeforeInitialization是由区别的,postProcessBeforeInitialization是在bean初始化前被调用)。
但是经过代码运行,resolveBeforeInstantiation方法针对logAspect会返回null
-
调用doCreateBean方法,方法内逻辑如下
- 首先创建logAspect实例,给实例中的属性设置值
- initializeBean方法,初始化方法,在调用bean的初始化前后会分别调用postProcessBeforeInitialization和postProcessAfterInitialization
-
总结
logAspect实例不需要创建其对应的代理对象,这里只是描述了bean实例的创建过程。在下面介绍MathCalculator实例的创建过程中也会经历这些阶段,但是在postProcessAfterInitialization中会创建其代理对象并方法
2.3 业务类bean实例创建过程(即MathCalculator)
MathCalculator的创建类似于logAspect实例的创建,主要的区别在initializeBean(org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition))方法的applyBeanPostProcessorsAfterInitialization方法,下面会详细说明applyBeanPostProcessorsAfterInitialization方法的执行过程
1. applyBeanPostProcessorsAfterInitialization内容
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
从代码中可以看到,for循环获取所有的后置处理器并依次执行postProcessAfterInitialization方法。由于主要是分析AOP相关功能,所以这里只看AnnotationAwareAspectJAutoProxyCreator这个后置处理器(注:在IDEA中debug可以设置只在感兴趣的值进行断点设置)
2. postProcessAfterInitialization方法
AnnotationAwareAspectJAutoProxyCreator继承了AbstractAutoProxyCreator抽象类,并且调用的也是抽象类中的此方法
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
getCacheKey是获取当前bean在容器缓存中的key名称,然后调用wrapIfNecessary方法
3. wrapIfNecessary方法
部分代码如下:
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
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;
首先会调用getAdvicesAndAdvisorsForBean方法来获取当前待创建bean的advisors(可以理解为增强器)
3.1 获取当前bean的advice和advisor
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
findEligibleAdvisors方法获取到合适的增强器(当前的增强器就是logStart等四个)。如果没有增强器,则直接返回DO_NOT_PROXY(就是null),否则返回数组。
findEligibleAdvisors主要逻辑:
- 找到被@Aspect注解标注的类,获取其中的切入点方法
- 然后根据当前bean中方法来获取符合条件的切入点
- 然后返回满足条件的切入点方法
findEligibleAdvisors返回值中包括五个,其中四个就是上面定义的logStart,logEnd,logReturn,logThrow。
3.2 创建代理对象(createProxy)
此方法就是利用JDK的动态代理或者CGLIB来创建代理对象,根据当前bean是否实现了接口来选择JDK还是CGLIB
2.4 业务类方法的执行(div方法的执行)
在演示的代码中,即MathCalculator calculator = ioc.getBean(MathCalculator.class);.这里获取到的bean就是代理对象。
div方法,被委托给代理对象,代理对象会根据切入点的顺序来依次调用切入方法。
spring4:正常情况,方法执行顺序 @Before ---》@After ----》@AfterReturn
异常情况,顺序@Before ---》@After---》@AfterThrow,
@Around方法较为特殊,会首先进入,具体执行顺序可以查看官网介绍
三、总结
本文介绍了spring 中aop实现的原理。主要思路就是利用后置处理器来生成对应的代理对象。
这里也介绍了@EnableAspectJAutoProxy的应用,类似于Spring中其他的@EnableXXX,也可以进行类似分析。