Spring starts the component-scan class scan loading process-source code analysis

Source: Internet
Author: User

I haven't written a blog for a while because of my book writing recently. A friend recently asked meSpringThe process of loading classes, especially based onAnnotationThe annotation loading process may be confusing if it cannot be loaded due to some system deployment problems! To address this problem, I will talk about the spring startup process in this blog and use the source code to describe it. This part will also appear in the book, but the expressions will be slightly different, I will use the spring 3.0 version to explain (although the version is different, but the changes are not very big). In addition, here we will start using spring in the Web, and we will use newClasspathxmlapplicationcontext.


To view the source code, we usually configure a servelet in spring 3.0 and later versions, as shown below:

    <servlet>        <servlet-name>spring</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <load-on-startup>1</load-on-startup>    </servlet>

Of courseServletThe name is determined. You can get it by yourself.SpringcontextIn the previous article: "various methods for obtaining applicationcontext in Spring", I will not elaborate on it here, we use dispatcherservlet to illustrate and trace (note that we will not talk about request forwarding here, but the bean loading process). We know that in servlet specifications, if load-on-startup is set, then it will be loaded during initialization, And the servlet will call itsInit() Method, so naturally it is calledDispatcherservletOfInitMethod. You can see from the source code that it does not exist, but it does not contain tables. You will find that in the parent class of the parent class: Org. springframework. web. servlet. httpservletbean has this method, as shown in:

     public final void init() throws ServletException {if (logger.isDebugEnabled()) {logger.debug("Initializing servlet '" + getServletName() + "'");}// Set bean properties from init parameters.try {PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader));initBeanWrapper(bw);bw.setPropertyValues(pvs, true);}catch (BeansException ex) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);throw ex;}// Let subclasses do whatever initialization they like.initServletBean();if (logger.isDebugEnabled()) {logger.debug("Servlet '" + getServletName() + "' configured successfully");}}

Note:Initservletbean ();The relationship between the rest and the loaded bean is not very large. After tracking it, I will find that this method is in the class org. springframework. Web. servlet.FrameworkservletClass (YesDispatcherservletParent class,HttpservletbeanBy callingInitwebapplicationcontext() To initializeWebapplicationcontext, Source code snippets (limited space, not copying all source code, just intercepting fragments)


Next we need to know how to initialize this context (according to the usage habits, as long as we get applicationcontext, we get Bean information, so when initializing applicationcotext, the bean information has been initialized. At least, it initializes the bean path and description information.ApplicationcotextYou can see how it is initialized.BeanHow is it loaded.


The parent here is basically unnecessary, because the rootApplicationcontextThe information has not been created yet, so the main thing is to look at the createwebapplicationcontext method. After going in, the first part of this method is to set some relevant parameters, for example, we need to set the Web Container and container configuration information, and then callRefresh ()Method. This method is actually used for refresh and initialization bean, that is, after Configuration modification, if you can call this method, you can reload it.SpringLet's take a look at the snippets in the source code as follows (similarly, we will not post too many irrelevant parts ):


In fact, whether it is throughClasspathxmlapplicationcontextOr web load will be called here. Let's take a look.ClasspathxmlapplicationcontextPart of the call:


Their difference is that,WebIn container, useServletLoaded,ServletContainsXmlwebapplicationcontextAndClasspathxmlapplicationcontextIs called directly. What they have in common is thatXmlwebapplicationcontext, OrClasspathxmlapplicationcontextBoth inherit the class (indirect inheritance ):

Abstractapplicationcontext, In this classRefresh ()Methods are shared, that is, they all call this method to loadBeanIn this method, the obtainfreshbeanfactory method is used to constructBeanfactory, As shown in:


Isn't it annoying to see that calling a layer at one layer actually comes with its own processing actions? After all, spring does not simply load a bean, we also need to do at least the XML parsing, class loading and instantiation process. Each step may have many requirements. Therefore, the separation design makes the code more extensible. Let's continue to look at it.ObtainfreshbeanfactoryMethod description:


Many people may not pay attention to it here.Refreshbeanfactory() This method, especially the first time you read this code. If you ignore it, you may not find where the bean is loaded. As mentioned aboveRefreshIn fact, it can be used for initialization. This is also the case here,RefreshbeanfactoryIf no InitializationBeanfactoryInitialize it. What you will see later isGetbeanfactoryThe refreshbeanfactory method class has been initialized.AbstractrefreshableapplicationcontextMethod in, it isAbstractapplicationcontextSubclass, whether it isXmlwebapplicationcontext, OrClasspathxmlapplicationcontextAll inherit from it, so you can call the same initialization method to see the code in the body part:


Note that the first place in the Red Circle is to create a beanfactory, and the following method can be seen by name as"Load bean DefinitionBeanfactory is passed in. It is naturally loaded into beanfactory. createbeanfactory is to instantiate a beanfactory. It depends on where the bean is loaded. It seems that the focus is not yet displayed, continue tracking

Loadbeandefinitions(Defaultlistablebeanfactory) Method

It consistsAbstractxmlapplicationcontextClass, the web project will be implemented by the class:XmlwebapplicationcontextThe main reason is that the startup file is there. If there is no custom xmlapplicationcontext in a non-web project, you can referXmlwebapplicationcontextCan be considered as the same function. The loadbeandefinitions method is as follows:


Xmlbeandefineitionreader reads spring-related information in XML (that is, parsing springcontext. XML ).Getconfiglocations ()The obtained path is the path of this or multiple files.XmlbeandefineitionreaderThe method implementation body is located in the parent class of xmlbeandefineitionreader: abstractbeandefinitionreader. The Code is as follows:



You may wonder why there is anotherLoadbeandefinitionsAs you can see, we only parse our springcontext. XML is not resolved to springcontext. what is the content of XML? There may be multiple spring configuration files. Here there will be multiple resources, so it is an array (how to locate the file through location here, when we look for a class, it is clear that we don't have to worry about this issue first ).

Next, there will be several layers of calls, which will be called as follows:

Abstractbeandefinitionreader. loadbeandefinitions (resources [])Loop resource array, call method:

Xmlbeandefinitionreader. loadbeandefinitions (Resource)It has a parent-child relationship with the above class, and will do the following:Doloadbeandefinitions, registerbeandefinitionsWhen registering beandefinitions, you must actually start parsing XML.


It callsDefaultbeandefinitiondocumentreaderThe registerbeandefinitions method of the class, as shown in:


There is a process of parsing XML in the middle, but it seems that we are not very concerned about it. We have loaded the relational class. Although the XML parsing part has been reached, we mainly look at the parsebeandefinitions method, it will call the parsecustomelement method of the beandefinitionparserdelegate class to parse bean information:

Z

The XML Information is parsed and tracked.NamespacehandlersupportThe parse method, which will find a suitable resolution based on the node type.Beandefinitionparser (Interface)They are pre-registered by spring and put in a hashmap. For example, in spring's Annotation scan, we usually configure:

<context:component-scan base-package="com.xxx" />

In this case,Component-Scan"Will find the corresponding Parser for parsing, And it corresponds to the componentscanbeandefinitionparser'sParseMethod. The concept of scanning beans is clearly in this place. After the parse obtains the method, a very important step is defined.Classpathbeandefinition.pdfTo scan the class information. What is it scanning? Is the loaded class or class file? The answer is the latter. Why, because some classes have not been loaded during initialization, And the classloader has not yet been loaded, but the classloader can find the paths of these classes:


Note that after the secret is created, the most important thing isDoscanThis is not a problem. If you are not familiar with it, you can first look at it, then we get something similar:Com. xxxThis information starts to scan the list of classes. Where can I scan? Here, doscan returnsSet <beandefinitionholder>We hope it's not far away. Go in and have a look.DoscanMethod.


We can see the code that we don't care about at the moment. For the moment, we can see how to scan the code. We can see that the most critical scan code is:Findcandidatecomponents (string basepackage)Method, that is, through eachBasepackageFind the classes that match. Here we assume thatCom. ABC, Or configured*Two cases are described.



The main focus is on the red line section. The non-red line section below shows the class definition. The red line section will assemble information. If we configure com. ABC, it will be assembled as follows:Classpath *: COM/ABC/**/*. ClassIf the configuration is *, it will be assembledClasspath *: */**/*. ClassBut it seems that this is not the same as what we use. We haven't seen such a URL in Java. How does spring work? The code for the second red line is as follows:

Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);


It actually magically obtains the URL through this path. Once you trace it, you will find that all you get is. the class path, including the relevant class path in the jar package. Here are some details. Let's not talk about it first. Let's take a look at what type of resourcepatternresolover is. See the definition section:

private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

For this reason, fat brother also made a test and wrote a section using a simple main method:

ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();Resource[] resources = resourcePatternResolver.getResources("classpath*:com/abc/**/*.class");

The obtained results are exactly the same. Fat brother started to guess that this has something to do with the getresource method of classloader, because it is too similar. Let's track it and see:


ThisClasspath_all_url_prefixIs the stringClasspath *:When we pass the parameter in, it will naturally take the first code circled in red, but what is the second code circled in red, fat brother tells you that the crowdsourced security testing has this feature, and it will be useful later.FindpathmatchingresourcesMethod. Well, it's getting closer and closer to the truth.


Here isRootdirpathThis is prone to errors. If you configure com. ABC, The rootdirpath part should be:Classpath *: COM/ABC/If the configuration is *,Classpath *:This is the only result, notClasspath *:*(I will not intercept the source code of the string here), return to the previous code, and call the getresources (string) method again here, and return to the previous method. This time, it still starts with classpath *: So the if statement at the first layer will go in, but the second layer won't. Why? In the ispattern () implementation, it is written as follows:

        public boolean isPattern(String path) {return (path.indexOf('*') != -1 || path.indexOf('?') != -1);}

Before matching,SubstringThe"Classpath *:"Remove this string, if it is configured with COM. ABC is changed to "com/ABC/". If it is configured as *, "" is obtained, that is, a string with a length of 0. Therefore, on our path, if this method returns false, it will go to the code segment.FindallclasspathresourcesThat's why the above mentioned application is useful. Well, the most important thing is coming. For example, if we know a COM/ABC/prefix, we need to know which classes under the relevant classpath match and how to do it? Naturally, use classloader. Let's see if spring does this:

As expected, it also uses classloader, but its own getclassloader () method, that is, it uses the same loader range as the spring class, to ensure that the same classpath can be identified. During Self-simulation, you can use a class

Class Name. Class. getclassloader (). getresources ("")

If it is left blank, it is to obtain the relevant root path of the classpath (there may be many classpath, but the root path can be merged), that is, if you configure *, you may obtain the root path of the project (under classes and the lib directory of Tomcat) in the web Project ).

If you write one:COM/ABC/Scan the path information of the matched Class Name (prefix) in all the classes and jar packages under the relevant classpath, but note that ifTwo-tier jar packageAnd the classes you want to scan or the classes you want to load through spring areIn the second jar packageThis method cannot be obtained. This is not because spring didn't do this, but the getresources method provided by Java is like this. When someone asks me, we have encountered similar things. In addition, we need to note that,GetresourcesThis method contains a recursive file query of the current path (usually configured in environment variables .), so if you want to run a jar package, remember to put it in a root directory, because the current directory, that is, the root directory, will also be recursive, your program will be inexplicably slow.

Return to the code aboveFindpathmatchingresourcesWe have just obtained the base path list here, that is, all the paths that contain a prefix similar to com/ABC/or the root directory path after classpath is merged; now we need all the following classes, so what we need is recursion. Here I will not trace them any more. You can track several method calls in them by yourself:Dofindpathmatchingjarresources, dofindpathmatchingfileresources.

Almost never used: vfsresourcematchingdelegate. findmatchingresources, so the above two classes are respectively in the jar package and the class in the project. We will find that the Code will recursively and cyclically call the path information of the class file under the directory path, the related class list information will be obtained in the end, but these classes have not been checked for annotation, which is the next step, but the next step is very simple, because it is necessary to detect a classAnnotationIn the previous article, the secrets of Java annotation and framework have been mentioned.

You can also test the call path in the following simple way:

ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);Set<BeanDefinition> beanDefinitions = provider.findCandidateComponents("com/abc");for(BeanDefinition beanDefinition : beanDefinitions) {System.out.println(beanDefinition.getBeanClassName() + "\t" + beanDefinition.getResourceDescription()+ "\t" + beanDefinition.getClass());}

This is the content that directly references the source code of spring. If you can obtain it here,And the path is correct.Normally, it can be loaded to the class.

After reading so much, is it a bit dizzy? It doesn't matter. When you look back at it for the first time, you just have a thought, I didn't figure out their hierarchy and call relationships like UML here. I just explained them layer by layer for code calls. If we initially look at it, we will create applicationcontext through servlet initialization, after servelt-related parameters are set, obtain the servlet configuration file path or the configuration file path (applicationcontext. XML or other names, can be one or more), and then use the XML parsing series, and use different loading methods for different node types.Component-ScanA class corresponds to a specified scan class. It obtains the class path information through the getresources method of classloader, so the class path can be obtained, and nothing can be obtained for the class? Haha!

Well, the basic content of this article will be mentioned here.Spring MVCIn the simple jump parsing, some of the source code is read here, but it is not the focus here.

This is also what I want to talk about. In fact, although this article is about startup, there are actually a lot of code that I didn't say, because that's why I just copied and pasted the machine;

In fact, to look at the source code, we need to take a purpose. Everyone needs to know the subject situation or the functions implemented, not to look at the source code but the source code. One is that we cannot remember it at all, the Code does not make much sense.

When you have doubts, problems, or the reasons for discovering the New World, you are very interested. Let's take a look at it. Maybe you will think about how to do it if I come to implement it? Let's look at how others do it. What's the difference? Keep learning from the excellent quality in these open-source frameworks, including the code design level, and understand what it uses and why it should be designed like this, your code will become more and more beautiful, And your code from the open source will become more and more familiar, just like your loved ones.

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.