Original text: chenmingyu. Top/spring – sour…

This article first provides a implementation of spring AOP demo, through demo source analysis

By reading the source code, we can learn how Spring parses XML, loads beans, creates beans, implements AOP operations, and how the various operations are implemented in detail

Speaking of the source code when I will carry out some trade-offs, according to the above problems combined with demo to explain the main process, to be able to explain the above problems

Source code address:Github.com/mingyuHub/s…

Aop demo

Code:

  1. A classUserController, provide a methodlogin
  2. A planeUserAspect, the entry point isloginmethods
  3. A configuration filespring-aop.xmlLoad the class into the Spring container

Create a UserController class

public class UserController {
    
    public void login(a){
        System.out.println("Login"); }}Copy the code

Define a plane UserAspect, don’t understand the concept of aop can see: chenmingyu. Top/springboot -…

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

    /** * pointcut */
    @Pointcut("execution(public * com.my.spring.*.*(..) )")
    public void execute(a){}/** **@param joinPoint
     */
    @Before(value ="execute()")
    public void Before(JoinPoint joinPoint) {
        System.out.println("Before the method is executed");
    }

    /** * after notification *@param joinPoint
     */
    @After(value ="execute()")
    public void After(JoinPoint joinPoint) {
        System.out.println("After executing the method"); }}Copy the code

Customize an XML file named spring-aop.xml

<? The 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>Copy the code

Now that the debug code is ready, write a test class to test it

@Test
public void test(a){
	ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
    UserController userController = (UserController) applicationContext.getBean("userController");
    userController.login();
}
Copy the code

The normal output is as follows:

Core classes

Before getting started with spring’s source code, it’s worth taking a look at some of spring’s core classes

First about what these classes are doing, do not go into depth, when the follow-up to read the source code will focus on the explanation

Let’s start with a spring container class diagram:

  1. BeanFactory

    The top-level interface of the factory class, used to obtain beans and their various properties, provides the most basic form of the IOC container and provides the specification for the implementation of a concrete IOC container

    ListableBeanFactory, HierarchicalBeanFactory and AutowireCapableBeanFactory the BeanFactory interface as part of the interface, Eventually default implementation class is DefaultListableBeanFactory, define the interface is mainly in order to distinguish the Spring more internal object in the process of operation in the process of the transfer and conversion, for what you did to the object’s data access restrictions

  2. DefaultListableBeanFactory

    The realization of the ioc container, DefaultListableBeanFactory as an ioc container can be used independently, is a central part of the whole Bean loading, is spring registration and load the default implementation of the Bean

  3. xmlBeanFactory

    DefaultListableBeanFactory xmlBeanFactory inheritance and extends to its, added to a custom XML reader XmlBeanDefinitionReader, implements the personalized BeanDefinitionReader read, The main function is to parse the XML configuration into a BeanDefinition

  4. ApplicationContext

    ApplicationContext inherits from BeanFactory and contains all the functionality of The BeanFactory, which is used in this case

  5. BeanDefinition

    The data structure used in Spring to wrap beans

  6. BeanDefinitionRegistory

    Defines various operations to add or remove beanDefinitions

  7. BeanDefinitionReader

    Defines an interface for reading BeanDefinitions from resource files. XmlBeanDefinitionReader is its implementation class

  8. SingletonBeanRegistry

    Defines the registration and retrieval of singletons

  9. AliasRegistry

    Define a simple add, delete, or change operation to alias

Now that we know some of the core classes we are ready to read the source code

Source code analysis

Our source code starts with the test class as the entry point for analysis

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
Copy the code

Both ApplicationContext and BeanFactory are used to load beans, and ApplicationContext provides more extended functionality

ClassPathXmlApplicationContext eventually call this constructor

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

   super(parent);
   // Set the configuration file
   setConfigLocations(configLocations);
   if(refresh) { refresh(); }}Copy the code

Refresh () is the core method of ApplicationContext, which contains almost all of the functionality provided by ApplicationContext

@Override
public void refresh(a) throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Ready to refresh this context, not important.
      prepareRefresh();
 
      // Initialize the bean factory, load the XML, parse the default tags, parse the custom tags, and register BeanDefinitions
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Set the properties of the bean factory for function padding.
      prepareBeanFactory(beanFactory);

      try {
         // Subclass override methods do extra processing.
         postProcessBeanFactory(beanFactory);

         // Activate the bean handler.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register the bean handler created by intercepting beans, just for registration
         registerBeanPostProcessors(beanFactory);

         // Initialize the message source.
         initMessageSource();

         // Initializes the application message broadcaster
         initApplicationEventMulticaster();

         Leave it to subclasses to load other beans.
         onRefresh();

         // Register the Listeners bean to the Listeners
         registerListeners();

         // Instantiate all remaining (non-lazy init) singletons.
         finishBeanFactoryInitialization(beanFactory);

         // Refresh notifications.
         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.
         throwex; }}}Copy the code

How does Spring load the parsed XML and register the BeanDefinition

The logic for loading the parsed XML and registering the BeanDefinition is in the obtainFreshBeanFactory() method, which initializes the bean factory, loads the XML, parses the default tags and custom tags, and registers the parsed BeanDefinitions with the container

protected ConfigurableListableBeanFactory obtainFreshBeanFactory(a) {
   // Core logic: Create bean factory, parse XML, register BeanDefinition
   refreshBeanFactory();
   
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ":" + beanFactory);
   }
   returnbeanFactory; } ------------------ calls the following method ------------------/ / call AbstractRefreshableApplicationContext refreshBeanFactory ()
protected final void refreshBeanFactory(a) throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
            / / create the bean factory, type of DefaultListableBeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
            // Load BeanDefinitions into this method
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory; }}catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code

The createBeanFactory() method has very simple logic, so let’s look at the loadBeanDefinitions(beanFactory) method in more detail

Loadbeandefinitionreader loadBeanDefinitionReader () instantiates an XmlBeanDefinitionReader. What is BeanDefinition

BeanDefinition: Bean definitions are mainly described by BeanDefinitions. As the data structure used in Spring to wrap beans

As a top-level interface, BeanDefinition has three implementations: RootBeanDefinition, ChildBeanDefinition, and GenericBeanDefinition all inherit AbstractBeanDefinition

Spring converts tags in a configuration file to an internal representation of a container through BeanDefinition, and registers BeandefinitionRegistry with BeandefinitionRegistry, Containers in Spring store BeanDefinitions primarily in the form of maps

BeanDefinitionReader again:

BeanDefinitionReader addresses parsing from a resource file (XML,propert) to a BeanDefinition, so XmlBeanDefinitionReader is clearly for converting XML to BeanDefinition

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create XmlBeanDefinitionReader according to beanFactory
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Load environment variables
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
	
   // initBeanDefinitionReader
   initBeanDefinitionReader(beanDefinitionReader);
    // In this method, load beanDefinitions
   loadBeanDefinitions(beanDefinitionReader);
}

Copy the code

In loadBeanDefinitions (beanDefinitionReader); Method, and the final method for parsing XML calls is doLoadBeanDefinitions(InputSource InputSource, Resource Resource).

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      // Convert XML to a Document object
      Document doc = doLoadDocument(inputSource, resource);
      // The core logic in this method parses the Document and registers the beanDefinition
      return registerBeanDefinitions(doc, resource);
   }
   catch (BeanDefinitionStoreException ex) {
      throwex; }... Omit other exception information}Copy the code

registerBeanDefinitions(doc, resource); Element method in the final call doRegisterBeanDefinitions (root) will be turned out by the XML Document (by doc. GetDocumentElement () to the Element Element) is resolved to beanDefinition and registered

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; }}}// Parse the prefixes and leave them to subclasses
   preProcessXml(root);
   // Core logic: Parse and register BeanDefinition
   parseBeanDefinitions(root, this.delegate);
   // Parse the post-operation and leave it to subclasses
   postProcessXml(root);

   this.delegate = parent;
}

Copy the code

So as we go through the layers and we finally get to the core method, parse the beanDefinition, how does it parse

There are two types of tags in Spring, default and custom, but the way they are resolved is quite different

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   / / according to whether the namespace root element is equal to http://www.springframework.org/schema/beans
   // Use the above judgment to determine if it belongs to spring's default tag
   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)) {
               // Default tag parsing
               parseDefaultElement(ele, delegate);
            }
            else {
               // Customize tag parsingdelegate.parseCustomElement(ele); }}}}else {
      // Customize tag parsingdelegate.parseCustomElement(root); }}Copy the code

Default tag parsing

The parseDefaultElement() method provides parsing of import, alias, bean, and beans tags

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   // Parse 
      
        tags
      
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   // Parse the 
      
        tag
      
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   // Parse the 
      
        tag
      
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   // Parse the 
      
        tag
      
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recursedoRegisterBeanDefinitions(ele); }}Copy the code

Explain the bean tag parsing in detail

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // Element to BeanDefinitionHolder.
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if(bdHolder ! =null) {
      // Decorate bdHolder, parse for custom attributes, find the corresponding processor according to the custom tag, parse (custom parsing method will be described below)
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Step 2, register the resolved 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(newBeanComponentDefinition(bdHolder)); }}Copy the code

Step 1: BeanDefinitionHolder: This class is a utility class that holds BeanDefinition data

public class BeanDefinitionHolder implements BeanMetadataElement {

   private final BeanDefinition beanDefinition;

   private final String beanName;

   private finalString[] aliases; . }Copy the code

delegate.parseBeanDefinitionElement(ele); Method converts Element to BeanDefinitionHolder, and identifies beanName and aliases for the bean. BeanDefinitionHolder means the default tag has been resolved

There’s no complicated logic to this method, it’s pretty clear

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
   return parseBeanDefinitionElement(ele, null); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- call the following method -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        // Get the id attribute
		String id = ele.getAttribute(ID_ATTRIBUTE);
    	// Get the name attribute
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<String>();
        // If the name attribute is not null
		if (StringUtils.hasLength(nameAttr)) {
            / / press,; Split into an array of strings
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            // add to the set of aliases
			aliases.addAll(Arrays.asList(nameArr));
		}
		// Assign the id attribute to beanName. If id is empty, select the first one from the set of 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) {
            // Check whether beanName and aliases are used. If beanName and aliases are used, add an exception to beanName and aliases
			checkNameUniqueness(beanName, aliases, ele);
		}
		// Creates a BeanDefinition of type GenericBeanDefinition and populates the properties
		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);
            // Create a BeanDefinitionHolder return
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}
		return null;
	}    

Copy the code

Once you have the BeanDefinitionHolder, all that is left is to register the BeanDefinition

Step 2, call BeanDefinitionReaderUtils. RegisterBeanDefinition (bdHolder, getReaderContext () getRegistry ()) method, registered BeanDifinition

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

   // use beanName as the unique identifier for registration
   String beanName = definitionHolder.getBeanName();
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // Register with all aliases
   String[] aliases = definitionHolder.getAliases();
   if(aliases ! =null) {
      for(String alias : aliases) { registry.registerAlias(beanName, alias); }}}Copy the code

Registration: through beanName definitionHolder. GetBeanName (), the default label and custom tag are using this method for a BeanDefinition registration

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");
   // Whether beanDefinition is an instance of AbstractBeanDefinition
   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         // Verify the existence of methodOverrides and engineering method and the existence of methodOverrides corresponding method
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Validation of bean definition failed", ex);
      }
   }

   BeanDefinition oldBeanDefinition;
   // Use beanName to check whether BeanDefinition is already registered
   oldBeanDefinition = this.beanDefinitionMap.get(beanName);
   // Throw an exception if it is registered and overwriting is not allowed
   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 {
      / / register BeanDefinition
      this.beanDefinitionNames.add(beanName);
      this.manualSingletonNames.remove(beanName);
      this.frozenBeanDefinitionNames = null;
   }
   this.beanDefinitionMap.put(beanName, beanDefinition);
   // If oldBeanDefinition does not throw an exception or beanName is a singleton
   if(oldBeanDefinition ! =null || containsSingleton(beanName)) {
      // Update the corresponding cacheresetBeanDefinition(beanName); }}Copy the code

Through this. BeanDefinitionMap. Put (beanDefinition beanName) this line of code that we know, Spring uses a ConcurrentHashMap called beanDefinitionMap to store parsed BeanDefinitions

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

RegisterAlias (beanName, alias); alias (beanName, alias); I think you’ll see how

This is the general flow of parsing the default tags in Spring. The details are not explained in great detail, but this should not hinder our understanding of the overall flow of Spring. Take a look, and the code logic is not particularly complicated

Custom label resolution

When a custom tag is parsed, it will first obtain the corresponding NamespaceHandler according to the namespaceUri obtained from Element, and then carry out the custom parsing according to the NamespaceHandler. Take AOP as an example. Aspectj-autoproxy proxy-target-class=”true”/> < AOP: Aspectj-autoProxy proxy-target-class=”true”/> < AOP: Aspectj-AutoProxy proxy-target-class=”true”/>

delegate.parseCustomElement(root); Defines the process for customizing tag resolution

public BeanDefinition parseCustomElement(Element ele) {
   return parseCustomElement(ele, null); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- call the following method -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    // Get the namespace
	String namespaceUri = getNamespaceURI(ele);
    // Find the corresponding NamespaceHandler based on the namespace
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
    // Step 2, parse according to the custom NamespaceHandler
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}    

Copy the code

Step one: with namespaceUri we can according to this. ReaderContext. GetNamespaceHandlerResolver () resolve (namespaceUri) method to obtain corresponding NamespaceHandler instance

//DefaultNamespaceHandlerResolver.java

public NamespaceHandler resolve(String namespaceUri) {
   // get all parsers
   Map<String, Object> handlerMappings = getHandlerMappings();
   //2, obtain the corresponding Handle according to namespaceUri
   Object handlerOrClassName = handlerMappings.get(namespaceUri);
   if (handlerOrClassName == null) {
      return null;
   }
   else if (handlerOrClassName instanceof NamespaceHandler) {
      //3, strong return
      return (NamespaceHandler) handlerOrClassName;
   }
   else {
      //4 instantiate NamespaceHandler according to handlerOrClassName
      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);
         // Call the custom NamespaceHandler initialization method
         namespaceHandler.init();
         // Add to the cache
         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); }}}Copy the code

This process is fairly clear. Debug it:

The handlerMappings. Get (namespaceUri) is to string: org. Springframework. Aop. Config. AopNamespaceHandler, . Then according to the process go call BeanUtils instantiateClass handlerClass is instantiate this class, and then call the init method, and then added to the cache, and then return the NamespaceHandler example of this

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(a) {
      // 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".newSpringConfiguredBeanDefinitionParser()); }}Copy the code

Register BeanDefinitionParser, which is put into a HashMap called parsers. There are 4 Configs, AspectJ-AutoProxy, Scoped-proxy, and spring-configured

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

Copy the code

Step 2: Return the NamespaceHandler instance and call its parse method

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

There is only one custom tag in our configuration file:

So the findParserForElement(Element, parserContext) method gets the BeanDefinition of aspectj-AutoProxy: AspectJAutoProxyBeanDefinitionParser

//NamespaceHandlerSupport.java
// Get the BeanDefinition of the corresponding parser and call its parse method
/ / such as aspectj - corresponding AspectJAutoProxyBeanDefinitionParser autoproxy tag
public BeanDefinition parse(Element element, ParserContext parserContext) {
		returnfindParserForElement(element, parserContext).parse(element, parserContext); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- call the following method -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --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;
}

Copy the code

AspectJAutoProxyBeanDefinitionParser BeanDefinitionParser interface

BeanDefinitionParser only defines a parse method in the BeanDefinitionParser interface. All custom processors need to implement BeanDefinitionParser for custom tags

Next we see AspectJAutoProxyBeanDefinitionParser the parse method of the class

//AspectJAutoProxyBeanDefinitionParser.java

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

Copy the code

The main logic on the registered AspectJAnnotationAutoProxyCreator this method

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {
   / / core logic: registered or upgrade beanName AutoProxyCreator definition of org. Springframework. Aop. Config. InternalAutoProxyCreator BeanDefinition
   BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
         parserContext.getRegistry(), parserContext.extractSource(sourceElement));
   useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
   registerComponentIfNecessary(beanDefinition, parserContext);
}

Copy the code

This method calls the registerOrEscalateApcAsRequired (AnnotationAwareAspectJAutoProxyCreator. Class, registry, source) method

The method called is also particularly logical

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
   returnregisterOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- call the following method -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -private static BeanDefinition registerOrEscalateApcAsRequired(Class
        cls, BeanDefinitionRegistry registry, Object source) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		// Determine whether BeanDefinitionRegistry contains the AUTO_PROXY_CREATOR_BEAN_NAME static variable
    	if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
            // If BeanClassName is not equal to BeanClassName, rename it to 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;
		}
    	// If not, create a RootBeanDefinition, populate the property and register
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    	// This is one of the methods we used to register beanDefinitions with default tags
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
}

Copy the code

We’ve also covered the process of customizing tag resolution to register beanDefinitions

Now that we know how Spring parses default tags and custom tags, the overall flow is a little clearer

To summarize how Spring loads XML and registers BeanDefinition:

First convert the XML file to an Element object, get the namespace, and determine if it is a spring default tag or a custom tag based on the namespace

  1. Default tags: Import, Alias, bean, beans tags all have different parsing logic. After parsing into BeanDefinition, register. The registration process is put into a ConcurrentHashMap

  2. Custom tags: use a custom NamespaceHandler that implements the NamespaceHandler interface for parsing registration processing

    First, find the corresponding NamespaceHandler handler based on namespaceUri

    Then calls it the init method, a corresponding custom tag parser (such as aspectj – autoproxy corresponding AspectJAutoProxyBeanDefinitionParser)

    Call NamespaceHandler’s parse method, which finds the corresponding parser based on the custom tag, and call the corresponding parser’s parse method to register the BeanDefinition

I want to explain all the problems in an article, but I find that the length of writing a problem is longer

How does Spring load beans, create beans, and implement AOP operations

Feel that you can also remember to pay attention to the next update of the article directly can see