[Java EE] Detailed explanation of Spring MVC development process

Spring MVC development process


With the above initial configuration, it is not difficult to develop the Spring MVC process. To develop Spring MVC programs, you need to master the components and processes of Spring MVC, so the development process will also run through the operation process of Spring MVC.

In the current development process, most of them will use annotated development methods. The use of annotations in Spring MVC is very simple, mainly marked with an annotation @Controller, generally only need to scan configuration, you can scan it, but often combined with annotation @RequestMapping to configure it. @RequestMapping can be configured on top of a class or method. Its role is to specify the URI and which class (or method) as a processor that processes requests. For more flexibility, Spring MVC also defines an interceptor for the processor. When Spring is started When MVC, Spring MVC will parse the configuration of @RequestMapping in @Controller, and then combine the configured interceptors, so that it will form multiple interceptors and a controller in the form of a HandlerMapping. When the request comes to the server, the first is to find the corresponding HandlerMapping through the request information, and then the corresponding interceptor and processor can be found, so that the corresponding controller and interceptor can be run.

1. Configure @RequestMapping

The source code of @RequestMapping is as follows:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

	/**
	 * Assign a name to this mapping.
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used on both levels, a combined name is derived by concatenation
	 * with "#" as separator.
	 * @see org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder
	 * @see org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy
	 */
        // 请求路径
	String name() default "";

	/**
	 * The primary mapping expressed by this annotation.
	 * <p>In a Servlet environment this is an alias for {@link #path}.
	 * For example {@code @RequestMapping("/foo")} is equivalent to
	 * {@code @RequestMapping(path="/foo")}.
	 * <p>In a Portlet environment this is the mapped portlet modes
	 * (i.e. "EDIT", "VIEW", "HELP" or any custom modes).
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings inherit
	 * this primary mapping, narrowing it for a specific handler method.
	 */
        // 请求路径,可以是数组
	@AliasFor("path")
	String[] value() default {};

	/**
	 * In a Servlet environment only: the path mapping URIs (e.g. "/myPath.do").
	 * Ant-style path patterns are also supported (e.g. "/myPath/*.do").
	 * At the method level, relative paths (e.g. "edit.do") are supported within
	 * the primary mapping expressed at the type level. Path mapping URIs may
	 * contain placeholders (e.g. "/${connect}")
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings inherit
	 * this primary mapping, narrowing it for a specific handler method.
	 * @see org.springframework.web.bind.annotation.ValueConstants#DEFAULT_NONE
	 * @since 4.2
	 */
        // 请求路径,数组
	@AliasFor("value")
	String[] path() default {};

	/**
	 * The HTTP request methods to map to, narrowing the primary mapping:
	 * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings inherit
	 * this HTTP method restriction (i.e. the type-level restriction
	 * gets checked before the handler method is even resolved).
	 * <p>Supported for Servlet environments as well as Portlet 2.0 environments.
	 */
        // 请求类型,比如是HTTP的GET请求还是POST请求等,HTTP请求枚举取值范围为:GET、HEAD、PUT、PATCH、DELETE、OPTIONS、TRACE,常用的是GET和POST请求
	RequestMethod[] method() default {};

	/**
	 * The parameters of the mapped request, narrowing the primary mapping.
	 * <p>Same format for any environment: a sequence of "myParam=myValue" style
	 * expressions, with a request only mapped if each such parameter is found
	 * to have the given value. Expressions can be negated by using the "!=" operator,
	 * as in "myParam!=myValue". "myParam" style expressions are also supported,
	 * with such parameters having to be present in the request (allowed to have
	 * any value). Finally, "!myParam" style expressions indicate that the
	 * specified parameter is <i>not</i> supposed to be present in the request.
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings inherit
	 * this parameter restriction (i.e. the type-level restriction
	 * gets checked before the handler method is even resolved).
	 * <p>In a Servlet environment, parameter mappings are considered as restrictions
	 * that are enforced at the type level. The primary path mapping (i.e. the
	 * specified URI value) still has to uniquely identify the target handler, with
	 * parameter mappings simply expressing preconditions for invoking the handler.
	 * <p>In a Portlet environment, parameters are taken into account as mapping
	 * differentiators, i.e. the primary portlet mode mapping plus the parameter
	 * conditions uniquely identify the target handler. Different handlers may be
	 * mapped onto the same portlet mode, as long as their parameter mappings differ.
	 */
        // 请求参数,当请求带有配置的参数时,才匹配处理器
	String[] params() default {};

	/**
	 * The headers of the mapped request, narrowing the primary mapping.
	 * <p>Same format for any environment: a sequence of "My-Header=myValue" style
	 * expressions, with a request only mapped if each such header is found
	 * to have the given value. Expressions can be negated by using the "!=" operator,
	 * as in "My-Header!=myValue". "My-Header" style expressions are also supported,
	 * with such headers having to be present in the request (allowed to have
	 * any value). Finally, "!My-Header" style expressions indicate that the
	 * specified header is <i>not</i> supposed to be present in the request.
	 * <p>Also supports media type wildcards (*), for headers such as Accept
	 * and Content-Type. For instance,
	 * <pre class="code">
	 * &#064;RequestMapping(value = "/something", headers = "content-type=text/*")
	 * </pre>
	 * will match requests with a Content-Type of "text/html", "text/plain", etc.
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings inherit
	 * this header restriction (i.e. the type-level restriction
	 * gets checked before the handler method is even resolved).
	 * <p>Maps against HttpServletRequest headers in a Servlet environment,
	 * and against PortletRequest properties in a Portlet 2.0 environment.
	 * @see org.springframework.http.MediaType
	 */
        // 请求头,当HTTP请求头为配置项时,才匹配处理器
	String[] headers() default {};

	/**
	 * The consumable media types of the mapped request, narrowing the primary mapping.
	 * <p>The format is a single media type or a sequence of media types,
	 * with a request only mapped if the {@code Content-Type} matches one of these media types.
	 * Examples:
	 * <pre class="code">
	 * consumes = "text/plain"
	 * consumes = {"text/plain", "application/*"}
	 * </pre>
	 * Expressions can be negated by using the "!" operator, as in "!text/plain", which matches
	 * all requests with a {@code Content-Type} other than "text/plain".
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings override
	 * this consumes restriction.
	 * @see org.springframework.http.MediaType
	 * @see javax.servlet.http.HttpServletRequest#getContentType()
	 */
        // 请求类型为配置类型才匹配处理器
	String[] consumes() default {};

	/**
	 * The producible media types of the mapped request, narrowing the primary mapping.
	 * <p>The format is a single media type or a sequence of media types,
	 * with a request only mapped if the {@code Accept} matches one of these media types.
	 * Examples:
	 * <pre class="code">
	 * produces = "text/plain"
	 * produces = {"text/plain", "application/*"}
	 * produces = "application/json; charset=UTF-8"
	 * </pre>
	 * <p>It affects the actual content type written, for example to produce a JSON response
	 * with UTF-8 encoding, {@code "application/json; charset=UTF-8"} should be used.
	 * <p>Expressions can be negated by using the "!" operator, as in "!text/plain", which matches
	 * all requests with a {@code Accept} other than "text/plain".
	 * <p><b>Supported at the type level as well as at the method level!</b>
	 * When used at the type level, all method-level mappings override
	 * this produces restriction.
	 * @see org.springframework.http.MediaType
	 */
        // 处理器之后的响应用户的结果类型,比如{“application/json;charset=UTF-8”,"text/plain","application/*"}
	String[] produces() default {};

}

The most commonly used here is the request path and request type, and most of the others are used as limiting items and configured as needed. For example, add an index2 method to MyController, the code is as follows:

@RequestMapping(value = "/index2",method = RequestMethod.GET)
	public ModelAndView  index2() {
		ModelAndView mv = new ModelAndView();
		mv.setViewName("index");
		return mv;
	}

This provides a response to the HTTP GET request of /my/index2.do.

2. Development of the controller

Controller development is the core content of Spring MVC, and its steps are generally divided into 3 steps.

  • Get request parameters
  • Processing business logic
  • Bind model and view

2.1 Get request parameters

There are many ways to receive parameters in Spring MVC, it is recommended not to use the API given by the Servlet container, because the controller will depend on the Servlet container, such as:

@RequestMapping(value = "/index2",method = RequestMethod.GET)
	public ModelAndView  index2(HttpSession session, HttpServletRequest request) {
		ModelAndView mv = new ModelAndView();
		mv.setViewName("index");
		return mv;
	}

Spring MVC will automatically parse the method parameters session and request in the code, and then pass the API about the Servlet container, so it can be obtained. Through request or session, you can easily get the parameters from the HTTP request. This is a method, but it is not a good method . Because if this is done, then for the index2 method, it is closely related to the Servlet container, which is not conducive to expansion and testing. In order to give better flexibility, Spring MVC gives more methods and annotations to get parameters.

If you want to get the parameter of an HTTP request-id, it is a long integer, then you can use the annotation @RequestParam to get it, the code is modified to:

@RequestMapping(value = "/index2",method = RequestMethod.GET)
	public ModelAndView  index2(@RequestParam("id") Long id) {
		System.out.println("params[id] = " + id);
		ModelAndView mv = new ModelAndView();
		mv.setViewName("index");
		return mv;
	}

By default, for the parameter annotated with @RequestParam, it requires that the parameter cannot be empty, that is, when the HTTP request parameter cannot be obtained, Spring MVC will throw an exception. Sometimes I also want to give a default value to the parameter. In order to solve such difficulties, @RequestParam also gave two useful configuration items:

  • required is a boolean (boolean), the default is true, that is, the parameter is not allowed to be empty, if you want to allow null, then configure it to false.
  • The default value of defaultValue is "\ n \ t \ t \ n \ t \ t \ n \ uE000 \ uE001 \ uE002 \ n \ t \ t \ t \ t \ n", it can be modified to the desired content through configuration . 

To obtain the content in the session, assuming that the userName is set in the current Session, how should it be obtained? Spring MVC also provides the annotation @SessionAttribute to get the corresponding data from the Session. code show as below:

@RequestMapping(value = "/index3",method = RequestMethod.GET)
	public ModelAndView  index3(@SessionAttribute("userName") String userName) {
		System.out.println("session[userName] = " + userName);
		ModelAndView mv = new ModelAndView();
		mv.setViewName("index");
		return mv;
	}

 

2.2 Implement logic and bound views

Generally speaking, the logic implemented is related to the database. If you use XML, you only need to configure the database part in applicationContext.xml; if you use the Java configuration method, you need to configure the WebConfig GetRootConfigClasses can be added to the corresponding configuration class.

Sometimes when using third-party package development, it is more convenient to use XML than annotations, because there is no need to design too much Java code about the content of third-party packages, or even mixed use. The sample configuration is as follows:

<?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:p="http://www.springframework.org/schema/p"
	xmlns:tx="http://www.springframework.org/schema/tx" 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-4.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
	<!-- 使用注解驱动 -->
	<context:annotation-config />
	<!-- 数据库连接池 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/chapter14" />
		<property name="username" value="root" />
		<property name="password" value="123456" />
		<property name="maxActive" value="255" />
		<property name="maxIdle" value="5" />
		<property name="maxWait" value="10000" />
	</bean>

	<!-- 集成mybatis -->
	<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:/mybatis/mybatis-config.xml" />
	</bean>

	<!-- 配置数据源事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<!-- 采用自动扫描方式创建mapper bean -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
	    <property name="basePackage" value="com.ssm.chapter14" />
	    <property name="SqlSessionFactory" ref="SqlSessionFactory" />
	    <property name="annotationClass" value="org.springframework.stereotype.Repository" />
	</bean>
</beans>

Assuming that the above XML configuration file has initialized a Bean-RoleService in a Spring IoC container by scanning, and it provides a long method getRole to obtain the role, then it can be controlled by automatic assembly. Into it. The role controller code is as follows:

/**************import ***************/
@Controller
@RequestMapping("/role")
public class RoleController {
	// 注入角色服务类
	@Autowired
	private RoleService roleService = null;

	@RequestMapping(value = "/getRole", method = RequestMethod.GET)
	public ModelAndView getRole(@RequestParam("id") Long id) {
		Role role = roleService.getRole(id);
		ModelAndView mv = new ModelAndView();
		mv.setViewName("roleDetails");
		// 给数据模型添加一个角色对象
		mv.addObject("role", role);
		return mv;
	}
}

 The RoleService is injected from the code, so that you can use the passed parameter id to get the role through this service class, and finally add the queried role to the model and view for future use.

3. View rendering 

In general, Spring MVC will use JstlView for rendering by default, that is, it binds the queried model to the JSTL (JSP Standard Tag Library) model, so that the data model can be read out and displayed in the JSP through JSTL. In Spring MVC, there are a large number of views available, so that you can easily render data into the view to respond to user requests.

In the above code, the view name of roleDetails is used. According to the configuration, it will use the file /WEB-INF/jsp/roleDetail.jsp to respond, that is, to write JSTL tags in this file to read the model data. ,E.g:

<%@ page pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>out标签的使用</title>
</head>
<body>
</body>
<center>
	<table border="1">
		<tr>
			<td>标签</td>
			<td>值</td>
		</tr>
		<tr>
			<td>角色编号</td>
			<td><c:out value="${role.id}"></c:out></td>
		</tr>
		<tr>
			<td>角色名称</td>
			<td><c:out value="${role.roleName}"></c:out></td>
		</tr>
		<tr>
			<td>角色备注</td>
			<td><c:out value="${role.note}"></c:out></td>
		</tr>
	</table>
</center>
</html>

In the current front-end technology, Ajax technology is commonly used. In such cases, the background often needs to return JSON data to the front-end. For this, Spring MVC also provides good support for models and views. The code of getRole is modified to:

// 获取角色
@RequestMapping(value = "/getRole2", method = RequestMethod.GET)
public ModelAndView getRole2(@RequestParam("id") Long id) {
	Role role = roleService.getRole(id);
	ModelAndView mv = new ModelAndView();
	mv.addObject("role", role);
	// 指定视图类型
	mv.setView(new MappingJackson2JsonView());
	return mv;
}

The view type in the code is MappingJackson2JsonView, which requires downloading the Jackson2 package. Since this is a JSON view, Spring MVC will use this view to render the desired results. So we will get the required JSON data after our request and provide it to Ajax asynchronous request. Its execution flow is as follows:

JSON data flow

It's just that this is not the only way to turn the result into JSON. Using the annotation @ResponeBody is a simpler and more widely used method .

 

Published 281 original articles · praised 170 · 320,000 views +

Guess you like

Origin blog.csdn.net/ARPOSPF/article/details/105490945