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:
- A class
UserController
, provide a methodlogin
- A plane
UserAspect
, the entry point islogin
methods - A configuration file
spring-aop.xml
Load 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:
-
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
-
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
-
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
-
ApplicationContext
ApplicationContext inherits from BeanFactory and contains all the functionality of The BeanFactory, which is used in this case
-
BeanDefinition
The data structure used in Spring to wrap beans
-
BeanDefinitionRegistory
Defines various operations to add or remove beanDefinitions
-
BeanDefinitionReader
Defines an interface for reading BeanDefinitions from resource files. XmlBeanDefinitionReader is its implementation class
-
SingletonBeanRegistry
Defines the registration and retrieval of singletons
-
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
-
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
-
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