Spring MVC 實踐
標籤 : Java與Web
Spring Web MVC
Spring-Web-MVC是一種基於請求驅動的輕量級Web-MVC設計模式框架, Spring MVC使用MVC架構思想, 對Web層進行
職責解耦
,使用請求-響應模型將數據、業務與視圖進行分離, 簡化開發.
MVC
MVC(模型-視圖-控制器)是一個以設計界面應用程序爲基礎的架構模式,通過分離模型-視圖-控制器在應用中的角色將業務邏輯從界面中解耦:
- 模型負責封裝應用數據和業務邏輯;
- 視圖僅負責展示數據;
- 控制器負責接收用戶請求,並調用模型(Service/Manger/DAO)來處理業務邏輯.模型可能會返回一些數據需要在視圖層展示,控制器就需要整理模型數據並調用視圖展示.
MVC模式的核心思想就是將業務邏輯從界面中分離出來,允許它們單獨改變而不會相互影響.
Spring 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 實現. |
初識Spring MVC
需求: 用戶列表查詢.
- 創建Maven Web項目
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>
- View(/views/users.vm)
<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
小結
前端控制器
Spring MVCDispatcherServlet
的url-pattern
有兩種配置方式:*.do
: 攔截所有以.do
結尾的URI./
: 攔截 所有 URI.
處理器映射器
BeanNameUrlHandlerMapping
指定將Bean的Name作爲URI映射; Spring還提供了SimpleUrlHandlerMapping
將URI和Controller
的id統一映射配置.處理器適配器
SimpleControllerHandlerAdapter
對所有實現了Controller
接口的JavaBean適配.Spring還提供HttpRequestHandlerAdapter
對所有實現了HttpRequestHandler
接口的JavaBean適配.視圖解析器
前面使用Velocity作爲視圖展示,如果使用的是JSP的話, 需要配置另一種視圖解析器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引入了註解開發:
- 註解驅動(mvc-servlet.xml)
- 從3.1版本開始,Spring MVC使用
RequestMappingHandlerMapping
註解式處理器映射器 對標有@ResquestMapping
的方法進行映射. - 從3.1版本開始,Spring MVC使用
RequestMappingHandlerAdapter
註解式處理器適配器 對標記有@ResquestMapping
的方法進行適配. - Spring MVC使用
<mvc:annotation-driven/>
註解驅動自動加載RequestMappingHandlerMapping
和RequestMappingHandlerAdapter
及其他相關配置. 因此在mvc-servlet.xml最簡單有效的配置方式是使用<mvc:annotation-driven/>
替代註解處理器和適配器:
- 從3.1版本開始,Spring MVC使用
<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`裏面,便於模塊劃分,且減少類的數量.
集成MyBatis
- 通過Spring將各層整合:
- 持久層DAO(MyBatis的Mapper)註冊到Spring容器中.
- 業務層Service/Manager從Spring容器中拿到DAO接口, 並註冊Service服務.
- 表現層Controller從Spring容器中拿到Service接口, 通過Spring MVC進行數據展示.
需求: 展示所有用戶數據(由於數據模型較簡單, 因此從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>
集成MyBatis
- mybatis-configuration.xml
<?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>
- domain: User
/**
* @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;
}
}
- UserDAO
/**
* @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>
- applicationContext-datasource.xml(配置數據源)
<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>
- db.properties
## 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
集成Service
public interface UserService {
List<User> getAllUsers();
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDAO dao;
@Override
public List<User> getAllUsers() {
return dao.selectAllUsers();
}
}
- applicationContext.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"
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>
集成MVC
- Controller
@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);
}
}
- 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"
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>
- 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>
<!-- 加載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>
- View視圖同前
集成log
爲了能夠看到Spring MVC的運行時信息, 最好在應用中集成一種Log實現, 在此選用LogBack:
- logback.xml
<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添加.
- add_user.vm
<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>
- Controller
@RequestMapping("/add_user.do")
public String addUser(User user){
service.addUser(user);
return "redirect: users.do";
}
- Service/DAO實現簡單,不再贅述.
請求參數解析
在傳統的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 路徑映射).
ModelAttribute
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());
}
重定向與Flash屬性
使用重定向的一個重要場景是避免用戶重新加載頁面再次調用相同動作. 比如
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返回值
應用了@Controller
與@RequestMapping
註解之後,Controller
不再需要implements
特定的接口, 因此Controller
的返回值也變得多種多樣:
返回值 | 描述 |
---|---|
ModelAndView |
包含視圖名與Model數據 |
Model |
|
Map |
包含模型的屬性 |
View |
|
String |
代表邏輯視圖名 |
void |
可以在參數中傳入request/response完成對參數的解析, 對客戶端的響應 |
HttpEntity /ResponseEntity |
提供對Servlet的訪問, 以響應HTTP頭部和內容 |
Callable |
|
DeferredResult |
|
其他任意類型 | 常與響應JSON/XML結合 |