Spring-Web-MVC是一种 基于请求驱动的 轻量级Web-MVC 设计模式 框架, Spring MVC使用MVC架构思想, 对Web层进行 职责解耦
,使用 请求-响应 模型将 数据 、 业务 与 视图 进行分离, 简化开发.
MVC(模型-视图-控制器)是一个 以设计界面应用程序为基础 的架构模式,通过分离模型-视图-控制器在应用中的角色将业务逻辑从界面中解耦:
MVC模式的核心思想就是 将业务逻辑从界面中分离出来,允许它们单独改变而不会相互影响.
Spring MVC框架是基于Java语言的MVC架构具体实现.他的设计围绕 DispatcherServlet
展开, DispatcherServlet
负责将请求分派给指定的 Controller
( Handler
),通过可配置的 HandlerMapping
、 HandlAdapter
、 Controller
、 ViewResolver
来 处理请求 拿到数据并填充对应的视图 View :
组件 | 名称 | 描述 |
---|---|---|
DispatcherServlet | 调度器/前端控制器 | DispatcherServlet 是 前端控制器 模式的具体实现(详细可参考 Front Controller ),他提供了整个Web应用的集中访问点,截获请求并将其分派给指定的 Controller ,相当于MVC中的 C . 他的存在降低了组件之间的耦合性. |
HandlerMapping | 处理器映射器 | HandlerMapping 负责根据用户请求 URI 找到对应的 Controller 与 Interceptor ,并将它们封装在 HandlerExecutionChain 中返回给 DispatcherServlet . |
HandlAdapter | 处理器适配器 | Spring MVC通过 HandlerAdapter 执行 Controller ,这是 适配器模式 的应用,通过扩展适配器可以执行更多类型的 Controller . |
Controller | 处理器 | Controller (又称 Handler )是继 DispatcherServlet 前端控制器之后的 后端控制器 ,在 DispatcherServlet 的控制下 Controller 对具体的用户请求进行处理. |
ViewResolver | 视图解析器 | 负责将 Model 数据填充到 View , 组合生成视图展示:他首先将 逻辑视图名 解析成 物理视图名 ,生成 View 视图对象,最后对 View 进行渲染(填充数据). Spring默认提供了针对JSP/Velocity/PFD等视图的 ViewResolver 实现. |
需求: 用户列表查询.
mvn archetype:generate -DgroupId=com.fq.mvc -DartifactId=MVC -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false -DarchetypeCatalog=internal
依赖管理
在pom.xml中添加 Spring 、 Spring MVC 、 Sevlet 及 Velocity 依赖:
<!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- WEB --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlet.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- Velocity --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>${velocity.version}</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-tools</artifactId> <version>${velocity.tools.version}</version> </dependency>
DispatcherServlet
(web.xml) <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>MVC</display-name> <!-- 配置SpringMVC --> <servlet> <servlet-name>mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- contextConfigLocation: 指定MVC配置文件位置 --> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/mvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <!-- 拦截所有以.do结尾的URL --> <servlet-name>mvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
HandlerMapping
/ HandlerAdapter
(mvc-servlet.xml) <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 处理器映射器 --> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> <!-- 处理器适配器 --> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> </beans>
Controller
/** * @author jifang * @since 16/3/15 下午4:40. */ public class UserController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { List<User> users = new ArrayList<>(); users.add(new User(1, "翡青", new Date(), 1, "浙江-杭州")); users.add(new User(2, "小芳", new Date(), 2, "山东-青岛")); ModelAndView result = new ModelAndView(); result.addObject("users", users); result.setViewName("users"); return result; } }
<!-- 装配 Controller --> <bean name="/mvc/users.do" class="com.fq.mvc.controller.UserController"/>
配置 ViewResolver
(mvc-servlet.xml)
由于我们视图选用的是 Velocity ,因此配置 VelocityViewResolver
:
<!-- 配置视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"> <property name="suffix" value=".vm"/> <property name="contentType" value="text/html;charset=UTF-8"/> <property name="dateToolAttribute" value="dateTool"/> <property name="numberToolAttribute" value="numberTool"/> <property name="exposeRequestAttributes" value="false"/> <property name="exposeSessionAttributes" value="true"/> </bean> <!-- 配置Velocity --> <bean id="velocityConfigurer" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <!-- 设置VM模板放置位置--> <property name="resourceLoaderPath" value="views"/> <!-- 防止乱码 --> <property name="velocityProperties"> <props> <prop key="input.encoding">UTF-8</prop> <prop key="output.encoding">UTF-8</prop> </props> </property> </bean>
<html> <head> <title>用户列表</title> <link rel="stylesheet" href="/css/main.css"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> </head> <body> <div id="global"> <fieldset> <legend>用户列表:</legend> <table width="100%" border=0> <tr> <td>ID</td> <td>username</td> <td>birthday</td> <td>sex</td> <td>address</td> </tr> #foreach($user in $users) <tr> <td>${user.id}</td> <td>${user.name}</td> <td>${user.birthday}</td> #if(${user.sex} == 1) <td>男</td> #else <td>女</td> #end <td>${user.address}</td> </tr> #end </table> </fieldset> </div> </body> </html>
参考: main.css地址: OSChina
DispatcherServlet
的 url-pattern
有两种配置方式: *.do
: 拦截所有以 .do
结尾的URI. /
: 拦截 所有 URI. BeanNameUrlHandlerMapping
指定将Bean的Name作为URI映射; Spring还提供了 SimpleUrlHandlerMapping
将URI和 Controller
的id统一映射配置. SimpleControllerHandlerAdapter
对所有实现了 Controller
接口的JavaBean适配.Spring还提供 HttpRequestHandlerAdapter
对所有实现了 HttpRequestHandler
接口的JavaBean适配. InternalResourceViewResolver
: <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- "JstlView"表示可以在JSP页面JSTL标签库,所以需要在项目中添加JSTL依赖 --> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/views"/> <property name="suffix" value=".jsp"/> </bean>
从2.5版本开始, Spring引入了注解开发:
RequestMappingHandlerMapping
注解式处理器映射器 对标有 @ResquestMapping
的方法进行 映射 . RequestMappingHandlerAdapter
注解式处理器适配器 对标记有 @ResquestMapping
的方法进行 适配 . <mvc:annotation-driven/>
注解驱动 自动加载 RequestMappingHandlerMapping
和 RequestMappingHandlerAdapter
及其他相关配置. 因此在mvc-servlet.xml最简单有效的配置方式是使用 <mvc:annotation-driven/>
替代注解处理器和适配器: <mvc:annotation-driven/>
尽管 <mvc:annotation-driven/>
很小, 但他具有足够的威力, 它注册了很多的特性, 包括对JSR-303校验、信息转换以及对域格式化的支持.
组件扫描(mvc-servlet.xml):
使用Spring的组件扫描来省去为每个 Controller
注册的麻烦:
<context:component-scan base-package="com.fq.mvc.controller"/>
Controller
@Controller @RequestMapping("/mvc") public class UserController { @RequestMapping("/users.do") public ModelAndView users() { List<User> users = new ArrayList<>(); users.add(new User(1, "翡青", new Date(), 1, "浙江-杭州")); users.add(new User(2, "小芳", new Date(), 2, "山东-青岛")); ModelAndView result = new ModelAndView(); result.addObject("users", users); result.setViewName("users"); return result; } }
使用基于注解的`Controller`有以下优点: - 基于注解的`Controller`请求映射关系不存储在配置文件中,使用`@RequestMappeing`便可以对一个方法进行映射. - 一个`Controller`内可以有多个处理方法,可以响应多个动作,这样就允许将同一模块的多个操作写在同一个`Controller`里面,便于模块划分,且减少类的数量.
需求: 展示所有用户数据(由于数据模型较简单, 因此从DAO到Controller只使用一个DO对象-User).
<!-- Spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- AOP --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <!-- WEB --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlet.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- Velocity --> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>${velocity.version}</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-tools</artifactId> <version>${velocity.tools.version}</version> </dependency> <!-- DB --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>${hikaricp.version}</version> </dependency> <!-- Spring DB支持 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis.spring.version}</version> </dependency> <!-- log --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency>
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <mappers> <mapper resource="mybatis/mapper/UserDAO.xml"/> </mappers> </configuration>
/** * @author jifang. * @since 2016/5/26 11:16. */ public class User { private int id; private String name; private Date birthday; private int sex; private String address; public User() { } public User(int id, String name, Date birthday, int sex, String address) { this.id = id; this.name = name; this.birthday = birthday; this.sex = sex; this.address = address; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public int getSex() { return sex; } public void setSex(int sex) { this.sex = sex; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
/** * @author jifang * @since 16/3/20 下午5:47. */ public interface UserDAO { List<User> selectAllUsers(); }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.fq.mvc.dao.UserDAO"> <select id="selectAllUsers" resultType="com.fq.mvc.domain.User"> SELECT * FROM user; </select> </mapper>
<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" 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"> <context:property-placeholder location="classpath:db.properties"/> <!-- 配置数据源 --> <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig"> <property name="driverClassName" value="${mysql.driver.class}"/> <property name="jdbcUrl" value="${mysql.url}"/> <property name="username" value="${mysql.user}"/> <property name="password" value="${mysql.password}"/> <property name="maximumPoolSize" value="5"/> <property name="maxLifetime" value="700000"/> <property name="idleTimeout" value="600000"/> <property name="connectionTimeout" value="10000"/> <property name="dataSourceProperties"> <props> <prop key="dataSourceClassName">com.mysql.jdbc.jdbc2.optional.MysqlDataSource</prop> <prop key="cachePrepStmts">true</prop> <prop key="prepStmtCacheSize">250</prop> <prop key="prepStmtCacheSqlLimit">2048</prop> </props> </property> </bean> <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close"> <constructor-arg ref="hikariConfig"/> </bean> <!-- 配置SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis/mybatis-configuration.xml"/> </bean> <!-- 基于包扫描的mapper配置 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.fq.mvc.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> </beans>
## Data Source mysql.driver.class=com.mysql.jdbc.Driver mysql.url=jdbc:mysql://host:port/db?characterEncoding=utf-8 mysql.user=user mysql.password=password
public interface UserService { List<User> getAllUsers(); }
@Service public class UserServiceImpl implements UserService { @Autowired private UserDAO dao; @Override public List<User> getAllUsers() { return dao.selectAllUsers(); } }
<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <import resource="applicationContext-datasource.xml"/> <context:component-scan base-package="com.fq.mvc.service"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="select*" timeout="-1" propagation="REQUIRED" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.fq.mvc.service.impl.UserServiceImpl.*(..))"/> </aop:config> </beans>
@Controller @RequestMapping("/mvc") public class UserController { @Autowired private UserService service; @RequestMapping(value = "/user.do") public ModelAndView users() { List<User> users = service.getAllUsers(); return new ModelAndView("users", "users", users); } }
<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <mvc:annotation-driven/> <context:component-scan base-package="com.fq.mvc.controller"/> <!-- 配置视图解析器 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"> <property name="suffix" value=".vm"/> <property name="contentType" value="text/html;charset=UTF-8"/> <property name="dateToolAttribute" value="dateTool"/> <property name="numberToolAttribute" value="numberTool"/> <property name="exposeRequestAttributes" value="false"/> <property name="exposeSessionAttributes" value="true"/> </bean> <!-- 配置Velocity --> <bean id="velocityConfigurer" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <!-- 设置VM模板放置位置--> <property name="resourceLoaderPath" value="views"/> <!-- 防止乱码 --> <property name="velocityProperties"> <props> <prop key="input.encoding">UTF-8</prop> <prop key="output.encoding">UTF-8</prop> </props> </property> </bean> </beans>
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>MVC</display-name> <!-- 加载Spring --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <!-- 加载SpringMVC --> <servlet> <servlet-name>mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/mvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
为了能够看到Spring MVC的 运行时信息 , 最好在应用中集成一种Log实现, 在此选用 LogBack :
<configuration> <property name="log_root_dir" value="/data/logs/mvc/"/> <property name="log_pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{0} - %msg%n"/> <appender name="STD_OUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${log_pattern}</pattern> </encoder> </appender> <appender name="FILE_OUT" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${log_root_dir}/mvc-server.%d{YY-MM-dd}.log</fileNamePattern> <maxHistory>7</maxHistory> </rollingPolicy> <encoder> <pattern>${log_pattern}</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="STD_OUT"/> <appender-ref ref="FILE_OUT"/> </root> </configuration>
需求: 实现User添加.
<html> <head> <title>用户添加</title> <link rel="stylesheet" href="/css/main.css"> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> </head> <body> <div id="global"> <fieldset> <legend>Add a User</legend> <form action="/mvc/add_user.do" method="post"> <p> <label for="name">username: </label> <input type="text" name="name" tabindex="1"> </p> <p> <label for="birthday">birthday: </label> <input type="text" name="birthday" tabindex="2"/> </p> <p> <label for="sex">sex: </label> <select name="sex" tabindex="3"> <option value="1">男</option> <option value="2">女</option> </select> </p> <p> <label form="address">address: </label> <input type="text" name="address" tabindex="4"> </p> <p id="buttons"> <input type="reset" tabindex="5"> <input type="submit" tabindex="6" value="Add User"> </p> </form> </fieldset> </div> </body> </html>
@RequestMapping("/add_user.do") public String addUser(User user){ service.addUser(user); return "redirect: users.do"; }
在传统的Servlet编程中, 可以使用 HttpServletRequest
的 getParameter()
方法来获取请求参数值, 而在Spring MVC中, 它会调用 请求参数解析组件 将客户端传过来的 String
字符串 解析为指定的 Java对象 并传递给 Controller
, 这个过程称为 请求参数解析 .
Spring MVC默认提供了很多 参数解析组件 , 因此 Controller
的请求处理方法默认就可以接收很多不同类型:
JavaEE | JavaSE | Spring |
---|---|---|
ServletRequest / HttpServletRequest | InputStream / Reader | WebRequest / NativeWebRequest |
ServletResponse / HttpServletResponse | OutputStream / Writer | Model / ModelMap |
HttpSession | 命令或表单对象 (如 String / Integer / User ) | RedirectAttributes |
HttpEntity<?> | Array / List / Map | Errors / BindingResult |
带 @PathVariable / @MatrixVariable 注解的对象 | @RequestParam / @RequestHeader / @RequestBody / @RequestPart | |
Principal / Locale | SessionStatus | |
UriComponentsBuilder |
注:
1. 如果 Controller
形参名与URI中 name
不一致, 可使用 @RequestParam
注解对其进行修饰.
2. Spring MVC还支持 路径变量映射 (路径变量类似于请求参数, 但没有key部分, 只是一个值, 详细可参考博客 Spring MVC URL 路径映射 ).
Spring MVC会在每次调用请求处理方法时都创建一个 Model
对象, 若打算使用该实例, 则可以在方法的形参添加一个 Model
形参. 其实还可以使用 @ModelAttribute
注解来访问 Model
实例: 使用 @ModelAttribute
注解标注参数或方法,该方法会将其输入的或创建的参数对象添加到 Model
对象中:
@ModelAttribute
标注形参 @RequestMapping("/check_id.do") public String checkId(@ModelAttribute("id") String id, Model model){ return "check_id"; }
String
实例将以 id
做key添加到 Model
对象中(如果key未命名,则默认使用类型的名称(如 string
)做key).
@ModelAttribute
标注方法
@ModelAttribute
的第二个用途是标注一个 非请求处理方法 : 被 @ModelAttribute
标注的方法会在每次调用 Controller
的请求处理方法时调用.该方法可以返回对象或 void
:
如果返回对象, 该对象会自动添加到 Model
中:
@ModelAttribute private List<User> addUser() { return service.getAllUsers(); }
若返回 void
, 则必须添加一个 Model
类型参数, 自行将实例添加到 Model
中:
@ModelAttribute private void addUser(Model model) { model.addAttribute("users", service.getAllUsers()); }
使用重定向的一个重要场景是 避免用户重新加载页面再次调用相同动作 . 比如 add_user.do
: 当用户提交表单时会将用户信息插入数据库. 但如果在提交表单后重新加载页面, add_user.do
会被再次调用, 相同的用户可能会被再次添加.为了避免这种情况, 最好的办法是在提交表单后将用户重定向到一个不同的页面,这个页面任意重新加载都没有副作用.
但使用重定向的一个不便之处在于: 无法轻松的给目标页面传值 . 如果采用转发, 可属性添加到 Model
, 使得目标视图可以轻松访问. 但用户重定向需要经过客户端, 所以 Model
的一切内容都会在重定向中丢失. 幸运的是, 在Spring 3.1之后,可以通过 Flash
属性提供重定向传值的方法:
要使用Flash属性, 必须要有 <annotation-driven/>
注解驱动支持, 然后在方法上添加一个新的参数类型 RedirectAttributes
:
@RequestMapping("/add_user.do") public String addUser(User user, RedirectAttributes attributes){ service.addUser(user); attributes.addAttribute("message", "new user has been saved to DB."); return "redirect: /views/message.vm"; }
应用了 @Controller
与 @RequestMapping
注解之后, Controller
不再需要 implements
特定的接口, 因此 Controller
的返回值也变得多种多样:
返回值 | 描述 |
---|---|
ModelAndView | 包含视图名与Model数据 |
Model | |
Map | 包含模型的属性 |
View | |
String | 代表逻辑视图名 |
void | 可以在参数中传入request/response完成对参数的解析, 对客户端的响应 |
HttpEntity / ResponseEntity | 提供对Servlet的访问, 以响应HTTP头部和内容 |
Callable | |
DeferredResult | |
其他任意类型 | 常与响应JSON/XML结合 |