Spring Aop標籤解析原理詳解
對於Spring Aop的實現,是非常複雜的,其實現過程主要包含xml標籤的解析,切面表示式的解析,判斷bean是否需要應用切面邏輯,以及使用Jdk代理或者是Cglib代理生成代理類。本文主要講解Xml標籤的解析的實現原理,在接下來幾篇文章中,會依次對Spring Aop剩餘的實現過程進行講解。
關於Spring Aop的實現,由於其是使用自定義標籤進行驅動的,因而讀者朋友如果對Spring如何實現自定義標籤比較熟悉,那麼可以繼續往下閱讀。
1. Aop使用示例
首先我們聲明瞭一個切面類如下:
@Aspect public class DogAspect { @Around("execution(public void com.business.Dog.*(..))") public Object aspect(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("before run."); Object result = joinPoint.proceed(); System.out.println("after run."); return result; } }
該切面類主要用於環繞com.business.Dog類中的public型別的,並且返回值是void的所有方法,下面我們就在com.business包中宣告一個Dog類如下:
public class Dog { public void run() { System.out.println("Tidy is running."); } }
這裡切面類和目標類都已經宣告完成,但如果不將其加入Spring容器中,其是不會工作的,加入容器的方式非常簡單,下面就是一種方式:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="dog" class="com.business.Dog"/> <bean id="aspect" class="com.business.DogAspect"/> <aop:aspectj-autoproxy/> </beans>
這裡需要說明的是,將DogAspect宣告為一個bean並不能使其工作,因為其也僅僅只是一個bean而已,要使其工作還需要使用上面的<aop:aspectj-autoproxy/>標籤實現切面的自動裝配。下面使我們執行整個程式的驅動類:
public class DogApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Dog dog = context.getBean(Dog.class); dog.run(); } }
執行結果如下:
before run. Tidy is running. after run.
可以看到,我們在驅動類中獲取的是Dog的例項,並且執行其run()方法,但是最終的執行結果中也運行了切面類中的環繞邏輯。
2. 實現原理
根據前面對Spring自定義標籤使用的講解,我們知道這裡<aop:aspectj-autoproxy/>就是一個自定義標籤,並且該標籤會在相應jar包的META-INF目錄下有一個spring.handlers檔案,該檔案中聲明瞭解析該標籤的類。通過檢視該類我們得到如下配置:
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
這裡我們開啟AopNamespaceHandler,其實現如下:
public class AopNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); } }
可以看到,我們需要解析的標籤解析器在這個類中進行了註冊,即AspectJAutoProxyBeanDefinitionParser,開啟這個類其主要實現如下:
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser { // 解析標籤的時候將會執行的方法 @Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { // 註冊一個型別為AnnotationAwareAspectJAutoProxyCreator的bean到Spring容器中 AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); // 通過讀取配置檔案對擴充套件相關屬性 extendBeanDefinition(element, parserContext); return null; } private void extendBeanDefinition(Element element, ParserContext parserContext) { // 獲取前面註冊的AnnotationAwareAspectJAutoProxyCreator對應的BeanDefinition BeanDefinition beanDef = parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); // 解析當前標籤的子標籤 if (element.hasChildNodes()) { addIncludePatterns(element, parserContext, beanDef); } } // 解析子標籤中的name屬性,其可以有多個,這個name屬性最終會被新增到 // AnnotationAwareAspectJAutoProxyCreator的includePatterns屬性中, // Spring在判斷一個類是否需要進行代理的時候會判斷當前bean的名稱是否與includePatterns中的 // 正則表示式相匹配,如果不匹配,則不進行代理 private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) { ManagedList<TypedStringValue> includePatterns = new ManagedList<>(); NodeList childNodes = element.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); if (node instanceof Element) { Element includeElement = (Element) node; // 解析子標籤中的name屬性 TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name")); valueHolder.setSource(parserContext.extractSource(includeElement)); includePatterns.add(valueHolder); } } // 將解析到的name屬性設定到AnnotationAwareAspectJAutoProxyCreator // 的includePatterns屬性中 if (!includePatterns.isEmpty()) { includePatterns.setSource(parserContext.extractSource(element)); beanDef.getPropertyValues().add("includePatterns", includePatterns); } } }
這裡我們繼續看AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
方法,該方法的實現如下:
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) { // 註冊AnnotationAwareAspectJAutoProxyCreator的BeanDefinition BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); // 解析標籤中的proxy-target-class和expose-proxy屬性值, // proxy-target-class主要控制是使用Jdk代理還是Cglib代理實現,expose-proxy用於控制 // 是否將生成的代理類的例項防禦AopContext中,並且暴露給相關子類使用 useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); // 將註冊的BeanDefinition封裝到BeanComponentDefinition中 registerComponentIfNecessary(beanDefinition, parserContext); } private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) { if (sourceElement != null) { // 解析標籤中的proxy-target-class屬性值 boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)); if (proxyTargetClass) { // 將解析得到的proxy-target-class屬性值設定到上面生成的 // AnnotationAwareAspectJAutoProxyCreator的BeanDefinition的proxyTargetClass // 屬性中 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } // 解析標籤中的expose-proxy屬性值 boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE)); if (exposeProxy) { // 將解析得到的expose-proxy屬性值設定到 // AnnotationAwareAspectJAutoProxyCreator的exposeProxy屬性中 AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry); } } } private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) { // 如果生成的AnnotationAwareAspectJAutoProxyCreator的BeanDefinition成功,則將其封裝到 // BeanComponentDefinition中,並且將其新增到ParserContext中 if (beanDefinition != null) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME); parserContext.registerComponent(componentDefinition); } }
這裡可以看到AnnotationAwareAspectJAutoProxyCreator的BeanDefinition在第一步進行了註冊,然後讀取標籤中的proxy-target-class和expose-proxy屬性,並且將屬性值設定到生成的BeanDefinition中。最後將生成的BeanDefinition註冊到ParserContext中。這裡我們繼續看AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement))方法,其實現如下:
@Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) { // 註冊AnnotationAwareAspectJAutoProxyCreator型別的BeanDefinition return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } @Nullable private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); // 如果已經註冊過AnnotationAwareAspectJAutoProxyCreator的Definition,如果其 // 和當前將要註冊的BeanDefinition是同一個型別,則不再註冊,如果不同,則判斷其優先順序比 // 當前將要註冊的BeanDefinition要高,則將其類名設定為當前要註冊的BeanDefinition的名稱 if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) { BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME); if (!cls.getName().equals(apcDefinition.getBeanClassName())) { int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName()); int requiredPriority = findPriorityForClass(cls); if (currentPriority < requiredPriority) { apcDefinition.setBeanClassName(cls.getName()); } } return null; } // 如果不存在已經註冊的Aop的bean,則生成一個,並且設定其執行優先順序為最高優先順序,並且標識 // 該bean為Spring的系統Bean,設定完之後則對該bean進行註冊 RootBeanDefinition beanDefinition = new RootBeanDefinition(cls); beanDefinition.setSource(source); beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
可以看到,在真正生成AnnotationAwareAspectJAutoProxyCreator的BeanDefinition的時候,首先會判斷是否已經生成過該bean,這裡不會將已經生成的bean進行覆蓋;如果沒有生成該bean,則建立一個並進行註冊。這裡需要說明的是,Spring註冊該bean的時候使用的order是Ordered.HIGHEST_PRECEDENCE,這麼設定的原因在於Spring使用該bean進行切面邏輯的織入,因而這個bean必須在所有使用者自定義的bean例項化之前進行例項化,而使用者自定義的bean的例項化優先順序是比較低的,這樣才能實現織入代理邏輯的功能。
-
小結
本文首先使用一個簡單的示例展示了Spring Aop的使用方式,然後對標籤中的<aop:aspectj-autoproxy/>解析過程進行了講解。可以看到,該標籤的解析過程最終是生成了一個AnnotationAwareAspectJAutoProxyCreator的BeanDefinition。