【SSM Spring 5.0.2 SpringMVC IDEA Maven】SpringMVC學習記錄 2019_6_30

SpringMVC

    • QuickStart
        • 基礎配置
        • 進階配置
        • 入門SpringMVC圖解
        • 詳細SpringMVC圖解
    • 重要注解
        • @RequestMapping()
        • @RequestParam()
        • @RequestBody()
        • @PathVaribale()
            • 基於HiddentHttpMethodFilter的Restful風格Controller實現
        • @RequestHeader
        • @RequestCookie
        • @ModelAttribute
        • @SessionAttribute()
    • 請求參數綁定(映射)
        • 支持的數據類型
        • 映射規則
        • 參數傳遞
            • 自定義類型轉換器
            • 獲取/傳遞原生Servlet API對象
            • 解決Post提交方法的中文亂碼
    • Controller返回值类型
        • String
        • void
        • ModelAndView对象
    • SpringMVC提供的转发与重定向关键字
    • @ResponseBody() 响应json数据
    • 文件上传
        • 前提条件
        • 上传原理
        • 第三方工具(放弃)
        • 框架内部MultipartFile对象实现上传
        • 跨服務器上傳
    • 異常處理器
        • 具體操作
        • 流程示意圖
    • 攔截器
        • 與過濾器filter的區別
        • 實現自定義攔截器步驟
            • 一個攔截器
            • 多個攔截器
    • Web-App標簽子標簽順序
    • SSM整合之Spring框架的配置路徑問題

QuickStart

基礎配置

  1. 创建maven项目
    选择archetype:maven-archetype-webapp
    注:如果创建项目过慢,输入在创建项目Properties的时候

  2. 补全工程文件目录
    java,resource,并mark directory为Re/Source root

  3. 在pom中引入依赖

	
	<properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <maven.compiler.source>1.8maven.compiler.source>
        <maven.compiler.target>1.8maven.compiler.target>
        
        <spring.version>5.0.2.RELEASEspring.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>${spring.version}version>
        dependency>

        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webartifactId>
            <version>${spring.version}version>
        dependency>

        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webmvcartifactId>
            <version>${spring.version}version>
        dependency>

        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>servlet-apiartifactId>
            <version>2.5version>
            <scope>providedscope>
        dependency>

        <dependency>
            <groupId>javax.servlet.jspgroupId>
            <artifactId>jsp-apiartifactId>
            <version>2.0version>
            <scope>providedscope>
        dependency>
    dependencies>
  1. 在web.xml配置核心控制器DispatcherServlet
<web-app>
    
    <servlet>
        <servlet-name>dispatcherServletservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:springmvc.xmlparam-value>
        init-param>
        
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
    	
        <servlet-name>dispatcherServletservlet-name>
        
        <url-pattern>/url-pattern>
    servlet-mapping>
web-app>

輸入生成兩行,第二行為沒寫完的<光標下移,然後輸入servlet-class補全,然後在内輸入
DispatcherServlet,就可自動生成。init-param與servlet-mapping都需要自己手動輸入,不自動匹配。
提示:init-param不需要輸入,如果沒有指定使用默认值:/WEB-INF/-servlet.xml

  1. 在類路徑Resouce文件下創建springmvc.xml文件(選擇創建Spring XML即可)。

以上配置完畢,就可以部署在Tomcat上。當然只有一個index.jsp頁面罷了

進階配置

  1. 在index.jsp上使用標簽
    【入口頁面】
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
	<body>
		<h2>Hello World!h2>
		<a href="hello" >新手程序,但是我好像都懂hhhha>
	body>
html>
  1. 新建controller類
    【控制器】
    2個要點
    第一個是使用@Controller標記這個類為控制器
    第二個是使用@RequestMapping(path = “/hello”)標記接收的請求url為/hello
package cn.itcast.controller;

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

/**
 * 控制器标记
 */
@Controller
public class HellowController {
    /**
     * 接收请求路徑为`hello`千萬別寫反斜杠/
     * @return
     */
    @RequestMapping(path = "/hello")
    public String sayHello() {
        System.out.println("Hello SpringMVC!!");
        return "success";
    }
}
  1. 在WEB-INF中創建pages文件夾
    裏面放置跳轉的jsp頁面,這個路徑由SpringMVC-servlet.xml配置文件中 視圖解析器 決定的。

  2. 新建success JSP頁面
    【跳轉頁面】

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Titletitle>
head>
<body>
    <h2>Welcome to SpringMVC!h2>
body>
html>
  1. 修改springMVC.xml配置
    1個要點:在springMVC的視圖解析器中,配置pages文件的路徑【可以不在WEB-INF中】

<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.atguigu.mybatis" use-default-filters="false">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	context:component-scan>
	
	
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/pages/">property>
		<property name="suffix" value=".jsp">property>
	bean>
	
	
	<mvc:annotation-driven>mvc:annotation-driven>
	<mvc:default-servlet-handler/>
beans>

说明
在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使用 自动加载 RequestMappingHandlerMapping(处理映射器)
和RequestMappingHandlerAdapter(处理适配器)可用在SpringMVC.xml配置文件中使用
替代注解处理器和适配器的配置。

入門SpringMVC圖解

詳細SpringMVC圖解

【SSM Spring 5.0.2 SpringMVC IDEA Maven】SpringMVC學習記錄 2019_6_30_第1张图片
個人理解:
前端控制器DispatchServlet:只是中轉站,傳入request請求,返回response響應。這個中間經它的調用有
1.處理器映射器2.處理器適配器3.視圖解析器4.動靜態頁面

處理器映射器handlermapping:將傳入的請求路徑進行解析,生成這個請求路徑+背後全部需要的其他請求的執行鏈Chain。返回給前端控制器。

處理器適配器HandlerAdapter:將由前端控制器轉發的執行鏈中的全部servlet執行,返回Model結果數據和View需要的頁面

視圖解析器ViewResolver:將由前端控制器轉發的View頁面 拼裝 為完整的頁面訪問路徑

視圖:渲染–由前端控制器裝載model數據到返回的具體頁面request域中

注:爲什麽這個前端控制器這麽,是因爲和Spring IOC容器一樣,降低這些組件閒的相互耦合.

重要注解

@RequestMapping()

出现位置:  
	类上:请求 URL 的第一级访问目录。此处不写的话,就相当于应用的根目录。写的话需要以/开头。   
	功用:使 URL 訪問可以按照模块化管理:   
		例如:    
			账户模块:     /account/add     /account/update   /account/delete     ... 
   			订单模块:     /order/add       /order/update     /order/delete 
  		實例:
  		@RequestMapping("/account") ,使 URL 更加精细【將重複性的類名從方法上的requestmapping中抽取出來放在上面而已】。  

  	方法上:请求 URL 的第二级访问目录。
  		實例:
  		@RequestMapping(path="/add"),path,可寫可不寫,如果是一個參數時

属性:  
	value:用于指定请求的 URL。它和 path 属性的作用是一样的。  
	method:用于指定请求的方式(get/post/head/put/patch/delete/options/trace)可單個,可多個值。
		實例:
			@RequestMapping(value="/saveAccount",method=RequestMethod.POST)
			@RequestMapping(value="/saveAccount",method={RequestMethod.POST,RequestMethod.GET})
		注:使用get方法

	params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的 key 和 value 必须和 配置的一模一样。 可單多   
			半實例:   
				params = {"accountName"},表示请求参数必须有 accountName     
				params = {"moeny!100"},表示请求参数中 money 不能是 100。  
	headers:用于指定请求消息头的条件 限制。  
			半實例:
				headers = {"Accept"}

	注意:   以上四个属性只要出现 2 个或以上时,他们的关系是与的关系。 

@RequestParam()

	作用:  指定(從前臺傳入後臺的)參數名称,给控制器中的形参賦值。 
	属性:  
			value=name:请求参数中的名称。  
			required:请求参数中是否必须提供此参数。默认值為true必须提供,如果不提供将报错。
			defaultValue:默認值
	實例:
			@RequestMapping("/useRequestParam") 
			public String useRequestParam(
				@RequestParam("name")String username,
				@RequestParam(value="age",required=false)Integer age)
			{  
				System.out.println(username+","+age);  
				return "success"; 
			}

@RequestBody()

GET請求方法是將所有參數封裝在url上,而POST方法則是封裝成一個對象【所以Form表單是POST請求】
請求體包含Form表單内容,controller函數形參只能寫一個@RequestBody String body。

	作用:	用于获取请求体内容。直接使用得到是 key=value&key=value...结构的数据。  
			get 请求方式不适用。
			【異步的時候會用到。傳遞Json數據,封裝的時候用到@RequestBody】
	属性:  required:是否必须有请求体。默认值是:true。当取值为 true 时,get 请求方式会报错。
					 如果取值 为 false,get 请求得到是 null。 

controller.java

    @RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body){
        System.out.println("执行了...");
        System.out.println(body);
        return "success";
    }

實例:如果表單為,username:abc,age:12。則請求體為:username=abc&age=12

@PathVaribale()

	作用:  用于獲取 請求路徑 中的占位符`{}`内容(/usePathVariable/100)獲取100這樣就不需要input。
		   也不需要key=value的形式獲取。
	属性:  
			name=value:用于指定 url 中占位符名称。  
			required:是否必须提供占位符

	實例: 	controller函數上的@RequestMapping("/usePathVariable/{id}") 中的{id}就是 url 占位符。
			url 支持占位符是 spring3.0 之后加入的。是 springmvc 支持 rest 风格 URL 的一个重要标志。 

jsp 代码:

 
pathVariable 注解 

controller 代碼:

	@RequestMapping("/usePathVariable/{id}") 
	public String usePathVariable(@PathVariable("id") Integer id){
		System.out.println(id);  
		return "success"; 
	}

Restful風格:
個人理解——用不同的請求方法+佔位符來區分相同請求路徑調用的不同方法——>用最簡單的方法來實現複雜重複代碼
對於緩存的優點是——共用一個局部緩存,因爲路徑上訪問的都是這個Controller類對象,利於管理。
【SSM Spring 5.0.2 SpringMVC IDEA Maven】SpringMVC學習記錄 2019_6_30_第2张图片
Rest案例:
【SSM Spring 5.0.2 SpringMVC IDEA Maven】SpringMVC學習記錄 2019_6_30_第3张图片

基於HiddentHttpMethodFilter的Restful風格Controller實現

由于浏览器 form 表单只支持 GET 与 POST 请求(a標簽支持的是GET),而 DELETE、PUT 等 method 并不支持
Spring3.0 添加了一个过滤器,可以将浏览器请求改为指定的请求方式,发送给我们的控制器方法,使得支持 GET、POST、PUT 与 DELETE 请求。
這樣就可以實現@PathVaribale()注解的功用,創建Restful風格代碼。

【除了這個filter,還可以通過WebClient類,直接調用靜態方法模擬各種請求方式。以及瀏覽器插件也可以做到】

使用方式:

  1. 在web.xml中添加此filter過濾器
  2. 请求方式必须使用 post 请求。
  3. 按照要求提供_method 请求参数,该参数的取值就是我们需要的请求方式。

filter完全實現類
【SSM Spring 5.0.2 SpringMVC IDEA Maven】SpringMVC學習記錄 2019_6_30_第4张图片

原理:雖然form表單只能發post或者get
但下面有一行input 隱藏域
這個會被filter攔截到,從而將post請求轉換為put請求。

.jsp

	 
	<form action="springmvc/testRestPOST" method="post"> 
		用户名称:<input type="text" name="username"><br/>   
	 	<input type="submit" value=" 保存 "> 
	 form> 
	 	<hr/> 
	 
	<form action="springmvc/testRestPUT/1" method="post"> 
		用户名称:<input type="text" name="username"><br/>   
		<input type="hidden" name="_method" value="PUT"> 
	 	<input type="submit" value=" 更新 "> 
	form>
	<hr/> 
	 
	<form action="springmvc/testRestDELETE/1" method="post"> 
 		<input type="hidden" name="_method" value="DELETE"> 
 		<input type="submit" value=" 删除 "> 
 	form>
 	<hr/> 
	 
	<form action="springmvc/testRestGET/1" method="post"> 
		<input type="hidden" name="_method" value="GET"> 
 		<input type="submit" value=" 查询 ">
 	form>  

controller

	/** 
 	* post 请求:保存  
 	* @param username  
 	* @return  */ 
 	@RequestMapping(value="/testRestPOST",method=RequestMethod.POST) 
 	public String testRestfulURLPOST(User user){  
 		System.out.println("rest post"+user);  
 		return "success"; 
 	}     
 	/** 
 	* put 请求:更新  
 	* @param username  
 	* @return  */ 
 	@RequestMapping(value="/testRestPUT/{id}",method=RequestMethod.PUT) 
 	public String testRestfulURLPUT(@PathVariable("id")Integer id,User user){  
 		System.out.println("rest put "+id+","+user);  
 		return "success"; 
 	}   
 	/** 
 	* post 请求:删除  
 	* @param username  
 	* @return  */ 
 	@RequestMapping(value="/testRestDELETE/{id}",method=RequestMethod.DELETE) 
 	public String testRestfulURLDELETE(@PathVariable("id")Integer id){  
 		System.out.println("rest delete "+id); 
 		return "success"; 
 	}   
  	/** 
 	* post 请求:查询  
 	* @param username  
 	* @return  */ 
 	@RequestMapping(value="/testRestGET/{id}",method=RequestMethod.GET) 
 	public String testRestfulURLGET(@PathVariable("id")Integer id){  
 		System.out.println("rest get  "+id);  
 		return "success"; 
 	} 

@RequestHeader

	属性:	value 指定Header名称(前端具有的属性),
			required

	实例:	public getHeader(@RequestHeader(value=“Accept”)String header){}

@RequestCookie

	属性:	value 指定Cookie名称(前端/session具有的属性),
			required

	实例:	public getCookie(@RequestCookie(value=“JSESSIONID”)String cookie){}

@ModelAttribute

	位置:	方法头上,controller方法形参前
	作用:	有该注解的方法(不需要写@RequestMapping),会在请求路径url的controller方法==先执行==。
	属性:	value 获取传入参数的key值,key可以是数据库类名,也可以是map的key

	应用场景:在表单提交用户资料修改的时候,数据库POJO类的某些属性是定死的,肯定不允许修改的,那么这些属性值如何赋予?
			 【先将不可修改的数据项查询出来,直接传递给请求路径url的controller方法】就會用到这个注解

实例:

  1. 只有1个需要@ModelAttribute传递前置参数的情况,不需要使用Map
	@RequestMapping(/user/save)
	public String testModelAttribute(User user){
	}

	@ModelAttribute
	public User searchUser(String name){
		User user = new User();
		user.setName();
		user.setAge();
		user.setDate();
		return user
	}

@ModelAttribute public User serchUser(String name)内set赋值的属性值会通过return user传入

@RequestMapping(/user/save)
public String testModelAttribute(User user)

并且这个请求路径url的controller方法会再从网页前端获取user属性值,并覆盖掉@ModeAttribute的set置入的属性值。

  1. 多个需要@ModelAttribute传递前置参数的情况,使用Map存儲並通过指定@ModelAttribute String 传递的参数
	@RequestMapping(/user/save)
	public String testModelAttribute(@ModelAttribute("abc") User user){
	}

	@ModelAttribute
	public void searchUser(String name,Map<String,User> map){
		User user = new User();
		user.setName();
		user.setAge();
		user.setDate();
		map.put("abc",user)
	}

@SessionAttribute()

	作用:  用于controller类的多个方法间参数共享。 
	属性:  
			name=value:用于指定存入的属性名称  
		    type:用于指定存入的数据类型。 
	位置:	controller类上注解

实例:

	 
	<a href="springmvc/testPut">存入 SessionAttributea> <hr/> 
	<a href="springmvc/testGet">取出 SessionAttributea> <hr/> 
	<a href="springmvc/testClean">清除 SessionAttributea>

注:Model接口是替代Request类的存在,降低耦合,该接口有一个完全实现类ModelMap/ExtendedModelMap,可用于取出Session对象内属性

@Controller("sessionAttributeController") 
@RequestMapping("/springmvc") 
@SessionAttributes(value={"username","password"},types={Integer.class}) 
public class SessionAttributeController {    /** 
  * 把数据存入 SessionAttribute   
  * @param model   
  * @return 
  *  Model 是 spring 提供的一个接口,该接口有一个实现类 ExtendedModelMap   
  *  该类继承了 ModelMap,而 ModelMap 就是 LinkedHashMap 子类   */   
	@RequestMapping("/testPut")    
	public String testPut(Model model){           
		model.addAttribute("username", "泰斯特");           
		model.addAttribute("password","123456");           
		model.addAttribute("age", 31);   
        //跳转之前将数据保存到 username、password 和 age 中,因为注解@SessionAttribute 中有 这几个参数           
    return "success";

    @RequestMapping("/testGet")       
    public String testGet(ModelMap model){           
    	System.out.println(model.get("username")+";"+model.get("password")+";"+model.get("a ge"));
    	return "success";
    }

    @RequestMapping("/testClean")        
    public String complete(SessionStatus sessionStatus){         
    	sessionStatus.setComplete();            
    	return "success";        
    }
}

請求參數綁定(映射)

表单中请求参数都是基于 key=value
SpringMVC 绑定请求参数的过程是通过把表单提交请求参数,作为前端控制器中的方法的参数进行绑定,内置映射規則。

支持的數據類型

基本类型参数      :   包括基本类型和String类型 
POJO类型参数      :   包括实体类,以及关联的实体类 
数组和集合类型参数 :   包括 List 结构和 Map 结构的集合(包括数组) 
 
SpringMVC 绑定请求参数是自动实现的,但是要想使用,必须遵循使用要求。

映射規則

基本类型或者String类型:  
	要求参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写) 
POJO类型,或者它的关联对象:  
	要求表单中参数名称和 POJO 类的属性名称保持一致。并且控制器方法的参数类型是 POJO 类型。 
集合类型,有两种方式: 
	第一种:   要求集合类型的请求参数必须在 POJO 中。在表单中请求参数名称要和 POJO 中集合属性名称相同。
			  	给 List 集合中的元素赋值,使用下标。   
			  	给 Map 集合中的元素赋值,使用键值对。  
   	第二种:   接收的请求参数是 json 格式数据。需要借助一个注解实现

注意:  它还可以实现一些数据类型自动转换。内置转换器在:org.springframework.core.convert.support包

Java數據類型轉換如下:
【SSM Spring 5.0.2 SpringMVC IDEA Maven】SpringMVC學習記錄 2019_6_30_第5张图片

參數傳遞

  1. 少量參數
    一兩個方法所需的參數,直接在url中使用?``&傳遞即可
    只要求,傳遞的參數名與方法的參數名相同即可。

  2. 多個參數
    少量參數可以用? aaa=value1 & bbb=value2 & ...與符號來傳遞多個參數到後臺
    如果再多,可以用JavaBean來傳遞【使用form表單提交請求,而不是href=url】

JavaBean要求:
1.實現序列化接口 implements Serializable
2.所有的屬性有Set Get ToString方法

JSP form表單要求:
input name屬性值必須是與Javabean類的屬性名相同。不同則JavaBean的Set方法就找不到封裝失敗

Controller類方法將 參數寫為JavaBean類形參即可。(真的方便啊)
實例:

	@RequestMapping("/saveAccount")
    public String saveAccount(Account account){
        System.out.println("执行了...");
        System.out.println(account);
        return "success";
    }
  1. Javabean包含關聯類屬性
    如果Javabean的屬性關聯另一個對象,則在JSP 表單中name值=關聯類.屬性名
    例如:A 有 B類 屬性,B類有2個屬性 name age,則 JSP表單中 name=“b.name”//name="b.age"即可
    當然,一定要生成對應屬性的Get/Set方法,toString。

  2. List/Map集合屬性
    前提:對應屬性的Get/Set方法,toString

實例:

	JavaBean Account:
	    private List list;
   	 	private Map map;
   	JavaBean User:
   		private String uname;
    	private Integer age;
   	JSP:
   		
姓名:
密码:
金额:
用户姓名:
用户年龄:
用户姓名:
用户年龄:
自定義類型轉換器

SpringMVC 可以實現 2000/10/20 自動轉換為 日期類型
但不能識別 2000-10-20

  1. 繼承Converter接口,實現自定義轉換器類
package cn.itcast.converter;

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

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

//繼承Converter接口
public class StringToDateConverter implements Converter<String,Date> {
    /**
     * 页面传入的字符串s,返回转换完的Date类型变量df。
     * 下面为实现Converter接口的声明函数
     * @param s
     * @return
     */
    @Override
    public Date convert(String s) {
    	//判斷字符串
        if (s == null){
            throw new RuntimeException("空数据内容");
        }
        DateFormat df = new SimpleDateFormat("yyyy-mm-dd");
        try {
            return df.parse(s);
        } catch (Exception e) {
            throw new RuntimeException("格式错误");
        }
    }
}
  1. 配置springmvc(springmvc-service).xml注入這個自定義轉換器bean對象
	
    <bean id="conversionService2" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="cn.itcast.converter.StringToDateConverter">bean>
            set>
        property>
    bean>

    
    <mvc:annotation-driven conversion-service="conversionService2">mvc:annotation-driven>
獲取/傳遞原生Servlet API對象

Request Response對象
直接在Controller方法的形參中聲明即可得到
HttpServletRequest request,
HttpServletResponse response,
HttpSession session

    /**
     * 原生的API
     * @return
     */
    @RequestMapping("/testServlet")
    public String testServlet(HttpServletRequest request, HttpServletResponse response){
        System.out.println("执行了...");
        System.out.println(request);

        HttpSession session = request.getSession();
        System.out.println(session);

        ServletContext servletContext = session.getServletContext();
        System.out.println(servletContext);

        System.out.println(response);
        return "success";
    }
解決Post提交方法的中文亂碼

SpringMVC可以通過 設置過濾器filter(CharacterEncodingFilter)攔截所有的servlet請求,進行request處理。
web.xml

    
    <filter>
        <filter-name>characterEncodingFilterfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>UTF-8param-value>
        init-param>
    filter>
    <filter-mapping>
        <filter-name>characterEncodingFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>

就不需要在每個Servlet裏對request對象進行設置

Controller返回值类型

开启EL表达式
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false"%>

String

Controller类中的方法返回字符串可以指定逻辑视图的名称,根据视图解析器为实际视图的访问地址

实例:
controller.java

	@RequestMapping(value="/hello")    
	public String sayHello() {        
		System.out.println("Hello SpringMVC!!");        
		// 跳转到XX页面        
		return "success";    
	}

void

如果控制器的方法返回值编写成void,执行程序报404的异常,默认查找JSP页面没有找到。
但是controller方法确实执行了。404异常请求的页面为————方法名.jsp

例如请求的方法是
@@RequestMapping(value="/user")
@RequestMapping(value="/initUpdate")
则异常为 找不到默认跳转【前缀】user/initUpdate【.jsp】的页面。

消除404:
1.手动请求跳转页面【转发】
问题1:手动request不经过视图解析器,所以要写完整路径
问题2:如果这个controller方法后续还有其他方法执行,如果不想执行,则写return;即可

2.重定向页面
转发是可以直接进入到具体路径的页面,重定向是重新发了一个新的请求
新的请求是不允许直接进入具体路径的页面需要request.getContextPath()

3.直接回复字节流至客户端

实例:

	@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("testVoid方法执行了...");
        // 手动请求转发————手动request不经过视图解析器,所以要写完整路径
        //request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);

        // 重定向
        // response.sendRedirect(request.getContextPath()+"/index.jsp");

        // 设置中文乱码
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        // 直接会进行响应
        response.getWriter().print("你好");

        //阻断后续其他方法执行
        return;
    }

ModelAndView对象

ModelAndView对象是Spring提供的一个对象,可以用来调整具体的JSP视图
个人理解:与手动转发请求的区别只是在于是否通过springmvc的视图解析器,与返回字符串跳转页面也没什么区别。

实例
controller.java

	@RequestMapping(value = "/findAll")
    public ModelAndView findAll() throws Exception {
        ModelAndView mv = new ModelAndView();
        // 跳转到list.jsp的页面
        mv.setViewName("list");

        // 模拟从数据库中查询所有的用户信息
        List<User> users = new ArrayList<>();
        User user1 = new User();
        user1.setUsername("张三");
        user1.setPassword("123");

        User user2 = new User();
        user2.setUsername("赵四");
        user2.setPassword("456");

        users.add(user1);
        users.add(user2);
        // 添加对象
        mv.addObject("users", users);

        return mv;

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title heretitle>head>
<body>

    <h3>查询所有的数据h3>
    <c:forEach items="${ users }" var="user">
        ${ user.username }
    c:forEach>

body>
html>

SpringMVC提供的转发与重定向关键字

1.forward请求转发
重点!!:不走视图解析器,写完整路径,以及可以转发至controller类其他方法的请求路径

实例:

@RequestMapping("/delete")    
public String delete() throws Exception {        
	System.out.println("delete方法执行了...");        
	// return "forward:/WEB-INF/pages/success.jsp";        
	return "forward:/user/findAll";    
}

2.redirect重定向
重点!!:redirect底层封装了request.getContextPath(),所以不需要写了【重定向也可以到controller类其他方法的请求路径】

实例:

@RequestMapping("/count")    
public String count() throws Exception {        
	System.out.println("count方法执行了...");        
	return "redirect:/add.jsp";        
	// return "redirect:/user/findAll";    
}

@ResponseBody() 响应json数据

多应用于ajax异步请求,接受json格式数据在服务器。

  1. 前置问题
    DispatcherServlet会拦截到所有的资源。静态资源(img、css、js)也会被拦截到,从而不能被使用。
    解决问题就是需要配置静态资源不进行拦截,在springmvc.xml配置文件添加如下配置

配置springmvc.xml


    




<mvc:annotation-driven>mvc:annotation-driven>
<mvc:default-servlet-handler/>

location元素表示webapp目录下的包下的所有文件
mapping元素表示以/static开头的所有请求路径,如/static/a 或者/static/a/b
注意:必须放在最后

  1. 使用@RequestBody注解参数获取请求体数据

controller.java

@RequestMapping("/testJson")    
public void testJson(@RequestBody String body) {        
	System.out.println(body);    
}

Ajax.jsp

<header>
	<script>
        //绑定btn按钮的单击事件
        $(function(){
            $("#btn").click(function(){
                // alert("hello btn");
                // 发送ajax请求
                $.ajax({
                    // 编写json格式,设置属性和值
                    url:"user/testAjax",
                    contentType:"application/json;charset=UTF-8",
                    data:'{"username":"hehe","password":"123","age":30}',
                    //后端相应的返回值类型
                    dataType:"json",
                    type:"post",
                    //成功传至服务器后,Ajax执行的success函数
                    success:function(data){
                    }
                });

            });
        });
    script>
header>
<body>
	<button id="btn">发送ajax的请求button>
body>
  1. 使用@RequestBody注解返回值类型将响应数据转换为json格式

进阶版:使用@ResponseBody 注解实现将 controller 方法返回对象转换为 json 响应给客户端
前置要求:jackson3个jar包(annotations,databind,core)
Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入 jackson 包。

pom.xml的dependencies标签内引入依赖

<dependency>            
	<groupId>com.fasterxml.jackson.coregroupId>            
	<artifactId>jackson-databindartifactId>            
	<version>2.9.0version>        
dependency>        
<dependency>            
	<groupId>com.fasterxml.jackson.coregroupId>            
	<artifactId>jackson-coreartifactId>            
	<version>2.9.0version>        
dependency>        
<dependency>            
	<groupId>com.fasterxml.jackson.coregroupId>            
	<artifactId>jackson-annotationsartifactId>            
	<version>2.9.0version>        
dependency>

controller.java

@RequestMapping("/testJson")  
//@ResponseBody 注在返回值类型Address前,自动将Address类型转换为json格式数据响应给客户端
public @ResponseBody Address testJson(@RequestBody Address address) {
	//客户端发送ajax请求,传入的是json字符串(请求体内)由SpringMVC框架自动转换封装到JavaBean中        
	System.out.println(address);        
	address.setAddressName("上海");        
	return address;    
}

客户端发送ajax请求,传入到controller方法的是json字符串(请求体内)由SpringMVC框架自动转换封装到JavaBean中
@ResponseBody注在返回值类型Address前,自动将Address类型(Javabean)转换为json格式数据响应给客户端

ajax.jsp

$(function(){        
 	// 绑定点击事件        
 	$("#btn").click(function(){            
 		$.ajax({                
 		url:"user/testJson",                
 		contentType:"application/json;charset=UTF-8",
        data:'{"addressName":"哈哈","addressNum":100}',
        dataType:"json",                
        type:"post",                
        success:function(data){                    
        	alert(data);                    
        	alert(data.addressName);                
        	}            
        });        
    });    
});

文件上传

前提条件

	A form 表单的 enctype(表单请求正文的类型) 取值必须是:multipart/form-data      
									(默认值是:application/x-www-form-urlencoded)
	B method 属性取值必须是 Post (get方法是将请求体写在URL里的。有大小限制) 
	C 提供一个文件选择域 

上传原理

不论是传输字符串,还是传输文件,都是通过form表单,其实文件只是长度很长的字符串罢了。

当 form 表单的 enctype=”application/x-www-form-urlencoded”时,
form 表单的正文内容是键值对+&:    key=value&key=value&key=value 

当 form 表单的 enctype 取值为 Mutilpart/form-data 不是默认值时,request.getParameter()将失效。 
请求正文内容就变成: 每一部分都是 MIME 类型描述的正文 
-----------------------------7de1a433602ac   分界符 
Content-Disposition: form-data; name="userName"  协议头
aaa              协议的正文(文件内容)
-----------------------------7de1a433602ac 

那么我们的工作就是,解析Post请求方法的请求体,将文件内容、文件名拆分出来,然后在服务器指定路径下建立同名文件

自己来写很麻烦,借助第三方工具实现上传

第三方工具(放弃)

使用SpringMVC框架内置MultipartFile对象

使用 Commons-fileupload 组件实现文件上传,需要导入该组件相应的支撑 jar 包:Commons-fileupload 和commons-io。
commons-io 不属于文件上传组件的开发 jar 文件,但Commons-fileupload 组件从 1.1 版本开始依赖commons-io包。

  1. 导入依赖,pom.xml
<dependency>            
	<groupId>commons-fileupload</groupId>            
	<artifactId>commons-fileupload</artifactId>            
	<version>1.3.1</version>        
</dependency>        
<dependency>            
	<groupId>commons-io</groupId>            
	<artifactId>commons-io</artifactId>            
	<version>2.4</version>        
</dependency>
  1. jsp
<h3>文件上传h3>        
<form action="user/fileupload" method="post" enctype="multipart/form-data">        
	选择文件:<input type="file" name="upload"/><br/>        
	<input type="submit" value="上传文件"/>    
form>
  1. controller.java
	/**
     * 文件上传     * @throws Exception
     */
    @RequestMapping(value = "/fileupload")
    public String fileupload(HttpServletRequest request) throws Exception {
        // 定义服务器端上传文件的目录 最后的/写不写都行
        String path = request.getSession().getServletContext().getRealPath("/uploads/");
        // 创建File对象,一会向该路径下上传文件【注意是在tomcat服务器下而不是Target】
        File file = new File(path);
        // 判断路径是否存在,如果不存在,创建该路径
        if (!file.exists()) {
            file.mkdirs();
        }
        // 创建磁盘文件项工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload fileUpload = new ServletFileUpload(factory);
        // 解析request对象
        List<FileItem> list = fileUpload.parseRequest(request);
        // 遍历
        for (FileItem fileItem : list) {
            // 判断文件项是普通字段,还是上传的文件
            if (fileItem.isFormField()) {
            } else {
                // 上传文件项
                // 获取到上传文件的名称                
                String filename = fileItem.getName();
                //解决重名文件覆盖,把文件名设置为唯一值
                String uuid = UUID.randomUUID().ToString.replace("-","");
                filename = uuid + filename;
                // 上传文件                
                fileItem.write(new File(file, filename));
                //删除临时文件
                fileItem.delete();
            }
        }
        return "success";
    }

框架内部MultipartFile对象实现上传

SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件
注意:形参变量名称必须和前端表单的 name属性名称相同。
与上面的第三方工具比较的优势:
1.不需要引入jar包,减少依赖
2.不需要再解析request,springMVC封装好了,在前端控制器中调用文件解析器对象来帮助解析。
3.代码简洁,干净。

原理

代碼們:
jsp

<form action="user/fileupload" method="post" enctype="multipart/form-data">        
	选择文件:<input type="file" name="upload"/><br/>        
	<input type="submit" value="上传文件"/>    
form>

springmvc.xml

    
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">        
	<property name="maxUploadSize" value="10485760"/>    
bean>

controller.java

	@RequestMapping(value = "/fileupload2")
    public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
        System.out.println("SpringMVC方式的文件上传...");
        // 先获取到要上传的文件目录        
        String path = request.getSession().getServletContext().getRealPath("/uploads");
        // 创建File对象,一会向该路径下上传文件        
        File file = new File(path);
        // 判断路径是否存在,如果不存在,创建该路径        
        if (!file.exists()) {
            file.mkdirs();
        }
        // 获取到上传文件的名称        
        String filename = upload.getOriginalFilename();
        String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        // 把文件的名称唯一化        
        filename = uuid + "_" + filename;
        // 上传文件        
        upload.transferTo(new File(file, filename));
        return "success";
    }

跨服務器上傳

在实际开发中,爲了提升模塊功能的性能會單獨的將一臺服務器或多臺作爲集群服務器來延展增强功能來服務更多的用戶。
例如:  
	应用服务器:负责部署我们的应用  
	数据库服务器:运行我们的数据库  
	缓存和消息服务器:负责处理大并发访问的缓存和消息  
	文件服务器:负责存储用户上传文件的服务器。

1.引入依賴jar包
pom.xml

	<dependency>
        <groupId>com.sun.jerseygroupId>
        <artifactId>jersey-coreartifactId>
        <version>1.18.1version>
    dependency>
    <dependency>
        <groupId>com.sun.jerseygroupId>
        <artifactId>jersey-clientartifactId>
        <version>1.18.1version>
    dependency>

2.index.jsp

<h3>跨服务器的文件上传h3>        
<form action="user/fileupload3" method="post" enctype="multipart/form-data">        
	选择文件:<input type="file" name="upload"/><br/>        
	<input type="submit" value="上传文件"/>    
form>

3.controller.java
本地文件對象創建改變為服务資源器對象,由服務器資源對象來進行文件書寫。

	@RequestMapping(value = "/fileupload3")
    public String fileupload3(HttpServletRequest request, MultipartFile upload) throws Exception {
        System.out.println("SpringMVC跨服务器文件上传...");
        // 定义图片服务器的请求路径
        String path = "http://localhost:9090/springmvc_day02_03fileService_war/uploads/";

        // 获取到上传文件的名称
        String filename = upload.getOriginalFilename();
        String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
        // 把文件的名称唯一化
        filename = uuid + "_" + filename;
        // 向图片服务器上传文件
        // 创建客户端对象
        Client client = Client.create();
        // 连接图片服务器
        WebResource webResource = client.resource(path + filename);
        // 上传文件
        webResource.put(upload.getBytes());
        return "success";
    }

異常處理器

系统中异常包括两类:预期异常和运行时异常 RuntimeException,前者通过捕获异常从而获取异常信息, 后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。 系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端 控制器交由异常处理器进行异常处理

【SSM Spring 5.0.2 SpringMVC IDEA Maven】SpringMVC學習記錄 2019_6_30_第6张图片

具體操作

  1. 編寫異常消息傳遞類
public class customException extends Exception {
    //消息对象
    private String message;
    //构造函数
    public customException(String message) {
        this.message = message;
    }
    //get方法
    public String getMessage() {
        return message;
    }
}
  1. 跳轉的錯誤頁面
    error.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>执行失败title>head>
<body>
执行失败!
${message }

body>
html>
  1. 編寫自定義異常處理器接口完全實現類
public class customExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        e.printStackTrace();
        customException customException = null;
        if (e instanceof customException) {
            customException = (customException) e;
        } else {
            customException = new customException("系统错误,请与系统管理 员联系!");
        }
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("message", customException.getMessage());
        modelAndView.setViewName("error");
        return modelAndView;
    }
}
  1. 配置自定義異常處理(攔截)器bean對象
    spring.xml
	
    <bean id="handlerExceptionResolver" class="cn.itcast.eResolver.customExceptionResolver"/>

流程示意圖

原理:所有的異常都會經過前端控制器,那麽在前端控制器做人爲的異常攔截(處理)將可識別的異常情況s,通過手寫代碼設置特別響應s
這樣就不會給用戶看一坨Tomcat的報錯了。

customException類,主要功能是獲取xxx.jsp頁面抛出的異常,在異常被捕獲的時候,并將異常信息傳遞給customExceptionResolver異常處理器,由異常處理器保存異常信息至modelAndView對象並跳轉頁面至error.jsp
【SSM Spring 5.0.2 SpringMVC IDEA Maven】SpringMVC學習記錄 2019_6_30_第7张图片

攔截器

Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理,也就是AOP思想具體應用的一種

拦截器链(Interceptor Chain)。拦截器链就是将拦截器按一定的顺 序联结成一条链。
在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

【SSM Spring 5.0.2 SpringMVC IDEA Maven】SpringMVC學習記錄 2019_6_30_第8张图片

與過濾器filter的區別

过滤器是 servlet 规范中的一部分,任何 java web 工程都可以使用。  
拦截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用。 

过滤器在 url-pattern 中配置了/*之后,可以对所有要访问的资源拦截。  
拦截器它是只会拦截访问請求——控制器方法,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦 截的

實現自定義攔截器步驟

自定义拦截器, 要求必须实现:HandlerInterceptor 接口

一個攔截器
  1. 編寫HandlerInterceptor 接口 完全實現類
    HandlerInterceptorDemo1.java
public class HandlerInterceptorDemo1 implements HandlerInterceptor {
	/**
     * 预处理回调方法,实现处理器的预处理(如检查登陆),第三个参数为响应的处理器,自定义Controller
     * 返回值:true表示继续流程(如调用下一个拦截器或处理器);false表示流程中断(如登录检查失败),
     * 不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
   	*/
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器preHandle拦截方法执行了,但放行了");
        //request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
        return true;
    }

    /**
     * 后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)
     * 对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
    */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器postHandle后处理方法执行了,无法拦截,但可手动跳转");
    }

    /**
    * 整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,
    * 还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
   */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion (final)方法执行了");
    }
}
  1. 配置拦截器bean以及攔截範圍
    springmvc.xml
	
    <mvc:interceptors>
        <mvc:interceptor>
        	
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path=""/>
            <bean id="handlerInterceptorDemo1" class="cn.itcast.interceptor.HandlerInterceptorDemo1">bean>
        mvc:interceptor>
    mvc:interceptors>
  1. index.jsp
<html>
<body>
	<h2>Hello World!h2>
	<a href="user/testInterceptor">Souta>
body>
html>
  1. success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Titletitle>
head>
<body>
    <h2>You are Success!!h2>
    <% System.out.println("success.jsp页面渲染了");%>
body>
html>
  1. 测试运行结果
    拦截器preHandle拦截方法执行了,但放行了
    Controller类的testInterceptor方法执行了…
    拦截器postHandle后处理方法执行了,无法拦截,但可手动跳转
    success.jsp页面渲染了
    afterCompletion (final)方法执行了

特殊情况:
当preHandle方法中写了手动跳转页面则不继续执行postHandle方法,即便写了return true;但仍会继续执行afterCompletion

实例:
HandlerInterceptorDemo1完全实现类.java

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器preHandle拦截方法执行了,但放行了");
        request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
        return true;
    }

success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Titletitle>
head>
<body>
    <h2>You are Success!!h2>
    <% System.out.println("suceesss.jsp页面渲染了");%>
body>
html>

測試执行结果:
preHandle 拦截器拦截了
suceesss.jsp页面渲染了
afterCompletion 方法执行了

多個攔截器

按照springMVC中再配置多個,寫同名同路經的完全實現類即可。

如何调用:   
		按拦截器定义顺序调用   
何时调用:   
		只要配置了都会调用    
有什么用:   
		如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回 true。   
		如果程序员决定不需要再调用其他的组件去处理请求,则返回 false.

		[注:false对客户端来说就是空白页面]

测试執行结果
拦截器1preHandle
拦截器2preHandle
controller
拦截器2postHandle
拦截器1postHandle
success.jsp渲染
拦截器2afterCompletion
拦截器1afterCompletion

Web-App標簽子標簽順序


SSM整合之Spring框架的配置路徑問題

  1. web.xml

<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>MyBatis_06_ssmdisplay-name>
  
    
  	
	<context-param>
		<param-name>contextConfigLocationparam-name>
		<param-value>classpath:applicationContext.xmlparam-value>
	context-param>

	
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
	listener>
	
	
	
	<servlet>
		<servlet-name>springservlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
		<load-on-startup>1load-on-startup>
	servlet>
	
	
	<servlet-mapping>
		<servlet-name>springservlet-name>
		<url-pattern>/url-pattern>
	servlet-mapping>
  
web-app>

提示:如果在eclipse中安裝了Spring IDE插件則這裏的Spring和SpringMVC標簽都直接Alt+/選擇#ContextLoaderListener
#dispatcherservlet即可添加

注意:關於SpringMVC中的以及Spring
SpringMVC的配置文件路徑可以根據默認約定:
使用前端控制器(servlet)的servlet-name,"/WEB-INF/"+servlet-name+"-servlet.xml"
也就是/WEB-INF/spring-servlet.xml

Spring的配置文件若沒有通過context-param标签指定,則其param-name值默认是contextConfigLocation,param-value默认是/WEB-INF/applicationContext.xml

一般來説,Spring的配置文件都是在類路徑下
也就是需要寫明param-value=classpath:applicationContext.xml
以及配置spring,必须要有listener标签。

你可能感兴趣的:(SpringFramework)