接着上篇继续说Spring AOP,篇幅有限,上篇主要介绍了搭建Spring AOP的初始化环境,以及Spring实现这一功能的核心思路,这篇专门从源码分析Spring是如何实现AOP的。
AOP流程核心源码分析
此处分析源码也是按照我们之前所猜测的AOP实现步骤来逐条分析和验证。
看一下实例化我们的UserService前,Spring容器里都有什么?
可以看到,此时bean容器里只有切面类userAspect,要获取的userService,以及我们提前实例化好的BeanPostProcessor----AnnotationAwareAspectJAutoProxyCreator。仅仅3个对象,足够精简吧。
1.收集并解析切面
首先需要Spring能先从所有bean中识别出我们的切面类,AnnotationAwareAspectJAutoProxyCreator类重写的postProcessBeforeInstantiation方法就是专门处理这个问题的。跟着getBean进入实例化的流程,就会执行到下面代码处。
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// 此处代码略过
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// 实例化bean前,执行后置处理器,给我们一个干预Spring实例化操作的机会。
// 如果返回对象,就不需要再执行后续的实例化操作。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
// 准备实例化此对象
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
// 此处代码略过
}
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) {
// 执行InstantiationAwareBeanPostProcessor后置处理器的方法
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
// 执行BeanPostProcessor的方法
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 遍历所有的InstantiationAwareBeanPostProcessor对象,执行其实现的postProcessBeforeInstantiation方法
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
以上三段代码是IOC的必经流程,resolveBeforeInstantiation方法内回调InstantiationAwareBeanPostProcessor的后置处理器,这里可以直接返回一个对象给Spring容器,难点在这下面的逻辑,着重看看我们的AnnotationAwareAspectJAutoProxyCreator在其内部做了什么处理?
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
// 获取此类的key,一般就是对应的beanName。
Object cacheKey = getCacheKey(beanClass, beanName);
// 这里的targetSourcedBeans用于缓存通过自定义TargetSource创建出来的beanName。
// 默认是没有自定义的,所以下面的判断默认都是true。
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
// 这里的advisedBeans用于缓存所有已被解析过的bean,
// key/value形式。value为true表示此bean是可以被代理的。false表示不需要被代理。
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
// 此处主要判断此类是否为Aspect,如果是,则直接return,不需要解析。
// isInfrastructureClass方法判断此Class是否为AspectJ提供的基础类
// 基础类Advice/Pointcut/Advisor/AopInfrastructureBean不会被代理。
// AspectJAwareAdvisorAutoProxyCreator类重写了部分逻辑,
// 会通过isInfrastructureClass额外判断是否有@Aspect注解且此类没有被ajc编译器编译过
// shouldSkip方法内部很复杂,不同的实现类会重写其中部分逻辑。
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
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就会始终为null,下面if语句不会执行。
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
// 获取自定义的Advisor
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
// 根据自定义的Advisor创建代理对象
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
// 缓存解析过的bean
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
这个方法执行后就会提前找出所有的切面并生成Advisor数组缓存起来,核心逻辑在于其中的shouldSkip方法内。AnnotationAwareAspectJAutoProxyCreator类重写了部分逻辑,导致此方法内部很复杂,且其内部逻辑并不像方法名shouldSkip这么简单。此方法,只有此bean不是aspec切面,才会执行进去。由于我们这是实例化UserService,所以可以进入执行,一看究竟。
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
// TODO: Consider optimization by caching the list of the aspect names
// 查找候选的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重写的逻辑,后面super.shouldSkip才会调用原来逻辑。
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
// 获取Spring中实现了Advisor接口的bean
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// Spring支持@Aspect注解,这里获取所有的@Aspect类bean,然后解析成Advisor.
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
此方法其实就说明了两件事,其一,Spring支持原生Advisor接口。其二,AspectJ的影响力过大,于是Spring支持了AspectJ相关的注解,将其解析为Advisor。
这个findCandidateAdvisors方法内部会调用父类重写的此方法,看着名字一样,不要混乱。这里Spring会查找出容器内实现了Advisor接口的bean。
/**
* Find all eligible Advisor beans in the current bean factory,
* ignoring FactoryBeans and excluding beans that are currently in creation.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] advisorNames = this.cachedAdvisorBeanNames;
// 查找容器内实现了Advisor接口的bean,将其名称缓存起来。
if (advisorNames == null) {
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the auto-proxy creator apply to them!
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
// 遍历所有查找出来的Advisor bean。根据beanName获取其对应的bean对象。
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
// 实例化Advisor对象
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}
由于我们并没有实现此接口,所以返回的就是空集合。
紧接着就会查找解析容器内标注了@Aspect注解的类。
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 获取容器内所有的beanName,这里真的很暴力。。。
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
// 遍历容器内所有的beanName,构建Advisor。
for (String beanName : beanNames) {
// 这里默认是符合条件的bean,结果始终为fasle。
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;
}
// 这里是真正的判断节点,判断此类上是否有@Aspect注解,且没有被ajc编译器编译过。
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
// Spring作者的习惯,构建一个元信息方便后续操作。
AspectMetadata amd = new AspectMetadata(beanType, beanName);
// 这里涉及到Aspect框架的相关知识点,不太明白,看注释是判断AspectJ实例化的类型是否匹配。
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// 构建一个能解析出Aspect元信息的工厂。
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 解析此Aspect,获取Advisor。
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
// 做缓存。
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
// 收集Advisor。
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;
}
代码逻辑有点多,但不复杂。首先就是从容器内取出所有bean的beanName。然后遍历,获取其Class,利用反射判断其是否添加了@Aspect注解,如果是则要准备解析此类,将其中的每一个Advice都解析成Advisor,然后缓存起来。
下面我们就准备研究如何AspectJAdvisorFactory去调用getAdvisors方法解析此Class,返回Advisor。
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
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.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
// getAdvisorMethods获取此Class内所有的非Pointcut方法。@Pointcut注解标注的方法不是Advice。无法封装成Advisor。
for (Method method : getAdvisorMethods(aspectClass)) {
// 遍历非Pointcut方法,获取Advisor,如果此方法没有添加@BeforeAdvice等通知方法,则返回null。
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), 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;
}
getAdvisors方法核心逻辑主要就是获取此Class的所有没有标注@Pointcut注解的方法,然后遍历方法,每个方法都是一个Advisor。@Pointcut注解标注的方法仅表示一个切点,没有通知相关的代理逻辑。下面就看看具体根据Method创建Advisor的逻辑代码。
@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
// 创建Pointcut对象,用于将来过滤要代理的bean。
// 内部会判断,如果方法上没有[Pointcut.class, Around.class, Before.class,
// After.class, AfterReturning.class, AfterThrowing.class]任何一个通知注解,则返回null。
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
// 根据切点Pointcut和Method对象等信息创建Advisor。
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
这里就接近真相了,可以看到,getAdvisor方法会根据Pointcut切点和Method对象以及切面元信息等创建一个Advisor。但是有一点还不清晰,我们都知道Advisor主要是由Pointcut和Advice构成的,Pointcut已经有了,那代理逻辑也就是Advice在哪里呢?查看InstantiationModelAwarePointcutAdvisorImpl的构造器创建对象过程。
这里我们debug看一下。
可以看到此Advisor内部维护了非常多的信息,其中包含了Pointcut对象,此Advisor所在的Class,所在的方法及名称,方法参数类型等信息。最下边的instantiatedAdvice就是此Advisor对应的Advice,它会在这个构造器内被创建,看一下创建过程。
@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// 根据Aspect元信息获取此切面所在的Class类
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;
// 重点,这里就会根据方法上注解的类型选择创建具体的Advice类
// AtAround, AtBefore, AtAfter, AtAfterReturning, AtAfterThrowing中其中一个。
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;
}
到这里就会发现,切面内的每一个通知方法都会被解析为一个Advice,具体的类型根据注解来决定。
如此,我们debug回到创建好的一个Advisor处观察结果。
如此,基本就完成了Aspect的解析和Advisor的创建。回到最开始我们的shouldSkip方法处,查看一下一共扫描出来多少个Advisor。
看一下debug级别下的日志。
2021-01-18 00:33:12 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory Found AspectJ method: public java.lang.Object com.binghuazheng.aop.config.UserAspect.adviceAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable
2021-01-18 00:53:17 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory Found AspectJ method: public void com.binghuazheng.aop.config.UserAspect.adviceBefore(org.aspectj.lang.JoinPoint)
2021-01-18 00:53:28 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory Found AspectJ method: public void com.binghuazheng.aop.config.UserAspect.adviceAfter()
2021-01-18 00:53:28 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory Found AspectJ method: public void com.binghuazheng.aop.config.UserAspect.adviceAfterReturning()
2021-01-18 00:53:28 [main] DEBUG o.s.a.a.a.ReflectiveAspectJAdvisorFactory Found AspectJ method: public void com.binghuazheng.aop.config.UserAspect.adviceAfterThrowing(java.lang.Throwable)
可以确定,我们切面内的5个Advice方法都被解析成了Advisor。
如此,收集并解析Advisor的步骤就完成了,下面开始进入Bean的实例化步骤。
2.实例化Bean
这个步骤本质上就是IOC的过程,因为上面的解析Advisor过程仅仅是IOC流程中的一个后置处理器的方法中实现的,接下来Spring会通过此Bean对应的Class,使用构造器创建对象,然后进行依赖注入操作,直到执行到initializeBean方法处,就算bean实例化结束。
3.判断此Bean是否需要被代理
这里就要再一次进入到AnnotationAwareAspectJAutoProxyCreator这个类中,Spring判断此Bean是否需要被代理的执行节点是在initializeBean方法中BeanPostProcessor中的postProcessAfterInitialization方法中。我们跟代码看一看它是如何实现这部分功能的?
上面可以看到,程序执行到了BeanPostProcessor的postProcessAfterInitialization方法,此方法也可以说是IOC流程的最后一个阶段了。bean已经实例化且初始化方法执行完。我们已经拿到了目标对象userService。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 判断是否非Aspect基础类,并且shouldSkip内部会查找收集所有Advisor。
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 获取此bean所匹配的Advisor
// 如果没有合适的Advisor,说明此bean不需要被代理。
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能匹配的Advisor,如果返回null,就不需要代理此对象。进入此方法内看看判断的逻辑。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取所有的Advisor
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 获取能匹配此Class的Advisor
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
// Advisor的排序,不同切面的Advisor执行顺序,这里就可以排出来。
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
上边方法第一行的findCandidateAdvisors()方法就是找到容器内所有的Advisor,第二行就是根据此bean的class,过滤这些Advisors。
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
// 遍历所有Advisor,找到合适的Advisor
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
// 判断此Advisor是否匹配此Class
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
此方法基本可以看出,Spring会遍历所有的Advisors,然后通过canApply方法判断此Advisor是否匹配此Class。快接近真相了,进入canApply方法看一看。
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
// 判断此Advisor内的Pointcut,此切点能否匹配此类
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
// 获取此切点的方法匹配器
MethodMatcher methodMatcher = pc.getMethodMatcher();
// 某些自定义的切点,内部的方法匹配器是恒为true。那就直接返回true。不需要再逐个方法进行下面的匹配过滤。
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
// 此类可能已经被JDK代理,获取它的目标类Class
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
// 获取此目标类的所有接口Class
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
// 遍历所有的Class
for (Class<?> clazz : classes) {
// 反射获取所有的方法
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
// 遍历所有方法,开始进行方法匹配
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
// 有一个方法能满足此切点,就可以直接返回。
return true;
}
}
}
return false;
}
这里就可以看到Spring会先根据此切点,进行类的匹配,如果不匹配,则直接返回,不需要再执行后面方法的过滤;而后,就准备获取所有方法对象,逐个方法匹配,若有一个满足条件,则返回true,表明此Class类需要被代理。
好,可以看到满足条件的5个Advisor,再加上默认扩展添加的一个Advisor,一共6个Advisor。此bean是否需要被代理的判断这里就结束了,接下来就要开始执行代理过程了。
4.将目标对象变成代理对象
获取代理对象,需要需要使用代理工厂来选择具体类型的代理。因为面向接口编程么,默认一般我们使用JDK这种。当然也可以手动指定Cglib代理。Spring会根据你自己的目标类,自行判断使用什么代理。
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
// 将此bean的beanClass缓存在其beanDefinition内,可用于获取此bean的原生Class
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 代理工厂,用于获取代理类,并设置一些基本的配置属性,比如代理方式等。
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 决定是否使用Cglib还是JDK
if (!proxyFactory.isProxyTargetClass()) {
// 这里除非提前设置beanDefinition,否则默认就是false
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 这里判断此Class是否有接口,添加可以代理的接口到proxyFactory
// 如果没有接口,则设置ProxyTargetClass为true,表示使用Cglib方式。
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 这里会将所有的方法拦截器或Advice转换为Advisor。一般我们都是Advisor这种,不需要再转换包装。
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 添加Advisor
proxyFactory.addAdvisors(advisors);
// 添加目标类对象
proxyFactory.setTargetSource(targetSource);
// 自定义此代理工厂,默认不需要配置,可以重写此方法
customizeProxyFactory(proxyFactory);
// 冻结配置,不能再添加Advice。默认为false.
proxyFactory.setFrozen(this.freezeProxy);
// 预先过滤,后期再执行代理方法时,Pointcut的类过滤器匹配类时,可以短路匹配,直接执行方法匹配。
// 默认为true,因为前面已经执行过类的匹配过滤,不需要重复执行。
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 执行动态代理
return proxyFactory.getProxy(getProxyClassLoader());
}
以上代码,仅仅是在代理前,做一些初始化配置,封装配置信息在ProxyFactory。我们都知道Spring的动态代理由由两种,JDK和Cglib。来看一看它是如何判断使用哪一种的。
public Object getProxy(@Nullable ClassLoader classLoader) {
// 选择具体的代理方式去获取代理对象
return createAopProxy().getProxy(classLoader);
}
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 判断使用JDK还是Cglib
// isOptimize判断是否可以对此代理优化,默认false
// isProxyTargetClass则可以通过应用注解配置,强制使用JDK或Cglib.默认false,JDK。
// hasNoUserSuppliedProxyInterfaces主要判断是否实现了接口
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
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.");
}
// 这里如果此Class是接口,或者已经继承了Proxy类,则可以使用JDK代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 其它情况则使用Cglib代理
return new ObjenesisCglibAopProxy(config);
}
else {
// 面向接口编程,默认就是执行到此处,使用JDK动态代理。
return new JdkDynamicAopProxy(config);
}
}
这里的判断逻辑很简单,如果不手动指定ProxyTargetClass代理方式,Spring就是根据hasNoUserSuppliedProxyInterfaces内部判断,会根据此Class的接口有无来判断JDK或Cglib,这里算是工厂加策略的体现吧。
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
这里可以看到,JDK动态代理就是如此呈现在我们面前,看着眼熟吧。
Cglib的太繁琐,但功能很强大,就不展示了。
5.将代理对象放入Spring容器
代理对象出来了,我们看一看它内部属性。
以上可以看到,它内部的代理逻辑就是JdkDynamicAopProxy类,此类实现了InvocationHandler,重写了其invoke方法。同时内部存储着ProxyFactory这个代理所需的各种信息,包括目标类,Advisor,接口等信息。
好了,代理对象已经有了,再往下就要将此bean方法Spring容器中,后续还要注入到其它bean。此过程是IOC的基本流程。我们直接看一看容器中有没有我们的代理对象吧。
6.调用代理对象方法
到这里,我们可以认为代理对象已经注入到其它bean,比如说已经注入到我们的Controller内,然后一个请求过来后,就会通过此代理对象调用JDK代理InvocationHandler的invoke方法。我们主要看看其实现JdkDynamicAopProxy的逻辑。
前面分析Spring实现思路时我们说过,Spring此时并不知道当前方法是否需要被代理,所以需要一个过程就是遍历所有的Advisors,判断每一个Advisor的Pointcut是否匹配此方法。此过程就在JdkDynamicAopProxy的invoke方法内。
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
// TargetSource里有我们的目标类对象
TargetSource targetSource = this.advised.targetSource;
Object target = null;
// 下面几个特殊的方法,equals和hashCode方法等,如果目标类没有重写,则执行这里的逻辑。
// 一般我们不代理这些方法。
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;
// 这里可以将代理对象暴露给AopContext,防止同一Service的两个代理方法直接调用,造成代理失效问题。
// 默认不会暴露。
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
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
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
// 这里会遍历之前存入ProxyFactory的Advisors,判断此方法是否需要被代理。
// 找出匹配的Advisor,
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
// 准备执行代理方法和目标方法
// 这里如果找不到chain方法拦截器,说明此方法不需要被代理,执行下面逻辑,直接执行目标方法。
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
// 如果有方法拦截器链,则要依次执行各种Advice
// 已知代理对象,目标对象,目标方法,方法参数,目标类Class,方法拦截器链,基本就可以执行代理方法和目标方法了。
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 准备执行方法拦截器链里的各种Advice逻辑。
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);
}
}
}
上面this.advised.getInterceptorsAndDynamicInterceptionAdvice方法就是获取此方法匹配的MethodInterceptor chain,也就是方法拦截器。来看一看此方法的逻辑。
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
// 遍历匹配此Class的所有Advisor
for (Advisor advisor : config.getAdvisors()) {
// 默认情况下,我们的Advisor都是此接口类型
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// 判断是否匹配此Class,isPreFiltered之前创建代理类时,设置为true。因为已经判断过Class是否匹配,不需要再判断。
// isPreFiltered会短路掉后面的判断条件
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
// 判断方法是否匹配
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
// 获取MethodInterceptor链
// 这里会判断Advisor是否实现MethodInterceptor。
// before通知/AfterReturn通知/throw通知,在此处会通过适配器转换为MethodInterceptor。
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) {
// 这里应该是处理@DeclareParents这种注解的。功能挺强大,但用处不多。
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
此方法内部,它会获取ProxyFactory中的Advisors,遍历所有的Advisors,先判断Pointcut是否匹配此Class,由于在获取代理对象时,已经提前判断过,所以这里就通过isPreFiltered直接过滤,不需要再次判断。紧接着就是进行方法的匹配判断,如果方法匹配成功,则根据Advisor获取MethodInterceptor对象。一般的Advice都已经实现了MethodInterceptor。只有@Before,@AfterReturning以及@AfterThrowing需要用适配器模式转换为MethodInterceptor,这样后期才能统一执行。
好,看一看我们这里有多少个MethodInterceptor。
算上第一个额外扩展的MethodInterceptor,正好6个。如果没有匹配到,就会进入第一个逻辑,执行原目标方法。
回到实际,接下来就要准备执行这些MethodInterceptor Chain了,具体各种类型的MethodInterceptor执行有点复杂,这里暂时不详细介绍,后续我会专门写一篇博客解释Advice也就是MethodInterceptor的调用链情况。
但有一点这里要知道,就是下面代码。
// We need to create a method invocation...
// 如果有方法拦截器链,则要依次执行各种Advice
// 已知代理对象,目标对象,目标方法,方法参数,目标类Class,方法拦截器链,基本就可以执行代理方法和目标方法了。
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 准备执行方法拦截器链里的各种Advice逻辑。
retVal = invocation.proceed();
如代码里注释,知道了这么信息,并且其中每个切面内的Advice方法,都可以在MethodInterceptor内部获取到,那代理功能就基本实现了。
总结
AOP的实现源码看起来很复杂,但如果心里面提前有个大概的实现思路,那debug时候,就不太容易迷失在代码中。Spring AOP在Spring框架中,非常重要,它是很多其它功能的实现基础,比如说事务,Spring的异步等功能都是利用AOP来实现的,所以掌握Spring AOP的实现还是很重要的。