Analysis of container relationship between Spring and SpringMVC

Spring and SpringMVC, as the default framework of the Bean management container and MVC layer, have been adopted by many WEB applications. In actual use, due to the powerful annotation function, many XML-based configuration methods have been replaced, but in actual projects, When configuring Spring and SpringMVC at the same time, there will be some strange exceptions. For example, when a bean is loaded multiple times, instantiated multiple times, or dependency injection, the bean cannot be automatically injected, but you have already registered the bean. To find the reason, we still need to look at the root of the problem. Let's start with the container.
In the core concept of Spring's overall framework, the container is the core idea, which is used to manage the entire life cycle of beans. In a project, there is not necessarily only one container. Spring can include multiple containers, and the container has upper and lower layers. relationship, one of the most common scenarios at present is to introduce the two frameworks of Spring and SpringMVC into a project, which is actually two containers, Spring is the root container, SpringMVC is its sub-container, and in the Spring root container, for SpringMVC container The Bean is invisible, but in the SpringMVC container, the Bean in the Spring root container is visible, that is, the child container can see the registered Bean in the parent container, and vice versa. It's important to understand this, because this is a rule, set by Spring itself, but looking down, we'll see that it doesn't use this rule by default in some places.
 
When we use annotations, we do not need to configure XML for each bean to implement the function of bean registration, as long as we use the following unified configuration.
 
<context:component-scan base-package=“com.test" />
 
According to the reference manual provided by Spring, the function of this configuration is to scan all @Component annotations under the default package and automatically register them in the container, and also scan the three annotations @Controller, @Service, @Respository, which are inherited from @Component.
 
In addition to the scanning configuration we used above, what we often see in the project is the configuration of <context:annotation-config/>. In fact, with the above configuration, this can be omitted.
Another SpringMVC-related configuration is the <mvc:annotation-driven /> configuration. After verification, this must be configured because it is used in conjunction with @RequestMapping. Here are some knowledge points related to the SpringMVC framework.
 
HandlerMapping is used in SpringMVC to process Request request URLs to specific Controllers, and it is also divided into many types;
HandlerAdapter is used in SpringMVC to map specific requests to specific methods, and it is also divided into many types;
The main purpose of the @RequestMapping annotation is to register specific Controllers and methods to facilitate HandlerMapping to process the request mapping. But @RequestMapping needs to be used in conjunction with <mvc:annotation-driven /> to take effect.
 
Well, with the foreshadowing of the above basic knowledge, let's take a look at the reason for the conflict between Spring and SpringMVC containers in such a usage scenario!
 
Spring configuration file applicationContext.xml, SpringMVC configuration file applicationContext-MVC.xml, so that there are two containers in the project, the configuration method A is as follows:
<context:component-scan base-package="com.test" /> is configured in applicationContext.xml, which is responsible for scanning all beans that need to be registered. <mvc:annotation-driven /> is configured in applicationContext-MVC.xml, Responsible for the use of springMVC related concepts, start the project and find that springMVC is invalid and cannot be jumped. Turn on the DEBUG level of the log to debug, and find that the request in the springMVC container does not seem to be mapped to the specific controller;
 
Configuration mode B is as follows:
In order to quickly verify the effect, scan and configure <context:component-scan base-package="com.test" /> into applicationContext-MVC.xml. After restarting, the verification is successful and the springMVC jump is valid.
 
To see the specific reason, look at the source code, start with the DispatcherServlet of springMVC, what happens after a request comes in? After a long review, I found the reason, as follows.
 
When springMVC is initialized, it will look for all @Controller-annotated beans in the current container to determine whether it is a handler, and there is no @Controller annotation in the beans registered in the current container springMVC. Note that the configuration method mentioned above A, All beans configured by @Controller are registered in the parent container of Spring, see the code.
 
protected void initHandlerMethods() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for request mappings in application context: " + getApplicationContext());
        }
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));
        for (String beanName: beanNames) {
            if (isHandler(getApplicationContext().getType(beanName))){
                detectHandlerMethods(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }
 
 
In the method isHandler, it will judge whether the annotation of the current bean is a controller, the code is as follows:
 
protected boolean isHandler(Class<?> beanType) {
        return AnnotationUtils.findAnnotation(beanType, Controller.class) != null;
    }
 
In configuration mode B, the springMVC container includes all beans annotated with @Controller, so it can be found naturally.
The above is the reason, what is the solution? Pay attention to the switch of detectHandlerMethodsInAncestorContexts in the initHandlerMethods() method, which mainly controls whether to get the beans in the container from there, whether to include the parent container or not, which is not included by default. So there is a solution, that is, configure the detectHandlerMethodsInAncestorContexts property of HandlerMapping as true in the springMVC configuration file (here you need to see which HandlerMapping is used according to the specific project), so that it can detect the beans of the parent container. as follows:
 
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="detectHandlerMethodsInAncestorContexts">
            <value>true</value>
        </property>
    </bean>
 
There are already 2 solutions above, but in the actual project, there will be a lot of configurations, which are divided according to different business modules, so our general idea is to be responsible for each, clear boundaries, and the Spring root container is responsible for all other non-controllers Bean registration, while SpringMVC is only responsible for controller-related Bean registration. The third option is as follows:
 
Spring container configuration, exclude all @controller beans
<context:component-scan base-package="com.fsnip.open">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
 
SpringMVC container configuration, let it only include @controller beans
<context:component-scan base-package="com.fsnip.open" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>
 

 

Personally, I recommend the third option. By extension, the configuration scheme using transactions in the project will also fail in this scenario. In the final analysis, it is also caused by the visibility problem of the two containers. You can find the reasons according to the above ideas in combination with specific problems!

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326988482&siteId=291194637