spring aop 實現原理(一)加載xml,註冊BeanDefinition

我的網站原文:chenmingyu.top/spring-sour…java

本文首先提供了一個實現了spring aop的demo,經過demo進行源碼分析node

經過讀源碼咱們能夠學習到spring是如何解析xml的,如何加載bean的,如何建立bean的,又是如何實現aop操做的,及其中各類操做的細節是如何實現的git

講源碼的時候我會進行一些取捨,根據上面的問題結合demo對主要流程進行講解,爭取能把上述的問題說明白github

源碼地址:github.com/mingyuHub/s…spring

Aop demo

代碼:數組

  1. 一個類UserController,提供一個方法login
  2. 一個切面UserAspect,切入點爲login方法
  3. 一個配置文件spring-aop.xml將類加載到spring容器中

建立UserController緩存

public class UserController {
    
    public void login(){
        System.out.println("登陸");
    }
}
複製代碼

定義一個切面UserAspect,不瞭解aop概念的能夠看一下:chenmingyu.top/springboot-…springboot

/** * @author: chenmingyu * @date: 2019/3/19 18:29 * @description: */
@Aspect
public class UserAspect {

    /** * 切入點 */
    @Pointcut("execution(public * com.my.spring.*.*(..))")
    public void execute(){
    }

    /** * 前置通知 * @param joinPoint */
    @Before(value ="execute()")
    public void Before(JoinPoint joinPoint) {
        System.out.println("執行方法以前");
    }

    /** * 後置通知 * @param joinPoint */
    @After(value ="execute()")
    public void After(JoinPoint joinPoint) {
        System.out.println("執行方法以後");
    }
}
複製代碼

自定義一個xml文件,名爲spring-aop.xml數據結構

<?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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
           ">

    <aop:aspectj-autoproxy    proxy-target-class="true"/>
    <bean id="userController" class="com.my.spring.UserController"/>
    <bean id="userAspect" class="com.my.spring.UserAspect"/>
</beans>
複製代碼

調試的代碼已經準備好,首先寫一個測試類測一下app

@Test
public void test(){
	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
    UserController userController = (UserController) applicationContext.getBean("userController");
    userController.login();
}
複製代碼

正常狀況下的輸出以下:

核心類

開始學習spring的源碼以前,有必要先了解一下spring中幾個較爲核心的類

先大概瞭解一下這些類是幹什麼的,沒必要深究,後續讀源碼的時候碰到會着重講解一下

先看一個spring容器的類圖:

  1. BeanFactory

    工廠類的頂級接口,用於獲取bean及bean的各類屬性,提供了ioc容器最基本的形式,給具體的IOC容器的實現提供了規範

    ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory是BeanFactory接口的子接口,最終的默認實現類是 DefaultListableBeanFactory,定義這多接口主要是爲了區分在 Spring 內部在操做過程當中對象的傳遞和轉化過程當中,對對象的數據訪問所作的限制

  2. DefaultListableBeanFactory

    ioc容器的實現,DefaultListableBeanFactory做爲一個能夠獨立使用的ioc容器,是整個Bean加載的核心部分,是spring註冊及加載bean的默認實現

  3. xmlBeanFactory

    xmlBeanFactory繼承DefaultListableBeanFactory,對其進行了擴展,增長了自定義的xml讀取器XmlBeanDefinitionReader,實現了個性化的BeanDefinitionReader讀取,主要做用就是將xml配置解析成BeanDefinition

  4. ApplicationContext

    ApplicationContext繼承自BeanFactory,包含BeanFactory的全部功能,狀況下都使用這個

  5. BeanDefinition

    Spring中用於包裝Bean的數據結構

  6. BeanDefinitionRegistory

    定義對BeanDefinition的各類增刪操做

  7. BeanDefinitionReader

    定義了讀取BeanDefinition的接口,主要做用是從資源文件中讀取Bean定義,XmlBeanDefinitionReader是其具體的實現類

  8. SingletonBeanRegistry

    定義對單例的註冊及獲取

  9. AliasRegistry

    定義對alias的簡單增刪改操做

瞭解一些核心類以後咱們就要開始讀源碼了

源碼分析

咱們的源碼以測試類爲入口開始分析

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
複製代碼

ApplicationContextBeanFactory都是用於加載Bean的,相比之下ApplicationContext提供了更多的擴展功能

ClassPathXmlApplicationContext最終調用下面這個構造函數

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {

   super(parent);
   //設置配置文件
   setConfigLocations(configLocations);
   if (refresh) {
      refresh();
   }
}
複製代碼

refresh()ApplicationContext的核心方法,這個方法基本包含了ApplicationContext提供的所有功能

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 準備刷新此上下文,不重要.
      prepareRefresh();
 
      // 初始化bean工廠,加載xml,解析默認標籤,解析自定義標籤,註冊BeanDefinitions
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 設置bean工廠的屬性,進行功能填充.
      prepareBeanFactory(beanFactory);

      try {
         // 子類覆蓋方法作額外的處理.
         postProcessBeanFactory(beanFactory);

         // 激活bean處理器.
         invokeBeanFactoryPostProcessors(beanFactory);

         // 註冊攔截bean建立的bean處理器,只是註冊
         registerBeanPostProcessors(beanFactory);

         // 初始化message源.
         initMessageSource();

         // 初始化應用消息廣播器
         initApplicationEventMulticaster();

         // 留給子類加載其餘bean.
         onRefresh();

         // 註冊Listeners bean,到消息廣播器
         registerListeners();

         // 實例化全部剩餘的(非lazy init)單例.
         finishBeanFactoryInitialization(beanFactory);

         // 刷新通知.
         finishRefresh();
      }

      catch (BeansException ex) {
         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;
      }
   }
}
複製代碼

spring是如何加載解析xml,註冊BeanDefinition的

加載解析xml和註冊BeanDefinition的邏輯都在obtainFreshBeanFactory()方法中,這個方法的做用是初始化bean工廠,加載xml,解析默認標籤和自定義標籤,將解析出來的BeanDefinition註冊到容器中

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   // 核心邏輯:建立bean工廠,解析xml,註冊BeanDefinition
   refreshBeanFactory();
   
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
   }
   return beanFactory;
}

------------------調用下面這個方法------------------

//調用AbstractRefreshableApplicationContext的refreshBeanFactory()
protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
            //建立bean工廠,類型爲DefaultListableBeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
            //核心邏輯:加載BeanDefinitions,進入這個方法
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
}    
複製代碼

createBeanFactory()方法邏輯特別簡單,咱們詳細說一下loadBeanDefinitions(beanFactory)方法

loadBeanDefinitions()這個方法實例化了一個XmlBeanDefinitionReader,介紹XmlBeanDefinitionReader以前須要介紹一下什麼是BeanDefinition

BeanDefinition:Bean的定義主要由BeanDefinition來描述的。做爲Spring中用於包裝Bean的數據結構

BeanDefinition做爲頂級接口 ,擁有三種實現:RootBeanDefinitionChildBeanDefinitionGenericBeanDefinition,三種BeanDefinition均繼承了AbstractBeanDefinition

spring經過BeanDefinition將配置文件中的標籤轉換爲容器的內部表示,並將BeanDefinition註冊到BeandefinitionRegistry中,spring中的容器主要以map的形式進行存儲BeanDefinition

再介紹一下BeanDefinitionReader

BeanDefinitionReader解決的是從資源文件(xml,propert)解析到BeanDefinition的過程,因此XmlBeanDefinitionReader的做用就很明顯了,將xml轉爲BeanDefinition

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 根據beanFactory建立XmlBeanDefinitionReader
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // 加載環境變量啥的
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
	
   // initBeanDefinitionReader
   initBeanDefinitionReader(beanDefinitionReader);
    //核心邏輯在這個方法裏,加載beanDefinitions
   loadBeanDefinitions(beanDefinitionReader);
}

複製代碼

loadBeanDefinitions(beanDefinitionReader);方法中咱們層層調用,發現最終解析xml調用的方法是doLoadBeanDefinitions(InputSource inputSource, Resource resource)

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
   try {
      //將xml轉成document對象 
      Document doc = doLoadDocument(inputSource, resource);
      //核心邏輯在這個方法裏,解析Document,註冊beanDefinition
      return registerBeanDefinitions(doc, resource);
   }
   catch (BeanDefinitionStoreException ex) {
      throw ex;
   }
   ......省略其餘異常信息
}

複製代碼

registerBeanDefinitions(doc, resource);方法中最終調用doRegisterBeanDefinitions(Element root)將由xml轉出來的Document(經過doc.getDocumentElement()取到Element 元素)解析成beandefinition並註冊

protected void doRegisterBeanDefinitions(Element root) {
  
   BeanDefinitionParserDelegate parent = this.delegate;
   this.delegate = createDelegate(getReaderContext(), root, parent);

   if (this.delegate.isDefaultNamespace(root)) {
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
            return;
         }
      }
   }
   //解析前置操做,留給子類實現
   preProcessXml(root);
   // 核心邏輯:解析並註冊BeanDefinition
   parseBeanDefinitions(root, this.delegate);
   //解析後置操做,留給子類實現
   postProcessXml(root);

   this.delegate = parent;
}

複製代碼

經歷層層調用咱們終於找到了核心方法,解析beanDefinition,咱們看看它是如何進行解析的

spring中的標籤包括默認標籤和自定義標籤兩種,可是兩種標籤的解析方式有很大的區別

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   //根據root元素的namespace是否等於http://www.springframework.org/schema/beans
   //經過上面的判斷,肯定是不是屬於spring的默認標籤 
   if (delegate.isDefaultNamespace(root)) {
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         Node node = nl.item(i);
         if (node instanceof Element) {
            Element ele = (Element) node;
            if (delegate.isDefaultNamespace(ele)) {
               //默認標籤解析
               parseDefaultElement(ele, delegate);
            }
            else {
               //自定義標籤解析 
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      //自定義標籤解析 
      delegate.parseCustomElement(root);
   }
}

複製代碼

默認標籤解析

parseDefaultElement()方法提供了對import,alias,bean,beans標籤的解析

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   //解析<import>標籤
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   //解析<alias>標籤 
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   //解析<bean>標籤 
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   //解析<beans>標籤 
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}

複製代碼

詳細的講解一下對bean標籤的解析

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   //步驟一,將Element解析成BeanDefinitionHolder. 
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      // 對 bdHolder 進行裝飾,針對自定義的屬性進行解析,根據自定義標籤找到對應的處理器,進行解析(自定義解析方式下面會細說)
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         //步驟二,註冊解析出來的BeanDefinition.
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

複製代碼

步驟一:先介紹下BeanDefinitionHolder:這個類是一個工具類,做用是承載BeanDefinition數據的

public class BeanDefinitionHolder implements BeanMetadataElement {

   private final BeanDefinition beanDefinition;

   private final String beanName;

   private final String[] aliases;
   ...
}   

複製代碼

delegate.parseBeanDefinitionElement(ele);方法中將Element轉化爲BeanDefinitionHolder,而且識別出bean的beanName和aliases(別名),獲得BeanDefinitionHolder其實默認標籤的解析就已經結束了

這個方法沒有啥複雜邏輯,挺清晰的

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
   return parseBeanDefinitionElement(ele, null);
}

----------------------調用下面這個方法------------------------------

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        //獲取id屬性
		String id = ele.getAttribute(ID_ATTRIBUTE);
    	//獲取name屬性
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<String>();
        //若是name屬性配置的不爲空
		if (StringUtils.hasLength(nameAttr)) {
            //按,; 分割成字符串數組
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            //加到別名的集合裏
			aliases.addAll(Arrays.asList(nameArr));
		}
		//把id屬性賦值給beanName,若是id爲空就在aliases別名的集合裏取第一個
		String beanName = id;
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
			if (logger.isDebugEnabled()) {
				logger.debug("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}

		if (containingBean == null) {
            //檢查beanName和aliases是否已經使用,若是使用了就報異常,沒使用就加到一個
			checkNameUniqueness(beanName, aliases, ele);
		}
		//建立了一個GenericBeanDefinition類型的BeanDefinition,並對個屬性進行填充
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
						beanName = this.readerContext.generateBeanName(beanDefinition);
						
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug("Neither XML 'id' nor 'name' specified - " +
								"using generated bean name [" + beanName + "]");
					}
				}
				catch (Exception ex) {
					error(ex.getMessage(), ele);
					return null;
				}
			}
			String[] aliasesArray = StringUtils.toStringArray(aliases);
            //建立一個BeanDefinitionHolder返回
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}
		return null;
	}    

複製代碼

獲得BeanDefinitionHolder後,剩下的就是註冊BeanDefinition

步驟二,調用BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())方法,註冊BeanDifinition

public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {

   // 使用beanName作惟一標識註冊
   String beanName = definitionHolder.getBeanName();
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // 使用全部別名進行註冊
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

複製代碼

經過beanName進行註冊:definitionHolder.getBeanName(),默認標籤和自定義標籤都使用這個方法進行BeanDefinition的註冊

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {

   Assert.hasText(beanName, "Bean name must not be empty");
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");
   // beanDefinition是否屬於AbstractBeanDefinition的實例
   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         // 進行校驗,主要是校驗methodOverrides與工程方法是否存在以及methodOverrides對應的方法存不存在
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Validation of bean definition failed", ex);
      }
   }

   BeanDefinition oldBeanDefinition;
   // 經過beanName獲取BeanDefinition是否已經註冊
   oldBeanDefinition = this.beanDefinitionMap.get(beanName);
   //若是已經註冊而且不容許覆蓋就拋出異常 
   if (oldBeanDefinition != null) {
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
               "': There is already [" + oldBeanDefinition + "] bound.");
      }
      else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
         // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
         if (this.logger.isWarnEnabled()) {
            this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                  "' with a framework-generated bean definition: replacing [" +
                  oldBeanDefinition + "] with [" + beanDefinition + "]");
         }
      }
      else {
         if (this.logger.isInfoEnabled()) {
            this.logger.info("Overriding bean definition for bean '" + beanName +
                  "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
         }
      }
   }
   else {
      //註冊BeanDefinition
      this.beanDefinitionNames.add(beanName);
      this.manualSingletonNames.remove(beanName);
      this.frozenBeanDefinitionNames = null;
   }
   this.beanDefinitionMap.put(beanName, beanDefinition);
   // 若是oldBeanDefinition經過上述校驗沒拋出異常或者beanName是單例
   if (oldBeanDefinition != null || containsSingleton(beanName)) {
      //則更新對應的緩存
      resetBeanDefinition(beanName);
   }
}

複製代碼

經過this.beanDefinitionMap.put(beanName, beanDefinition)這行代碼咱們就知道,spring使用一個叫beanDefinitionMapConcurrentHashMap來存儲解析出來的beanDefinition

Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);

經過別名註冊的方式跟經過beanName註冊的區別不大,仔細看下registry.registerAlias(beanName, alias);方法應該就能瞭解

spring默認標籤的解析大體流程就是這樣,細枝末節並無特別詳細的講解,不過這並不會對咱們理解spring的總體流程有阻礙,你們可自行看一下,代碼邏輯也不是特別複雜

自定義標籤解析

自定義標籤解析的時候會先根據從Element獲取到的namespaceUri獲取到對應的NamespaceHandler,根據NamespaceHandler進行自定義的解析,以aop爲例,咱們在配置文件中配置了<aop:aspectj-autoproxy proxy-target-class="true"/>,解析會根據aspectj-autoproxy找到對應的處理器,而後調用其parse方法建立

delegate.parseCustomElement(root);定義了自定義標籤解析的流程

public BeanDefinition parseCustomElement(Element ele) {
   return parseCustomElement(ele, null);
}
-----------------------調用下面這個方法-----------------------
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    //獲取命名空間
	String namespaceUri = getNamespaceURI(ele);
    //步驟一,根據命名空間找到對應的NamespaceHandler
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
    //步驟二,根據自定義的NamespaceHandler進行解析
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}    

複製代碼

步驟一:有了namespaceUri咱們就能夠根據this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)方法獲取對應NamespaceHandler的實例

//DefaultNamespaceHandlerResolver.java

public NamespaceHandler resolve(String namespaceUri) {
   //1,獲取到全部的解析器 
   Map<String, Object> handlerMappings = getHandlerMappings();
   //2,根據 namespaceUri 獲取到對應的handle
   Object handlerOrClassName = handlerMappings.get(namespaceUri);
   if (handlerOrClassName == null) {
      return null;
   }
   else if (handlerOrClassName instanceof NamespaceHandler) {
      //3,強轉返回 
      return (NamespaceHandler) handlerOrClassName;
   }
   else {
      //4,根據handlerOrClassName實例化NamespaceHandler
      String className = (String) handlerOrClassName;
      try {
         Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
         if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
            throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                  "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
         }
         NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
         //調用自定義的NamespaceHandler的初始化方法
         namespaceHandler.init();
         //添加到緩存裏 
         handlerMappings.put(namespaceUri, namespaceHandler);
         return namespaceHandler;
      }
      catch (ClassNotFoundException ex) {
         throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
               namespaceUri + "] not found", ex);
      }
      catch (LinkageError err) {
         throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
               namespaceUri + "]: problem with handler class file or dependent class", err);
      }
   }
}

複製代碼

這個過程仍是比較清晰的,debug一下:

這個handlerMappings.get(namespaceUri)取到是字符串:org.springframework.aop.config.AopNamespaceHandler,接下來按流程走調用BeanUtils.instantiateClass(handlerClass)就是實例化這個類,而後調用它的init方法,而後加到緩存裏,而後返回這個NamespaceHandler的實例

public class AopNamespaceHandler extends NamespaceHandlerSupport {

   /** * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}' * and '{@code scoped-proxy}' tags. */
   @Override
   public void init() {
      // In 2.0 XSD as well as in 2.1 XSD.
      registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
      registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
      registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

      // Only in 2.0 XSD: moved to context namespace as of 2.1
      registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
   }

}

複製代碼

註冊BeanDefinitionParser,就是放到一個叫parsersHashMap裏,總共4個configaspectj-autoproxyscoped-proxyspring-configured

protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
   this.parsers.put(elementName, parser);
}

複製代碼

步驟二:返回NamespaceHandler實例以後調用它的parse方法

handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

咱們的配置文件中只有一個自定義標籤:<aop:aspectj-autoproxy proxy-target-class="true"/>

因此findParserForElement(element, parserContext)這個方法根據標籤aspectj-autoproxy取到的是取到的BeanDefinition是:AspectJAutoProxyBeanDefinitionParser

//NamespaceHandlerSupport.java
//獲取到對應解析器的BeanDefinition,調用其parse方法
//好比aspectj-autoproxy標籤對應AspectJAutoProxyBeanDefinitionParser
public BeanDefinition parse(Element element, ParserContext parserContext) {
		return findParserForElement(element, parserContext).parse(element, parserContext);
}
--------------------調用下面這個方法-----------------------
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
		String localName = parserContext.getDelegate().getLocalName(element);
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal(
					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
		}
		return parser;
}

複製代碼

AspectJAutoProxyBeanDefinitionParser實現BeanDefinitionParser接口

BeanDefinitionParser接口中只定義了一個parse方法,全部自定義處理器都須要實現BeanDefinitionParser接口進行自定標籤的解析

接下來咱們看下AspectJAutoProxyBeanDefinitionParser類的parse方法

//AspectJAutoProxyBeanDefinitionParser.java

public BeanDefinition parse(Element element, ParserContext parserContext) {
   //註冊 AspectJAnnotationAutoProxyCreator
   AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
   extendBeanDefinition(element, parserContext);
   return null;
}

複製代碼

主要的邏輯就在註冊AspectJAnnotationAutoProxyCreator這個方法上

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {
   //核心邏輯:註冊或升級AutoProxyCreator定義beanName爲org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition
   BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
         parserContext.getRegistry(), parserContext.extractSource(sourceElement));
   useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
   registerComponentIfNecessary(beanDefinition, parserContext);
}

複製代碼

這個方法調用了registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source)方法

調用的這個方法邏輯也特別清晰

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
   return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

-----------------------調用下面這個方法---------------------

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		//判斷BeanDefinitionRegistry是否包含AUTO_PROXY_CREATOR_BEAN_NAME這個靜態變量
    	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            //包含說明就註冊過,將這個BeanDefinition取出來,而後判斷BeanClassName若是不相等,從新BeanClassName爲cls.getName()
			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;
		}
    	//若是不包含就建立一個RootBeanDefinition,填充屬性而後註冊
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    	//這個註冊方法咱們前面講默認標籤註冊BeanDefinition的時候講過,用的一個方法
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
}

複製代碼

自定義標籤解析註冊BeanDefinition的過程咱們也講解完了

如今咱們知道了spring是若是解析默認標籤和自定義標籤的了,總體流程仍是比較清晰的

總結一下spring如何加載xml及註冊BeanDefinition:

首先將xml文件轉化爲Element對象,獲取命名空間,根據命名空間判斷是spring的默認標籤仍是自定義標籤

  1. 默認標籤:使用spring的流程進行處理,遇到默認標籤首先判斷是哪一種標籤,import,alias,bean,beans標籤都有着不一樣的解析處理邏輯,解析成BeanDefinition以後進行註冊,註冊的過程就是放到一個ConcurrentHashMap

  2. 自定義標籤:使用自定義的命名空間處理器(實現了NamespaceHandler接口)進行解析註冊處理

    首先根據namespaceUri找到對應的NamespaceHandler處理器

    而後調用它的init方法,註冊對應自定義標籤的解析器(好比aspectj-autoproxy對應AspectJAutoProxyBeanDefinitionParser

    調用NamespaceHandlerparse方法,在這個方法里根據自定義標籤找到對應的解析器,調用對應的解析器的parse方法進行註冊BeanDefinition

本想一篇文章把全部的問題都說明白,發現寫完一個問題篇幅就比較長了

那關於spring是如何加載bean的,如何建立bean的,又是如何實現aop操做的,咱們下篇分解

以爲寫得還能夠記得點關注,下次更新文章直接就能看到了

相關文章
相關標籤/搜索