天天看點

吃透Spring源碼(十八):AOP建立過程之注解配置方式

一,例子準備

切面類:LogUtil.java

@Aspect
@Component
public class LogUtil {

    @Pointcut("execution(public Integer com.mashibing.aop.annotation.service.MyCalculator.*(Integer,Integer))")
    public void myPointCut(){}

    @Around("myPointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Signature signature = pjp.getSignature();
        Object[] args = pjp.getArgs();
        Object result = null;
        try {
            System.out.println("log---環繞通知start:"+signature.getName()+"方法開始執行,參數為:"+Arrays.asList(args));
            result = pjp.proceed(args);
            System.out.println("log---環繞通知stop"+signature.getName()+"方法執行結束");
        } catch (Throwable throwable) {
            System.out.println("log---環繞異常通知:"+signature.getName()+"出現異常");
            throw throwable;
        }finally {
            System.out.println("log---環繞傳回通知:"+signature.getName()+"方法傳回結果是:"+result);
        }
        return result;
    }

    @Before(value = "myPointCut()")
    private int start(JoinPoint joinPoint){
        //擷取方法簽名
        Signature signature = joinPoint.getSignature();
        //擷取參數資訊
        Object[] args = joinPoint.getArgs();
        System.out.println("log---"+signature.getName()+"方法開始執行:參數是"+Arrays.asList(args));
        return 100;
    }

    @After("myPointCut()")
    public static void logFinally(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        System.out.println("log---"+signature.getName()+"方法執行結束。。。。。over");
    }


    @AfterReturning(value = "myPointCut()",returning = "result")
    public static void stop(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        System.out.println("log---"+signature.getName()+"方法執行結束,結果是:"+result);
    }

    @AfterThrowing(value = "myPointCut()",throwing = "e")
    public static void logException(JoinPoint joinPoint,Exception e){
        Signature signature = joinPoint.getSignature();
        System.out.println("log---"+signature.getName()+"方法抛出異常:"+e.getMessage());
    }
}
           

需要被代理的類:MyCalculator.java

@Service
public class MyCalculator {
    public Integer add(Integer i, Integer j) throws NoSuchMethodException {
        Integer result = i+j;
        return result;
    }

    public Integer sub(Integer i, Integer j) throws NoSuchMethodException {
        Integer result = i-j;
        return result;
    }

    public Integer mul(Integer i, Integer j) throws NoSuchMethodException {
        Integer result = i*j;
        return result;
    }

    public Integer div(Integer i, Integer j) throws NoSuchMethodException {
        Integer result = i/j;
        return result;
    }

    public Integer show(Integer i){
        System.out.println("show .....");
        return i;
    }
}
           

配置類:SpringConfiguration.java

@Configuration
@ComponentScan(basePackages = "com.bobo.aop.annotation")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
           

測試類:MyTest.java

public class MyTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    }
}
           

二,源碼分析

1,AnnotationAwareAspectJAutoProxyCreator的注入

invokeBeanFactoryPostProcessors()

方法中,會去執行ConfigurationClassPostProcessor(BFPP)的postProcessBeanDefinitionRegistry()方法,用于解析

SpringConfiguration

配置類:

@Configuration
@ComponentScan(basePackages = "com.bobo.aop.annotation")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}
           

@EnableAspectJAutoProxy

注解類:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
           

首先,解析

@ComponentScan

注解,把

com.bobo.aop.annotation

包名下面的被

@Component

注解修飾的

LogUtil.java

MyCalculator.java

注入到

BeanFactory

容器的

BeanDefinitionMap

中。

其次,解析

@Import(AspectJAutoProxyRegistrar.class)

注解,并執行個體化

AspectJAutoProxyRegistrar

類,發現其實作了

ImportBeanDefinitionRegistrar

接口,并且調用其

registerBeanDefinitions()

方法:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

	/**
	 * 注冊、更新和配置自動代理建立器依賴對應的proxyTargetClass屬性在解析@Configuration類
	 */
	@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);
			}
		}
	}

}
           

方法中調用

AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry)

方法把AnnotationAwareAspectJAutoProxyCreator.class包裝成BeanDefinition注入到BeanDefinitionMap中:

public abstract class AopConfigUtils {
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {
		// 注入AnnotationAwareAspectJAutoProxyCreator.class
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}
    
    private static BeanDefinition registerOrEscalateApcAsRequired(
			Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {

		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        // 注入帶BeanDefinitionMap中
        // AUTO_PROXY_CREATOR_BEAN_NAME=org.springframework.aop.config.internalAutoProxyCreator
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}
}
           

2,AnnotationAwareAspectJAutoProxyCreator類介紹

吃透Spring源碼(十八):AOP建立過程之注解配置方式

AnnotationAwareAspectJAutoProxyCreator繼承于AspectJAwareAdvisorAutoProxyCreator,主要負責對使用注解定義的@Aspect進行查找和解析。

  • AspectJAwareAdvisorAutoProxyCreator:當我們使用xml進行定義切面時會注入此類
  • AnnotationAwareAspectJAutoProxyCreator:當我們使用

    @EnableAspectJAutoProxy

    注解方式來定義切面時會注入此類

AbstractAdvisorAutoProxyCreator

findCandidateAdvisors()

方法定義的是從BeanDefinitionMap中查找

Advisor

類,是以

AspectJAwareAdvisorAutoProxyCreator

繼承了此方法,符合xml方式的定義查找

Advisor

類。

但是注解方式是對XMl的擴充,AnnotationAwareAspectJAutoProxyCreator繼承于

AspectJAwareAdvisorAutoProxyCreator

并且重寫了

findCandidateAdvisors()

,加入了注解的方式去掃描Advisor。例如去掃描我們本例子所定義的

LogUtil

切面類。

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
    /**
	 * 查找Advisor
	 */
	@Override
	protected List<Advisor> findCandidateAdvisors() {
        
		// 調用父類方法從BeanDefinitionMap中查找Advisor接口的BeanDefinition,并執行個體化
		List<Advisor> advisors = super.findCandidateAdvisors();
	
		if (this.aspectJAdvisorsBuilder != null) {
            // 注解方式的擴充,查找使用注解定義的@Aspect
			// 找到系統中使用@Aspect标注的bean,并且找到該bean中使用@Before,@After等标注的方法,
			// 将這些方法封裝為一個個Advisor
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}
}
           

3,代理類的建立

本例子中依然是

MyCalculator.java

需要建立代理類,當然,配置類

SpringConfiguration.java

也是需要被建立代理的,我們前面文章已經對配置類做了分析,不了解的請移步吃透Spring源碼(十六):ConfigurationClassPostProcessor詳細介紹

這裡以執行個體化MyCalculator來分析:

源碼還是在getBean()—>doGetBean()—>createBean()—>doCreateBean()—>initializeBean()—>applyBeanPostProcessorsAfterInitialization()執行Bean的後置處理器

AnnotationAwareAspectJAutoProxyCreator

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
		implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			// 擷取目前bean的key:如果beanName不為空,則以beanName為key,如果為FactoryBean類型,
			// 前面還會添加&符号,如果beanName為空,則以目前bean對應的class為key
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			// 判斷目前bean是否正在被代理,如果正在被代理則不進行封裝
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				// 如果它需要被代理,則需要封裝指定的bean
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		
		// 擷取目前bean的Advices和Advisors
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		// 對目前bean的代理狀态進行緩存
		if (specificInterceptors != DO_NOT_PROXY) {
			// 對目前bean的代理狀态進行緩存
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// 根據擷取到的Advices和Advisors為目前bean生成代理對象
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			// 緩存生成的代理bean的類型,并且傳回生成的代理bean
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}
}
           

調到父類的postProcessAfterInitialization()方法中,然後在wrapIfNecessary()方法中查找Advisor并建立代理類!

在wrapIfNecessary中查找Advisor會調用到子類

AnnotationAwareAspectJAutoProxyCreator

來查找注解定義的Advisor并執行個體化。

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {
    /**
	 * 查找Advisor
	 */
	@Override
	protected List<Advisor> findCandidateAdvisors() {
        
		// 調用父類方法從BeanDefinitionMap中查找Advisor接口的BeanDefinition,并執行個體化
		List<Advisor> advisors = super.findCandidateAdvisors();
	
		if (this.aspectJAdvisorsBuilder != null) {
            // 注解方式的擴充,查找使用注解定義的@Aspect
			// 找到系統中使用@Aspect标注的bean,并且找到該bean中使用@Before,@After等标注的方法,
			// 将這些方法封裝為一個個Advisor
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}
}
           

其中

BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors()

是對注解定義的Advisor進行查找和初始化:

public class BeanFactoryAspectJAdvisorsBuilder {
    /**
	 * 尋找Aspect注解的切面,然後解析他的方法,通過注解來生成對應的通知器Advisor
	 */
	public List<Advisor> buildAspectJAdvisors() {
		// 擷取切面名字清單
		List<String> aspectNames = this.aspectBeanNames;

		// 緩存字段aspectNames沒有值,注意執行個體化第一個單執行個體bean的時候就會觸發解析切面
		if (aspectNames == null) {
			// 雙重檢查
			synchronized (this) {
				aspectNames = this.aspectBeanNames;
				if (aspectNames == null) {
					// 用于儲存所有解析出來的Advisors集合對象
					List<Advisor> advisors = new ArrayList<>();
					// 用于儲存切面的名稱的集合
					aspectNames = new ArrayList<>();
					/**
					 * AOP功能中在這裡傳入的是Object對象,代表去容器中擷取到所有的元件的名稱,然後再
					 * 進行周遊,這個過程是十分的消耗性能的,是以說Spring會再這裡加入了儲存切面資訊的緩存。
					 * 但是事務功能不一樣,事務子產品的功能是直接去容器中擷取Advisor類型的,選擇範圍小,且不消耗性能。
					 * 是以Spring在事務子產品中沒有加入緩存來儲存我們的事務相關的advisor
					 */
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
					// 周遊我們從IOC容器中擷取處的所有Bean的名稱
					for (String beanName : beanNames) {
						// 判斷目前bean是否為子類定制的需要過濾的bean
						if (!isEligibleBean(beanName)) {
							continue;
						}
						
						// 通過beanName去容器中擷取到對應class對象
						Class<?> beanType = this.beanFactory.getType(beanName, false);
						if (beanType == null) {
							continue;
						}
						// 判斷目前bean是否使用了@Aspect注解進行标注
						if (this.advisorFactory.isAspect(beanType)) {
							aspectNames.add(beanName);
							// 對于使用了@Aspect注解标注的bean,将其封裝為一個AspectMetadata類型。
							// 這裡在封裝的過程中會解析@Aspect注解上的參數指定的切面類型,如perthis
							// 和pertarget等。這些被解析的注解都會被封裝到其perClausePointcut屬性中
							AspectMetadata amd = new AspectMetadata(beanType, beanName);
							// 判斷@Aspect注解中标注的是否為singleton類型,預設的切面類都是singleton類型
							if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
								// 将BeanFactory和目前bean封裝為MetadataAwareAspect-
								// InstanceFactory對象,這裡會再次将@Aspect注解中的參數都封裝
								// 為一個AspectMetadata,并且儲存在該factory中
								MetadataAwareAspectInstanceFactory factory =
										new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
								// 通過封裝的bean擷取其Advice,如@Before,@After等等,并且将這些
								// Advice都解析并且封裝為一個個的Advisor
								List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
								// 如果切面類是singleton類型,則将解析得到的Advisor進行緩存,
								// 否則将目前的factory進行緩存,以便再次擷取時可以通過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.
								// 如果@Aspect注解标注的是perthis和pertarget類型,說明目前切面
								// 不可能是單例的,因而這裡判斷其如果是單例的則抛出異常
								if (this.beanFactory.isSingleton(beanName)) {
									throw new IllegalArgumentException("Bean with name '" + beanName +
											"' is a singleton, but aspect instantiation model is not singleton");
								}
								// 将目前BeanFactory和切面bean封裝為一個多例類型的Factory
								MetadataAwareAspectInstanceFactory factory =
										new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
								// 對目前bean和factory進行緩存
								this.aspectFactoryCache.put(beanName, factory);
								advisors.addAll(this.advisorFactory.getAdvisors(factory));
							}
						}
					}
					this.aspectBeanNames = aspectNames;
					return advisors;
				}
			}
		}

		if (aspectNames.isEmpty()) {
			return Collections.emptyList();
		}
		// 通過所有的aspectNames在緩存中擷取切面對應的Advisor,這裡如果是單例的,則直接從advisorsCache
		// 擷取,如果是多例類型的,則通過MetadataAwareAspectInstanceFactory立即生成一個
		List<Advisor> advisors = new ArrayList<>();
		for (String aspectName : aspectNames) {
			List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
			// 如果是單例的Advisor bean,則直接添加到傳回值清單中
			if (cachedAdvisors != null) {
				advisors.addAll(cachedAdvisors);
			}
			else {
				// 如果是多例的Advisor bean,則通過MetadataAwareAspectInstanceFactory生成
				MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
				advisors.addAll(this.advisorFactory.getAdvisors(factory));
			}
		}
		return advisors;
	}
}
           

具體Advisor類的建立,和代理類的建立和上一篇xml定義方式的建立過程是一樣的,這裡就不再介紹了,請直接移步到上一篇文章即可:吃透Spring源碼(十七):AOP建立過程之XML配置方式

三,總結

  • 注解方式的Advisor掃描是通過

    AnnotationAwareAspectJAutoProxyCreator

    類來完成的,掃描Advisor的時機與xml方式不同,注解的掃描時機是在建立代理類的時候(在BPP的後置處理器裡面),而XML方式的Advisor掃描是在執行個體化第一個對象之前的

    AbstractAutoProxyCreator#applyBeanPostProcessorsBeforeInstantiation()

    方法中
  • AnnotationAwareAspectJAutoProxyCreator

    繼承于

    AspectJAwareAdvisorAutoProxyCreator

    AspectJAwareAdvisorAutoProxyCreator

    的基礎上做了注解的擴充。

繼續閱讀