SpringMVC(SpringMVC的强大注解们)

1.springMVC介绍

概述:Spring为视图层提供的基于MVC设计理念的优秀的Web框架,是目前最主流的MVC框架之一,SpringMVC通过一套MVC注解,让POJO成为处理请求的处理器,而无须实现任何接口,它也支持REST风格的URL请求;它采用了松散耦合的可插拔组件结构,比其他MVC框架更具扩展性和灵活性

2.springMVC的第一个程序

1.导包

  • commons-logging-1.1.3.jar
  • spring-aop-4.0.0.RELEASE.jar
  • spring-beans-4.0.0.RELEASE.jar
  • spring-context-4.0.0.RELEASE.jar
  • spring-core-4.0.0.RELEASE.jar
  • spring-expression-4.0.0.RELEASE.jar
  • spring-web-4.0.0.RELEASE.jar
  • spring-webmvc-4.0.0.RELEASE.jar

2.写配置

  • web.xml
  • 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">
  <display-name>springMVC_01</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 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>
			<!-- 指定springmvc配置文件位置 -->
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springMVC.xml</param-value>
		</init-param>
		<!-- servlet启动加载,servlet原来是第一次访问创建对象;
		load-on-startup:服务器启动的时候创建对象,值越小,越先创建对象 -->
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- /*和/都是拦截所有请求;
		/*的范围更大,还会拦截*.jsp这些请求,一旦拦截jsp页面就不能显示了-->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

springMVC的配置文件:

<?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.luyi"></context:component-scan>
	
	<!-- 配置一个视图解析器,能帮我们拼接页面地址 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/page/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
</beans>

jsp页面:

//index.jsp

...
<body>
	<a href="hello">HelloWorld</a>
</body>
...

//success.jsp
...
<body>
	访问成功
</body>
...

java类(处理请求):

package com.luyi.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 告诉Spring这是一个处理器,可以处理请求
 * @Controller:标识哪个组件是控制器
 * @author luyi
 *
 */

@Controller
public class HelloController {
	
	/**
	 *  /代表当前项目下:处理当前项目下的hello请求
	 */
	@RequestMapping("/hello")
	public String helloRequest(){
		System.out.println("请求收到了,正在处理。。。");
		//视图解析器解析成/WEB-INF/page/success.jsp
		return "success";
	}
}

3.springMVC的一些细节

运行流程

  • 客户端点击“HelloWorld”超链接会发送http://localhost:8080/springMVC_01/hello请求
  • 来到tomcat服务器
  • SpringMVC的前端控制器收到请求
  • 来看请求地址和@RequestMapping标注的哪个匹配,来找找到底使用哪个类的哪个方法
  • 前端控制器找到了目标处理器类和目标方法,直接利用返回来执行目标方法
  • 方法执行完成以后会有一个返回值,SpringMVC认为这个返回值就是要去的页面地址
  • 拿到方法返回值以后,用视图解析器进行拼串得到完整的页面地址
  • 拿到页面,前端控制器帮我们转发到页面

@RequestMapping的使用

概述:这个注解就是为了告诉springMVC,这个方法用来处理什么请求;这个/可以省略,即使省略了,也就是默认从当前项目下开始,习惯加上比较好;一个方法只能处理一个请求,如果两个方法都去处理一个请求,就会报错

@RequestMapping可以标注在控制器的类定义及方法定义处:

  • 类定义处:提供初步的请求映射信息,相对于web应用的根目录
  • 方法处:提供进一步的细分映射信息,相对于类定义处的url,若类定义出为标注@RequestMapping,则方法处标志的URL相对于WEB应用的根目录

@RequestMapping的几个属性:

  • value:就是处理的请求路径,如value="/handle"

  • method:就是规定请求方式(get,post等),是对应的请求方式才处理,不是则报405错,如method=RequestMethod.GET

  • params:规定请求参数,不满足则报错404

    • param1:表示请求必须包含有名为param1的请求参数,如params=
    • !param1:表示请求不能包含名为param1的请求参数,如params=
    • param1 != value1:表示请求包含名为param1的请求参数,但其值不能为value1,如params=
    • param1 = value1, param2:表示多个条件,如params=
  • headers:规定请求头,如headers=

  • consumes:只接受内容类型是某种的请求,规定请求头中的Content-Type

  • produce:告诉浏览器返回的内容类型是什么,给响应头中加上Content-Type:text/html;charset=utf-8

@RequestMapping的RequestMapping-ant风格的url(@RequestMapping的模糊匹配功能):

  • ?:能替代任意一个字符
  • *:能替代任意多个字符,和一层路径
  • **:能替代多层路径

eg:

@RequestMapping(value="/hello0?")
@RequestMapping(value="/*/hello")
@RequestMapping(value="/**/hello")

@PathVariable获取路径上的占位符:

@RequestMapping(value="/hello/{id}")
	public String helloRequest(@PathVariable("id")String id){
		System.out.println(id);
		//视图解析器解析成/WEB-INF/page/success.jsp
		return "success";
	}

指定配置文件位置

概述:如果不指定配置文件的位置,它就会默认去找/WEB-INF/下的 前端控制名-servlet.xml文件,如springDispatcherServlet-servlet.xml

web.xml中的url-pattern配置

处理*.jsp页面是tomcat做的事,所有项目的小web.xml都是继承于大web.xml:

  • 服务器的大web.xml中有一个DefaultServlet是url-pattern=/,访问静态资源时,如果我们走的是DefaultServlet的url-pattern,那tomcat就会在服务器下找到这个资源并返回;而我们配置的前端控制器的/覆盖了tomcat服务器中的DefaultServlet,因此我们访问不到静态资源
  • 我们的配置中前端控制器url-pattern=/
  • 我们配置url-pattern=/却能访问到jsp页面是因为我们没有覆盖服务器中大web.xml中的jspServlet的配置
  • /* 直接就是拦截所有请求,所以我们写/,也是为了迎合后面的Rest风格的url地址

SpringMVC获取请求带来的各种信息的方式:

1.获取请求参数

1).默认方式获取请求参数:直接给方法参数上写一个和请求参数名相同的变量,这个变量就来接收请求参数的值,如果请求带这个参数,但是没有值(如user=),则这个参数为空串,如果请求没有带参数,则为null

2).@RequestParam:获取请求参数的,参数默认是必须带的,如@RequestParam("user")String use,如果请求没带这个参数就会报错,@RequestParam还有三个属性,分别是:

  • value:指定要获取的参数的key
  • required:这个参数是否必须的
  • defaultValue:默认值,没有这个属性的话默认值是null

注意:@RequestParam是用来获取参数的,获取不了路径的值,而@PathVariable则是用来获取路径的,获取不到参数的值

2.获取请求头

  • @RequestHeader,如@RequestHeader("User-Agent") String userAgent,如果获取没有这个请求头,然后去获取,就会报错

注意:@RequestHeader也有三个属性,value,required和defaultValue

3.获取cookie值

  • @CookieValue:获取某个cookie的值,去获取没有这个cookie名的cookie也会报错

注意:@RequestHeader也有三个属性,value,required和defaultValue

请求处理

1.传入POJO

概述:如果传入的参数是一个POJO,那么springMVC会自动的为这个POJO进行封装赋值

  • 将POJO中的每一个属性,从request参数中尝试获取出来,并封装即可
  • 还可以级联封装

代码示例:

index.jsp页面:

<form action="book" method="post">
		书名:<input type = "text" name = "bookName"><br>
		作者:<input type = "text" name = "author"><br>
		价格:<input type = "text" name = "price"><br>
		<hr>
		省份:<input type = "text" name = "address.provice"><br>
		市:<input type = "text" name = "address.city"><br>
		<input type="submit" value="提交数据">
	</form>

BookController.java文件:

package com.luyi.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.luyi.dao.Book;

@Controller
public class BookController {
	@RequestMapping(value="/book", method=RequestMethod.POST)
	public String addBook(Book book){
		System.out.println("我要保存的图书" + book);
		return "success";
	}	
	
}

POJO类:

//Book.java

package com.luyi.dao;

public class Book {
	private String bookName;
	private String author;
	private Address address;
	private Integer price;
	
	public Book(){};
	
	
	
	public Book(String bookName, String author, Address address, Integer price) {
		super();
		this.bookName = bookName;
		this.author = author;
		this.address = address;
		this.price = price;
	}



	

	public String getBookName() {
		return bookName;
	}



	public void setBookName(String bookName) {
		this.bookName = bookName;
	}



	public String getAuthor() {
		return author;
	}



	public void setAuthor(String author) {
		this.author = author;
	}



	public Address getAddress() {
		return address;
	}



	public void setAddress(Address address) {
		this.address = address;
	}



	public Integer getPrice() {
		return price;
	}



	public void setPrice(Integer price) {
		this.price = price;
	}



	@Override
	public String toString() {
		return "Book [bookName=" + bookName + ", author=" + author
				+ ", address=" + address + ", price=" + price + "]";
	}
	
	
	
}

//Address.java

package com.luyi.dao;

public class Address {
	private String provice;
	private String city;
	
	public Address(){};
	
	public Address(String provice, String city) {
		super();
		this.provice = provice;
		this.city = city;
	}
	
	public String getProvice() {
		return provice;
	}
	public void setProvice(String provice) {
		this.provice = provice;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
	@Override
	public String toString() {
		return "Address [provice=" + provice + ", city=" + city + "]";
	}
	
	
}

注意:发送请求和响应数据如果有中文字符会有乱码现象,解决方案如下:

request乱码:

  • get请求:修改server.xml文件:URIEncoding="UTF-8"

  • post请求:在第一次获取请求参数之前设置request.setCharacterEncoding("UTF-8");springMVC中有一个过滤器就在帮我们做这样的事情,需要注意的是这个字符编码过滤器的配置一般在其他过滤器之前 ,关于这个过滤器的配置如下:

     <filter>
     	<filter-name>CharacterEncodingFilter</filter-name>
     	<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
     	<init-param>
     		<!-- encoding:指定解决POST请求乱码 -->
     		<param-name>encoding</param-name>
     		<param-value>UTF-8</param-value>
     	</init-param>
     	<init-param>
     		<!-- forceEncoding:解决了响应乱码问题 -->
     		<param-name>forceEncoding</param-name>
     		<param-value>true</param-value>
     	</init-param>
     	
     </filter>
     <filter-mapping>
     	<filter-name>CharacterEncodingFilter</filter-name>
     	<url-pattern>/*</url-pattern>
     </filter-mapping>
     
     <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>
    

response乱码:response.setContentType("text/html;charset=utf-8")

2.传入原生API

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • java.security.Principal
  • Locale:与国际化有关的区域信息对象
  • InputStream
  • OutputStream
  • Reader
  • Writer

代码示例:

@RequestMapping(value="/handle")
	public String handle(HttpSession session, HttpServletRequest request){
		session.setAttribute("sessionParam", "session域中的东西");
		request.setAttribute("requestParam", "request域中的东西");
		return "success";
	}

数据输出

1.传入Map,Model,ModelMap类型的参数

2.方法返回值返回ModelAndView类型,既包含模型数据(给页面带的数据),而且数据是放在请求域中的

3.springMVC还提供了一种可以临时给Session域中保存数据的方式,使用一个注解@SessionAttribute,只能标在类上,这个注解的属性value指定保存数据时要给session中放的数据的key,types指定只要保存的是这种类型的数据,给session中也放一份,不推荐使用,可能会引发异常,给session中放数据还是推荐使用原生API

4.@ModelAttribute注解的应用场景是在修改数据时不用全部修改所有数据时使用,后来有了Mybatics之后,应用场景就不多了;当我们传入一个POJO时,SpringMVC会帮我们封装这个POJO,springMVC自动封装这个POJO的原理就是帮我们new了一个这样的对象出来,而new出来的对象如果有属性是没有赋值的话,那么就会是null值,但是我们的应用场景是想要它是之前保留的数据,此时,@ModelAttribute就派上了用场

代码示例:

//TestController.java

package com.luyi.controller;

import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class TestController {
	
	@RequestMapping("/handle01")
	public String handle01(Map<String, String> map){
		map.put("msg1", "map是可以的");
		return "success";
	}
	
	@RequestMapping("/handle02")
	public String handle02(Model model){
		model.addAttribute("msg2", "model是可以的");
		return "success";
	}
	
	@RequestMapping("/handle03")
	public String handle03(ModelMap modelMap){
		modelMap.addAttribute("msg3", "ModelMap是可以的");
		return "success";
	}
	
	/**
	 * 返回值是ModelAndView,可以为页面携带数据
	 * @return
	 */
	@RequestMapping("/handle04")
	public ModelAndView handle04(){
		ModelAndView mv = new ModelAndView();
		mv.setViewName("success");
		mv.addObject("msg4", "方法返回值为ModelAndView类型");
		return mv;
	}
}

//success.jsp
<body>
<h1>成功跳转页面</h1>
	${requestScope.msg1}
	${requestScope.msg2}
	${requestScope.msg3}
	${requestScope.msg4}
</body>

视图解析

概述:视图解析器只是为了得到视图对象,视图对象才是真正的转发(将模型数据全部放在请求中)或者重定向到页面,视图对象才是真正的渲染视图

1.forward前缀指定一个转发操作

代码示例:

@RequestMapping("/handle")
	public String handle(){
		/**
		 * 当我们已经配置了视图解析器,前缀为/WEB-INF/,后缀为.jsp时,
		 * 如果我们想要发送一个从根路径开始的请求的话,有两种方法:
		 *1)使用相对路径../../
		 *2)forward:前缀
		 */
		return "../../index";
	}

2.redirect前缀指定重定向到页面

代码示例:

@RequestMapping("/handle01")
	public String handle01(){
		//重定向到hello.jsp页面
		return "redirect:/hello.jsp";
	}
	
	@RequestMapping("/handle02")
	public String handle02(){
		//重定向到/handle01请求(多次重定向)
		return "redirect:/handle01";
	}

3.视图和视图解析器

  • 请求处理方法执行完成后,最终都会返回一个ModelAndView对象,对于那些返回String,View,或ModelMap等类型的处理方法,SpringMVC也会在内部将他们装配成一个ModelAndView对象,它包含了逻辑名和模型对象的视图
  • SpringMVC借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是jsp,也可能是Excel,JFreeChart等各种表现形式的视图
  • 对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关系,处理器工作重点聚焦在生产模型数据的工作上,从而实现MVC的充分解耦

4.jstlView支持国际化功能

步骤:

  • 导包导入了jstl的时候会自动创建为一个jstlView,可以快速方便的支持国际化功能;
  • 快速国际化:
    • javaWeb国际化步骤:
      • 得得到一个locale对象
      • 使用ResourceBundle绑定国际化资源文件
      • 使用ResourceBundle.getString("key")来获取国际化配置文件中的值
      • web页面的国际化,fmt标签来做(fmt:setLocale,fmt:setBundle,fmt:message
    • 有了jstlView以后:
      • 让spring管理国际化资源就行
      • 直接去页面使用fmt:message
        代码示例:

index.jsp(首页):

<body>
<a href="toLoginPage">login</a>
</body>

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">
  <display-name>springMVC_01</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 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>
			<!-- 指定springmvc配置文件位置 -->
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springMVC.xml</param-value>
		</init-param>
		<!-- servlet启动加载,servlet原来是第一次访问创建对象;
		load-on-startup:服务器启动的时候创建对象,值越小,越先创建对象 -->
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- /*和/都是拦截所有请求;
		/*的范围更大,还会拦截*.jsp这些请求,一旦拦截jsp页面就不能显示了-->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 配置一个字符编码的Filter -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<!-- encoding:指定解决POST请求乱码 -->
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<!-- forceEncoding:解决了响应乱码问题 -->
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
		
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<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"
	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.luyi"></context:component-scan>
	
	
	<!-- 导入jstlView包,可以支持便捷的国际化功能,还有一种就是直接设置viewClass属性值为JstlView也行 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/page/"></property>
		<property name="suffix" value=".jsp"></property>
		<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
	</bean>
	
	<!-- 让springMVC管理国际化资源文件:配置一个资源文件管理器 ,id名必须为messageSource-->
	<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
		<!-- basename指定基础名-->
		<property name="basename" value="i18n"></property>
	</bean>
</beans>

TestController.java(前端控制器)

package com.luyi.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class TestController {
	
	@RequestMapping("/toLoginPage")
	public String toLoginPage(){
		return "login";
	}
	
}

国际化资源文件:

i18n_en_US.properties:

welcomeinfo=WELCOME TO LOGIN PAGE
username=USERNAME
password=PASSWORD
loginBtn=LOGIN

i18n_zh_CN.properties:

welcomeinfo=\u6B22\u8FCE\u6765\u5230\u767B\u5F55\u9875\u9762
username=\u7528\u6237\u540D
password=\u5BC6\u7801
loginBtn=\u63D0\u4EA4

注意:要想达到国际化的效果,一定要过springMVC的视图解析流程,人家会创建一个JstlView帮你快速国际化,也不能写forward

5.在spring配置文件中将一个请求映射到一个页面

代码示例:

<!-- 发送一个请求,直接来到web-inf下的页面login页面,需要mvc名称空间 -->
<!-- path="":指定哪个请求 -->
<!-- view-name:指定映射到哪个视图 -->
<mvc:view-controller path="/toLoginPage" view-name="login" />
<!-- 开启MVC注解驱动模式 ,如果没有这一段代码,
则除了toLoginPage请求以外,其他的请求都会请求不到数据了-->
<mvc:annotation-driven></mvc:annotation-driven>

4.知识点补充之REST

概述:REST也就是Representational State Transfer(表现层状态转化),是目前最流行的一种互联网软件架构,它结构清晰、符合标准、易于理解,扩展方便,所以正得到越来越多网站的采用

REST推荐的url命名方式:/资源名/资源标识符

  • /book/1:请求方式为get,查询1号图书
  • /book/1: 请求方式为delete,删除1号图书
  • /book/1: 请求方式为put,修改1号图书
  • /book:请求方式为post,增加1号图书

那么,如何从页面发起put和delete形式的请求呢?Spring非常友好的为我们提供了发起put和delete请求的支持,只要按步骤来即可搞定put和delete请求

  • 首先,SpringMVC中有一个Filter(HiddenHttpMethodFilter),它可以把普通的请求转化为规定形式的请求,配置这个filter
  • 其次,创建一个post类型的表单
  • 最后,表单项中携带一个_method的参数,而这个_method的值就是DELETE,PUT

代码示例:

配置文件:

//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"
	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.luyi"></context:component-scan>
	
	<!-- 配置一个视图解析器,能帮我们拼接页面地址 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/page/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
</beans>

//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">
  <display-name>springMVC_01</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 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>
			<!-- 指定springmvc配置文件位置 -->
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springMVC.xml</param-value>
		</init-param>
		<!-- servlet启动加载,servlet原来是第一次访问创建对象;
		load-on-startup:服务器启动的时候创建对象,值越小,越先创建对象 -->
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- /*和/都是拦截所有请求;
		/*的范围更大,还会拦截*.jsp这些请求,一旦拦截jsp页面就不能显示了-->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 使得其支持REST风格 -->
	<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>

jsp文件:

//index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" isErrorPage="true"%> 
<!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="book/1">查询图书馆</a><br>
	<form action="book" method="post">
		<input type="submit" value="添加图书"/>
	</form><br>
	<form action="book/1" method="post">
		<input name="_method" value="delete"/>
		<input type="submit" value="删除1号图书"/>
	</form><br>
	<form action="book/1" method="post">
		<input name="_method" value="put"/>
		<input type="submit" value="修改1号图书"/>
	</form><br>
</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>
	操作成功
</body>
</html>

java类文件:

//HelloController

package com.luyi.controller;

import org.apache.catalina.connector.Request;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * 告诉Spring这是一个处理器,可以处理请求
 * @Controller:标识哪个组件是控制器
 * @author luyi
 *
 */

@Controller
public class HelloController {
	
	/**
	 *  /代表当前项目下:处理当前项目下的hello请求
	 */
	@RequestMapping(value="/book/{id}", method=RequestMethod.GET)
	public String getBook(@PathVariable("id")String id){
		System.out.println("查询了" + id + "号书");
		//视图解析器解析成/WEB-INF/page/success.jsp
		return "success";
	}
	
	@RequestMapping(value="/book", method=RequestMethod.POST)
	public String addBook(){
		System.out.println("添加了一本图书");
		//视图解析器解析成/WEB-INF/page/success.jsp
		return "success";
	}
	
	@RequestMapping(value="/book/{id}", method=RequestMethod.PUT)
	public String setBook(@PathVariable("id")String id){
		System.out.println("修改了" + id + "号图书");
		//视图解析器解析成/WEB-INF/page/success.jsp
		return "success";
	}
	
	@RequestMapping(value="/book/{id}", method=RequestMethod.DELETE)
	public String deleteBook(@PathVariable("id")String id){
		System.out.println("删除了" + id + "号图书");
		//视图解析器解析成/WEB-INF/page/success.jsp
		return "success";
	}
}

注意:高版本的tomcat(8.0以上)对REST的支持有点问题,对put和delete的请求会报405错误,解决方法是在jsp页面中把isErrorPage设置为true(isErrorPage="true")

5.springMVC源码知识

springMVC的整个执行大致流程:

1).所有请求过来DispatcherServlet收到请求

2).调用doDispatch()方法进行处理

  • getHandler():根据当前请求地址找到能处理这个请求的目标处理器类(处理器),根据当前请求在HandleMapping中找到这个请求的映射信息,获取到目标处理器类
  • getHandlerAdapter():根据当前处理器类获取到能执行这个处理器方法的适配器;
  • 使用刚才获取到的适配器(AnnotationMethodHandlerAdapter)执行目标方法
  • 目标方法执行后会返回一个ModelAndView对象
  • 根据ModelAndView的信息转发到具体的页面,并可以在请求域中取出ModelAndView中的模型数据

DispatcherServlet中有几个引用类型的属性:SpringMVC的九大组件(在springMVC工作的时候,关键位置都是用九大组件完成的):

  • MultipartResolver:文件上传解析器
  • LocaleResolver:区域信息解析器,和国际化有关
  • ThemeResolver:主题解析器,强大的主题效果更换
  • HandlerMapping:handler映射信息
  • HandlerAdapter:Handler的适配器
  • HandlerExceptionResolver:springMVC强大的异常解析功能,异常解析器
  • RequestToViewNameTranslator:当没有返回视图名时,请求地址就转换为视图名
  • FlashMapManager:SpringMVC中运行重定向携带数据的功能
  • ViewResolver:视图解析器

注意:九大组件都是接口,接口就是规范,提供了非常强大的扩展性

确定处理请求的方法的每个参数的值

1.标了注解:保存注解的信息,最终得到这个注解应该对应解析的值

2.没标注解:

  • 看是否是原生API

  • 看是否是Model或者Map等

  • 看是否是简单类型;paramName;

  • 以上都不是的话,看是否是给attrName赋值;attrName(参数标了@ModelAttribute("")就是指定的,没标的话就是空串),确定自定义类型参数:

    • 先看隐含模型中有没有这个attrName作为key对应的值,如果有就从隐含模型中获取并赋值
    • 看是否是@SessionAttributes标注的属性,如果是,从Session中拿,如果拿不到就会抛异常
    • 如果以上都不是,则利用反射创建一个新的 对象
  • 拿到之前创建好的对象,使用数据绑定器(WebDataBinder)将请求中的每个数据绑定到这个对象中

6.springMVC的数据绑定

数据转换&数据格式化&数据校验

概述:当我们前端页面提交了数据之后,springMVC封装自定义类型对象的时候,javaBean要和页面提交数据绑定,就需要牵扯到三种操作,数据绑定期间的数据转换(如:String-->Integer),数据格式化问题(如日期的表达形式有多种:2020-8-25,2020.8.25等),以及前端数据和后端数据的校验(如我们提交的数据是否合法:邮箱格式,手机号码等都有一定的格式)

WebDataBinder

概述:WebDataBinder为数据绑定器,负责数据的绑定工作,数据绑定期间产生的类型转换,格式化,数据校验等问题都由他来搞定

在WebDataBinder中,有几个组件分别负责绑定期间的不同的问题:

  • ConversionService组件:负责数据类型的转换以及格式化功能

  • Validators组件:负责数据的校验

  • BindingResult组件:负责保存以及解析数据绑定期间数据产生的错误

数据绑定流程

  • SpringMVC主框架将ServletRequest对象及目标方法的入参实例传递给WebDataBinderFactory实例,以创建DataBinder实例对象
  • DataBinder调用装配在SpringMVC上下文中的ConversionService组件进行数据类型转换、数据格式化工作,将Servlet中的请求信息填充到入参对象中
  • 调用Validator组件对已经绑定了请求信息的入参对象进行数据合法性校验,并最终生产数据绑定结果BindingData对象
  • SpringMVC抽取BindingResult中的入参对象和校验错误对象,将它们赋给处理方法的响应入参

如图所示:

数据转化--自定义类型转化器

步骤:

  • 实现Converter接口(Converter接口是ConversionService接口里面的),做一个自定义类型的转换器

  • 配置出ConversionService:

    • 自定义的Converter得放进ConversionService中;
    • 将WebDataBinder中的ConversionServlet设置成我们这个加了自定义类型转换器的COnversionService
  • 让springMVC用我们的ConversionService

代码实现:

1.实现Converter接口:

package com.luyi.component;

import org.springframework.core.convert.converter.Converter;

import com.luyi.dao.Book;

/**
 * 实现Converter<S, T>接口,泛型S为要转换的目标,T为要转换为什么
 * @author luyi
 *
 */
public class MyConverter implements Converter<String, Book>{

	@Override
	public Book convert(String arg0) {
		//两者相互转换的一些规则
		...
		return null;
	}

}

2.配置出ConversionService

<!-- 告诉springMVC别用默认的ConversionService,
 而是用我们自定义的ConversionService,里面有我们自定义的Converter-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
	<!-- 往converters转换器(一个set集合)中添加我们自定义的类型转换器 -->
	<property name="converters">
		<set>
			<bean class="com.luyi.component.MyConverter"></bean>
		</set>
	</property>
	
</bean>	

3.让springMVC用我们的ConversionService

<!-- 开启MVC注解驱动模式 ,如果没有这一段代码,
则除了toLoginPage请求以外,其他的请求都会请求不到数据了,
让springMVC用我们的ConversionService(id为conversionService)-->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>

mvc:annotation-driven标签的解析

<mvc:annotation-driven /> 会自动注册RequestMappingHandlerMapping 、RequestMappingHandlerAdapter 与 ExceptionHandlerExceptionResolver三个bean。
还将提供以下支持:

  • 支持使用 ConversionService 实例对表单参数进行类型转换
  • 支持使用 @NumberFormat annotation、@DateTimeFormat 注解完成数据类型的格式化
  • 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证
  • 支持使用 @RequestBody 和 @ResponseBody 注解

<mvc:annotation-driven />标签与<mvc:default-servlet-handler /> 标签的配合使用:

  • 如果这两个标签都不配置上的话,动态资源(@RequestMapping映射的资源能访问到,静态资源(.html,.js,.img等)访问不到)
  • 只加了<mvc:default-servlet-handler />标签,不加<mvc:annotation-driven />标签,则可以访问静态资源,但动态资源访问不到
  • 两个标签都加上的话,则静态资源和动态资源都能访问得到

数据格式化

日期格式化的注解:@DateTimeFormat(pattern="yyyy-MM-dd")

代码示例:

//日期格式化,转换为yyyy-MM-dd格式
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date brith;

除了日期格式化,还有数值的格式化,代码示例如下:

//数值的格式化,格式如10,000.66
@NumberFormat(pattern="#,###.##")
private Integer price;

注意:如果我们前面有配置自定义的Converter,如果bean配置的classs是ConversionServiceFactoryBean,则日期格式化注解仍然没有起作用,他没有走默认的Converters,而为了可以走默认的Converters,又可以实现我们自己自定义的Converter,则需要配置bean的class为FormattingConversionServiceFactoryBean,该类内部已经注册了 :

  • NumberFormatAnnotationFormatterFactroy:支持对数字类型的属性使用 @NumberFormat 注解
  • JodaDateTimeFormatAnnotationFormatterFactroy:支持对日期类型的属性使用 @DateTimeFormat 注解

数据校验

概述:数据校验只做前端校验是不安全的,在重要的数据上一定要加上后端验证,实现前端校验的方法:

  • 可以写程序将我们每一个数据取出进行校验,如果失败直接来到添加页面,提示其重新填写
  • SpringMVC:可以使用JSR303来做数据校验

如何快速的进行后端校验:

1).导入校验框架的jar包:

  • hibernate-validator-5.0.0.CR2.jar
  • hibernate-validator-annotation-processor-5.0.0.CR2.jar
  • classmate-0.8.0.jar
  • jboss-logging-3.1.1.GA.jar
  • validation-api-1.1.0.CR1.jar

注意:从下载来的hibernate-validator-5.0.0.CR2文件夹中的required文件夹下中还有几个带el的jar包(el-api-2.2.jar,javax.el-2.2.4.jar
,javax.el-api-2.2.4.jar),我们没有导入进去,原因是tomcat7.0版本以上el表达式比较强大,不用导入这几个带el的jar包,如果你使用的是tomcat7.0版本以下的,那么就需要将带el的几个jar包放在tomcat的lib文件夹下

2).导完包后只需要给javaBean的属性添加上校验注解即可

3).在springMVC封装对象的时候,告诉SpringMVC这个javaBean需要校验,使用@Valid注解即可达到这个效果

4).如何知道校验结果:给需要校验的javaBean后面紧跟一个BindingResult,这个BindingResult就封装了bean的校验结果

5).根据校验结果,执行相应的操作,form:errors标签可以回显错误

代码示例:

index.jsp(首页):

<body>
	<a href="toAddBookPage">添加图书</a>
</body>

Book.java(给javaBean的属性添加上校验注解):

package com.luyi.dao;

import java.util.Date;

import javax.validation.constraints.Past;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

public class Book {
	@NotEmpty
	private String bookName;
	@Length(min=6, max=18)
	private String author;
	@Email
	private String authorEmail;
	
	private Address address;
	
	@NumberFormat(pattern="#,###.##")
	private Integer price;
	
	/*//日期格式化,转换为yyyy-MM-dd格式
	@DateTimeFormat(pattern="yyyy-MM-dd")
	@Past //规定必须是一个过去的时间
	private Date brith;*/
	
	public Book(){}

	
	
	public Book(String bookName, String author, String authorEmail,
			Address address, Integer price) {
		super();
		this.bookName = bookName;
		this.author = author;
		this.authorEmail = authorEmail;
		this.address = address;
		this.price = price;
	}



	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getAuthorEmail() {
		return authorEmail;
	}

	public void setAuthorEmail(String authorEmail) {
		this.authorEmail = authorEmail;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	public Integer getPrice() {
		return price;
	}

	public void setPrice(Integer price) {
		this.price = price;
	}

	/*public Date getBrith() {
		return brith;
	}

	public void setBrith(Date brith) {
		this.brith = brith;
	}*/

	@Override
	public String toString() {
		return "Book [bookName=" + bookName + ", author=" + author
				+ ", authorEmail=" + authorEmail + ", address=" + address
				+ ", price=" + price  + "]";
	};
	
	
}

BookController.java(控制器,转发请求)

package com.luyi.controller;


import javax.validation.Valid;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.luyi.dao.Book;

@Controller
public class BookController {
	
	@RequestMapping(value="/toAddBookPage")
	public String toAddBookPage(Model model){
		Book book1 = new Book();
		book1.setBookName("西游记");
		model.addAttribute("book", book1);
		return "addBook";
	}
	
	@RequestMapping(value="/addBook", method=RequestMethod.POST)
	//使用@Valid注解告诉springMVC这个javaBean需要校验
	//这个BindingResult就封装了bean的校验结果
	public String addBook(@Valid Book book, BindingResult result){
		
		if(result.hasErrors()){
			System.out.println("数据输入错误");
		}
		System.out.println("我要保存的图书" + book);
		return "success";	
	}
}

addBook.jsp(添加图书页面):

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!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>
<form:form action="addBook" modelAttribute="book" method="POST">
	书名:<form:input path="bookName"/>
	<!-- 错误回显 -->
	<form:errors path="bookName" /><br>
	作者:<form:input path="author"/><form:errors path="author" /><br>
	作者邮箱:<form:input path="authorEmail"/><form:errors path="authorEmail" /><br>
	书价:<form:input path="price"/><form:errors path="price" /><br>
	<hr>
	省份:<form:input path="address.provice"/><br>
	城市:<form:input path="address.city"/><br>
	<input type="submit" value="提交数据">
</form:form>
</body>
</html>

springMVC.xml(spring配置文件):

<?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/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
		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.luyi"></context:component-scan>
	
	
	<!-- 导入jstlView包,可以支持便捷的国际化功能,还有一种就是直接设置viewClass属性值为JstlView也行 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/page/"></property>
		<property name="suffix" value=".jsp"></property>

	</bean>
	
	
	<!-- 开启MVC注解驱动模式 ,如果没有这一段代码,
	则除了toLoginPage请求以外,其他的请求都会请求不到数据了-->
	<mvc:default-servlet-handler/>
	<mvc:annotation-driven ></mvc:annotation-driven>
	
	
</beans>

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">
  <display-name>springMVC_01</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 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>
			<!-- 指定springmvc配置文件位置 -->
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springMVC.xml</param-value>
		</init-param>
		<!-- servlet启动加载,servlet原来是第一次访问创建对象;
		load-on-startup:服务器启动的时候创建对象,值越小,越先创建对象 -->
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- /*和/都是拦截所有请求;
		/*的范围更大,还会拦截*.jsp这些请求,一旦拦截jsp页面就不能显示了-->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 配置一个字符编码的Filter -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<!-- encoding:指定解决POST请求乱码 -->
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<!-- forceEncoding:解决了响应乱码问题 -->
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
		
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 使得其支持REST风格 -->
	<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>

注意:如果是普通表单,不能通过form:errors标签获取错误信息时,可以把错误信息放在域中给页面获取

自定义固定消息:在注解上面加上message属性即可,如@NotEmpty(message="不能为空")

自定义国际化错误消息:

每个属性在数据绑定和数据校验发生错误时,都会生成一个对应的 FieldError 对象。当一个属性校验失败后,校验框架会为该属性生成 4 个消息代码,这些代码以校验注解类名为前缀,结合 modleAttribute、属性名及属性类型名生成多个对应的消息代码,这4个消息代码就是我们国际化资源文件的键,4个消息代码如下:

  • Email.book.email:如果是隐含模型中book对象中的email属性字段发送了@Email校验错误,就会生成Email.book.email
  • Email.email:所有email属性只要发生了@Email错误,就会生成这个错误
  • Email.java.lang.String:只要是String类型发送了@Email错误,就会生成这个错误
  • Email:只要发生了@Email校验错误就会生成这个错误

4个消息代码作为国际化资源文件的键的应用:

errors_en_US.properties:

Email.email=email incorrect!
NotEmpty=must not empty!
Length.java.lang.String=length incorrect ,must between {2} and {1}!

errors_zh_CN.properties:

Email.email=\u90AE\u7BB1\u683C\u5F0F\u4E0D\u6B63\u786E
NotEmpty=\u4E0D\u80FD\u4E3A\u7A7A
Length.java.lang.String=\u957F\u5EA6\u6CA1\u6709\u5728\u6307\u5B9A\u8303\u56F4\u5185\uFF0\uFF0C\u53D6\u503C\u8303\u56F4\u5FC5\u987B\u5728{2}\u5230{1}\u4E4B\u95F4

注意:{0},{1}等等是资源文件的占位符,{0}永远代表的就是当前属性名,后面的顺序1,2代表的数就要根据它的这个注解的属性名的顺序决定的,如min小于max,因此{1}代表的就是max,{2}代表的就是min

JSR303:是java为Bean数据合法性校验提供的标准框架,它已经包含在JavaEE6.0中,JSR303通过在Bean属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对 Bean 进行验证,我们的Hibernate Validator就实现了这样的JSR303规范

所有注解以及这些注解的功能:

Hibernate Validator 扩展注解:Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:

  • @Email:被注释的元素必须是电子邮箱地址
  • @Length:被注释的字符串的大小必须在指定的范围内
  • NotEmpty:被注释的字符串必须非空
  • @Range:被注释的元素必须在合适的范围内

SpringMVC数据校验:

  • Spring 4.0 拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。
  • Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在 Spring MVC 中,可直接通过注解驱动的方式进行数据校验
  • Spring 的 LocalValidatorFactroyBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在 Spring 容器中定义了一个 LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean 中。
  • Spring 本身并没有提供 JSR303 的实现,所以必须将 JSR303 的实现者的 jar 包放到类路径下。
  • mvc:annotation-driven/ 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @valid 注解即可让 Spring MVC 在完成数据绑定后执行数据校验的工作
  • 在已经标注了 JSR303 注解的表单/命令对象前标注一个 @Valid,Spring MVC 框架在将请求参数绑定到该入参对象后,就会调用校验框架根据注解声明的校验规则实施校验
  • Spring MVC 是通过对处理方法签名的规约来保存校验结果的:前一个表单/命令对象的校验结果保存到随后的入参中,这个保存校验结果的入参必须是 BindingResult 或 Errors 类型,这两个类都位于 org.springframework.validation 包中
  • 需校验的 Bean 对象和其绑定结果对象或错误对象时成对出现的,它们之间不允许声明其他的入参
  • Errors 接口提供了获取错误信息的方法,如 getErrorCount() 或 getFieldErrors(String field)
  • BindingResult 扩展了 Errors 接口

7.SpringMVC支持ajax

原生javaWeb实现ajax:

  • 导入GSON包
  • 返回的数据用GSON转成json
  • 再把得到的json数据写出去

SpringMVC-ajax:

  • 导包

    • jackson-annotations-2.1.5.jar
    • jackson-core-2.1.5.jar
    • jackson-databind-2.1.5.jar
  • 写配置

代码示例:

@Controller
public class AjaxTestController {
	/**
	 * @ResponseBody将返回的数据放在响应体中,
	 * 如果是对象,jackson包自动将对象转为json格式
	 * @return
	 */
	@ResponseBody
	@RequestMapping("/ajaxGetAll")
	public Book ajaxGetAll(){
		Book book = new Book();
		book.setBookName("西游记");
		book.setAuthor("罗贯中");
		return book;
		//这里也可以写一些数据出去,如return "<h1>成功了</h1>"
	}
}
  • 测试

除了可以通过@ResponseBody把数据放在响应体里以外,我们还可以通过@RequestBody获取请求体

代码实现如下:

index.jsp页面:

<form action="ajaxGetRequest" method="POST">
	<input name="username" value="luyi"/>
	<input name="password" value="123"/>
	<input type="submit" value="提交数据" />
</form>

处理请求的方法:

@RequestMapping("/ajaxGetRequest")
	public String ajaxGetRequest(@RequestBody String body){
		System.out.println("请求体 " + body);
		return "success";
	}

除此之外,如果我们的参数位置写的是HttpEntity 对象参数的话,那我们还可以获取到所有的请求头信息

除了以上这些,还有一个更厉害的东西,那就是ResponseEntity对象,它可以封装响应体,响应头,响应状态等信息

@ResponseBody
	@RequestMapping("/haha")
	public ResponseEntity<String> haha(){

		String body = "<h1>Success</h1>";
		MultiValueMap<String, String> headers = new HttpHeaders();
		headers.add("Set-Cookie", "usernmae=hahaha");
		return new ResponseEntity<String>(body, headers, HttpStatus.OK);
	}

8.springMVC的文件下载与上传

文件的下载

1.得到要下载的文件的流

2.将要下载的文件流返回

代码实现:

/**
 * 处理文件下载请求
 * @param request
 * @return
 * @throws IOException
 */
@RequestMapping("/download")
	public ResponseEntity<byte[]> download(HttpServletRequest request) throws IOException{
		//得到要下载的文件的路径
		//找到要下载的文件的真实路径
		ServletContext context = request.getServletContext();
		String realPath = context.getRealPath("/WEB-INF/page/success.jsp");
		FileInputStream fis = new FileInputStream(realPath);
		byte[] tmp = new byte[fis.available()];
		fis.read(tmp);
		fis.close();
		
		//将要下载的文件流返回
		HttpHeaders headers = new HttpHeaders();
		headers.set("Content-Disposition", "attachment;filename=" + "success.jsp");
		return new ResponseEntity<byte[]>(tmp, headers, HttpStatus.OK);
	}

文件上传的步骤

1.文件上传表单准备:enctype="multipart/form-data"

2.导入fileupload包:

  • commons-fileupload-1.2.1jar
  • commons-io-2.0.jar

3.在springMVC配置文件中,编写一个配置,配置文件上传解析器(MutipartResolver)

4.文件上传请求处理,在处理方法上写上:@RequestParam("headerimg")MultipartFile file,封装当前文件的信息,可以直接保存,通过file.transferTo方法保存

代码示例:

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">
  <display-name>springMVC_01</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 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>
			<!-- 指定springmvc配置文件位置 -->
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springMVC.xml</param-value>
		</init-param>
		<!-- servlet启动加载,servlet原来是第一次访问创建对象;
		load-on-startup:服务器启动的时候创建对象,值越小,越先创建对象 -->
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- /*和/都是拦截所有请求;
		/*的范围更大,还会拦截*.jsp这些请求,一旦拦截jsp页面就不能显示了-->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 配置一个字符编码的Filter -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<!-- encoding:指定解决POST请求乱码 -->
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<!-- forceEncoding:解决了响应乱码问题 -->
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
		
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 使得其支持REST风格 -->
	<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(SpringMVC配置文件):

<?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/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
		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.luyi"></context:component-scan>
	
	
	<!-- 导入jstlView包,可以支持便捷的国际化功能,还有一种就是直接设置viewClass属性值为JstlView也行 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/page/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>	
	
	<!-- 开启MVC注解驱动模式 ,这是springMVC配置文件的标配-->
	<mvc:default-servlet-handler/>
	<mvc:annotation-driven ></mvc:annotation-driven>
	
	<!-- 配置文件上传解析器 -->
	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<!-- 设置文件上传最大的大小 -->
		<property name="maxUploadSize" value="#{1024*1024*20}"></property>
		<!-- 设置默认的编码 -->
		<property name="defaultEncoding" value="utf-8"></property>
		<!--  -->
	</bean>
</beans>

index.jsp(首页):

...
<body>
	${msg} 
	<form action="${ctp}/upload" method="POST" enctype="multipart/form-data">
		用户头像:<input type="file" name="headerimg"/><br>
		用户名:<input type="text" name="username"/>
		<input type="submit" value="提交数据">
	</form>
</body>
...

FileUploadController(转发请求):

package com.luyi.controller;

import java.io.File;
import java.io.IOException;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

@Controller
public class FileUploadController {
	
	@RequestMapping("/upload")
	public String upload(@RequestParam(value="usernmae", required=false)String username, 
			@RequestParam(value="headerimg")MultipartFile file,
			Model model){
		
		System.out.println("--上传的文件的信息--");
		//获取的是文件项的name值(input name="headeriimg")
		System.out.println("input的nam属性的值为" + file.getName());
		//获取的是文件的名字
		System.out.println("文件的名字" + file.getOriginalFilename());
		
		//文件保存
		try {
			file.transferTo(new File("D:\\" + file.getOriginalFilename()));
			model.addAttribute("msg", "文件上传成功");
		}catch (Exception e) {
			model.addAttribute("msg", "文件上传失败了!" + e.getMessage());
		}
		return "forward:/index.jsp";
	}
}

注意:如果是多文件上传,直接使用@RequestParam("headerimg")MultipartFile[] file封装当前文件们的信息

9.SpringMVC的拦截器

概述:SpringMVC提供了拦截器机制,允许运行目标方法之前进行一些拦截工作,或者目标方法运行之后进行一些其他处理;可以说是JavaWeb中过滤器的加强版。SpringMVC的拦截器是一个接口(HandlerInterceptor),这个接口有三个方法:

  • boolean preHandle(HttpServletRequest, HttpServletResponse, Object):在目标方法运行之前调用,返回boolean;return true,则chain.doFilter()方法放行;return false,则chain.doFilter()方法不放行
  • void postHandle(HttpServletRequest, HttpServletResponse,Object, ModelAndView):在目标方法运行之后调用
  • void afterCompletion(HttpServletRequest, HttpServletResponse,Object,Exception):在请求整个完成之后,来到目标页面之后调用

实现拦截器的步骤:

  • 实现拦截器(HandlerInterceptor)的接口
  • 在springMVC配置文件中配置拦截器
  • 测试

拦截器的运行流程:

拦截器的preHandle(只要preHandler不放行就没有后面的流程了,只要放行了,afterCompletion就会执行)----目标方法----拦截器postHandle----页面----拦截器的afterCompletion

测试拦截器的代码实现:

实现拦截器(HandlerInterceptor)的接口:

package com.luyi.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyFirstInterceptor implements HandlerInterceptor {

	/**
	 * 目标方法执行之前调用
	 */
	@Override
	public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,
			Object arg2) throws Exception {
		System.out.println("preHandle方法执行");
		return true;
	}
	
	/**
	 * 目标方法执行之后调用
	 */
	@Override
	public void afterCompletion(HttpServletRequest arg0,
			HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		System.out.println("afterCompletion方法执行");
		
	}

	/**
	 * 资源响应之后调用
	 */
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
			Object arg2, ModelAndView arg3) throws Exception {
		System.out.println("postHandle方法执行");
	}
	
}

在springMVC配置文件中配置拦截器:

<?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/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
		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.luyi"></context:component-scan>
	
	
	<!-- 导入jstlView包,可以支持便捷的国际化功能,还有一种就是直接设置viewClass属性值为JstlView也行 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/page/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>	
	
	<!-- 开启MVC注解驱动模式 ,这是springMVC配置文件的标配-->
	<mvc:default-servlet-handler/>
	<mvc:annotation-driven ></mvc:annotation-driven>
	
	<!-- 测试拦截器 -->
	<mvc:interceptors>
		<!-- 配置拦截器:默认是拦截所有的请求 -->
		<!--<bean class="com.luyi.controller.MyFirstInterceptor"></bean>-->
		<mvc:interceptor>
			<!-- 只拦截test1请求 -->
			<mvc:mapping path="/test1"/>
			<bean class="com.luyi.controller.MyFirstInterceptor"></bean>
		</mvc:interceptor>
	</mvc:interceptors>
</beans>

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">
  <display-name>springMVC_01</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 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>
			<!-- 指定springmvc配置文件位置 -->
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springMVC.xml</param-value>
		</init-param>
		<!-- servlet启动加载,servlet原来是第一次访问创建对象;
		load-on-startup:服务器启动的时候创建对象,值越小,越先创建对象 -->
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- /*和/都是拦截所有请求;
		/*的范围更大,还会拦截*.jsp这些请求,一旦拦截jsp页面就不能显示了-->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 配置一个字符编码的Filter -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<!-- encoding:指定解决POST请求乱码 -->
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<!-- forceEncoding:解决了响应乱码问题 -->
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
		
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 使得其支持REST风格 -->
	<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>

测试:

index.jsp页面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" isErrorPage="true"%> 
<!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>
<%
	pageContext.setAttribute("ctp", request.getContextPath());
%>

</head>
<body>
	<a href="${ctp}/test1">test1</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>
<%
System.out.println("success...");
%>
<h1>成功跳转页面</h1>
</body>
</html>

转发请求的java文件(InterceptorTestController.java):

package com.luyi.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class InterceptorTestController {
	
	@RequestMapping("test1")
	public String test1(){
		System.out.println("test1方法被调用");
		return "success";
	}
}

多拦截器的执行流程

正常流程(放行流程,拦截器的流程就是过滤器的流程):

  • 哪一个拦截器配置在前面,哪个拦截器就先执行他的prehandle
  • 拦截器的preHandle是按照顺序执行的
  • 拦截器的postHandle是按照逆序执行的
  • 拦截去的afterCompletion是按照逆序执行的

异常流程(不放行时的流程):哪一个拦截器不放行,则从这个拦截器之后的拦截器就不会走了,哪一个拦截器放行了,他的afterCompletion就一定会执行

过滤器与拦截器(拦截器是配置在容器中的,使用其他组件时直接通过@Autowired创建出一个组件来)

如果某些功能,需要其他组件配合完成,我们就使用拦截器,其他情况可以写filter

10.国际化

国际化的实现原理

前面已经有讲过简单的国际化的实现,那么,我们的国际化是怎么实现的呢?我们的国际化实现显示不同语言的信息是根据浏览器带来的语言信息决定的,可以获取浏览器的区域信息(通过Locale locale = request.getLocale()获取),在浏览器发送请求的时候,request中会携带一个locale的属性,里面标注着当前浏览器语言环境;在SpringMVC底层,ResourceBundleMessageSource类主要处理国际化请求,当请求发送过来以后,该类会根据容器中配置的basename找到配置文件中的国际化配置文件,根据该locale值会得到配置文件中配置的键值对来发送给前端使用。相反,如果没找到的话,会采用默认方式展示。

自定义区域信息解析器(默认的区域信息解析器AcceptHeaderLocaleResolver)

通过自定义区域信息解析器实现点击链接切换国际化:

配置文件:

//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/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
		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.luyi"></context:component-scan>
	
	
	<!-- 导入jstlView包,可以支持便捷的国际化功能,还有一种就是直接设置viewClass属性值为JstlView也行 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/page/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>	
	
	<!-- 开启MVC注解驱动模式 ,这是springMVC配置文件的标配-->
	<mvc:default-servlet-handler/>
	<mvc:annotation-driven ></mvc:annotation-driven>
	
	<!-- 配置国际化 -->
	<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basename" value="login/i18n"></property>
	</bean>
	
	<!-- 自定义区域信息解析器 -->
	<bean id="localeResolver" class="com.luyi.controller.MyLocaleResolver">
		
	</bean>
</beans>

//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">
  <display-name>springMVC_01</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <!-- 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>
			<!-- 指定springmvc配置文件位置 -->
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springMVC.xml</param-value>
		</init-param>
		<!-- servlet启动加载,servlet原来是第一次访问创建对象;
		load-on-startup:服务器启动的时候创建对象,值越小,越先创建对象 -->
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
	<servlet-mapping>
		<servlet-name>springDispatcherServlet</servlet-name>
		<!-- /*和/都是拦截所有请求;
		/*的范围更大,还会拦截*.jsp这些请求,一旦拦截jsp页面就不能显示了-->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	
	<!-- 配置一个字符编码的Filter -->
	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<!-- encoding:指定解决POST请求乱码 -->
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<!-- forceEncoding:解决了响应乱码问题 -->
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
		
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	
	<!-- 使得其支持REST风格 -->
	<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>

国际化资源文件:

//i18n_en_US.properties:

welcomeinfo=WELCOME TO LOGIN PAGE
username=USERNAME
password=PASSWORD
loginBtn=LOGIN


//i18n_zh_CN.properties:

welcomeinfo=\u6B22\u8FCE\u6765\u5230\u767B\u5F55\u9875\u9762
username=\u7528\u6237\u540D
password=\u5BC6\u7801
loginBtn=\u63D0\u4EA4

自定义的区域信息解析器:

package com.luyi.controller;

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.LocaleResolver;

public class MyLocaleResolver implements LocaleResolver {

	@Override
	public Locale resolveLocale(HttpServletRequest request) {
		Locale l = null;
		String localeStr = request.getParameter("locale");
		if(localeStr != null && !"".equals(localeStr)){
			l = new Locale(localeStr.split("_")[0], localeStr.split("_")[1]);
		}else{
			l = request.getLocale();
		}
		return l;
	}

	@Override
	public void setLocale(HttpServletRequest arg0, HttpServletResponse arg1,
			Locale arg2) {
		// TODO Auto-generated method stub

	}

}

转发请求:

package com.luyi.controller;

import java.util.Locale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class I18nTestController {
	
	@Autowired
	private MessageSource messageSource;
	@RequestMapping("/toLoginPage")
	public String toLogin(Locale locale){
		System.out.println(locale);
		String welcome = messageSource.getMessage("welcomeinfo", null, null);
		System.out.println(welcome);
		return "login";
	}
}

其他的区域信息解析器

SessionLocaleReslover和CookieLocaleReslover等

11.异常处理

概述:所有的异常处理器尝试解析异常,解析完成进行后续,解析失败则尝试用下一个解析器解析

四个异常解析器

  • ExceptionHandlerExceptionResolver:@ExceptionHandler,用于处理异常
  • ResponseStatusExceptionResolver:@ResponseStatus,用于自定义异常类
  • DefaultHandlerExceptionResolver:默认处理异常的解析器
  • SimpleMappingExceptionResolver:以配置的方式进行异常处理(这个不配置的话默认只有三个异常处理器)

异常处理-@ExceptionHandler

告诉SpringMVC这个方法专门处理这个类发生的异常:

  • 给方法上随便写一个Exception,用来接收发生的异常
  • 要携带异常信息不能给参数位置写Model
  • 返回ModelAndView类型即可把数据放到隐含模型里去
  • 如果有多个@ExceptionHandler都能处理这个异常,精确优先
  • 全局异常处理与本类异常处理同时出现时,使用本类的异常处理

代码示例:

//这里可以写多个Exception
@ExceptionHandler(value={ArithmeticException.class})
//这里不能在参数位置写Model
public ModelAndView handleException1(Exception exception){
	System.out.println("handleException1" + exception);
	
	ModelAndView modelAndView = new ModelAndView("error");
	modelAndView.addObject("ex", exception);
	return modelAndView;
}

全局异常(几种处理所有异常)的实现:必须把这个处理所有异常的类加入到ioc容器中(通过@ControllerAdvice注解加入)

代码实现:

package com.luyi.controller;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class MyExceptionHandler {
		//这里可以写多个Exception
		@ExceptionHandler(value={ArithmeticException.class})
		//这里不能在参数位置写Model
		public ModelAndView handleException01(Exception exception){
			System.out.println("handleException1" + exception);
			
			ModelAndView modelAndView = new ModelAndView("error");
			modelAndView.addObject("ex", exception);
			return modelAndView;
		}
		
		//这里可以写多个Exception
		@ExceptionHandler(value={NullPointerException.class})
		//这里不能在参数位置写Model
		public ModelAndView handleException02(Exception exception){
			System.out.println("handleException1" + exception);
			
			ModelAndView modelAndView = new ModelAndView("error");
			modelAndView.addObject("ex", exception);
			return modelAndView;
		}
		
		//这里可以写多个Exception
		@ExceptionHandler(value={Exception.class})
		//这里不能在参数位置写Model
		public ModelAndView handleException03(Exception exception){
			System.out.println("handleException1" + exception);
			
			ModelAndView modelAndView = new ModelAndView("error");
			modelAndView.addObject("ex", exception);
			return modelAndView;
		}
}

@ResponseStatus标注在自定义异常上返回一个服务器错误(自己选择报错的信息)

代码实现:

package com.luyi.controller;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(reason="用户被拒绝", value = HttpStatus.NOT_ACCEPTABLE)
public class UserNotFoundException extends RuntimeException{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

}

DefaultHandlerExceptionResolver:

概述:默认处理异常的解析器,当没有使用@ExceptionHandler或者没有自定义抛出异常(@RequestStatus)时,就走默认的异常解析器DefaultHandlerExceptionResolver

SimpleMappingExceptionResolver

概述:这个异常解析器不配置的话默认只有三个异常解析器,不会有这一个SimpleMappingExceptionResolver解析器,这个解析器需要通过配置来得到,这个解析器的优先级是低于@ExceptionHandler和@ResponseStatus的,但是高于默认的解析器

代码演示(配置文件中配置):

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
	<property name="exceptionMappings">
		<props>
			<!-- key:异常全类名,value:要去的页面视图名 -->
			<prop key="java.lang.ArithmeticException">error</prop>
		</props>
	</property>
	<!-- 指定错误信息取出时默认的值,如果这里不配置,默认在隐含模型中的key为exception -->
	<property name="exceptionAttribute" value="ex"></property>
</bean>

12.SpringMVC的执行流程

1.所有请求,前端控制器(DispatcherServlet)收到请求,调用doDispatch进行处理

2.根据HandlerMapping中保存的请求映射信息找到这个请求,处理当前请求的,处理器执行链(包含拦截器)

3.根据当前处理器找到它的HandlerAndView(适配器)

4.拦截器的preHandle先执行

5.适配器执行目标方法,并返回ModelAndView

  • ModelAttribute注解标注的方法提前运行
  • 执行目标方法的时候(确定目标方法的参数)
    • 有注解
    • 没注解
      • 看是否有Model,Map,以及其他的
      • 如果是自定义类型
        • 从隐含模型中看有没有这个参数,如果有,从隐含模型中拿
        • 如果没有。再看是否是SessionAttributes标注的属性。如果是,从Session中拿,如果拿不到会抛异常
        • 都不是,就利用反射创建对象

6.拦截器的postHandle执行

7.处理结果(页面渲染流程):

  • 如果有异常使用异常解析器处理异常,处理完后还会返回ModelAndView

  • 调用render进行页面渲染

    • 视图解析器根据视图名得到视图对象
    • 视图对象调用render方法
  • 执行拦截器的afterCompletion

13.Spring与SpringMVC的整合

SpringMVC和Spring整合的目的:分工明确

SpringMVC的配置文件就来配置和网站转发逻辑以及网站功能有关的功能(视图解析器,文件上传解析器,支持ajax等)

Spring的配置文件来配置和业务有关的东西(事务控制,数据源等)

SpringMVC和Spring整合的方法:

在web.xml文件中加入这两个配置文件,就会有两个容器,这两个容器再分别扫描不同的组件,即可达到与一个容器一样的功能。但是,我们的spring容器和springMVC容器同时存在时,其实是有子父关系的,springMVC是Spring容器的子类,而子类是可以访问父类的组件的,但是父类是访问不了子类的组件的,这就会导致在Spring容器中,想要自动装配SpringMVC容器中的组件时会报错

代码实现:

在spring配置文件中扫描除了Controller注解和ControllerAdvice注解之外的所有添加了注解的类:

<context:component-scan base-package="com.luyi">
	<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>

在springMVC配置文件中扫描添加了Controller注解和ControllerAdvice注解的类:

<context:component-scan base-package="com.luyi" 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>
posted @ 2020-09-03 16:33  luyi001  阅读(270)  评论(0编辑  收藏  举报