1. Background
Recently, due to problems with the project's package scanning, in the process of solving the problem, I accidentally discovered that Spring and SpringMVC have a parent-child container relationship, and it is precisely because of this that the problem of package scanning often occurs. Here we analyze and understand the parent-child container relationship between Spring and SpringMVC and give the official recommended way of package scanning in Spring and SpringMVC configuration files.
2. Concept understanding and knowledge laying the foundation
In the core concept of Spring's overall framework, containers are the core idea, which is used to manage the entire life cycle of a bean. In a project, there is not necessarily only one container. Spring can include multiple containers, and the container has upper and lower levels. The most common scenario at present is to introduce the two frameworks of Spring and SpringMVC in a project. Then it is actually two containers. Spring is a parent container and SpringMVC is its child container. The bean registered in the Spring parent container is visible to the SpringMVC container, while the bean registered in the SpringMVC container is invisible to the Spring parent container, that is, the child container can see the registered bean in the parent container, otherwise it will not work.
We can use the unified annotation configuration as follows to batch register the beans, without configuring each bean using XML separately.
<context:component-scan base-package="com.hafiz.www" />
From the reference manual provided by Spring, we know that the function of this configuration is to scan all classes under the configured base-package package that use the @Component annotation, and automatically register them in the container. At the same time, they also scan the three annotations of @Controller, @Service, and @Respository because they are inherited from @Component.
In the project, we often see the following configuration. In fact, with the above configuration, this can be omitted, because the above configuration will turn on the following configuration by default. The following configuration will declare the annotations such as @Required, @Autowired, @PostConstruct, @PersistenceContext, @Resource, @PreDestroy, etc. by default.
<context:annotation-config/>
In addition, there is another configuration related to SpringMVC. After verification, this must be configured for SpringMVC because it declares @RequestMapping, @RequestBody, @ResponseBody, etc. Moreover, this configuration loads many parameter binding methods by default, such as json conversion parser.
<mvc:annotation-driven />
The above sentence configuration version before spring 3.1 is equivalent to the following configuration method
<!--Configure annotation controller mapper, it is used in SpringMVC to map the Request request URL to a specific controller --><bean/><!--Configure annotation controller mapper, it is used in SpringMVC to map specific requests to a specific method --><bean/>
The version after spring3.1 is equivalent to the following configuration methods
<!--Configure annotation controller mapper, it is used in SpringMVC to map the Request request URL to a specific controller --><bean/><!--Configure annotation controller mapper, it is used in SpringMVC to map specific requests to a specific method --><bean/>
3. Specific scenario analysis
Let's take a closer look at the causes of the container conflict between Spring and SpringMVC?
We have two containers, Spring and SpringMVC, and their configuration files are applicationContext.xml and applicationContext-MVC.xml respectively.
1. The <context:component-scan base-package="com.hafiz.www" /> is configured in the applicationContext.xml to be responsible for scanning and registering all beans that need to be registered.
2. Configure <mvc:annotation-driven /> in applicationContext-MVC.xml to be responsible for the use of SpringMVC-related annotations.
3. When starting the project, we found that SpringMVC could not jump. We set the log printing level of the log to DEBUG for debugging. We found that the requests in the SpringMVC container did not seem to be mapped to the specific controller.
4. Configure <context:component-scan base-package="com.hafiz.www" /> in applicationContext-MVC.xml. After restarting, the verification is successful and the springMVC jump is valid.
Let's check the specific reason, look at the source code, and start from SpringMVC's DispatcherServlet to search down. We found that when SpringMVC is initialized, we will look for all beans in the SpringMVC container that use the @Controller annotation to determine whether it is a handler. The two-step configuration of 1 and 2 makes the current springMVC container not register the bean with the @Controller annotation, but all beans with the @Controller annotation are registered in the parent container of Spring, so springMVC cannot find the processor and cannot jump. The core source code is as follows:
protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludedAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); for (String beanName: beanNames) { if (isHandler(getApplicationContext().getType(beanName))){ detectHandlerMethods(beanName); } } handlerMethodsInitialized(getHandlerMethods());}In the method isHandler, we will determine whether the annotation of the current bean is a controller. The source code is as follows:
protected boolean isHandler(Class<?> beanType) { return AnnotationUtils.findAnnotation(beanType, Controller.class) != null;}In the 4th step configuration, all beans with @Controller annotation are also registered in the SpringMVC container, so SpringMVC can find the processor for processing, so it jumps normally.
We found the reason why it cannot jump correctly, so what is its solution?
We noticed that in the initHandlerMethods() method, detectHandlerMethodsInAncestorContexts Switch, it mainly controls which beans in the container are obtained and whether the parent container is included. It is not included by default. So the solution is to configure the detectHandlerMethodsInAncestorContexts property of HandlerMapping to true in the springMVC configuration file (here you need to see which type of HandlerMapping is used according to the specific project), so that it can detect the bean of the parent container. as follows:
<bean> <property name="detectHandlerMethodsInAncestorContexts"> <value>true</value> </property></bean>
However, in actual projects, there will be many configurations. We divide different types of beans in different containers according to the official recommendations according to different business modules: the Spring parent container is responsible for the registration of all other beans that are not annotated by @Controller, while SpringMVC is only responsible for the registration of beans annotated by @Controller, so that they each assume their own responsibilities and clarify boundaries. The configuration method is as follows
1.Configure in applicationContext.xml:
<!-- Register beans in Spring containers that are not annotated by @controller --><context:component-scan base-package="com.hafiz.www"> <context:exclude-filter type="annotation" expression="org.springframework.steretype.Controller"/></context:component-scan>
2.Configuration in applicationContext-MVC.xml
<!-- Only beans with @controller annotation are registered in SpringMVC container --><context:component-scan base-package="com.hafiz.www" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.steretype.Controller" /></context:component-scan>
3. Summary
In this way, after we have understood the parent-child container relationship between spring and springMVC and the principle of scanning and registration, according to the official suggestions, we can allocate different types of beans to different containers for management. If there are problems such as the bean cannot be found, SpringMVC cannot be redirected, and the transaction configuration fails, we can quickly locate and solve the problem. Very happy, is there any ~
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.