Spring MVC框架
官网文档
\spring-framework-4.2.6.RELEASE\docs\spring-framework-reference\htmlsingle
------------ springmvc-1 ------------
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
HiddenHttpMethodFilter
<!--
配置 org.springframework.web.filter.HiddenHttpMethodFilter: 可以把 POST 请求转为 DELETE 或 POST 请求
-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
DispatcherServlet
<!-- 配置 DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置 DispatcherServlet 的一个初始化参数: 配置 SpringMVC 配置文件的位置和名称 -->
<!--
实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的.
默认的配置文件为: /WEB-INF/<servlet-name>-servlet.xml
-->
<!--
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
dispatcherServlet-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: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-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
component-scan
<!-- 配置自定扫描的包 -->
<context:component-scan base-package="com.atguigu.springmvc"></context:component-scan>
InternalResourceViewResolver
<!-- 配置视图解析器: 如何把 handler 方法返回值解析为实际的物理视图 -->
<!-- InternalResourceViewResolver 的order 默认为integer最大值,也就是优先级最低 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
BeanNameViewResolver
<!-- 配置视图 BeanNameViewResolver 解析器: 使用视图的名字来解析视图 -->
<!-- 配置完才能使用下面的helloworldview -->
<!-- 通过 order 属性来定义视图解析器的优先级, order 值越小优先级越高 -->
<!-- 处理 HelloView -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="100"></property>
</bean>
ResourceBundleMessageSource
<!-- 配置国际化资源文件 -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
view-controller
<!-- 配置直接转发的页面 -->
<!-- 可以直接相应转发的页面, 而无需再经过 Handler 的方法. -->
<!-- 配置了这个之后, 如果不加annotation-driven 的话requestMapping就失效了.-->
<mvc:view-controller path="/success" view-name="success"/>
annotation-driven
<!-- 在实际开发中通常都需配置 mvc:annotation-driven 标签 -->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
SpringMVCTest.java
package com.atguigu.springmvc.handlers;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
import com.atguigu.springmvc.entities.User;
@SessionAttributes(value={"user"}, types={String.class})
@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest {
private static final String SUCCESS = "success";
redirect/ forward
@RequestMapping("/testRedirect")
public String testRedirect(){
System.out.println("testRedirect");
// return "forward:/index.jsp";
return "redirect:/index.jsp";
}
BeanNameView
@RequestMapping("/testView")
public String testView(){
System.out.println("testView");
return "helloView"; //类名第一个字母小写
}
ViewAndViewResolver
@RequestMapping("/testViewAndViewResolver")
public String testViewAndViewResolver(){
System.out.println("testViewAndViewResolver");
return SUCCESS;
}
@ModelAttribute
/**
* 1. 有 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用!
* 2. @ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用:
* 1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中.
* 2). SpringMVC 会一 value 为 key, POJO 类型的对象为 value, 存入到 request 中.
*/
@ModelAttribute
public void getUser(@RequestParam(value="id",required=false) Integer id,
Map<String, Object> map){
System.out.println("modelAttribute method");
if(id != null){
//模拟从数据库中获取对象
User user = new User(1, "Tom", "123456", "[email protected]", 12);
System.out.println("从数据库中获取一个对象: " + user);
map.put("user", user);
}
}
/**
* 运行流程:
* 1. 执行 @ModelAttribute 注解修饰的方法: 从数据库中取出对象, 把对象放入到了 Map 中. 键为: user
* 2. SpringMVC 从 Map 中取出 User 对象, 并把表单的请求参数赋给该 User 对象的对应属性.
* 3. SpringMVC 把上述对象传入目标方法的参数.
*
* 注意: 在 @ModelAttribute 修饰的方法中, 放入到 Map 时的键需要和目标方法入参类型的第一个字母小写的字符串一致!
*
* SpringMVC 确定目标方法 POJO 类型入参的过程
* 1. 确定一个 key:
* 1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
* 2). 若使用了 @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值.
* 2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
* 1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的 key 一致, 则会获取到.
* 3. 若 implicitModel 中不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰,
* 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所
* 对应的 value 值, 若存在则直接传入到目标方法的入参中. 若不存在则将抛出异常.
* 4. 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则
* 会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数
* 5. SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中.
*
* 源代码分析的流程
* 1. 调用 @ModelAttribute 注解修饰的方法. 实际上把 @ModelAttribute 方法中 Map 中的数据放在了 implicitModel 中.
* 2. 解析请求处理器的目标参数, 实际上该目标参数来自于 WebDataBinder 对象的 target 属性
* 1). 创建 WebDataBinder 对象:
* ①. 确定 objectName 属性: 若传入的 attrName 属性值为 "", 则 objectName 为类名第一个字母小写.
* *注意: attrName. 若目标方法的 POJO 属性使用了 @ModelAttribute 来修饰, 则 attrName 值即为 @ModelAttribute
* 的 value 属性值
*
* ②. 确定 target 属性:
* > 在 implicitModel 中查找 attrName 对应的属性值. 若存在, ok
* > *若不存在: 则验证当前 Handler 是否使用了 @SessionAttributes 进行修饰, 若使用了, 则尝试从 Session 中
* 获取 attrName 所对应的属性值. 若 session 中没有对应的属性值, 则抛出了异常.
* > 若 Handler 没有使用 @SessionAttributes 进行修饰, 或 @SessionAttributes 中没有使用 value 值指定的 key
* 和 attrName 相匹配, 则通过反射创建了 POJO 对象
*
* 2). SpringMVC 把表单的请求参数赋给了 WebDataBinder 的 target 对应的属性.
* 3). *SpringMVC 会把 WebDataBinder 的 attrName 和 target 给到 implicitModel.
* 近而传到 request 域对象中.
* 4). 把 WebDataBinder 的 target 作为参数传递给目标方法的入参.
*/
// public String testModelAttribute(@ModelAttribute(“abc”) User user) 此处设置与sessionAttribute 的value值对应
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user){
System.out.println("修改: " + user);
return SUCCESS;
}
@SessionAttributes
/**
* @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外(实际上使用的是 value 属性值),
* 还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(实际上使用的是 types 属性值)
*
* 注意: 该注解只能放在类的上面. 而不能修饰放方法.
*/
@RequestMapping("/testSessionAttributes")
public String testSessionAttributes(Map<String, Object> map){
User user = new User("Tom", "123456", "[email protected]", 15);
map.put("user", user);
map.put("school", "atguigu");
return SUCCESS;
}
Map
/**
* 目标方法可以添加 Map 类型(实际上也可以是 Model 类型或 ModelMap 类型)的参数.
* @param map
* @return
*/
@RequestMapping("/testMap")
public String testMap(Map<String, Object> map){
System.out.println(map.getClass().getName());
map.put("names", Arrays.asList("Tom", "Jerry", "Mike"));
return SUCCESS;
}
ModelAndView
/**
* 目标方法的返回值可以是 ModelAndView 类型。
* 其中可以包含视图和模型信息
* SpringMVC 会把 ModelAndView 的 model 中数据放入到 request 域对象中.
* @return
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
String viewName = SUCCESS;
ModelAndView modelAndView = new ModelAndView(viewName);
//添加模型数据到 ModelAndView 中.
modelAndView.addObject("time", new Date());
return modelAndView;
}
ServletAPI
/**
* 可以使用 Serlvet 原生的 API 作为目标方法的参数 具体支持以下类型
*
* HttpServletRequest
* HttpServletResponse
* HttpSession
* java.security.Principal
* Locale InputStream
* OutputStream
* Reader
* Writer
* @throws IOException
*/
@RequestMapping("/testServletAPI")
public void testServletAPI(HttpServletRequest request,
HttpServletResponse response, Writer out) throws IOException {
System.out.println("testServletAPI, " + request + ", " + response);
out.write("hello springmvc");
// return SUCCESS;
}
Pojo
/**
* Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值。支持级联属性。
* 如:dept.deptId、dept.address.tel 等
*/
@RequestMapping("/testPojo")
public String testPojo(User user) {
System.out.println("testPojo: " + user);
return SUCCESS;
}
@CookieValue
/**
* 了解:
*
* @CookieValue: 映射一个 Cookie 值. 属性同 @RequestParam
*/
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
System.out.println("testCookieValue: sessionId: " + sessionId);
return SUCCESS;
}
@RequestHeader
/**
* 了解: 映射请求头信息 用法同 @RequestParam
*/
@RequestMapping("/testRequestHeader")
public String testRequestHeader(
@RequestHeader(value = "Accept-Language") String al) {
System.out.println("testRequestHeader, Accept-Language: " + al);
return SUCCESS;
}
@RequestParam
/**
* @RequestParam 来映射请求参数.
* value 值即请求参数的参数名
* required 该参数是否必须. 默认为 true
* defaultValue 请求参数的默认值
*/
@RequestMapping(value = "/testRequestParam")
public String testRequestParam(
@RequestParam(value = "username") String un,
@RequestParam(value = "age", required = false, defaultValue = "0") int age) {
System.out.println("testRequestParam, username: " + un + ", age: "
+ age);
return SUCCESS;
}
/**
* Rest 风格的 URL. 以 CRUD 为例:
* 新增: /order POST
* 修改: /order/1 PUT update?id=1
* 获取: /order/1 GET get?id=1
* 删除: /order/1 DELETE delete?id=1
*
* 如何发送 PUT 请求和 DELETE 请求呢 ?
* 1. 需要配置 HiddenHttpMethodFilter
* 2. 需要发送 POST 请求
* 3. 需要在发送 POST 请求时携带一个 name="_method" 的隐藏域, 值为 DELETE 或 PUT
*
* 在 SpringMVC 的目标方法中如何得到 id 呢? 使用 @PathVariable 注解
*
*/
REST 改
@RequestMapping(value = "/testRest/{id}", method = RequestMethod.PUT)
public String testRestPut(@PathVariable Integer id) {
System.out.println("testRest Put: " + id);
return SUCCESS;
}
REST 删
@RequestMapping(value = "/testRest/{id}", method = RequestMethod.DELETE)
public String testRestDelete(@PathVariable Integer id) {
System.out.println("testRest Delete: " + id);
return SUCCESS;
}
REST 增
@RequestMapping(value = "/testRest", method = RequestMethod.POST)
public String testRest() {
System.out.println("testRest POST");
return SUCCESS;
}
REST 查
@RequestMapping(value = "/testRest/{id}", method = RequestMethod.GET)
public String testRest(@PathVariable Integer id) {
System.out.println("testRest GET: " + id);
return SUCCESS;
}
@PathVariable
/**
* @PathVariable 可以来映射 URL 中的占位符到目标方法的参数中.
* @param id
* @return
*/
@RequestMapping("/testPathVariable/{id}")
public String testPathVariable(@PathVariable("id") Integer id) {
System.out.println("testPathVariable: " + id);
return SUCCESS;
}
@RequestMapping – URL
@RequestMapping("/testAntPath/*/abc")
public String testAntPath() {
System.out.println("testAntPath");
return SUCCESS;
}
/**
* 了解: 可以使用 params 和 headers 来更加精确的映射请求. params 和 headers 支持简单的表达式.
* 如果params不符合条件, 则无法访问页面
* 如果headers不符合条件, 则无法访问页面
*
*
* @return
*/
@RequestMapping – params - headers
@RequestMapping(value = "testParamsAndHeaders", params = { "username",
"age!=10" }, headers = { "Accept-Language=zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4" })
public String testParamsAndHeaders() {
System.out.println("testParamsAndHeaders");
return SUCCESS;
}
/**
* 常用: 使用 method 属性来指定请求方式
* <a href="" /> 通常为get
*/
@RequestMapping - method
@RequestMapping(value = "/testMethod", method = RequestMethod.POST)
public String testMethod() {
System.out.println("testMethod");
return SUCCESS;
}
/**
* 1. @RequestMapping 除了修饰方法, 还可来修饰类 2. 1). 类定义处: 提供初步的请求映射信息。相对于 WEB 应用的根目录
* 2). 方法处: 提供进一步的细分映射信息。 相对于类定义处的 URL。若类定义处未标注 @RequestMapping,则方法处标记的 URL
* 相对于 WEB 应用的根目录
*/
@RequestMapping
@RequestMapping("/testRequestMapping")
public String testRequestMapping() {
System.out.println("testRequestMapping");
return SUCCESS;
}
}
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="springmvc/testRedirect">Test Redirect</a>
<br><br>
<a href="springmvc/testView">Test View</a>
<br><br>
ViewAndViewResolver
<a href="springmvc/testViewAndViewResolver">Test ViewAndViewResolver</a>
<br><br>
ModelAttribute
<!--
模拟修改操作
1. 原始数据为: 1, Tom, 123456,[email protected],12
2. 密码不能被修改.
3. 表单回显, 模拟操作直接在表单填写对应的属性值
-->
<form action="springmvc/testModelAttribute" method="Post">
<input type="hidden" name="id" value="1"/>
username: <input type="text" name="username" value="Tom"/>
<br>
email: <input type="text" name="email" value="[email protected]"/>
<br>
age: <input type="text" name="age" value="12"/>
<br>
<input type="submit" value="Submit"/>
</form>
<br><br>
SessionAttributes
<a href="springmvc/testSessionAttributes">Test SessionAttributes</a>
<br><br>
Map
<a href="springmvc/testMap">Test Map</a>
<br><br>
ModelAndView
<a href="springmvc/testModelAndView">Test ModelAndView</a>
<br><br>
ServletAPI
<a href="springmvc/testServletAPI">Test ServletAPI</a>
<br><br>
Pojo
<form action="springmvc/testPojo" method="post">
username: <input type="text" name="username"/>
<br>
password: <input type="password" name="password"/>
<br>
email: <input type="text" name="email"/>
<br>
age: <input type="text" name="age"/>
<br>
city: <input type="text" name="address.city"/>
<br>
province: <input type="text" name="address.province"/>
<br>
<input type="submit" value="Submit"/>
</form>
<br><br>
CookieValue
<a href="springmvc/testCookieValue">Test CookieValue</a>
<br><br>
RequestHeader
<a href="springmvc/testRequestHeader">Test RequestHeader</a>
<br><br>
RequestParam
<a href="springmvc/testRequestParam?username=atguigu&age=11">Test RequestParam</a>
<br><br>
REST 改
<form action="springmvc/testRest/1" method="post">
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="TestRest PUT"/>
</form>
<br><br>
REST 删
<form action="springmvc/testRest/1" method="post">
<input type="hidden" name="_method" value="DELETE"/>
<input type="submit" value="TestRest DELETE"/>
</form>
<br><br>
REST 增
<form action="springmvc/testRest" method="post">
<input type="submit" value="TestRest POST"/>
</form>
<br><br>
REST 查
<a href="springmvc/testRest/1">Test Rest Get</a>
<br><br>
@PathVariable
<a href="springmvc/testPathVariable/1">Test PathVariable</a>
<br><br>
@RequestMapping – URL
<a href="springmvc/testAntPath/mnxyz/abc">Test AntPath</a>
<br><br>
@RequestMapping – params - headers
<a href="springmvc/testParamsAndHeaders?username=atguigu&age=10">Test ParamsAndHeaders</a>
<br><br>
@RequestMapping - method
<form action="springmvc/testMethod" method="POST">
<input type="submit" value="submit"/>
</form>
<br><br>
<a href="springmvc/testMethod">Test Method</a>
@RequestMapping
<br><br>
<a href="springmvc/testRequestMapping">Test RequestMapping</a>
<br><br>
<a href="helloworld">Hello World</a>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!-- 依赖包 jstl.jar standard.jar -->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>Sucess Page</h4>
time: ${requestScope.time }
<br><br>
Map
names: ${requestScope.names }
<br><br>
Pojo
request user: ${requestScope.user }
<br><br>
SessionAttribute
session user: ${sessionScope.user }
<br><br>
request school: ${requestScope.school }
<br><br>
session school: ${sessionScope.school }
<br><br>
abc user: ${requestScope.abc }
<br><br>
mnxyz user: ${requestScope.mnxyz }
<br><br>
i18n
<fmt:message key="i18n.username"></fmt:message>
<br><br>
<fmt:message key="i18n.password"></fmt:message>
<br><br>
</body>
</html>
i18n.properties
i18n.username=Username
i18n.password=Password
i18n_zh_CN.properties
i18n.username=\u7528\u6237\u540D
i18n.password=\u5BC6\u7801
i18n_en_US.properties
i18n.username=Username
i18n.password=Password
HelloView.java
package com.atguigu.springmvc.views;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.View;
//自定义的view, 需要在XML中配置BeanNameViewResolver
@Component //把view 放到ioc 容器里面
public class HelloView implements View{
@Override
public String getContentType() {
return "text/html";
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
response.getWriter().print("hello view, time: " + new Date());
}
}
User.java
package com.atguigu.springmvc.entities;
public class User {
private Integer id;
private String username;
private String password;
private String email;
private int age;
private Address address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
// @Override
// public String toString() {
// return "User [username=" + username + ", password=" + password
// + ", email=" + email + ", age=" + age + ", address=" + address
// + "]";
// }
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password="
+ password + ", email=" + email + ", age=" + age + "]";
}
public User(String username, String password, String email, int age) {
super();
this.username = username;
this.password = password;
this.email = email;
this.age = age;
}
public User(Integer id, String username, String password, String email,
int age) {
super();
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.age = age;
}
public User() {}
}
Address.java
package com.atguigu.springmvc.entities;
public class Address {
private String province;
private String city;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Address [province=" + province + ", city=" + city + "]";
}
}
------------ springmvc-2 ------------
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
DispatcherServlet
<!-- 配置 SpringMVC 的 DispatcherServlet -->
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
HiddenHttpMethodFilter
<!-- 配置 HiddenHttpMethodFilter: 把 POST 请求转为 DELETE、PUT 请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
springmvc.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: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-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
component-scan
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.atguigu.springmvc"></context:component-scan>
InternalResourceViewResolver
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
default-servlet-handler
<!--
default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,
它会对进入 DispatcherServlet 的请求进行筛查, 如果发现是没有经过映射的请求, 就将该请求交由 WEB 应用 服务器默认的 Servlet 处理. 如果不是静态资源的请求,才由 DispatcherServlet 继续处理
一般 WEB 应用服务器默认的 Servlet 的名称都是 default.
若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定
-->
<!-- 添加这个配置之后才能导入jquery等静态资源 -->
<!—如果加了这个,没有配置 annotation-driven 的话那requestmapping就不能用了-->
<mvc:default-servlet-handler/> <!-- 需配置annotation-driven -->
annotation-driven
<!-- 通常需要加这个,不然request mapping 无法正常使用 -->
<!-- 数据类型的转换与格式化也必须配置 mvc:annotation-driven -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
ConversionService注册自定义格式转换器
<!-- 配置 ConversionService, 注册自定义格式转换器 --> <!-- annotation-driven -->
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="employeeConverter"/>
</set>
</property>
</bean>
ResourceBundleMessageSource配置国际化资源文件
<!-- 配置国际化资源文件 -->
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n"></property>
</bean>
配置 SessionLocalResolver, 用于把locale存放到session中
<!-- 配置 SessionLocalResolver -->
<bean id="localeResolver"
class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
</bean>
配置自定义的拦截器interceptors
<mvc:interceptors>
配置自定义的拦截器FirstInterceptor
<!-- 配置自定义的拦截器 -->
<bean class="com.atguigu.springmvc.interceptors.FirstInterceptor"></bean>
配置拦截器(不)作用的路径SecondInterceptor
<!-- 配置拦截器(不)作用的路径 -->
<mvc:interceptor>
<mvc:mapping path="/emps"/>
<bean class="com.atguigu.springmvc.interceptors.SecondInterceptor"></bean>
</mvc:interceptor>
LocaleChangeInterceptor – i18n 国际化
<!-- 配置 LocaleChanceInterceptor -->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"></bean>
</mvc:interceptors>
配置 view-controller – i18n
<!--
<mvc:view-controller path="/i18n" view-name="i18n"/>
-->
<mvc:view-controller path="/i18n2" view-name="i18n2"/> <!-- annotation-driven -->
配置 MultipartResolver – 文件上传
<!-- 配置 MultipartResolver -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"></property>
<property name="maxUploadSize" value="1024000"></property>
</bean>
配置使用 SimpleMappingExceptionResolver 来映射异常
<!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionAttribute" value="ex"></property>
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
<!-- 这里转向了error 页面 -->
</props>
</property>
</bean>
</beans>
EmployeeHandler.java
package com.atguigu.springmvc.crud.handlers;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.atguigu.springmvc.crud.dao.DepartmentDao;
import com.atguigu.springmvc.crud.dao.EmployeeDao;
import com.atguigu.springmvc.crud.entities.Employee;
@Controller
@Controller
public class EmployeeHandler {
@Autowired
@Autowired
private EmployeeDao employeeDao;
@Autowired
private DepartmentDao departmentDao;
@ModelAttribute
@ModelAttribute
public void getEmployee(@RequestParam(value="id",required=false) Integer id,
Map<String, Object> map){
if(id != null){
map.put("employee", employeeDao.get(id));
}
}
改
@RequestMapping(value="/emp", method=RequestMethod.PUT)
public String update(Employee employee){
employeeDao.save(employee);
return "redirect:/emps";
}
查
@RequestMapping(value="/emp/{id}", method=RequestMethod.GET)
public String input(@PathVariable("id") Integer id, Map<String, Object> map){
map.put("employee", employeeDao.get(id));
map.put("departments", departmentDao.getDepartments());
return "input";
}
删
@RequestMapping(value="/emp/{id}", method=RequestMethod.DELETE)
public String delete(@PathVariable("id") Integer id){
employeeDao.delete(id);
return "redirect:/emps";
}
增 @Valid/BindingResult/Errors
@RequestMapping(value="/emp", method=RequestMethod.POST)
public String save(@Valid Employee employee, Errors result,
Map<String, Object> map){
System.out.println("save: " + employee);
if(result.getErrorCount() > 0){
System.out.println("出错了!");
for(FieldError error:result.getFieldErrors()){
System.out.println(error.getField() + ":" + error.getDefaultMessage());
}
//若验证出错, 则转向定制的页面
map.put("departments", departmentDao.getDepartments());
return "input";
}
employeeDao.save(employee);
return "redirect:/emps";
}
显示添加员工页面
@RequestMapping(value="/emp", method=RequestMethod.GET)
public String input(Map<String, Object> map){
map.put("departments", departmentDao.getDepartments());
map.put("employee", new Employee()); // 此处必须添加new Employee()到map中才能显示input.jsp
return "input";
}
@RequestMapping("/emps")
public String list(Map<String, Object> map){
map.put("employees", employeeDao.getAll());
return "list";
}
//设置lastName不进行赋值
// @InitBinder
// public void initBinder(WebDataBinder binder){
// binder.setDisallowedFields("lastName");
// }
}
SpringMVCTest.java
package com.atguigu.springmvc.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import com.atguigu.springmvc.crud.dao.EmployeeDao;
import com.atguigu.springmvc.crud.entities.Employee;
@Controller
@Controller
public class SpringMVCTest {
@Autowired : 把属性自动添加到XML 中bean 配置属性中
@Autowired
private EmployeeDao employeeDao;
ResourceBundleMessageSource 国际化资源信息获取
@Autowired
private ResourceBundleMessageSource messageSource;
SimpleMappingExceptionResolver
@RequestMapping("/testSimpleMappingExceptionResolver")
public String testSimpleMappingExceptionResolver(@RequestParam("i") int i){
String [] vals = new String[10];
System.out.println(vals[i]);
return "success";
}
DefaultHandlerExceptionResolver
@RequestMapping(value="/testDefaultHandlerExceptionResolver",method=RequestMethod.POST)
public String testDefaultHandlerExceptionResolver(){
System.out.println("testDefaultHandlerExceptionResolver...");
return "success";
}
ResponseStatusExceptionResolver @ResponseStatus
@ResponseStatus(reason="测试",value=HttpStatus.NOT_FOUND)
//如果在方法上加这个注解,则无论i是什么都会抛出异常
//如果在类上加这个注解,则i=13时抛出异常
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
if(i == 13){
throw new UserNameNotMatchPasswordException();
}
System.out.println("testResponseStatusExceptionResolver...");
return "success";
}
handleArithmeticException, @ExceptionHandler
// @ExceptionHandler({RuntimeException.class})
// public ModelAndView handleArithmeticException2(Exception ex){
// System.out.println("[出异常了]: " + ex);
// ModelAndView mv = new ModelAndView("error");
// mv.addObject("exception", ex);
// return mv;
// }
/**
* 1. 在 @ExceptionHandler 方法的入参中可以加入 Exception 类型的参数, 该参数即对应发生的异常对象
* 2. @ExceptionHandler 方法的入参中不能传入 Map. 若希望把异常信息传导页面上, 需要使用 ModelAndView 作为返回值
* 3. @ExceptionHandler 方法标记的异常有优先级的问题.
* 4. @ControllerAdvice: 如果在当前 Handler 中找不到 @ExceptionHandler 方法来出来当前方法出现的异常,
* 则将去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常.
*/
// @ExceptionHandler({ArithmeticException.class})
// public ModelAndView handleArithmeticException(Exception ex){
// System.out.println("出异常了: " + ex);
// ModelAndView mv = new ModelAndView("error");
// mv.addObject("exception", ex);
// return mv;
// }
ExceptionHandlerExceptionResolver
@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){
System.out.println("result: " + (10 / i));
return "success";
}
FileUpload
@RequestMapping("/testFileUpload")
public String testFileUpload(@RequestParam("desc") String desc,
@RequestParam("file") MultipartFile file) throws IOException{
System.out.println("desc: " + desc);
System.out.println("OriginalFilename: " + file.getOriginalFilename());
System.out.println("InputStream: " + file.getInputStream());
return "success";
}
国际化 i18n
@RequestMapping("/i18n")
public String testI18n(Locale locale){
String val = messageSource.getMessage("i18n.user", null, locale);
System.out.println(val);
return "i18n";
}
ResponseEntity
@RequestMapping("/testResponseEntity")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException{
byte [] body = null;
ServletContext servletContext = session.getServletContext();
InputStream in = servletContext.getResourceAsStream("/files/abc.txt");
body = new byte[in.available()];
in.read(body);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment;filename=abc.txt");
HttpStatus statusCode = HttpStatus.OK;
ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(body, headers, statusCode);
return response;
}
HttpMessageConverter
@ResponseBody
@RequestMapping("/testHttpMessageConverter")
public String testHttpMessageConverter(@RequestBody String body){
System.out.println(body);
return "helloworld! " + new Date();
}
@ResponseBody – 处理Json数据
@ResponseBody
@RequestMapping("/testJson")
public Collection<Employee> testJson(){
return employeeDao.getAll();
}
ConversionService Converer
@RequestMapping("/testConversionServiceConverer")
public String testConverter(@RequestParam("employee") Employee employee){
System.out.println("save: " + employee);
employeeDao.save(employee);
return "redirect:/emps";
}
}
list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<!--
SpringMVC 处理静态资源:
1. 为什么会有这样的问题:
优雅的 REST 风格的资源URL 不希望带 .html 或 .do 等后缀
若将 DispatcherServlet 请求映射配置为 /,
则 Spring MVC 将捕获 WEB 容器的所有请求, 包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理,
因找不到对应处理器将导致错误。
2. 解决: 在 SpringMVC 的配置文件中配置 <mvc:default-servlet-handler/>
-->
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
删
<!—此处用于将delete请求转换为 post 请求-->
<script type="text/javascript">
$(function(){
$(".delete").click(function(){
var href = $(this).attr("href");
$("form").attr("action", href).submit();
return false;
});
})
</script>
</head>
<body>
删
<!—此处用于将delete请求转换为 post 请求-->
<form action="" method="POST">
<input type="hidden" name="_method" value="DELETE"/>
</form>
<c:if test="${empty requestScope.employees }">
没有任何员工信息.
</c:if>
<c:if test="${!empty requestScope.employees }">
<table border="1" cellpadding="10" cellspacing="0">
<tr>
<th>ID</th>
<th>LastName</th>
<th>Email</th>
<th>Gender</th>
<th>Department</th>
<th>Edit</th>
<th>Delete</th>
</tr>
<c:forEach items="${requestScope.employees }" var="emp">
<tr>
<td>${emp.id }</td>
<td>${emp.lastName }</td>
<td>${emp.email }</td>
<td>${emp.gender == 0 ? ‘Female‘ : ‘Male‘ }</td>
<td>${emp.department.departmentName }</td>
改
<td><a href="emp/${emp.id}">Edit</a></td>
删
<td><a class="delete" href="emp/${emp.id}">Delete</a></td>
<!-- 需要将delete请求转换为 post 请求, 此处需要借助js -->
</tr>
</c:forEach>
</table>
</c:if>
<br><br>
<a href="emp">Add New Employee</a>
</body>
</html>
input.jsp
Errors
Hidden
input
<%@page import="java.util.HashMap"%>
<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
测试字符串格式添加用户converter
<form action="testConversionServiceConverer" method="POST">
<!-- lastname-email-gender-department.id 例如: [email protected] -->
Employee: <input type="text" name="employee"/>
<input type="submit" value="Submit"/>
</form>
<br><br>
<!--
1. WHY 使用 form 标签呢 ?
可以更快速的开发出表单页面, 而且可以更方便的进行表单值的回显
2. 注意:
可以通过 modelAttribute 属性指定绑定的模型属性,
若没有指定该属性,则默认从 request 域对象中读取 command 的表单 bean
如果该属性值也不存在,则会发生错误。
-->
<br><br>
增
<form:form action="${pageContext.request.contextPath }/emp" method="POST"
modelAttribute="employee">
<form:errors path="*"></form:errors>
<br>
<c:if test="${employee.id == null }">
<!-- path 属性对应 html 表单标签的 name 属性值 -->
LastName: <form:input path="lastName"/>
<form:errors path="lastName"></form:errors>
</c:if>
<c:if test="${employee.id != null }">
<form:hidden path="id"/>
<input type="hidden" name="_method" value="PUT"/>
<%-- 对于 _method 不能使用 form:hidden 标签, 因为 modelAttribute 对应的 bean 中没有 _method 这个属性 --%>
<%--
<form:hidden path="_method" value="PUT"/>
--%>
</c:if>
<br>
Email: <form:input path="email"/>
<form:errors path="email"></form:errors>
<br>
<%
Map<String, String> genders = new HashMap();
genders.put("1", "Male");
genders.put("0", "Female");
request.setAttribute("genders", genders);
%>
Gender:
<br>
<form:radiobuttons path="gender" items="${genders }" delimiter="<br>"/>
<br>
Department: <form:select path="department.id"
items="${departments }" itemLabel="departmentName" itemValue="id"></form:select>
<br>
<!--
1. 数据类型转换
2. 数据类型格式化
3. 数据校验.
1). 如何校验 ? 注解 ?
①. 使用 JSR 303 验证标准
②. 加入 hibernate validator 验证框架的 jar 包
③. 在 SpringMVC 配置文件中添加 <mvc:annotation-driven />
④. 需要在 bean 的属性上添加对应的注解
⑤. 在目标方法 bean 类型的前面添加 @Valid 注解
2). 验证出错转向到哪一个页面 ?
注意: 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参
3). 错误消息 ? 如何显示, 如何把错误消息进行国际化
-->
<!-- private Date birth , 需要进行类型转换跟格式化 -->
Birth: <form:input path="birth"/>
<form:errors path="birth"></form:errors>
<br>
Salary: <form:input path="salary"/>
<br>
<input type="submit" value="Submit"/>
</form:form>
</body>
</html>
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="scripts/jquery-1.9.1.min.js"></script>
<script type="text/javascript">
$(function(){
$("#testJson").click(function(){
var url = this.href;
var args = {};
$.post(url, args, function(data){
for(var i = 0; i < data.length; i++){
var id = data[i].id;
var lastName = data[i].lastName;
alert(id + ": " + lastName);
}
});
return false;
});
})
</script>
</head>
<body>
FileUpload 文件上传
<form action="testFileUpload" method="POST" enctype="multipart/form-data">
File: <input type="file" name="file"/>
Desc: <input type="text" name="desc"/>
<input type="submit" value="Submit"/>
</form>
<br><br>
<a href="emps">List All Employees</a>
<br><br>
testJson
<a href="testJson" id="testJson">Test Json</a>
<br><br>
HttpMessageConverter 文件上传
<form action="testHttpMessageConverter" method="POST" enctype="multipart/form-data">
File: <input type="file" name="file"/>
Desc: <input type="text" name="desc"/>
<input type="submit" value="Submit"/>
</form>
<br><br>
ResponseEntity
<a href="testResponseEntity">Test ResponseEntity</a>
i18n
<!--
关于国际化:
1. 在页面上能够根据浏览器语言设置的情况对文本(不是内容), 时间, 数值进行本地化处理
2. 可以在 bean 中获取国际化资源文件 Locale 对应的消息
3. 可以通过超链接切换 Locale, 而不再依赖于浏览器的语言设置情况
解决:
1. 使用 JSTL 的 fmt 标签
2. 在 bean 中注入 ResourceBundleMessageSource 的示例, 使用其对应的 getMessage 方法即可
3. 配置 LocalResolver 和 LocaleChangeInterceptor
-->
<br><br>
<a href="i18n">I18N PAGE</a>
ExceptionHandlerExceptionResolver
<br><br>
<a href="testExceptionHandlerExceptionResolver?i=10">Test ExceptionHandlerExceptionResolver</a>
ResponseStatusExceptionResolver
<br><br>
<a href="testResponseStatusExceptionResolver?i=10">Test ResponseStatusExceptionResolver</a>
DefaultHandlerExceptionResolver
<br><br>
<a href="testDefaultHandlerExceptionResolver">Test DefaultHandlerExceptionResolver</a>
SimpleMappingExceptionResolver
<br><br>
<a href="testSimpleMappingExceptionResolver?i=2">Test SimpleMappingExceptionResolver</a>
</body>
</html>
i18n.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
fmt
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
Fmt 标签
<fmt:message key="i18n.user"></fmt:message>
<br><br>
<a href="i18n2">I18N2 PAGE</a>
Locale , 实现中英文切换效果,
需配置解析器SessionLocalResolver
和配置拦截器LocaleChangeInterceptor
<br><br>
<a href="i18n?locale=zh_CH">中文</a>
<br><br>
<a href="i18n?locale=en_US">英文</a>
</body>
</html>
i18n2.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<fmt:message key="i18n.password"></fmt:message>
<br><br>
<a href="i18n">I18N PAGE</a>
</body>
</html>
error.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>Error Page</h4>
${requestScope.exception }
</body>
</html>
EmployeeConverter.java
package com.atguigu.springmvc.converters;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import com.atguigu.springmvc.crud.entities.Department;
import com.atguigu.springmvc.crud.entities.Employee;
@Component
@Component
public class EmployeeConverter implements Converter<String, Employee> {
//string 转为 Employee
convert
@Override
public Employee convert(String source) {
if(source != null){
String [] vals = source.split("-");
//[email protected]
if(vals != null && vals.length == 4){
String lastName = vals[0];
String email = vals[1];
Integer gender = Integer.parseInt(vals[2]);
Department department = new Department();
department.setId(Integer.parseInt(vals[3]));
Employee employee = new Employee(null, lastName, email, gender, department);
System.out.println(source + "--convert--" + employee);
return employee;
}
}
return null;
}
}
DepartmentDao.java
package com.atguigu.springmvc.crud.dao;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Repository;
import com.atguigu.springmvc.crud.entities.Department;
@Repository
@Repository
public class DepartmentDao {
private static Map<Integer, Department> departments = null;
static{
departments = new HashMap<Integer, Department>();
departments.put(101, new Department(101, "D-AA"));
departments.put(102, new Department(102, "D-BB"));
departments.put(103, new Department(103, "D-CC"));
departments.put(104, new Department(104, "D-DD"));
departments.put(105, new Department(105, "D-EE"));
}
public Collection<Department> getDepartments(){
return departments.values();
}
public Department getDepartment(Integer id){
return departments.get(id);
}
}
EmployeeDao.java
package com.atguigu.springmvc.crud.dao;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.atguigu.springmvc.crud.entities.Department;
import com.atguigu.springmvc.crud.entities.Employee;
@Repository
@Repository
public class EmployeeDao {
private static Map<Integer, Employee> employees = null;
@Autowired
@Autowired
private DepartmentDao departmentDao;
static{
employees = new HashMap<Integer, Employee>();
employees.put(1001, new Employee(1001, "E-AA", "[email protected]", 1, new Department(101, "D-AA")));
employees.put(1002, new Employee(1002, "E-BB", "[email protected]", 1, new Department(102, "D-BB")));
employees.put(1003, new Employee(1003, "E-CC", "[email protected]", 0, new Department(103, "D-CC")));
employees.put(1004, new Employee(1004, "E-DD", "[email protected]", 0, new Department(104, "D-DD")));
employees.put(1005, new Employee(1005, "E-EE", "[email protected]", 1, new Department(105, "D-EE")));
}
private static Integer initId = 1006;
public void save(Employee employee){
if(employee.getId() == null){
employee.setId(initId++);
}
employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId()));
employees.put(employee.getId(), employee);
}
public Collection<Employee> getAll(){
return employees.values();
}
public Employee get(Integer id){
return employees.get(id);
}
public void delete(Integer id){
employees.remove(id);
}
}
Employee.java
package com.atguigu.springmvc.crud.entities;
import java.util.Date;
import javax.validation.constraints.Past;
import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
public class Employee {
private Integer id;
@NotEmpty -- 依赖包hibernate-validator-5.0.0.CR2.jar
@NotEmpty
private String lastName;
@Emai -- 依赖包hibernate-validator-annotation-processor-5.0.0.CR2.jar
private String email;
//1 male, 0 female
private Integer gender;
private Department department;
@Past : 依赖包 validation-api-1.1.0.CR1.jar
@DateTimeFormat
@Past
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birth;
@NumberFormat
@NumberFormat(pattern="#,###,###.#")
private Float salary;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Float getSalary() {
return salary;
}
public void setSalary(Float salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee [id=" + id + ", lastName=" + lastName + ", email="
+ email + ", gender=" + gender + ", department=" + department
+ ", birth=" + birth + ", salary=" + salary + "]";
}
public Employee(Integer id, String lastName, String email, Integer gender,
Department department) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
}
public Employee() {
// TODO Auto-generated constructor stub
}
}
Department.java
package com.atguigu.springmvc.crud.entities;
public class Department {
private Integer id;
private String departmentName;
public Department() {
// TODO Auto-generated constructor stub
}
public Department(int i, String string) {
this.id = i;
this.departmentName = string;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
@Override
public String toString() {
return "Department [id=" + id + ", departmentName=" + departmentName
+ "]";
}
}
i18n.properties
NotEmpty.employee.lastName=^^LastName\u4E0D\u80FD\u4E3A\u7A7A.
Email.employee.email=Email\u5730\u5740\u4E0D\u5408\u6CD5
Past.employee.birth=Birth\u4E0D\u80FD\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4.
typeMismatch.employee.birth=Birth\u4E0D\u662F\u4E00\u4E2A\u65E5\u671F.
i18n.user=User
i18n.password=Password
i18n_zh_CN.properties
#名字不能为空
NotEmpty.employee.lastName=^^LastName\u4E0D\u80FD\u4E3A\u7A7A.
Email.employee.email=Email\u5730\u5740\u4E0D\u5408\u6CD5
Past.employee.birth=Birth\u4E0D\u80FD\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4.
typeMismatch.employee.birth=Birth\u4E0D\u662F\u4E00\u4E2A\u65E5\u671F.
i18n.user=\u7528\u6237\u540D
i18n.password=\u5BC6\u7801
i18n_en_US.properties
#定制错误消息, 提示消息的国际化
#校验注解类名.bean类名.属性名
NotEmpty.employee.lastName=^^LastName\u4E0D\u80FD\u4E3A\u7A7A.
Email.employee.email=Email\u5730\u5740\u4E0D\u5408\u6CD5
Past.employee.birth=Birth\u4E0D\u80FD\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4.
typeMismatch.employee.birth=Birth\u4E0D\u662F\u4E00\u4E2A\u65E5\u671F.
i18n.user=User
i18n.password=Password
FirstInterceptor.java
package com.atguigu.springmvc.interceptors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
HandlerInterceptor 自定义拦截器
public class FirstInterceptor implements HandlerInterceptor{
/**
* 该方法在目标方法之前被调用.
* 若返回值为 true, 则继续调用后续的拦截器和目标方法.
* 若返回值为 false, 则不会再调用后续的拦截器和目标方法.
*
* 可以考虑做权限. 日志, 事务等.
* 正序执行
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("[FirstInterceptor] preHandle");
return true;
}
/**
* 调用目标方法之后, 但渲染视图之前.
* 可以对请求域中的属性或视图做出修改.
* 反序执行
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("[FirstInterceptor] postHandle");
}
/**
* 渲染视图之后被调用. 释放资源
* 反序执行
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("[FirstInterceptor] afterCompletion");
}
}
SecondInterceptor.java
package com.atguigu.springmvc.interceptors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class SecondInterceptor implements HandlerInterceptor{
/**
* 该方法在目标方法之前被调用.
* 若返回值为 true, 则继续调用后续的拦截器和目标方法.
* 若返回值为 false, 则不会再调用后续的拦截器和目标方法.
*
* 可以考虑做权限. 日志, 事务等.
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("[SecondInterceptor] preHandle");
return false;
}
/**
* 调用目标方法之后, 但渲染视图之前.
* 可以对请求域中的属性或视图做出修改.
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("[SecondInterceptor] postHandle");
}
/**
* 渲染视图之后被调用. 释放资源
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("[SecondInterceptor] afterCompletion");
}
}
SpringMVCTestExceptionHandler.java
package com.atguigu.springmvc.test;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice
@ControllerAdvice
public class SpringMVCTestExceptionHandler {
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("----> 出异常了: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
}
UserNameNotMatchPasswordException.java
package com.atguigu.springmvc.test;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用户名和密码不匹配!")
public class UserNameNotMatchPasswordException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
}
------------ springmvc-3 ------------
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<!-- 配置启动 Spring IOC 容器的 Listener -->
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
beans.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"
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-4.0.xsd">
<context:component-scan base-package="com.atguigu.springmvc">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<!-- 配置数据源, 整合其他框架, 事务等. -->
</beans>
springmvc.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: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-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<!--
需要进行 Spring 整合 SpringMVC 吗 ?
还是否需要再加入 Spring 的 IOC 容器 ?
是否需要再 web.xml 文件中配置启动 Spring IOC 容器的 ContextLoaderListener ?
1. 需要: 通常情况下, 类似于数据源, 事务, 整合其他框架都是放在 Spring 的配置文件中(而不是放在 SpringMVC 的配置文件中).
实际上放入 Spring 配置文件对应的 IOC 容器中的还有 Service 和 Dao.
2. 不需要: 都放在 SpringMVC 的配置文件中. 也可以分多个 Spring 的配置文件, 然后使用 import 节点导入其他的配置文件
-->
<!--
问题: 若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器扫描的包有重合的部分, 就会导致有的 bean 会被创建 2 次.
解决:
1. 使 Spring 的 IOC 容器扫描的包和 SpringMVC 的 IOC 容器扫描的包没有重合的部分.
2. 使用 exclude-filter 和 include-filter 子节点来规定只能扫描的注解
-->
<!--
SpringMVC 的 IOC 容器中的 bean 可以来引用 Spring IOC 容器中的 bean.
返回来呢 ? 反之则不行. Spring IOC 容器中的 bean 却不能来引用 SpringMVC IOC 容器中的 bean!
-->
<context:component-scan base-package="com.atguigu.springmvc" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:default-servlet-handler/>
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
HelloWorld.java
package com.atguigu.springmvc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HelloWorld {
@Autowired
private UserService userService;
public HelloWorld() {
System.out.println("HelloWorld Constructor...");
}
@RequestMapping("/helloworld")
public String hello(){
System.out.println("success");
System.out.println(userService);
return "success";
}
}
UserService.java
package com.atguigu.springmvc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private HelloWorld helloWorld;
public UserService() {
System.out.println("UserService Constructor...");
}
}
abc.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>Httml Page</h4>
</body>
</html>
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="helloworld">Hello World</a>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h4>Success Page</h4>
</body>
</html>