For SpringMVC profile

Who in the Java industry doesn’t know the SSM framework? Unless you tell me you just learned Java, I’m going to believe you don’t know SpringMVC.

There is no need to introduce the origin of SringMVC and why it is used. But one thing is for sure: there are many people who only use SpringMVC and don’t have a deep understanding of the underlying principles and source code of SpringMVC.

In this issue we will understand the underlying principle and source code of SpringMVC, in the previous JSP era, the front end and back end of the code are mixed together, perhaps older programmers have written the following code, this is the famous JSP and Servlet era.

In some old projects there may be such code, such code does not look very exciting, if let you maintain such code, want to die.

This kind of code front end and back end are mixed together, seriously coupling between JSP and Java Bean, Java code and Html code are mixed together, which requires developers to know both front end and post end, bringing a lot of inconvenience to the test, the code can not be reused.

Such problems, in order to solve such problems, the first is to strictly divide these codes, front-end and back-end code separated, gradually appeared the layered architecture of the code, each layer of responsibility.

However, there are problems with this model layer. First of all, each module requires a Servlet controller. If there are many modules, the controller will become many, which will lead to the complexity of the controller.

And changing views is technically cumbersome, relying heavily on the Servlet API. The Java Bean structure contains the persistence layer and the processing of business and the encapsulation of data, which leads to the bloat of the Java Bean structure.

According to the layering of our current code, Java beans can be divided into a persistence layer (DAO) and a Service layer (Service), and our application control layer (Controller).

For SpringMVC principle

To simplify the control layer (Servlet), we use DispatcherServlet (front Controller) in the SpringMVC framework to schedule our own application control layer (Controller).

In this gradual evolution, the three-tier Web MVC architecture in the real sense emerged. The specific structure diagram is shown as follows:

Let’s start by explaining the core components of SpringMVC:

  1. DispatcherServlet: Front-end The front-end controller is mainly responsible for scheduling and global flow control. For example, schedule HandlerMapping and return to the chain of execution.
  2. HandlerMappingThe processor mapper returns a chain of execution, which in layman’s terms is a logical order of executionInterceptorAnd aHandler (processor).
  3. HandlerAdapterThe processor adapter contains calls to the processor, using the design principles of the adapter to call our own Controller through reflection.
  4. Handler: The handler is our Controller. The URL of the user’s request comes in, and the request corresponds to the HandlerMapping of our Controller. This is the handler.
  5. ModelAndView: Model and view, the Model that is our data, is called by reflection from aboveHandler (the Controller)The generated data, and the logical View. A logical view is not really a view name, it is just a logical view name, such as index.
  6. ViewIn this case, the corresponding physical view will be generated using the logical view name generated above, and the front-end display user will be returned.

Hello World

So much has been said above, in fact, it is only in the practice of the project that we will have a profound understanding. Below, we will carry out the above profound understanding through a practical case.

Here I use IDEA to build THE SSM Project. For those of you who also use Eclipse, I suggest that you should change tools. First of all, new-project

And I’m gonna go to the leftMaven, check on the rightCreate from archetypeAnd selectwebappModule:

Here are some to fill inGroupIdAs well asArtifactIdThe basic directory structure after creating the project is as follows:

XML, jdbc.properties, log4j.properties, and spring-mVC.xml are created in the resource directory.

Applicationcontext.xml is the core configuration file for Spring and reads as follows:

<? 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:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <! <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:jdbc.properties"/> </bean> <! - configuration data source - > < bean id = "dataSource" class = "com. Alibaba. Druid. Pool. DruidDataSource" > < property name = "driverClassName." value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </bean> <! -- Perfect integration of Mybatis and Spring Don't need mybatis configuration mapping file - > < bean id = "sqlSessionFactory" class = "org. Mybatis. Spring. SqlSessionFactoryBean" > < property name="dataSource" ref="dataSource"/> <! <property name="typeAliasesPackage" value=" com.ldC.model "/> <! <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> <! - Mapper dynamic proxy development, scanning the dao interface package - > < bean class = "org. Mybatis. Spring. Mapper. MapperScannerConfigurer" > <! - injection sqlSessionFactory - > < property name = "sqlSessionFactoryBeanName" value = "sqlSessionFactory" / > <! <property name="basePackage" value=" com.ldC.dao "/> </bean> <! - transaction - > < bean id = "transactionManager" class = ". Org. Springframework. JDBC datasource. DataSourceTransactionManager "> <property name="dataSource" ref="dataSource"/> </bean> </beans>Copy the code

This file is used to configure the connection configuration of the data source and database. The details of the data are stored in jdbc.properties:

driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/test? characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username=root password=user initialSize=0 maxActive=20 maxIdle=20 minIdle=1 maxWait=60000Copy the code

Here the database information, you only need to change the database user name and password on the line, the other information as a test basically need not change.

Here is the log configuration information: log4j.properties

# log output level log4j rootLogger = debug, stdout, D, E # set stdout log output console log4j. Appender. Stdout = org.. Apache log4j. ConsoleAppender Output log to console . The default for the System. The out log4j appenders. Stdout. Target = System. # set out using flexible layout Log4j. Appender. Stdout. Layout = org.. Apache log4j. Flexible PatternLayout # define the output format log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} -[%p] method:[%c (%rms)] - %m%nCopy the code

After configuring the logging information, we can configure the spring-mVC.xml information file in the SpringMVC framework:

<? 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:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd "> <! <context:component-scan base-package="com.ldc"/> <! -- Enable SpringMVC annotation mode --> < MVC :annotation-driven/> <! -- Static resource default servlet configuration --> < MVC :default-servlet-handler/> <! -- Configure the path to return to the view, And identify the suffix is a JSP file - > < bean class = "org. Springframework. Web. Servlet. The InternalResourceViewResolver" > < property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/>  <property name="suffix" value=".jsp"/> </bean> </beans>Copy the code

The configuration information is also simple, including enabling annotation driver, package scanning, and view resolver configuration.

After configuring SpringMVC, the final step is to configure web.xml, which is the entry file for the front-end request:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" Version = "3.1" > < display - name > mvcDemo < / display - name > <! <welcome-file-list> <welcome-file>index.jsp</ welcome-file-list> <! Register ServletContext listener, create container object, And place the ApplicationContext object in the Application domain --> < Listener > <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <! <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <! <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <! <servlet> <servlet-name> Springmvc </ servletname > <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <! / web-inf /<servlet-name>-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>Copy the code

The web.xml contains the configuration of the default welcome page, the configuration of the character encoding filter, the front controller, and the configuration file specifying the Spring core configuration file and SpringMVC.

The above is the most basic configuration, and other configuration information is generally configured as required. After such configuration, we have basically completed the project of building a simple SSM.

The final Maven coordinate dependencies are as follows:

<? The XML version = "1.0" encoding = "utf-8"? > < project XMLNS = "http://maven.apache.org/POM/4.0.0" XMLNS: xsi = "http://www.w3.org/2001/XMLSchema-instance" Xsi: schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion > 4.0.0 < / modelVersion > < groupId > com. The LDC < / groupId > < artifactId > mvcDemo < / artifactId > <version> 1.0-snapshot </version> <packaging>war</packaging> <name>mvcDemo Maven Webapp</name> <! RELEASE</srping.version> <mybatis. Version >3.2.8</mybatis. Version > < slf4j version > 1.7.12 < / slf4j version > < log4j. Version > 1.2.17 < / log4j version > < druid. Version > 1.0.9 < / druid version > </properties> <! <dependencies> <! -- Unit test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <! <scope>test</scope> </dependency> <! <dependency> <groupId>org.springframework</groupId> <artifactId> Spring-test </artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${srping.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${srping.version}</version> </dependency> <! -- Spring Framework package --> <! Mybatis </groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> < artifactId > mybatis - spring < / artifactId > < version > 1.2.2 < / version > < / dependency > <! Mybatis --> <! --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> The < version > 5.1.35 < / version > < / dependency > <! DBCP jar package; > <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> The < version > 1.4 < / version > < / dependency > <! JSTL </artifactId> JSTL </artifactId> <version>1.2</version> </dependency> <! -- log --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <! Alibaba </groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> </dependencies> <build> <! <plugins> <groupId>org.apache.maven.plugins</groupId> < artifactId > maven -- the compiler plugin < / artifactId > < version > 3.2 < / version > < configuration > < source > 1.8 < / source > <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>Copy the code

The main development in Maven tables includes dependency database drivers, logging, Mybaties, Spring coordinates, Web MVC coordinates, and inherited JSP coordinates.

Here the front-end technology can inherit what you want: Freemarker or Thymeleaf, just introduce the relevant Maven coordinates, because JSP is basically obsolete, and this is just for testing, not for the front-end technology.

We create our own test class under the Controller package: UserController:

@Controller @RequestMapping("/user") public class UserController { @Autowired private IUserService userService; @RequestMapping("/getUserById") public ModelAndView selectUser(@PathVariable("id") Long id) throws Exception { ModelAndView mv = new ModelAndView(); User user = userService.selectUser(id); mv.addObject("user", user); mv.setViewName("user"); return mv; }}Copy the code

Here’s a quick explanation:

  1. @Controller: this is a Controller managed by the Spring container. This annotation comes after @Component. To indicate the layering of code, there are @Controller, @Service, and @mapper annotations, which all do the same thing.
  2. @requestMapping: Indicates the accepted request. Does GetMapping and PostMapping indicate the different request methods?
  3. Autowired: indicates automatic injection, provided the injected object is managed by the Spring container.
  4. ModelAndView: This holds data and logical view names as mentioned earlier.

These are relatively simple, and you can start the project for testing by configuring the following Tomcat information to deploy:

DispatcherServlet source code parsing

After starting the project, let’s test the front. The following interface shows that you like the basic environment of the SSM project to be successful:

So how do our front-end requests go step by step from front -> back -> front? In fact, we have already said the basic principle of SpringMVC, on the basis of this basic principle, from the source point of view, a detailed analysis:

The source code parsing

As mentioned above, the core scheduler of SpringMVC is DispatcherServlet, which is responsible for dispatching the main process. The most important method in DispatcherServlet is doDispatch:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; int interceptorIndex = -1; try { ModelAndView mv; boolean errorView = false; // Check whether the request is multipart (e.g. file upload). If yes, processedRequest = checkMultipart(request) is resolved by MultipartResolver. MappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } // Step 3, processor adaptor, that is, our processor is packaged into the corresponding adapter (to support multiple types of processors) HandlerAdapter ha = getHandlerAdapter(mappedHandler.gethandler ()); / / 304 Not Modified cache support / / / / is omitted specific code execution processor-specific interceptor pretreatment (HandlerInterceptor. PreHandle) concrete code / / / / is omitted Step 4. The adapter executes the processor (calls the corresponding function processing method of the processor) mv = ha.handle(processedRequest, response, mappedHandler.gethandler ()); // Do we need view name translation? if (mv ! = null && ! mv.hasView()) { mv.setViewName(getDefaultViewName(request)); } / / perform processor-specific interceptor post-processing (HandlerInterceptor. PostHandle) / / is omitted specific code} the catch (ModelAndViewDefiningException ex) { logger.debug("ModelAndViewDefiningException encountered", ex); mv = ex.getModelAndView(); } catch (Exception ex) { Object handler = (mappedHandler ! = null ? mappedHandler.getHandler() : null); mv = processHandlerException(processedRequest, response, handler, ex); errorView = (mv ! = null); } / / step 5 step 6, analytic View and View of rendering / / step 5 from ViewResolver analytical View (ViewResolver. ResolveViewName (viewName, Render (mv.getmodelinternal (), request, response);) if (mv ! = null && ! mv.wasCleared()) { render(mv, processedRequest, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling"); }} / / execution processor is related to the completion of the interceptor post-processing (HandlerInterceptor. AfterCompletion) / / is omitted specific code catch (Exception ex) {/ / the Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } catch (Error err) { ServletException ex = new NestedServletException("Handler processing failed", err); // Trigger after-completion for thrown exception. triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex); throw ex; } finally { // Clean up any resources used by a multipart request. if (processedRequest ! = request) { cleanupMultipart(processedRequest); }}}Copy the code

HandlerMapping and HandlerExecutionChain

This method is a short one, basically responsible for other method calls, from our analysis above to the front end request. When the first step reaches SpringMVC, we call HandlerMapping and return the HandlerExecutionChain:

We debug the project, make a breakpoint to see, thisHandlerExecutionChainWhat the hell is it?

We can see that when the breakpoint executes toHandlerExecutionChainThe handler in the HandlerExecutionChain is our own request Controller. For example, if we request LoginController, the handler is our own LoginController.

The metadata contains both the BeanType of the LoginController, the method to be requested by the front end, and the parameters, which can be summarized as follows: The handler in the HandlerExecutionChain is the Controller we want to request along with some interceptors information.

All the beans in the Spring container and all the HandlerMapping objects whose urls correspond to the beans are initialized before the HandlerExecutionChain is obtained.

This is all done in Spring. We will learn more about HandlerMapping objects and getHandler methods.

HandlerMapping is a List object containing the seven member information, which we are familiar withBeanNameUrlMappingandSimpleUrlHandlerMappingObject, you can see it in theseHandlerMapping stores various mapping rules, mapped to the corresponding Bean object by beanName or URL.

Looking further inside, you can see that there is an applicationContext object, which is our context, and there is the beanFactory, which is the spring-managed Bean object, both Spring’s own and our own Bean information.

This is the HandlerMapping object, mainlyContains Bean mapping rules, Bean details.

The process from HandlerMapping->HandlerExecutionChain is summed up in a popular mobile phrase: find you out of the crowd (find the Controller and method to request from the beanFactory).

HandlerAdapter

After we get our chain of execution, we get our HandlerAdapter,

getHandlerAdapterAs can be seen in the method, according to the returnedhandlerMappingThe object of thehandlerObject to get the correspondingHandlerAdapterObject, which returns directly.

ModelAndView

Return the HandlerAdapter object and retrieve the ModelAndView object by executing the Handle method of the HandlerAdapter. Actually invoke the Handler.

This is actually a reflection that dynamically executes the methods in our Controller, the requested Controller, because the handler object returned by mappedHandler.gethandler () contains the details of the requested Controller, Includes full class names.

Interceptor post-processing

Once we get the ModelAndView, we then execute our interceptor post-processing method, postHandle.

As can be seen from his source code, it takes all interceptors and executes them one by one.

The view to render

After executing all the interceptor postprocessing, which is the final render of the 䣌 view, the processDispatchResult method is executed and the ModelAndView object is passed in as a parameter.

The most important of the processDispatchResult methods is the Render method, which performs the rendering of the view and finally presents the rendered result to the user.

Here DispatcherServlet main execution logic is finished, in fact, mainly about or SpringMVC from the front end request -> background -> front-end such a process, limited space, from the perspective of the source code about how to explain this process is running.

An article to SpringMVC is not clear, SpringMVC all talk down, can write a book, the subsequent source code we continue to progress, this as a general context of understanding.