SpringMVC source code deep parsing (context: component-scan) (scan and register annotation Bean)

Source: Internet
Author: User

SpringMVC source code deep parsing (context: component-scan) (scan and register annotation Bean)

Some of our SpringMVC development projects use annotations and XML to configure beans, which have their own advantages. XML is often used for data source configuration, the services dependent on the control layer often use annotations (which won't be changed during deployment). We often use annotation @ Component as a general annotation and @ Controller as a web Controller, @ Service: Mark the Service at the Servicec layer, and @ Respository: Mark the data access at the DAO layer. How can spring MVC be automatically scanned at startup and then parsed and registered to the Bean Factory (put in the DefaultListableBeanFactory Map )? BeanDefinitionMap uses BeanName as the key )? Today we will take these questions to understand the process of analyzing this implementation. We will first understand these annotations before analysis.

@ Controller: Mark the web Controller, @ Service: Mark the Service at the Service layer, and @ Respository: Mark the data access at the DAO layer. @ Component is a general annotation, but is defined as a Bean class. SpringMVC regards all classes added with @ Component annotation as alternative objects in the configuration path using automatic scan injection. @ Controller and @ Service \ @ Respository are more refined and marked by @ Component. Therefore, @ Component is not recommended. The source code is as follows:

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Service {String value() default "";}@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Controller {String value() default "";}@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Repository {String value() default "";}

@ Component

In the configuration file, we can mark which packages need to be scanned by the configuration. You can also configure not to scan a package. The Code is as follows:

 
  
  
 

Note:

The specified non-scan package, Specified scan package

SpringMVC first reads the configuration file, then scans the class and jar files under the specified package according to the attribute base-package in context: component-scan, and marks the @ Controller as the web Controller, @ Service: Mark the services at the Servicec layer. @ Respository: mark all the annotations such as data access at the DAO layer, and register them as Bean classes in the Bean Factory. Next we will analyze this process. We usually use this annotation in project development to implement the MVC mode. The Code is as follows:

For example: // control layer @ Controller @ RequestMapping (value = "/test") public class TestController2 {@ Autowiredprivate TestService testService; @ RequestMapping (value = "/index ") public String getIndex (Model model) {return "" ;}// Service layer @ Service ("testService") public class TestServiceImpl implements TestService {}

The entry point for today is here, because the registration of the Resolution annotation is also the first to read the configuration file and parse it. During the parsing, the corresponding JAVA class under the package is scanned, which contains the DefaultBeanDefinitionDocumentReader class, the doRegisterBeanDefinitions method parses the Bean in the configuration file. The Bean is read in the form of a Document and then parsed. It is implemented by the BeanDefinitionParserDelegate class. BeanDefinitionParserDelegate parses the Bean (for example: bean tag, import tag, etc.) This is resolved in the previous SpringMVC source code deep parsing IOC container (Bean parsing, registration). Today the annotation is an Extended Tag, it is parsed by NamespaceHandler and BeanDefinitionParser. The source code is as follows:

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {String namespaceUri = getNamespaceURI(ele);NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}

What is the role of NamespaceHandler? get different NamespaceHandler based on different namespaces. Because we have configured a Namespace for the Beans tag, we can then configure the corresponding tag. when parsing the tag, we can compare it with our own NamespaceHandler to parse it ,:



In NamespaceHandler, the parse method is its subclass NamespaceHandlerSuppZ accept? Http://www.bkjia.com/kf/ware/vc/ "target =" _ blank "class =" keylink "> vcnTKtc/release/OjrNXiuPa21M/z1Nq5pLPMs/XKvLuvyrG + release/release =" brush: java; "> 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 ;}

Why get BeanDefinitionParser, because the BeanDefinitionParser class is used to parse , And other tags, but different tags are parsed by different BeanDefinitionParser ,:



Next we will start to parse this tag, Tag Parsing is performed by the ComponentScanBeanDefinitionParser class. Next we will analyze how it parses the annotation Bean and register the Bean to the Bean factory. The source code is as follows:

Public BeanDefinition parse (Element element, ParserContext parserContext) {// obtain the value of base-package in the context: component-scan Configuration Attribute String [] basePackages = StringUtils. tokenizeToStringArray (element. getAttribute (BASE_PACKAGE_ATTRIBUTE), ConfigurableApplicationContext. CONFIG_LOCATION_DELIMITERS); // create a classpathbeandefinitionpolicscanner = configurecontext (parserContext, element) object to scan the class file in the corresponding package. // scan the class file in the corresponding package and wrap the annotation Bean into BeanDefinitionSet
 
  
BeanDefinitions = arrays. doScan (basePackages); registerComponents (parserContext. getReaderContext (), beanDefinitions, element); return null ;}
 

Note:

(1) obtain the value of the base-package attribute configured by context: component-scan and put it in an array.

(2) create a classpathbeandefinitiontion object to scan the class and jar files in the corresponding package. This class scans the class and jar files in the package and packs the annotation Bean into BeanDefinition.

(3) Register BeanDefinition to the Bean factory.

First, scanning is implemented by the doScan method of ComponentScanBeanDefinitionParser. The source code is as follows:

Protected Set
 
  
DoScan (String... basePackages) {// create a queue to save BeanDefinitionHolderSet
  
   
BeanDefinitions = new LinkedHashSet
   
    
(); // Loop the package to be scanned for (String basePackage: basePackages) {// scan annotation and package it into BeanDefinitionSet
    
     
Candidates = findCandidateComponents (basePackage); for (BeanDefinition candidate: candidates) {ScopeMetadata scopeMetadata = this. scopeMetadataResolver. resolveScopeMetadata (candidate); candidate. setScope (scopeMetadata. getScopeName (); String beanName = this. beanNameGenerator. generateBeanName (candidate, this. registry); if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition (AbstractBeanDefinition) candidate, beanName);} if (candidate instanceof role) {AnnotationConfigUtils. values (values) candidate);} if (checkCandidate (beanName, candidate) {includefinitionholder = new BeanDefinitionHolder (candidate, beanName); definitionHolder = AnnotationConfigUtils. applyScopedProxyMode (scopeMetadata, definitionHolder, this. registry); beanDefinitions. add (definitionHolder); // register registerBeanDefinition (definitionHolder, this. registry) ;}}return beanDefinitions ;}
    
   
  
 

Scanning annotation and packaging as BeanDefinition is implemented by the findCandidateComponents method of the parent class ClassPathScanningCandidateComponentProvider. The source code is as follows:

Public Set
 
  
FindCandidateComponents (String basePackage) {Set
  
   
Candidates = new LinkedHashSet
   
    
(); Replace the value in try {// base-package with classpath *: cn/test /**/*. classString packageSearchPath = ResourcePatternResolver. CLASSPATH_ALL_URL_PREFIX + resolveBasePackage (basePackage) + "/" + this. resourcePattern; // obtain the Resource [] resources = this in base-package. resourcePatternResolver. getResources (packageSearchPath); boolean traceEnabled = logger. isTraceEnabled (); boolean debugEnabled = logger. isDebugEnabled (); for (Resource resource: resources) {if (traceEnabled) {logger. trace ("Scanning" + resource);} if (resource. isReadable () {try {MetadataReader metadataReader = this. metadataReaderFactory. getMetadataReader (resource); // filter context: exclude-filter if (isCandidateComponent (metadataReader) {// package metadata sbd = new ScannedGenericBeanDefinition (metadataReader); sbd. setResource (resource); sbd. setSource (resource); if (isCandidateComponent (sbd) {if (debugEnabled) {logger. debug ("Identified candidate component class:" + resource);} candidates. add (sbd);} else {if (debugEnabled) {logger. debug ("Ignored because not a concrete top-level class:" + resource) ;}} else {if (traceEnabled) {logger. trace ("Ignored because not matching any filter:" + resource) ;}} catch (Throwable ex) {throw new BeanDefinitionStoreException ("Failed to read candidate component class:" + resource, ex) ;}} else {if (traceEnabled) {logger. trace ("Ignored because not readable:" + resource) ;}}} catch (IOException ex) {throw new BeanDefinitionStoreException ("I/O failure during classpath scanning ", ex);} return candidates ;}
   
  
 

Note:

(1) first, according to the base-package = "cn. cn. test "configuration to classpath *: cn/test /**/*. class, scan the corresponding class and jar files, and obtain the path of the class, and return Resources

(2) According The specified non-scan package, The specified scan package configuration is used to filter out the classes and jar corresponding to the packages not included.

(3) encapsulate it into BeanDefinition and put it in the queue.

1) how to obtain the class path corresponding to the packageSearchPath through the PathMatchingResourcePatternResolver class, findAllClassPathResources (locationPattern. substring (CLASSPATH_ALL_URL_PREFIX.length (); obtains the class path under the configuration package and encapsulates it into a Resource. The implementation is also getClassLoader (). getResources (path. The source code is as follows:

public Resource[] getResources(String locationPattern) throws IOException {Assert.notNull(locationPattern, "Location pattern must not be null");if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {// a class path resource (multiple resources for same name possible)if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {// a class path resource patternreturn findPathMatchingResources(locationPattern);}else {// all class path resources with the given namereturn findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));}}else {// Only look for a pattern after a prefix here// (to not get fooled by a pattern symbol in a strange prefix).int prefixEnd = locationPattern.indexOf(":") + 1;if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {// a file patternreturn findPathMatchingResources(locationPattern);}else {// a single resource with the given namereturn new Resource[] {getResourceLoader().getResource(locationPattern)};}}}protected Resource[] findAllClassPathResources(String location) throws IOException {String path = location;if (path.startsWith("/")) {path = path.substring(1);}Enumeration
 
   resourceUrls = getClassLoader().getResources(path);Set
  
    result = new LinkedHashSet
   
    (16);while (resourceUrls.hasMoreElements()) {URL url = resourceUrls.nextElement();result.add(convertClassLoaderURL(url));}return result.toArray(new Resource[result.size()]);}
   
  
 

Note: getClassLoader (). getResources gets the path information of the class under the cn/test package under the classpath *: cn/test/**/*. class. The URL is returned. Here we can get the corresponding class path to get the information in it.

2) The labels implemented by isCandidateComponent are configured in The specified non-scan package, The source code of the specified scan package is as follows:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, this.metadataReaderFactory)) {AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();if (!metadata.isAnnotated(Profile.class.getName())) {return true;}AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);return this.environment.acceptsProfiles(profile.getStringArray("value"));}}return false;}

Note: this. excludeFilters has the pattern attribute and the value is The value of cn. test. *. *. controller of this. pattern. matcher (metadata. getClassName (). matches (); through this match, if yes, false is returned. :


We have resolved the corresponding annotation scan in XML configuration and encapsulated it into BeanDefinition.

Next, let's analyze the doScan method registered to the Bean Factory. You still remember the doScan method of ComponentScanBeanDefinitionParser, and then it is implemented by registerBeanDefinition (definitionHolder, this. registry); the source code is as follows:

 protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);}  public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// Register bean definition under primary name.String beanName = definitionHolder.getBeanName();registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String aliase : aliases) {registry.registerAlias(beanName, aliase);}}}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");if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}synchronized (this.beanDefinitionMap) {Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);if (oldBeanDefinition != null) {if (!this.allowBeanDefinitionOverriding) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +"': There is already [" + oldBeanDefinition + "] bound.");}else {if (this.logger.isInfoEnabled()) {this.logger.info("Overriding bean definition for bean '" + beanName +"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");}}}else {this.beanDefinitionNames.add(beanName);this.frozenBeanDefinitionNames = null;}this.beanDefinitionMap.put(beanName, beanDefinition);}resetBeanDefinition(beanName);}

Description: ultlistablebeanfactory is saved to Map. BeanDefinitionMap uses BeanName as the key. If yes, you do not need to save it. DefaultListableBeanFactory we introduced in the previous SpringMVC source code deep parsing IOC container (Bean parsing and registration). DefaultListableBeanFactory inherits BeanFactory.


Summary:

(1) because the parsing annotation is registered, the configuration file is read and parsed first, and the JAVA class under the corresponding package is scanned during parsing, which contains the DefaultBeanDefinitionDocumentReader class, doRegisterBeanDefinitions: This method parses the Bean of the configuration file, which has been read to form a Document storage. The annotation is an Extended Tag, Which is parsed by NamespaceHandler and BeanDefinitionParser.

(2) scan the class and jar files in the specified package according to the attribute base-package in context: component-scan to obtain the corresponding path information, and then according to the configuration The specified scan package configuration filters out the Resources of the class and jar path corresponding to the package that does not contain the package.

(3) Mark @ Controller with web Controller, @ Service with Servicec layer services, and @ Respository with DAO layer data access and other annotation paths as BeanDefinition, and register the Bean class to the Bean factory, that is, DefaultListableBeanFactoryMap. BeanDefinitionMap uses BeanName as the key.





Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.