140 SpringMVC

文章目录

一、SpringMVC

1.1引言

  • java开源框架,Spring Framework的一个独立模块。
  • MVC框架,在项目中开辟MVC层次架构
  • 对控制器中的功能包装简化扩展践行工厂模式,功能架构在工厂之上

1.2 MVC架构

1.2.1概念
名称职责
Model模型:即业务模型,负责完成业务中的数据通信处理,对应项目中的service和dao
View视图:渲染数据,生成页面。对应项目中的 Jsp
Controller控制器:直接对接请求,控制MVC流程,调度模型,选择视图。对应项目中的Servlet
1.2.2好处
  • MVC是现下软件开发中的最流行的代码结构形态;

  • 人们根据负责的不同逻辑,将项目中的代码分成MVC3个层次;

  • 层次内部职责单一,层次之间耦合度低;

  • 符合低耦合高内聚的设计理念。也实际有利于项目的长期维护。

二、开发流程

2.1导入依赖

		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

2.2配置核心(前端)控制器

  • 作为一个MVC框架,首先要解决的是:如何能够收到请求!
  • 所以MVC框架大都会设计一款前端控制器,选型在Servlet或Filter两者之一,在框架最前沿率先工作,接收所有请求。(这里是选择Servlet,这是一个SpringMVC帮我们写好的)
  • 此控制器在接收到请求后,还会负责springMVC的核心的调度管理,所以既是前端又是核心。
  • web.xml :
	 <servlet>
        <servlet-name>mvc_wlw</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     	<!-- 局部参数,声明配置文件位置-->
     	<init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <!-- 懒汉式  饿汉式加载 (servlet启动时刻) 可选-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc_wlw</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

2.3后端控制器

  • 等价于之前定义的Servlet
package com.wlw.web;

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

@Controller //声明是后端控制器 ,(应该就是<bean> 标签的注解)
@RequestMapping("/hello") //这是访问路径
public class HelloController {

    @RequestMapping("/test01")
    public String hello01(){ //此方法可以理解为之前在servlet中写的service dopost  doget方法
        System.out.println("hello world 1");
        return null;
    }

    @RequestMapping("/test02")
    public String hello02(){ 
        System.out.println("hello world 2");
        return null;
    }
}

2.4配置文件(mvc.xml)

  • 默认名称:核心控制器名-servet.xml 默认位置: WEB-INF
  • 随意名称:mvc.xml 随意位置: resources 但需要配置在核心(前端)控制器中
  • mvc.xml 文件:
<beans 	xmlns="http://www.springframework.org/schema/beans"
          xmlns:context="http://www.springframework.org/schema/context"
          xmlns:mvc="http://www.springframework.org/schema/mvc"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
							http://www.springframework.org/schema/beans/spring-beans.xsd
							http://www.springframework.org/schema/context
							http://www.springframework.org/schema/context/spring-context.xsd
							http://www.springframework.org/schema/mvc
							http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 注解扫描 -->
    <context:component-scan base-package="com.qf.web"/>

    <!-- 注解驱动 -->
    <mvc:annotation-driven></mvc:annotation-driven>

    <!-- 视图解析器
	     作用:1.捕获后端控制器的返回值="hello"
	          2.解析: 在返回值的前后 拼接 ==> "/hello.jsp"
	 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/"></property>
        <!-- 后缀 -->
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

2.5访问

http://localhost:8080/SpringMVC01/hello/test01
http://localhost:8080/SpringMVC01/hello/test02

2.6过程分析

当我们项目启动时, (web.xml)前端控制器会启动,它启动时会加载mvc.xml文件,启动SprignMVC工厂,生产配置好的bean,扫描注解,后端控制器就创建出来;(就是项目启动时,配置文件就加载好了,就等着用户访问了)之后,用户请求,先经过前端控制器处理,再转发到后端控制器上,匹配访问路径,执行对应的后端控制器的方法,再根据返回值(通过视图解析)跳转到对应的jsp。

三、接收请求参数

  • 通过控制器中方法的形参,接收请求参数

3.1基本类型参数

  • 请求参数 和 方法的形参 同名即可
  • springMVC默认可以识别的日期字符串格式为: YYYY/MM/dd HH:mm:ss通过@DateTimeFormat可以修改默认日志格式
package com.wlw.web;

import com.wlw.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Date;

@Controller
@RequestMapping("/param")
public class ParamController {

    // http://xxxx/param/test01?id=1&name=shine&gender=true&birth=2020/11/11 12:20:30
    @RequestMapping("/test1")
    public String test01(Integer id, String name, Boolean gender, Date birth){
        System.out.println(id);
        System.out.println(name);
        System.out.println(gender);
        System.out.println(birth);
        return "hello";
    }
}
/*
1
shine
true
Wed Nov 11 12:20:30 CST 2020
*/

3.2实体收参【重点】

  • 请求参数 和 实体的属性 同名即可
package com.wlw.web;

import com.wlw.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Date;

@Controller
@RequestMapping("/param")
public class ParamController {

    // http://xxxx/param/test1?id=1&name=zhangsan&gender=true&birth=2020/12/12 12:13:20
    //请求参数 和 实体的属性 同名
    @RequestMapping("/test2")
    public String test2(User user){
        System.out.println("test2");
        System.out.println(user);
        return "hello";
    }
}
/*
test2
User{id=1, name='zhangsan', gender=true, birth=Wed Nov 11 12:20:30 CST 2020}
*/
package com.wlw.entity;

import java.util.Arrays;
import java.util.Date;

public class User {
    private Integer id;
    private String name;
    private Boolean gender;
    private Date birth;
	// get  set  toString() ......
}

3.3数组收参

  • 简单类型的数组
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>param</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/param/test3">
        id:<input type="text" name="id"> <br>
        name:<input type="text" name="name"><br>
        gender:<input type="text" name="gender"><br>
        birth:<input type="text" name="birth"><br>
        <input type="checkbox" name="hobby" value="football">足球
        <input type="checkbox" name="hobby" value="basketball">篮球
        <input type="checkbox" name="hobby" value="volleyball">排球 <br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

package com.wlw.web;

import com.wlw.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Date;

@Controller
@RequestMapping("/param")
public class ParamController {

    // http://xxxx/param/test3?hobby=football&hobby=basketball&hobby=volleyball
    // 请求路径中有多个重名的参数,这些参数可以存储到一个数组中
    @RequestMapping("/test3")
    public String test3(String[] hobby) {
        System.out.println("test3");
        for (String s : hobby) {
            System.out.println(s);
        }
        return "hello";
    }

     // http://xxxx/param/test3?hobby=football&hobby=basketball&hobby=volleyball
    // 请求路径中有多个重名的参数,这些参数可以存储到一个数组中
    @RequestMapping("/test3")
    public String test3(User user) {
        System.out.println("test3");
        System.out.println(user);
        return "hello";
    }
}
/*
test3
football
basketball
volleyball
*/

/*
test3
User{id=10, name='wlw', gender=false, birth=Wed Nov 11 12:20:30 CST 2020, hobby=[football, basketball, volleyball]}

*/

3.4集合收参【了解】

package com.wlw.entity;

import java.util.List;

public class UserList {
    public List<User> getUsers() {
        return users;
    }
    public void setUsers(List<User> users) {
        this.users = users;
    }
    private List<User> users;
}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>param</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/param/test4">
        id:<input type="text" name="users[0].id"> <br>
        name:<input type="text" name="users[0].name"><br>
        gender:<input type="text" name="users[0].gender"><br>
        <hr>
        id:<input type="text" name="users[1].id"> <br>
        name:<input type="text" name="users[1].name"><br>
        gender:<input type="text" name="users[1].gender"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

package com.wlw.web;

import com.wlw.entity.User;
import com.wlw.entity.UserList;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import sun.nio.cs.US_ASCII;

import java.util.Date;

@Controller
@RequestMapping("/param")
public class ParamController {
    // http://xxx/param/test4?users[0].id=1&users[0].name=shine&users[0].gender=true&users[1].id=2&users[1].name=zhangsan
    @RequestMapping("/test4")
    public String test4(UserList userList){
        System.out.println("test4");
        for (User user : userList.getUsers()) {
            System.out.println(user);
        }
        return "hello";
    }
}
/*
test4
User{id=12, name='wal', gender=false, birth=null, hobby=null}
User{id=13, name='wlw', gender=true, birth=null, hobby=null}
*/

3.5路径参数

package com.wlw.web;

import com.wlw.entity.User;
import com.wlw.entity.UserList;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import sun.nio.cs.US_ASCII;

import java.util.Date;

@Controller
@RequestMapping("/param")
public class ParamController {
    // http://localhost:8080/SpringMVC01/param/test5/2
    // {id} 命名路径
    // {id} 等价于*   /test5/1   test5/2   test5/xxxx
    @RequestMapping("/test5/{id}")
    public String test5( @PathVariable("id")  Integer id){
        System.out.println("test5");
        System.out.println("id:"+id);
        return "hello";
    }
}
/*
test5
id:2
*/

3.6中文乱码

  • 首先,页面中字符集统一

    JSP 文件要有: <%@page pageEncoding="utf-8" %>
    HTML 文件要有: <meta charset= "UTF-8">
    
  • 其次,tomcat中字符集设置,对get请求中, 中文参数乱码有效

    Tomcat配置: URIEncoding=utf-8
    
  • 最后,设置此filter,对post请求中,中文参数乱码有效

    <!-- 此过滤器会进行:request.setCharactorEncoding("utf-8"); -->
        <filter>
          <filter-name>encoding</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>utf-8</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>encoding</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    

四、跳转

跳转关键字:forward:(转发) ; redirect:(重定向)

4.1转发

package com.wlw.web;

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

@Controller
@RequestMapping("/jump")
public class ForwardController {

    @RequestMapping("/test1")
    public String test1(){
        System.out.println("test1");
        return "hello"; // 转发,这是转发跳转(经过视图解析器)
        //return "forward:/hello.jsp";  //如果返回这个就不会经过视图解析器解析
    }

    @RequestMapping("/test2")
    public String test2(){
        System.out.println("test2");
        //return "forward:/jump/test1"; //(绝对地址)转发
        return "forward:test1";//相对路径 (这个test1 是当前类中的test1),在本类中可以用相对地址,如果要跳转到另外一个类就要用绝对地址
    }

}

4.2重定向

package com.wlw.web;

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

@Controller
@RequestMapping("/jump")
public class ForwardController {

    @RequestMapping("/test3")
    public String test3(){
        System.out.println("test3");
        return "redirect:/hello.jsp"; // 重定向到helo.jsp
    }

    @RequestMapping("/test4")
    public String test4(){
        System.out.println("test4");
        return "redirect:test3";   //重定向到test3(相对地址)
        //return "redirect:/jump/test3"
    }

    @RequestMapping("/query")
    public String test5(){
        System.out.println("querr数据1");
        return "forward:test1";  //转发
        //return "redirect:test1";  //重定向
    }
}

4.3跳转细节

  • 在增删改之后,为了防止请求重复提交,重定向跳转
  • 在查询之后,可以做转发跳转

五、传值

  • C得到数据后,跳转到V,并向V传递数据。进而V中可以渲染数据,让用户看到含有数据的页面

  • 转发跳转:Request作用域

  • 重定向跳转: Session作用域

5.1 Request和Session

package com.wlw.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/data")
public class DataController {

    @RequestMapping("/test1")
    public String test1(HttpServletRequest request, HttpSession session){
        request.setAttribute("name","wlw");
        session.setAttribute("age",10);
        return "data";
    }
}

5.2 JSP中取值

  • 建议:重点复习EL JSTL
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>data</title>
</head>
<body>
    name:${requestScope.name} <br>
    age:${sessionScope.age}
</body>
</html>

5.3 Model

package com.wlw.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/data")
@SessionAttributes(names = {"city","street"})
public class DataController {

    @RequestMapping("/test2")
    public String test2(Model model){
        model.addAttribute("gender",true); //Model 默认的会把数据存放到request 域中
        return "data2";
    }

}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>data2</title>
</head>
<body>
    gender:${requestScope.gender} <br>
</body>
</html>

5.4 ModelAndView

package com.wlw.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/data")
public class DataController {

    @RequestMapping("/test4")
    public ModelAndView test4(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("claz","001班");
        modelAndView.setViewName("forward:/hello.jsp");
        return modelAndView;
    }
}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello</title>
</head>
<body>
    Hello World!  <br>
    claz:${requestScope.claz}
</body>
</html>

5.5 @SessionAttributes

  • @SessionAttributes({“gender”,“name”}) : model中的 name和gender会存入session中
  • SessionStatus 移除session
package com.wlw.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/data")
@SessionAttributes(names = {"city","street"})
public class DataController {


    @RequestMapping("/test2")
    public String test2(Model model){
        model.addAttribute("gender",true); //Model 默认的会把数据存放到request 域中
        model.addAttribute("city","北京");
        model.addAttribute("street","长安街"); //这两个数据会存放在session域中,因为类名上面定义了@SessionAttributes(names = {"city","street"})
        return "data2";
    }

    @RequestMapping("/test3")
    public String test3(SessionStatus sessionStatus){
        //清空所有 由model存入session域的数据
        sessionStatus.setComplete();
        return "data2";
    }

}

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>data2</title>
</head>
<body>
    gender:${requestScope.gender} <br>
    city:${sessionScope.city} <br>
    street:${sessionScope.street} <br>
</body>
</html>

六、静态资源

6.1静态资源问题

  • 静态资源: html, js文件, css文件,图片文件
  • 静态文件没有url-pattern,所以默认是访问不到的,之所以可以访问,是因为,tomcat中有 一个全局的servlet:org.apache.catalina.servlets.DefaultServlet,它的url-pattern是 “/”, 是全局默认的Servlet.所以每个项目中不能匹配的静态资源的请求,有这个Servlet来处理即可。
  • 但, 在SpringMVC中DispatcherServlet(前端控制器)也采用了 “/” 作为url-pattern,则项目中不会再使用全局的Serlvet,则静态资源不能完成访问。

6.2解决方案1

  • DispathcerServlet采用其他的url-pattern
  • 此时,所有访问handler的路径都要以action结尾! !
<servlet>
        <servlet-name>mvc_wlw</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 局部参数,声明mvc配置文件位置-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <!-- 懒汉式  饿汉式加载  可选-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc_wlw</servlet-name>
        <!--<url-pattern>/</url-pattern>-->
        <url-pattern>*.action</url-pattern>
    </servlet-mapping>

6.3解决方案2

  • DispathcerServlet的url-pattern依然采用"/",但追加配置
  • 在mvc.xml 中
 <!--
      会额外的增加一个handler(就是Controller中的一个方法),且其requestMapping:  "/**" 可以匹配所有请求,但是优先级最低
      所以如果其他所有的handler都匹配不上,请求会转向 "/**" ,恰好,这个handler就是处理静态资源的
      处理方式:将请求转发到tomcat中名为default的Servlet

      RequestMapping  /*   /a   /b  /c   /dxxxx    /a/b
                      /**
     -->
    <mvc:default-servlet-handler/>
  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- SpringMVC前端(核心)控制器
         1. 前端,接收所有请求
         2. 启动SpringMVC工厂  mvc.xml
         3. 负责springMVC流程调度
    -->
    <servlet>
        <servlet-name>mvc_wlw</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 局部参数,声明mvc配置文件位置-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <!-- 懒汉式  饿汉式加载  可选-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc_wlw</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>
  • 这样就可以访问静态资源了

6.4解决方案3

  • mapping是访问路径,location是静态资源存放的路径
  • 将/html/ ** 中/ **匹配到的内容, 拼接到/hhh/后 http://…/html/a.html 访问/hhh/a.html
<mvc:resources mapping="/html/**" location="/page/"/>
<mvc:resources mapping="/css/**" location="/css/"/>

七、Json处理

7.1导入依赖

<!-- Jackson springMVC默认的Json解决方案选择是Jackson, 所以只需要导入jackson的jar,即可使用。-->
<!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.9</version>
        </dependency>

7.2使用@ResponseBody

package com.wlw.web;

import com.wlw.entity.User;
import com.wlw.entity.User2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

@Controller
@RequestMapping("/json")
public class JsonController {

    @RequestMapping("/test1")
    @ResponseBody //这个注解的意思是将这个handler的返回值自动转成json,并响应给客户端
    public User test1(){
        System.out.println("test1");
        User user = new User(1,"张三",new Date());
        return user;
    }

    @RequestMapping("/test2")
    @ResponseBody //这个注解的意思是将这个handler的返回值自动转成json,并响应给客户端
    public List<User> test2(){
        System.out.println("test2");
        User user = new User(1,"张三",new Date());
        User user2 = new User(2,"李四",new Date());
        List<User> users = Arrays.asList(user, user2);
        return users;
    }

    @RequestMapping(value = "/test3",produces = "text/html;charset=utf-8")//produces防止中文乱码

    @ResponseBody //如果返回值是 String 类型,就将字符串显示出来,而不是跳转
    public String test3(){
        System.out.println("test3");
        return "你好";
    }
}

7.3使用@RestController

  • Controller类上加了@RestControllr注解,等价于在类中的每个方法上都加了@ResponseBody
package com.wlw.web;

import com.wlw.entity.User;
import com.wlw.entity.User2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

//@Controller
@RequestMapping("/json")
@RestController //这个注解如果写了 @Controller 和 类中的每个方法的 @ResponseBody 都不用加了
public class JsonController {

    @RequestMapping("/test1")
    //@ResponseBody //这个注解的意思是将这个handler的返回值自动转成json,并响应给客户端
    public User test1(){
        System.out.println("test1");
        User user = new User(1,"张三",new Date());
        return user;
    }

    @RequestMapping("/test2")
    //@ResponseBody //这个注解的意思是将这个handler的返回值自动转成json,并响应给客户端
    public List<User> test2(){
        System.out.println("test2");
        User user = new User(1,"张三",new Date());
        User user2 = new User(2,"李四",new Date());
        List<User> users = Arrays.asList(user, user2);
        return users;
    }

    @RequestMapping(value = "/test3",produces = "text/html;charset=utf-8") //produces防止中文乱码

    //@ResponseBody //如果返回值是 String 类型,就将字符串显示出来,而不是跳转
    public String test3(){
        System.out.println("test3");
        return "你好";
    }
}

7.4使用@RequestBody

  • @RequestBody,接收Json参数
7.4.1定义Handler
package com.wlw.entity;

public class User {
    private Integer id;
    private String name;
    private Date birth;
	//set get 
}

package com.wlw.web;

@Controller
@RequestMapping("/json")
public class JsonController {

    @RequestMapping("/test4")
    public String test4(@RequestBody User user){ //@RequestBody 是指将接收的json数据转成User ,post方式提交的(将请求体中的json数据转换为java对象)
        System.out.println(user);
        return "ok";
    }
}
7.4.2Ajax发生json
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>json</title>
    <script src="${pageContext.request.contextPath}/js/jquery-2.1.0.js"></script>
</head>
<body>
    <input type="button" value="ajax" οnclick="send_json();"/>
    <script>
        function send_json() {
            var user = {id:1,name:"wlw"};
            var userJSON = JSON.stringify(user);   //转换js对象成json
            $.ajax({
                url:"${pageContext.request.contextPath}/json/test4",
                type:"post",
                data:userJSON,
                contentType:"application/json",  //声明请求参数类型为json

                success:function (ret) {
                    alert(ret);
                }
            })
        }
    </script>
</body>
</html>

7.5 Jackson常用注解

7.5.1日期格式化
  • @JsonFormat(pattern=“yyy-MM-dd HH:mm:ss”,timezone = “GMT+8”)
package com.wlw.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
public class User {

    private Integer id;
    private String name;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") //日期格式化
    private Date birth;

    private List<String> hobby;

    private Double salary = 10000.126;
	//set get
}

7.5.2属性名修改
  • @JsonProperty(“new_ name”)
package com.wlw.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.wlw.serialize.MySerializer;

import java.util.Date;
import java.util.List;

public class User {

    @JsonProperty("id2") //修改属性名 ,json数据显示的为id2
    private Integer id;

    @JsonIgnore //json格式数据中 忽略这个属性
    private String name;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") //日期格式化
    private Date birth;

    //@JsonInclude(JsonInclude.Include.NON_NULL) //如果这个属性值为null,就不输出
    @JsonInclude(JsonInclude.Include.NON_EMPTY) //这个属性要有值,如果为null或者 size = 0 也不输出
    private List<String> hobby;// null   size=0

    @JsonSerialize(using = MySerializer.class)
    private Double salary = 10000.126;//在输出此属性时,使用MySerializer输出

    //set get
}

7.5.3属性忽略
  • @Jsonignore
package com.wlw.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.wlw.serialize.MySerializer;

import java.util.Date;
import java.util.List;

public class User {

    private Integer id;

    @JsonIgnore //json格式数据中 忽略这个属性
    private String name;

    private Date birth;
    private List<String> hobby;
    private Double salary = 10000.126;

    //set get
}
7.5.4 null和empty属性排除
  • Jackson默认会输出null值的属性,如果不需要,可以排除。
  • @Jsonlnclude(JsonInclude.Include.NON_NULL) //null值 属性不输出
  • @ JsonInclude(value= Jsonlnclude.Include.NON_EMPTY) // empty属性
    不输出(空串,长度为0的集合,null值)
package com.wlw.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.wlw.serialize.MySerializer;

import java.util.Date;
import java.util.List;

public class User {

    private Integer id;
    private String name;
    private Date birth;

    //@JsonInclude(JsonInclude.Include.NON_NULL) //如果这个属性值为null,就不输出
    @JsonInclude(JsonInclude.Include.NON_EMPTY) //这个属性要有值,如果为null或者 size = 0 就不输出
    private List<String> hobby;// null   size=0

    private Double salary = 10000.126;

    //set get
}
7.5.5自定义序列化
  • @JsonSerialize(using = MySerialzr.class) //使用MySerializer输出某属性
package com.wlw.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.wlw.serialize.MySerializer;

import java.util.Date;
import java.util.List;

public class User {

    private Integer id;
    private String name;
    private Date birth;
    private List<String> hobby;// null   size=0

    @JsonSerialize(using = MySerializer.class)
    private Double salary = 10000.126;//在输出此属性时,使用MySerializer输出

    //set get
}
package com.wlw.serialize;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.math.BigDecimal;

public class MySerializer extends JsonSerializer<Double> {
    @Override
    public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        // 将Double salary的值 四舍五入
        String number = BigDecimal.valueOf(value).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
        // 输出 四舍五入后的值
        gen.writeNumber(number);
    }
}

7.6 FastJson

  • 如果不想使用Jackson,则也可以安装其他的Json处理方案: FastJson
7.6.1导入依赖
 		<!-- FastJson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
7.6.2安装FastJson
  • mvc.xml文件:
  	 <!-- 注解驱动 -->
    <mvc:annotation-driven>
        <!-- 安装FastJson,转换器 -->
        <mvc:message-converters>
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <!-- 声明转换类型:json (将java对象转成josn)-->
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
7.6.3使用
  • @ResponseBody @RequestBody @RestController使用方法不变
7.6.4常用注解
  • 日期格式化: @JSONField(format=“yyyy/MM/dd”)
  • 属性名修改: @JSONField(name=“birth”)
  • 忽略属性: @JSONField(serialize = false)
  • 包含null值: @JSONField(serialzeFeatures = SerializerFeature .WriteMapNullValue)默认会忽略所有null值,有此注解会输出null
    • @JSONFieldlserialzeFeatures = SerializerFeature.WriteUullstringAsEmpty) null的String输出为"“
  • 自定义序列化: @JSONField(serializeUsing = MySrializer.cass)
package com.wlw.entity;

import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.wlw.serialize.MySerializer2;

import java.util.Date;

public class User2 {

    @JSONField(serialize = false)
    private Integer id; //忽略id

    @JSONField(name="NAME",serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)
    private String name;// 修改属性名为NAME , 当name为空时,输出一个空串

    @JSONField(serialzeFeatures = SerializerFeature.WriteMapNullValue)
    private String city;// 如果为空,输出null

    @JSONField(format="yyyy/MM/dd")
    private Date birth; //日期格式化

    @JSONField(serializeUsing = MySerializer2.class)
    private Double salary; // 序列化 拼接“ 元 ”

	// 构造 set get tostring
}

package com.wlw.serialize;

import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;

import java.io.IOException;
import java.lang.reflect.Type;

public class MySerializer2 implements ObjectSerializer {

    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType,
                      int features) throws IOException {
        Double value = (Double) object; // salary属性值
        String text = value + "元";// 在salary后拼接 “元”
        serializer.write(text); // 输出拼接后的内容
    }
}

package com.wlw.web;

import com.wlw.entity.User;
import com.wlw.entity.User2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import sun.nio.cs.US_ASCII;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

//@Controller
@RequestMapping("/json")
@RestController //这个注解如果写了 @Controller 和 类中的每个方法的 @ResponseBody 都不用加了
public class JsonController {

    @RequestMapping("/test11")
    public User2 test11(){
        System.out.println("test1");
        User2 user2 = new User2(1,"张三","北京",new Date(),1000.5);
        return user2;
    }

}
/*
{"NAME":"张三","birth":"2020/09/06","city":"北京","salary":"1000.5元"}
*/

八、异常解析器

8.1现有方案,分散处理

  • Controller中的每个Handler自己处理异常
  • 此种处理方案,异常处理逻辑,分散在各个handler中,不利于集中管理
@Controller
@RequestMapping("/ex")
public class ExController {
    
    @RequestMapping("/xx") //访问路径
    public string xx(){
        try{
            ...
        }catch(Exception1 e){
            e.printStackTrace();
            return"redirect:/xx/error1";
        }catch(Exception2 e){
            e.printStackTrace();
            return "redirect:/xx/error2";
        }
    }
    ...
    ...
}

8.2异常解析器,统一处理异常

  • Controller中的每个Handler不再自己处理异常,而是直接throws所有异常。
  • 定义一个“异常解析器”集中捕获处理所有异常
  • 此种方案,在集中管理异常方面,更有优势!
package com.wlw.resolver;

import com.wlw.ex.MyException1;
import com.wlw.ex.MyException2;
import com.wlw.ex.MyException3;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

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

// 异常解析器
// 任何一个Handler中抛出异常时,它就会执行(将异常处理代码提取出来)
public class MyExceptionResolver implements HandlerExceptionResolver {

    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

        ModelAndView modelAndView = new ModelAndView();

        if(e instanceof MyException1){
            // error1.jsp
            modelAndView.setViewName("redirect:/error1.jsp"); //重定向
        }else if(e instanceof MyException2){
            // error2.jsp
            modelAndView.setViewName("redirect:/error2.jsp");
        }else if(e instanceof MyException3){
            // error3.jsp
            modelAndView.setViewName("redirect:/error3.jsp");
        }else if(e instanceof MaxUploadSizeExceededException){
            modelAndView.setViewName("redirect:/uploadError.jsp");
        }

        return modelAndView;
    }
}

<!--mvc.xml文件中-->
    <!--异常解析器-->
    <bean class="com.wlw.resolver.MyExceptionResolver"></bean>

九、拦截器

9.1作用

  • 作用:抽取handler中的冗余功能 (使handler中重复的代码只写一遍,实现复用)

9.2定义拦截器

  • 执行顺序: preHandle–postHandle–afterCompletion
package com.wlw.interceptor;

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

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

public class MyInterceptor implements HandlerInterceptor {

    // 在handler之前执行
    // 再次定义 handler中冗余的功能
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("pre Handle");
        // 判断登录状态
        HttpSession session = request.getSession();
        if(session.getAttribute("state") != null){
            return true; //放行,执行后续的handler
        }
        //中断之前,做出响应
        response.sendRedirect("/login.jsp");

        return false; //中断请求,不再执行后续的handler
    }

    // 在handler之后执行,响应之前执行
    // 改动请求中数据
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("post Handle");
    }

    // 在视图渲染完毕后,
    // 资源回收
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("after Handle");
    }
}

9.3配置拦截路径

  • mvc.xml:
<!--拦截器配置,指明哪个拦截器拦截哪些路径访问请求-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/inter/test1"/>
            <mvc:mapping path="/inter/test2"/>
            <mvc:mapping path="/inter/test*"/> <!--test开头的路径-->
            <!--<mvc:mapping path="/inter/**"/>--><!--任意多级任意路径-->
            <mvc:exclude-mapping path="/inter/login"/><!--不拦截此路径-->
            <bean class="com.wlw.interceptor.MyInterceptor"/><!--拦截器类-->
        </mvc:interceptor>
    </mvc:interceptors>

十、上传

10.1导入jar(依赖)

 		<dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

10.2表单

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上传</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/upload/test1" method="post" enctype="multipart/form-data">
        file:<input type="file" name="source"/><br>
        <input type="submit" value="上传"/>
    </form>

</body>
</html>

10.3.上传解析器

  • mvc.xml 中追加:
<!-- 文件上传解析器-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 最大可上传的文件大小  单位:byte  超出后会抛出MaxUploadSizeExceededException异常,可以异常解析器捕获 (配置了之后有可能出现状态显示为200,而并没又上传成功,所以我们一般写一个拦截器代替这个功能)-->
        <!--<property name="maxUploadSize" value="1048576"></property>-->
</bean>

10.4 Handler

package com.wlw.web;

import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.util.UUID;

@Controller
@RequestMapping("/upload")
public class UploadController {

    @RequestMapping("/test1")
    public String test1(MultipartFile source, HttpSession session) throws IOException {
        System.out.println("tes1");
        // 获取上传文件的 原始名称
        String filename = source.getOriginalFilename();
        //生成一个唯一的文件名
        String uniqueFileName = UUID.randomUUID().toString();
        //获取上传文件的后缀名
        String extension = FilenameUtils.getExtension(filename);
        //拼接得到最终的唯一的文件名
        String uniqueFileName2 = uniqueFileName+"."+extension;

        // 获取上传文件的 类型
        String contentType = source.getContentType();
        System.out.println(filename);
        System.out.println(contentType);

        //保存
        String realPath = session.getServletContext().getRealPath("/upload");
        System.out.println("realPath:" + realPath);
        source.transferTo(new File(realPath+"\\"+uniqueFileName2));
        return "index";
    }
}

10.5自定义文件上传大小的拦截器

  • MyFileUploadInterceptor
package com.wlw.interceptor;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

public class MyFileUploadInterceptor implements HandlerInterceptor {

    private long maxFileUploadSize;

    public long getMaxFileUploadSize() {
        return maxFileUploadSize;
    }

    public void setMaxFileUploadSize(long maxFileUploadSize) {
        this.maxFileUploadSize = maxFileUploadSize;
    }

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断上传文件的大小,1M= 1048576byte
        ServletRequestContext servletRequestContext = new ServletRequestContext(request);
        //获取上传文件的大小
        long length = servletRequestContext.contentLength();
        if(length > maxFileUploadSize){
            throw new MaxUploadSizeExceededException(maxFileUploadSize);
        }
        return true;
    }
}

  • mvc.xml
<!--异常解析器-->
    <bean class="com.wlw.resolver.MyExceptionResolver"></bean>

<!--拦截器配置,指明哪个拦截器拦截哪些路径访问请求-->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/upload/test1"/>
            <bean class="com.wlw.interceptor.MyFileUploadInterceptor">
                <property name="maxFileUploadSize" value="1048576"></property>
            </bean>
        </mvc:interceptor>
    </mvc:interceptors>
  • 异常处理:MyExceptionResolver (文件过大就重定向到uploadError.jsp)
package com.wlw.resolver;

import com.wlw.ex.MyException1;
import com.wlw.ex.MyException2;
import com.wlw.ex.MyException3;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

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

// 异常解析器
// 任何一个Handler中抛出异常时,它就会执行(将异常处理代码提取出来)
public class MyExceptionResolver implements HandlerExceptionResolver {

    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

        ModelAndView modelAndView = new ModelAndView();

        if(e instanceof MyException1){
            // error1.jsp
            modelAndView.setViewName("redirect:/error1.jsp"); //重定向
        }else if(e instanceof MyException2){
            // error2.jsp
            modelAndView.setViewName("redirect:/error2.jsp");
        }else if(e instanceof MyException3){
            // error3.jsp
            modelAndView.setViewName("redirect:/error3.jsp");
        }else if(e instanceof MaxUploadSizeExceededException){
            modelAndView.setViewName("redirect:/uploadError.jsp");
        }

        return modelAndView;
    }
}

十一、下载

11.1超链接

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>download</title>
</head>
<body>
    <a href="${pageContext.request.contextPath}/download/test1?name=jquery-2.1.0.js">下载</a>
</body>
</html>

11.2 Handler

package com.wlw.web;

import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.FileInputStream;
import java.io.IOException;

@Controller
@RequestMapping("/download")
public class DowmLoadController {

    @RequestMapping("/test1")
    public void test1(String name, HttpSession session, HttpServletResponse response) throws IOException {

        //获取文件存放的地址 (就是下载这个文件夹里的文件)
        String realPath = session.getServletContext().getRealPath("/upload");
        String filePath = realPath + "\\"+ name;

        //设置响应头  告知浏览器,要以附件的形式保存内容   filename=浏览器显示的下载文件名
        response.setHeader("content-disposition","attachment;filename="+name);

        //响应
        IOUtils.copy(new FileInputStream(filePath),response.getOutputStream());

    }
}

十二、验证码

12.1作用

  • 防止暴力攻击,前端安全保障

12.2导入jar(依赖)

 		<!-- Kaptcha -->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>javax.servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

12.3声明验证码组件

  • web.xml :追加
 <!-- 验证码Servlet 验证码组件-->
    <servlet>
        <servlet-name>cap</servlet-name>
        <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
        <init-param>
            <param-name>kaptcha.border</param-name>
            <param-value>no</param-value>
        </init-param>
        <init-param>
            <param-name>kaptcha.textproducer.char.length</param-name>
            <param-value>4</param-value>
        </init-param>
        <init-param>
            <param-name>kaptcha.textproducer.char.string</param-name>
            <param-value>abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</param-value>
        </init-param>
        <init-param>
            <param-name>kaptcha.background.clear.to</param-name>
            <param-value>211,229,237</param-value>
        </init-param>
        <init-param>
            <!-- session.setAttribute("captcha","验证码") 存入session,便于校验-->
            <param-name>kaptcha.session.key</param-name>
            <param-value>captcha</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>cap</servlet-name>
        <url-pattern>/captcha</url-pattern>
    </servlet-mapping>
12.4page
  • capcha.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>验证码</title>
</head>
<body>

    <form action="${pageContext.request.contextPath}/captcha/test1">
        <img id="cap" src="${pageContext.request.contextPath}/captcha" style="width:100px" οnclick="refresh();">
        <input type="text" name="captcha"/>
        <br>
        <input type="submit" value="提交">
    </form>

    <script>
        function refresh(){
            var img = document.getElementById("cap");
            img.src="${pageContext.request.contextPath}/captcha?"+new Date().getTime()
        }
    </script>
</body>
</html>

  • CapchaController
package com.wlw.web;

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

import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/captcha")
public class CapchaController {

    @RequestMapping("/test1")
    public String test1(String captcha, HttpSession session){
        //验证码组件生成的验证码
        String realCaptcha = (String)session.getAttribute("captcha");
        //校验
        if(realCaptcha.equalsIgnoreCase(captcha)){
            return "index";
        }
        return "error1";
    }
}

十三、REST

13.1开发风格

  • 是一种开发风格,遵从此风格开发软件,符合REST风格,则RESTFUL。
  • 两个核心要求:
    • 每个资源都有唯一的标识(URL)
    • 不同的行为,使用对应的http-method
访问标识资源
http://localhost:8989/xxx/users所有用户
http://localhost:8989/xxx/users/1用户1
http://localhost:8989/xxx/users/1/orders用户1的所有订单
请求方式标识意图
GEThttp://localhost:8989/xxx/users查询所有用户
POSThttp://localhost:8989/xxx/users在所有用户中增加一个
PUThttp://localhost:8989/xxx/users在所有用户中修改一个
DELETEhttp://localhost:8989/xxx/users/1删除用户1
GEThttp://localhost:8989/xxx/users/1查询用户1
GEThttp://localhost:8989/xxx/users/1/orders查询用户1的所有订单
POSThttp://localhost:8989/xxx/users/1/orders在用户1的所有订单中增加一个

13.2优点

  • 输出json:

13.3使用

13.3.1定义Rest风格的Controller
  • @RequestMapping(value="/users",method = RequestMethod.GET)
    等价 @GetMapping("/users")
package com.wlw.web;

import com.wlw.entity.User;
import org.springframework.web.bind.annotation.*;
import sun.nio.cs.US_ASCII;

import java.util.Arrays;
import java.util.List;

/**
 *  查询: 所有用户
 *        查询id=xx 某一个用户
 *  删除: id=xx 某一个用户
 *  增加: 在所有用户中 增加一个
 *  修改: 在所有用户中 修改一个
 *
 *  资源: 所有用户    /users
 *        id=xx 某个用户   /users/{id}
 */
@RestController  //这个注解如果写了 @Controller 和 类中的每个方法的 @ResponseBody 都不用加了
public class MyRestController {

    @GetMapping("/users") //必须是get请求这个路径,才能访问到
    public List<User> queryUsers(){
        System.out.println("query users with get");
        User user1 = new User(1, "张三");
        User user2 = new User(2, "李四");
        return Arrays.asList(user1,user2);
    }

    @GetMapping("/users/{id}") //必须是get请求这个路径,才能访问到
    public User queryOne(@PathVariable("id") Integer id){
        System.out.println("query one User with get "+id);
        return new User(1, "张三");
    }

    @DeleteMapping("/users/{id}") //必须是delete请求这个路径,才能访问到
    public String deleteUser(@PathVariable("id") Integer id){
        System.out.println("delete one User with delete "+id);
        return "ok";
    }

    @PostMapping("/users") //必须是post请求这个路径,才能访问到
    public String saveUser(@RequestBody User user){
        System.out.println("save User with post "+user);
        return "ok";
    }

    @PutMapping("/users")//必须是put请求这个路径,才能访问到
    public String update(@RequestBody User user){
        System.out.println("update User with put "+user);
        return "ok";
    }
}
13.3.2ajax请求
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Rest</title>
    <script src="${pageContext.request.contextPath}/js/jquery-2.1.0.js"></script>
</head>
<body>
    <input type="button" value="queryAll" οnclick="queryAll();"/><br>
    <input type="button" value="queryOne" οnclick="queryOne();"/><br>
    <input type="button" value="saveUser" οnclick="saveUser();"/><br>
    <input type="button" value="updateUser" οnclick="updateUser();"/><br>
    <input type="button" value="deleteOne" οnclick="deleteOne();"/><br>
    <script>
        function queryAll() {
            $.ajax({
                type:"get",
                url:"${pageContext.request.contextPath}/users",
                success:function(ret){
                    console.log("查询所有:");
                    console.log(ret);
                }
            });
        }
        function queryOne() {
            $.ajax({
                type:"get",
                url:"${pageContext.request.contextPath}/users/1",
                success:function(ret){
                    console.log("查询一个:");
                    console.log(ret);
                }
            });
        }
        function saveUser() {
            var user={name:"wlw",birth:"2020-11-12 12:12:12"};
            $.ajax({
                type:"post",
                url:"${pageContext.request.contextPath}/users",
                data:JSON.stringify(user),
                contentType:"application/json",
                success:function(ret){
                    console.log("增加用户:");
                    console.log(ret);
                }
            });
        }
        function updateUser() {
            var user={id:1,name:"wlw2",birth:"2020-11-12 12:12:12"};
            $.ajax({
                type:"put",
                url:"${pageContext.request.contextPath}/users",
                data:JSON.stringify(user),
                contentType:"application/json",
                success:function(ret){
                    console.log("更新用户:");
                    console.log(ret);
                }
            });
        }
        function deleteOne() {
            $.ajax({
                type:"delete",
                url:"${pageContext.request.contextPath}/users/1",
                success:function(ret){
                    console.log("删除一个用户:");
                    console.log(ret);
                }
            });
        }
    </script>
</body>
</html>

十四、跨域请求

14.1域

域:协议+IP+端口

  • http://localhost:8989
  • http://locathost:8080
  • http://ww.baidu.com:80

14.2 Ajax跨域问题

  • Ajax发送请求时,不允许跨域,以防用户信息泄露。
  • 当Ajax跨域请求时, 响应会被浏览器拦截(同源策略),并报错。即浏览器默认不允许ajax跨域得到响应内容。(不是不能访问到对方的域,而是得不到响应,浏览器把对方的响应拦截)
  • 互相信任的域之间如果需要ajax访问, (比如前后端分离项目中,前端项目和后端项目之间),则需要额外的设置才可正常请求。

在这里插入图片描述

14.3解决方案

  • 允许其他域访问
  • 被访问方的Controller类上,添加注解@CrossOrigin
@RestController
@RequestMapping("/origin")
@CrossOrigin("http://localhost:8080")
public class OriginController {
 ......   
}
  • 携带对方cookie,使得session可用
  • 在访问方,ajax中添加属性: withCredentials: true
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>origin</title>
    <script src="${pageContext.request.contextPath}/js/jquery-2.1.0.js"></script>
</head>
<body>


    <input type="button" value="ajax" οnclick="cross_origin();">
    <input type="button" value="ajax2" οnclick="cross_origin2();">
    <script>

        function cross_origin(){
            $.ajax({
                type:"get",
                url:"http://localhost:9999/SpringMVC03/origin/test1",
                xhrFields: {
                    // 跨域携带cookie
                    withCredentials: true
                },
                success:function(ret){
                    console.log("ret:"+ret);
                }
            });
        }

        function cross_origin2(){
            $.ajax({
                type:"get",
                url:"http://localhost:9999/SpringMVC03/origin/test2",
                xhrFields: {
                    // 跨域携带cookie
                    withCredentials: true
                },
                success:function(ret){
                    console.log("ret:"+ret);
                }
            });
        }
    </script>
</body>
</html>

十五、SpringMVC执行流程

在这里插入图片描述

十六、Spring整合

16.1整合思路

此时项目中有两个工厂

  • web.xml文件是随着项目启动而加载的,里面配置基本都是servlet组件
    • DispatcherServlet 启动的springMVC工厂 == 负责生产C (Controller) 及springMVC自己的系统组件,(这是前端(核心)控制器,加载的是SpringMVC的配置文件:mvc.xml)
    • ContextLoaderlistener 启动的spring工厂 == 负责生产其他所有组件(比如service,dao及其相关),(这个类ContextLoaderlistener, 加载的是Spring的配置文件:applicationContext.xml)
    • springMVC的工厂会被设置为spring工厂的子工厂,可以随意获取spring工厂中的组件(jar包帮我们完成的)
    • 整合过程,就是累加:代码+依赖+配置。然后将service注入给controller即可

16.2整合技巧

  • 两个工厂不能有彼此侵入,即,生产的组件不能有重合。
  • 告知SpringMVC 哪些包中 存在被注解的类
    use-default-filters=true凡是被@Controller @Service @Repository注解的类, 都会被扫描,
    use- default- filters=false默认不扫描包内的任何类,只扫描include- filter中指定的类,只扫描被@Controller注解的类
  • mvc.xml :
    <!-- 注解扫描  下面这些配置是指SpringMVC在扫描com.wlw包下的注解时,只扫描有Controller注解的类(就是类名之上加了@Controller的类),其他的忽略-->
    <context:component-scan base-package="com.wlw" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
    </context:component-scan>
  • applicationContext.xml
    <!--扫描注解 下面这些配置是指Spring在扫描com.wlw包下的注解时,不扫描有Controller注解的类(就是类名之上加了@Controller的类,因为这样的类被SpringMVC扫描了,区分职责关系,可以对比mvc.xml文件),其他的都要扫描-->

<context:component-scan base-package="com.wlw" use-default-filters="true">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

16.3总结

在整合时,

  • xml配置文件与注解之间的选择:service层,controller层的代码中,注入dao,service、bean的声明

    建议用注解,service的实现类中的方法配置事务建议用注解;

  • SpringMVC配置文件(mvc.xml)中写的是:开启注解扫描、注解驱动、视图解析器、静态资源访问、拦截器、异常解析器等

  • Spring配置文件(applicationContext.xml)中写的是: DataSource、SqlSessionFactory、DAO接口 MapperScannerConfigurer、扫描注解、事务管理器、开启事务注解等

  • 整个项目有大致三个配置文件 web.xml 、 mvc.xml (SpringMVC配置文件)、 applicationContext.xml (Spring配置文件), 在web.xml文件中会声明另外两个配置文件地址,当tomcat启动时,会加载web.xml文件,进而加载另外两个配置文件,创建springmvc工厂(子工厂)与spring工厂(父工厂);

  • mvc.xml文件负责生产C (Controller) 及springMVC自己的系统组件,

  • 而applicationContext.xml文件中是整合了spring和mybatis两个配置文件的内容,除了数据源(DataSource)bean的配置,之前mybatis配置文件里写的内容基本都是写在(SqlSessionfactory)的bean中

  • web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- SpringMVC前端(核心)控制器
         1. 前端,接收所有请求
         2. 启动SpringMVC工厂  mvc.xml
         3. 负责springMVC流程调度
    -->
    <servlet>
        <servlet-name>mvc_wlw</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 局部参数,声明mvc配置文件位置-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:mvc.xml</param-value>
        </init-param>
        <!-- 懒汉式  饿汉式加载  可选-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc_wlw</servlet-name>
        <url-pattern>/</url-pattern>
        <!--<url-pattern>*.action</url-pattern>-->
    </servlet-mapping>

    <!-- 此过滤器会进行:request.setCharactorEncoding("utf-8");  对post请求中,中文参数乱码有效-->
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 启动Spring工厂 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
</web-app>
  • mvc.xml
<beans 	xmlns="http://www.springframework.org/schema/beans"
          xmlns:context="http://www.springframework.org/schema/context"
          xmlns:mvc="http://www.springframework.org/schema/mvc"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
							http://www.springframework.org/schema/beans/spring-beans.xsd
							http://www.springframework.org/schema/context
							http://www.springframework.org/schema/context/spring-context.xsd
							http://www.springframework.org/schema/mvc
							http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 注解扫描  下面这些配置是指SpringMVC在扫描com.wlw包下的注解时,只扫描有Controller注解的类(就是类名之上加了@Controller的类),其他的忽略-->
    <context:component-scan base-package="com.wlw" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:include-filter>
    </context:component-scan>

    <!-- 注解驱动 -->
    <mvc:annotation-driven>
        <!-- 安装FastJson,转换器 -->
        <mvc:message-converters>
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <!-- 声明转换类型:json (将java对象转成josn)-->
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!-- 视图解析器
        作用:1.捕获后端控制器的返回值="hello"
             2.解析: 在返回值的前后 拼接 ==> "/hello.jsp"
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/"></property>
        <!-- 后缀 -->
        <property name="suffix" value=".jsp"></property>
    </bean>

    <!-- 静态资源访问
      会额外的增加一个handler(就是Controller中的一个方法),且其requestMapping:  "/**" 可以匹配所有请求,但是优先级最低
      所以如果其他所有的handler都匹配不上,请求会转向 "/**" ,恰好,这个handler就是处理静态资源的
      处理方式:将请求转发到tomcat中名为default的Servlet
      RequestMapping  /*   /a   /b  /c   /dxxxx    /a/b     /**
     -->
    <mvc:default-servlet-handler/>

</beans>
  • applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--整合时,DAO放在工厂中(bean标签)是由MapperScannerConfigurer 来帮我们完成的 ,
        而MapperScannerConfigurer 又需要sqlSessionFactory支持 , sqlSessionFactory 又需要DataSource-->

    <!-- DataSource -->
    <!-- 导入文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--创建数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!--基本配置-->
        <property name="driverClassName" value="${jdbc.driverClass}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${jdbc.init}"/>
        <property name="minIdle" value="${jdbc.minIdle}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>

        <!-- 配置获取连接等待超时的时间 ms-->
        <property name="maxWait" value="60000"/>

        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>

        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>
    </bean>

    <!-- SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入连接池 -->
        <property name="dataSource" ref="dataSource"></property>
        <!-- (注册xxxDao-mapper文件)注入dao-mapper文件信息,如果映射文件和dao接口 同包且同名,则此配置可省略(要注意因为把mapper文件放在了dao文件夹下,所以要在pom.xml中更改maven编译规则)-->
        <property name="mapperLocations">
            <list>
                <value>classpath:com/wlw/dao/*.xml</value>
            </list>
        </property>
        <!-- 为实体类起别名 -->
        <property name="typeAliasesPackage" value="com.wlw.entity"></property>

        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <props>
                            <!-- 页号 调整到合理的值  0  max -->
                            <prop key="reasonable">true</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

    <!-- DAO接口 MapperScannerConfigurer-->
    <!-- mapperScannerConfigurer  管理DAO实现类的创建,并创建DAO对象,存入工厂管理,当执行完这个配置之后,在工厂中就会有一个id为userDAO 的bean,而这个bean就是UserDAO实现类的对象 -->
    <bean id="mapperScannerConfigurer9" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- dao接口所在的包  如果有多个包,可以用逗号或分号分隔 <property name="basePackage" value="com.a.dao,com.b.dao"></property>-->
        <property name="basePackage" value="com.wlw.dao"></property>
        <!-- 如果工厂中只有一个SqlSessionFactory的bean,此配置可省略 -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

    <!--扫描注解 下面这些配置是指Spring在扫描com.wlw包下的注解时,不扫描有Controller注解的类(就是类名之上加了@Controller的类,因为这样的类被SpringMVC扫描了,区分职责关系,可以对比mvc.xml文件),其他的都要扫描-->
    <context:component-scan base-package="com.wlw" use-default-filters="true">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 1. 引入一个事务管理器,其中依赖DataSource,借以获得连接,进而控制事务逻辑 -->
    <bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--开启事务注解  只针对 这个@Transactional 事务注解-->
    <tx:annotation-driven transaction-manager="tx"/>
</beans>
  • jdbc.properties
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_wlw?useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456
jdbc.init=1
jdbc.minIdle=1
jdbc.maxActive=3

在这里插入图片描述

整合:D:\Program Files\IDEAworkspace\projects\JavaWeb_high\SpringMVC04_SSM

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悬浮海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值