52.springMVC

SpringMVC

简介

mvc:指的是三层架构

m:model模型层

v:view视图层

c:controller控制层

​ springmvc是spring的后续产品,作用于控制层。

入门案例

1、创建maven的web项目

2、导入依赖

	<!--导入springmvc所需的依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.8.RELEASE</version>
    </dependency>
	<!--配置前端控制器时如果报错才配置-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>

3、web.xml中配置前端控制器

<!--
    如果不指定springmvc的配置文件名称和路径,此时web容器启动时,
    会自动到WEB-INF下去找springmvc的配置文件   ===><servlet-name>标签的值+"-servlet.xml"
    -->
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!--通过初始化参数改变springmvc项目的配置文件所在位置及配置文件名称-->
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!--如果springmvc项目中的url-pattern配置成/时,
    后续必须在springmvc的配置文件中进行相应的操作,
    否则就会导致静态资源无法访问   ====>静态资源映射
    -->
    <url-pattern>/</url-pattern>
  </servlet-mapping>

4、编写配置文件

处理器映射器、处理器适配器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.woniuxy.controller"/>

    <!--配置处理器映射器:用于解析用户请求-->
    <bean id="requestMappingHandlerMapping"
          class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>

    <!--配置处理器适配器:用于根据处理器映射器的解析结果匹配具体的业务控制器方法-->
    <bean id="requestMappingHandlerAdapter"
          class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>

    <!--配置视图解析器:将逻辑视图名解析为一个物理视图-->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

5、编写业务控制器

@Controller//被注解的类型会作为控制器注册到容器中
public class UserController {

    //用户发送的请求:   http://localhost:8080/login

    @RequestMapping("login")
    public String login(){
        System.out.println("执行了登录操作");
        return "main";//逻辑视图名
    }

}
入门案例分析

image-20211013155330863

静态资源映射

​ 如果dispatcherServlet的url-pattern配置的/,那么项目中所有的静态资源都将无法访问,需要去配置静态资源映射来解决此问题。

​ 在springmvc的配置文件中进行以下配置即可。

<!--
    静态资源映射
    请求URL   :http://localhost:8080/images/ring_5.jpg
    mapping  :用于匹配请求   /images/**  从请求URL中就匹配到了  /images/ring_5.jpg
    location :用于指定一个具体位置    /images/ring_5.jpg
    -->
    <mvc:resources mapping="/images/**" location="/images/"/>
    <mvc:resources mapping="/js/**" location="/js/"/>
    <mvc:resources mapping="/css/**" location="/css/"/>
    <mvc:resources mapping="/html/**" location="/html/"/>

绑定请求参数

1.基本数据类型和String
1.1.简单类型

​ 对于基本数据类型和String,spring提供了对应的类型转换器,只要传递的值能够被转换为处理器方法上的形参类型,那么spring会自动帮你实现转换的过程。如果传递的值不能够被转换为对应的类型,此时,会抛出400。

​ 对于字符串转换为日期,spring提供的类型转换器只能识别/连接符,不能识别其他连接符,就一定会抛出异常。此时需要自定义类型转换器来解决这个问题。

​ 自定义类型转换器需要实现一个spring提供的接口:converter

String2Date

package com.woniuxy.converter;

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

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

/**
 * 自定义类型转换器:将字符串转换为日期类型
 */
public class String2Date implements Converter<String, Date> {
    /**
     * 进行类型转换
     * @param source  :要被转换的数据
     * @return
     */
    @Override
    public Date convert(String source) {
        //实现转换逻辑

        Date date = null;
        String pattern1="[0-9]{4}-[0-9]{2}-[0-9]{2}";
        String pattern2="[0-9]{4}/[0-9]{2}/[0-9]{2}";

        try {
            if (source.matches(pattern1)) {
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
                date = format.parse(source);
            } else if (source.matches(pattern2)) {
                SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
                date = format.parse(source);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return date;
    }
}

配置文件

<!--将自定义类型转换器加入到spring的类型转换器集合中-->
    <bean id="conversionService2" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="com.woniuxy.converter.String2Date"/>
            </set>
        </property>
    </bean>

    <!--spring3.1版本之后,使用了<mvc:annotation-driven/>来替换处理器映射器和处理器适配器的配置,功能比单独配置处理器映射器和处理器适配器更强大-->
    <!--主动使用添加了自定义类型转换器的conversionService2-->
    <mvc:annotation-driven conversion-service="conversionService2"/>
1.2.同名参数的绑定

以数组的形式进行参数绑定,可以直接绑定进来

但是,如果以集合的方式进行绑定,此时要注意两点:

1、不能使用集合的接口类型(因为接口没有构造方法),只能使用集合的实现类,

2、在处理器方法形参上还需要加上@RequestParam注解,才可以完成参数绑定。

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/login" method="get">
        <table>
            <tr>
                <td>用户名</td>
                <td><input type="text" name="username"></td>
            </tr>
            <tr>
                <td>密码</td>
                <td><input type="text" name="password"></td>
            </tr>
            <tr>
                <td>年龄</td>
                <td><input type="text" name="age"></td>
            </tr>
            <tr>
                <td>出生日期</td>
                <td><input type="date" name="birthday"></td>
            </tr>
            <tr>
                <td>爱好</td>
                <td>
                    <input type="checkbox" name="hobbies" value="吃饭">吃饭
                    <input type="checkbox" name="hobbies" value="睡觉">睡觉
                    <input type="checkbox" name="hobbies" value="打豆豆">打豆豆
                </td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="立即登录"></td>
            </tr>
        </table>
    </form>
</body>
</html>

业务控制器

package com.woniuxy.controller;

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

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

@Controller//被注解的类型会作为控制器注册到容器中
public class UserController {

    //用户发送的请求:   http://localhost:8080/login
    @RequestMapping("login")
    //进行参数绑定时,需要保证处理器方法的形参名称与用户请求中附带的参数的key
    //hobbies可以直接使用数组来绑定请求参数,String[] hobbies
   	//集合则应该使用@RequestParam ArrayList<String> hobbies
    public String login(String username, String password,
                        Double age, Date birthday,
                        @RequestParam ArrayList<String> hobbies){
        System.out.println("执行了登录操作");
        System.out.println(username);
        System.out.println(password);
        System.out.println(++age);
        System.out.println(birthday);
        for (String hobby : hobbies) {
            System.out.println(hobby);
        }
        return "main";//逻辑视图名
    }


}

处理请求中文乱码

在web.xml中配置spring提供的字符编码过滤器

<!--配置字符编码过滤器:可以解决请求中文乱码问题-->
  <filter>
    <filter-name>charset</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>charset</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
2.javaBean

​ 当请求参数个数非常多时,在处理器方法上以形参方式进行绑定,会造成代码可读性降低。因此,当请求参数个数超过5个时,建议使用javaBean来完成参数绑定。

​ 要求:javaBean的属性名称必须和请求参数名称保持一致。

/**
 * 作用:用于绑定请求参数的。
 */
@Data
public class UserVO {
    String username;
    String password;
    Integer age;
    Date birthday;
    List<String> hobbies;
}

业务控制器

@Controller
public class UserController {

    @RequestMapping("login")
    public String login(UserVO userVO){
        System.out.println("执行了登录操作");
        System.out.println(userVO);
        return "main";
    }
}
4.获取Servlet API

​ 程序中要使用servlet api时,可以直接在处理器形参中使用对应的servlet api来进行绑定,此时可以直接在处理器方法内部使用对应的对象。

/**
 * 获取servlet-api
 * http://localhost:8080/register?username=tom
 * @return
 */
@RequestMapping("register")
public String register(HttpServletRequest request, HttpServletResponse response, HttpSession session){

    System.out.println(request);
    System.out.println(request.getParameter("username"));
    System.out.println(response);
    System.out.println(session);
    System.out.println(session.getServletContext());

    return "main";
}
5.restful风格参数绑定

​ http://localhost:8080/login/tom/111

​ 百度百科

/**
     * restful风格参数绑定   :路径传参
     * http://localhost:8080/query/tom/111
     * @return
     */
    @RequestMapping("query/{username}/{password}")
    public String query(@PathVariable String username, @PathVariable String password){
        System.out.println("执行了登录操作");
        System.out.println(username+":"+password);
        return "main";
    }
6.ajax传递的参数绑定
6.1 非json字符串

​ 与普通的参数绑定方式一致。

6.2 json字符串
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script type="text/javascript" src="/js/jquery-3.3.1.js"></script>
</head>
<body>
    <form id="myForm" action="/login" method="get">
        <table>
            <tr>
                <td>用户名</td>
                <td><input type="text" name="username" id="username"></td>
            </tr>
            <tr>
                <td>密码</td>
                <td><input type="text" name="password" id="password"></td>
            </tr>
            <tr>
                <td>年龄</td>
                <td><input type="text" name="age"></td>
            </tr>
            <tr>
                <td>出生日期</td>
                <td><input type="text" name="birthday"></td>
            </tr>
            <tr>
                <td>爱好</td>
                <td>
                    <input type="checkbox" name="hobbies" value="吃饭">吃饭
                    <input type="checkbox" name="hobbies" value="睡觉">睡觉
                    <input type="checkbox" name="hobbies" value="打豆豆">打豆豆
                </td>
            </tr>
            <tr>
                <td colspan="2"><input type="button" value="立即登录" id="btn"></td>
            </tr>
        </table>
    </form>
    <script>
        $(function () {
            $("#btn").click(function () {
                //如果ajax请求提交的数据不是json字符串,与其他的参数绑定方式完全一致
                //如果ajax请求提交的数据是一个json字符串,则处理方式完全不同
                //传递json字符串时,需要进行四个操作
                //1、导入jackson-databind(该依赖导入会同时导入jackson-core、jackson-annotations)
                //2、在处理器形参的javaBean前加@RequestBody
                //3、发送请求的方式只能是POST
                //4、发送请求时应指定请求的内容类型是application/json;charset=utf-8

                var user={
                    username:$("#username").val(),
                    password:$("#password").val()
                }


                $.ajax({
                    url:"/login",
                    data:JSON.stringify(user),
                    type:"POST",
                    contentType:"application/json;charset=utf-8",
                    dataType:"json",
                    success:function (respData) {
                        console.log(respData)
                    }
                })
            })
        })
    </script>
</body>
</html>

响应返回值

​ springmvc给用户的响应是通过处理器方法的返回值来决定的。

void:开发过程中基本不用。因为方法返回值为void时,该方法就没有return进行返回,此时会基于处理器方法上的@RequestMapping的值作为逻辑视图名去找视图解析器,并进行相应的处理。

/**
     * 一般不用
     * 方法无返回值,此时,会基于@RequestMapping("say")的值say作为逻辑视图名,
     * 去找视图解析器进行相应处理
     */
    @RequestMapping("say")
    public void say(){
        System.out.println("执行了say方法");
    }

object:

ModelAndView

/**
     * ModelAndView: 封装了模型和视图的一个类型
     * @return
     */
    @RequestMapping("query")
    public ModelAndView query(){
        User user = new User("tom", "111");
        ModelAndView mv = new ModelAndView();
        mv.addObject("user",user);//使用ModelAndView对象来保存数据,数据存放在requst域中
        mv.setViewName("main");//设置视图名

        return mv;
    }

普通object

	/**
     * 直接返回某个类型的对象,此时,还是将@RequestMapping("user")的值user作为逻辑视图名,
     * 去找视图解析器进行相应处理
     * 如果在处理器方法上加上@ResponseBody,则会将方法的返回值转换为son字符串并返回出去,
     * 不会再走视图解析器
     * @return
     */
    @RequestMapping("user")
    @ResponseBody//作用:将方法的返回值转换为json字符串并返回出去,不会再走视图解析器
    public User getUser(){
        return new User("tom","111");
    }

使用dto来完成统一返回格式

@Data
@AllArgsConstructor
public class Result<T> {

    private Boolean flag;//标识前端对后台发起的请求是否操作成功

    private String code;//设置一个业务状态码

    private String message;//用于描述当前操作的具体信息

    private T data;//用于保存返回给用户的实际数据

}


public class StatusCode {
    public static final String SUCCESS="20000";//表示业务执行成功的状态
    public static final String PASSWORDERROR="20007";//密码错误
    public static final String UNKOWNUSERNAME="20008";//账户尚未注册
}

业务控制器中

@Controller
public class StudentController {

    /**
     * dto   d: data   t:transfer  o:object    :用于统一返回格式
     *
     * vo    v:value   o:object   :用于封装用户传递的参数
     *
     */

    @GetMapping("student")
    @ResponseBody
    public Result query(){
        //表示去查询到了用户
        User user = new User("tom", "111");//是从数据库中查询得到的
        return new Result(true, StatusCode.SUCCESS,"查询用户成功",user);
    }

    @PutMapping("student")
    @ResponseBody
    public Result update(){
        try {
            System.out.println("执行了修改操作");
//            System.out.println(1/0);
            return new Result(true, StatusCode.SUCCESS,"修改用户成功",null);
        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false, StatusCode.PASSWORDERROR,"修改用户失败",null);
        }

    }

String:特殊的一种

逻辑视图 :返回的是逻辑视图时,会走视图解析器,最终去匹配一个物理视图,匹配上了就正常返回了该视图,如果没有匹配上,就返回404。

	@RequestMapping("login")
    public String login(){
        System.out.println("执行了登录操作");
        return "main";
    }

物理视图 : 返回的如果是物理视图时,必带关键字。forward(请求转发) redirect(重定向)

返回物理视图名:

	/**
     * forward:   默认值   请求转发
     * redirect:           重定向

     * 如果处理器方法返回值希望跳转到一个物理视图时,也不能不写关键字,
     *
     * 也就是说,如果处理器方法的返回值上不带关键字时,都会走视图解析器
     *
     * @return
     */
    @RequestMapping("update")
    public String update(){
        System.out.println("执行了update操作");
        return "forward:/main.jsp";
    }

返回另一个处理器方法的@RequestMapping的值

/**
     * ModelAndView: 封装了模型和视图的一个类型
     * @return
     */
    @RequestMapping("query")
    public ModelAndView query(String username){
        System.out.println(username);//null
        User user = new User("tom", "111");
        ModelAndView mv = new ModelAndView();
        mv.addObject("user",user);//使用ModelAndView对象来保存数据,数据存放在requst域中
        mv.setViewName("main");//设置视图名

        return mv;
    }


    /**
     * @return
     */
    @RequestMapping("update")
    public String update(){
        System.out.println("执行了update操作");
        return "forward:query";
    }

Restful风格接口设计

​ rest请求:

GET ====>查询操作

POST ====>新增操作

PUT ====>修改操作

DELETE ====>删除操作

​ restful风格就是基于rest请求去调用业务控制器中不同的数据接口。

​ 只是一种接口设计风格,而不是规范和规则,也就是说,不遵守该风格,程序是不会报错的。

​ 对于一份资源(user),在后台(控制器中)增删改查的操作,对该资源的映射值只会有一个(也就是说不管是执行增、删、改、查的哪种操作,@RequestMapping的值只会有一个)。

​ 要严格遵守restful风格进行接口设计时,会认为访问后台的都是一份一份的资源,因为@RequestMapping中的值只能出现名词,不能出现动词。因为restful风格认为对资源进行的操作应该由请求的提交方式来决定,而不是资源名称来决定。

设计案例

package com.woniuxy.controller;

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

/**
 * 该控制器操作应该是对teacher这个类型的资源
 * restful风格的设计,此时,增删改查4个方法的@RequestMapping的值只能有一个。
 */
@Controller
@RequestMapping("teacher")
public class TeacherController {

//    @RequestMapping(value = "teacher",method = RequestMethod.POST)
    @PostMapping("name")   //http://localhost:8080/teacher/name
    public String add(){
        System.out.println("add");
        return "redirect:/html/add.html";
    }

//    @RequestMapping(value = "teacher",method = RequestMethod.PUT)
    @PutMapping
    public String update(){
        System.out.println("update");
        return "redirect:/html/update.html";
    }

//    @RequestMapping(value = "teacher",method = RequestMethod.DELETE)
    @DeleteMapping
    public String delete(){
        System.out.println("delete");
        return "redirect:/html/delete.html";
    }

//    @RequestMapping(value = "teacher",method = RequestMethod.GET)
    @GetMapping
    public String query(){
        System.out.println("query");
        return "redirect:/html/query.html";
    }

}

幂等性:

对同一个数据接口进行多次调用,产生的结果不会破坏数据和发生数据更改。只要满足该条件,则认为该接口满足幂等性。

对于rest请求来说,除post请求以外,其余的都满足幂等性。

文件上传和文件下载

文件上传

​ springmvc将文件域(input标签)封装为一个MultipartFile对象,通过该对象即可完成文件的上传操作。底层的文件上传逻辑是通过commons-fileupload组件来完成的,所以,要实现文件上传,应该导入该依赖。

1、导入依赖

<dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.3</version>
    </dependency>

2、编写前端页面

<form action="/user/register" method="post" enctype="multipart/form-data">
        用户名:<input type="text" name="username"><br>
        密码:<input type="text" name="password"><br>
        上传文件:<input type="file" name="upload"><br>
        <input type="submit" value="立即提交">
    </form>

3、编写控制器上传接口

@RequestMapping("register")
    @ResponseBody
    public Result register(User user, MultipartFile upload) throws IOException {
        System.out.println(user);

        String path="D:\\files".replace("\\", File.separator);//上传文件的存储位置

        String randomStr = UUID.randomUUID().toString();

        String filename = upload.getOriginalFilename();//获取上传文件的原始名称
        String fileType=filename.substring(filename.lastIndexOf("."));

        String saveFileName=randomStr+fileType;//对上传文件的保存名称进行处理

        File saveFile = new File(path, saveFileName);

        upload.transferTo(saveFile);

        return new Result(true, StatusCode.SUCCESS,"上传成功",null);

        /**
         *
         *
         * 文件上传实现步骤:
         * 1、导入commons-fileupload依赖
         * 2、编写上传页面:表单的提交方法只能是post,enctype="multipart/form-data",文件域必须指定name属性
         * 3、编写接口,接口的形参MultipartFile来接收文件域的内容
         * 3.1指定文件存储路径
         * 3.2处理上传文件同名的问题
         * 3.3使用MultipartFile对象调用transferTo方法将内容传输到上传的文件中
         * 4、配置文件中配置CommonsMultipartResolver,id只能是multipartResolver
         * 在该bean指定上传文件的最大尺寸  maxuploadsize       #{}
         *
         *
         *
         */

    }

4、配置文件

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="#{100*1024*1024}"/>
    </bean>
文件下载

​ content-disposition :attachment(附件形式)

​ springmvc提供了一个ResponseEntity(响应实体),用于封装响应头、响应状态、文件内容。

下载测试页面

<a href="/user/download?filename=菜谱.txt">菜谱</a>

业务控制器中处理下载操作的接口

/**
     * 执行下载操作
     * @return
     */
    @RequestMapping("download")
    public ResponseEntity download(String filename) throws IOException {
        //文件内容
        String path="D:\\files".replace("\\", File.separator);

        File downloadFile = new File(path, filename);

        //使用commons-io组件中的api将文件内容读取到内存中
        byte[] bytes = FileUtils.readFileToByteArray(downloadFile);

        //处理文件名的中文乱码
        filename=URLEncoder.encode(filename,"UTF-8");

        //响应头信息
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Disposition","attachment;filename="+filename);

        //响应状态码
        HttpStatus status = HttpStatus.OK;

        //创建responseEntity对象时,需要提供三个参数:文件内容、响应头信息、响应状态码
        ResponseEntity responseEntity = new ResponseEntity(bytes,headers,status);
        return responseEntity;
    }

异常处理

自定义异常处理

​ springmvc提供了HandlerExceptionResolver接口以提供异常处理。只需要创建一个类实现该接口,就可以完成springmvc控制器接口中的异常处理。

测试页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="/js/jquery-3.3.1.js" type="text/javascript"></script>
</head>
<body>
    <form action="/user/login" method="get">
        <input type="submit" value="测试普通请求"/>
    </form>
    <input type="button" id="btn" value="测试ajax请求"/>
    <script>
        $(function () {
            $("#btn").click(function () {
                $.ajax({
                    url:"/user/login",
                    type:"get",
                    success:function (resp) {
                        console.log(resp)
                    }
                })
            })
        })
    </script>
</body>
</html>

自定义异常处理类

package com.woniuxy.exception;

import com.alibaba.fastjson.support.spring.FastJsonJsonView;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;

/**
 * 自定义异常处理器:
 * springmvc提供HandlerExceptionResolver接口,只要创建类实现该接口就可以完成自定义的异常处理了。
 */
@Component
public class MyExceptionHandler implements HandlerExceptionResolver {
    /**
     * 自定义异常处理的方法
     * @param request  :请求对象
     * @param response :响应对象
     * @param handler  :异常处理对象
     * @param ex       :异常对象
     * @return         :modelandview
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        //建议在这里将异常信息打印出来,便于排错
        ex.printStackTrace();
        ModelAndView mv = new ModelAndView();
        //先判断用户提交的请求是否是ajax请求
        if (StringUtils.hasLength(request.getHeader("X-Requested-With"))) {//ajax
            //借助于fastJson来产生一个jsonView,将这个视图返回js域
            FastJsonJsonView jsonView = new FastJsonJsonView();
            HashMap<String, String> map = new HashMap<>();
            map.put("message","系统繁忙,请稍候再使用");
            jsonView.setAttributesMap(map);
            mv.setView(jsonView);
        }else{//非ajax
            mv.setViewName("error");
        }
        return mv;
    }
}

业务控制器接口

@Controller
@RequestMapping("user")
public class UserController {
    @RequestMapping("login")  //http://localhost:8080/user/login
    public Result login(){
        System.out.println(1/0);
        return new Result(true, StatusCode.SUCCESS,"登录成功",null);
    }
}
全局异常处理

​ 使用全局异常处理可以针对性的去处理某个具体的异常。

​ 借助于spring提供的两个注解:@RestControllerAdvice @ExceptionHandler就可以完成全局异常配置。

全局异常总类:

package com.woniuxy.exception;

import com.woniuxy.dto.Result;
import com.woniuxy.dto.StatusCode;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {


    /**
     * 处理空指针异常
     * @return
     */
    @ExceptionHandler(NullPointerException.class)
    public Result handleNullPointerException(){
        return new Result(false, StatusCode.ERROR,"空指针异常",null);
    }

    /**
     * 处理算术异常
     * @return
     */
    @ExceptionHandler(ArithmeticException.class)
    public Result handleArithmeticException(){
        return new Result(false, StatusCode.ERROR,"算术异常",null);
    }

    /**
     * 兜底操作:处理所有异常
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e){
        e.printStackTrace();
        return new Result(false, StatusCode.ERROR,"服务器异常",null);
    }

}

拦截器

​ springmvc中有一个很重要的控件叫拦截器,该控件作用于前端控制器和业务控制器之间。拦截器的作用有点类似于过滤器。

​ springmvc提供了一个接口HandleInterceptor,实现该接口就可以创建一个拦截器,拦截器中有3个方法,preHandle()、postHandle()、afterCompletion(),这三方法决定了拦截器的运行过程。

preHandle():控制器方法执行之前执行

postHandle():控制器方法执行之后执行

afterCompletion():控制器方法执行之后,且渲染了页面之后再执行

​ 如果项目中存在多个拦截器,拦截器的执行顺序以配置文件中的配置顺序来决定。

​ 在配置文件中可以通过mvc:interceptors标签来配置拦截器,来指定拦截器的行为。

拦截器

FirstInterceptor

package com.woniuxy.interceptor;

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

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

/**
 * 拦截器:
 * 1、创建拦截器
 */
public class FirstInterceptor implements HandlerInterceptor {
    /**
     * 在业务控制器的接口方法执行之前执行的方法
     * @param request
     * @param response
     * @param handler
     * @return  方法返回值是一个boolean,如果返回true,放行,如果返回false,拦截
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("FirstInterceptor preHandle...");
        return true;
    }

    /**
     * 在业务控制器的接口方法执行之后再执行的方法
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("FirstInterceptor postHandle...");
    }

    /**
     * 在业务控制器的接口方法执行之后,并且进行视图渲染之后再执行的方法
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("FirstInterceptor afterCompletion...");
    }
}

SecondInterceptor

package com.woniuxy.interceptor;

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

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

/**
 * 拦截器:
 * 1、创建拦截器
 */
public class SecondInterceptor implements HandlerInterceptor {
    /**
     * 在业务控制器的接口方法执行之前执行的方法
     * @param request
     * @param response
     * @param handler
     * @return  方法返回值是一个boolean,如果返回true,放行,如果返回false,拦截
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("SecondInterceptor preHandle...");
        return true;
    }

    /**
     * 在业务控制器的接口方法执行之后再执行的方法
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("SecondInterceptor postHandle...");
    }

    /**
     * 在业务控制器的接口方法执行之后,并且进行视图渲染之后再执行的方法
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("SecondInterceptor afterCompletion...");
    }
}

配置文件

<!--
    配置拦截器:
    -->
    <mvc:interceptors>
        <!--指定拦截的规则-->
        <mvc:interceptor>
            <!--指定要被拦截的路径  :配置黑名单
                http://localhost:8080/user/login
            -->
            <mvc:mapping path="/user/**"/>
            <!--指定不被拦截的路径  :配置白名单-->
            <mvc:exclude-mapping path="/user/login"/>
            <!--当mvc:mapping的path从请求URL中匹配到具体的路径之后,
            就会使用具体的一个类型来处理被拦截的路径-->
            <bean class="com.woniuxy.interceptor.FirstInterceptor"/>
        </mvc:interceptor>
        <!--指定拦截的规则-->
        <mvc:interceptor>
            <!--指定要被拦截的路径  :配置黑名单
                http://localhost:8080/user/login
            -->
            <mvc:mapping path="/user/**"/>
            <!--指定不被拦截的路径  :配置白名单-->
            <mvc:exclude-mapping path="/user/login"/>
            <!--当mvc:mapping的path从请求URL中匹配到具体的路径之后,
            就会使用具体的一个类型来处理被拦截的路径-->
            <bean class="com.woniuxy.interceptor.SecondInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
登录验证

拦截器

package com.woniuxy.interceptor;

import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.HandlerInterceptor;

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

/**
 * 登录验证的拦截器
 */
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 在preHandle()中编写登录验证的逻辑
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object loginUser = request.getSession().getAttribute("loginUser");

        if (ObjectUtils.isEmpty(loginUser)) {//未登录状态
            response.sendRedirect("/html/login.html");
            return false;
        }else{//登录状态
            return true;
        }
    }
}

配置文件

<!--
    配置拦截器:
    -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/teacher/login"/>
            <mvc:exclude-mapping path="/teacher/register"/>
            <mvc:exclude-mapping path="/html/login.html"/>
            <mvc:exclude-mapping path="/html/register.html"/>
            <mvc:exclude-mapping path="/html/index.html"/>
            <bean class="com.woniuxy.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

参数校验

参数校验分为两个部分:

前端:验证的参数的传递格式

后台:针对非正常的向后台发送的请求,进行参数校验

参数校验使用的是JSR303校验方式,官方推荐的实现是hibernate validator。

image-20211018104602867

image-20211018104621946

1、导入依赖

	<dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.1.5.Final</version>
    </dependency>

2、在参数对象的成员变量上使用对应的注解来指定校验规则

package com.woniuxy.vo;

import lombok.Data;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.Pattern;

@Data
public class UserVo {

    @Length(min = 6,max = 20,message = "用户名字段的长度必须在6-20位之间")
    private String username;

    @Min(value = 1,message = "年龄不能小于1岁")
    @Max(value = 120,message = "年龄不能大于120岁")
    private Integer age;

    @Pattern(regexp = "[0-9a-zA-Z_]{6,12}@[0-9a-zA-Z_]{2,8}(\\.[a-z]{2,10}){1,2}",message = "邮箱格式不正确")
    private String email;

}

3、业务控制器的接口必须使用javaBean来接收参数,在参数对象前加@Valid注解,表示进行参数绑定时必须做参数校验,校验结果保存在一个紧临参数对象之后的BindingResult对象中。

/**
     * 在接收参数的javaBean前加一个@Valid注解,表示进行参数绑定时,要经过校验
     * bindingResult对象必须紧临接收参数的javaBean,用于接收参数校验结果
     * @param userVo
     * @return
     */
    @RequestMapping("login")
    public String login(@Valid UserVo userVo, BindingResult bindingResult){

        if (bindingResult.hasErrors()) {//如果校验结果中出现了错误,则表示参数校验失败
            List<ObjectError> errors = bindingResult.getAllErrors();//获取参数校验出现的错误
            errors.forEach(error->{
                System.out.println(error.getDefaultMessage());
            });
        }

        System.out.println("执行了登录操作");
        return "main";
    }

项目中参数校验的使用

1、自定义异常:该异常在参数校验未通过时抛出

/**
 * 用于参数校验失败时抛出的异常
 */
public class ParameterException extends Exception {
}

2、业务控制器的接口方法中,如果bindingResult.hasErrors()方法返回值为true时,抛出自定义异常

	@RequestMapping("login")
    public String login(@Valid UserVo userVo, BindingResult bindingResult) throws ParameterException {

        if (bindingResult.hasErrors()) {//如果校验结果中出现了错误,则表示参数校验失败
            throw new ParameterException();
        }

        System.out.println("执行了登录操作");
        return "main";
    }

3、在全局异常总类中,对自定义异常进行处理。

@RestControllerAdvice
public class GlobalExceptionHandler {
	//处理自定义异常
    @ExceptionHandler(ParameterException.class)
    public Result handleParameterException(){
        return new Result(false,"20010","参数异常",null);
    }

    @ExceptionHandler(Exception.class)
    public Result handleException(){
        return new Result(false,"20010","服务器异常",null);
    }
}

SSM整合(spring+springmvc+mybatis)

父子容器

​ 进行ssm整合时,可以让spring和springmvc使用同一个配置文件,但此时一个配置文件要服务于两个框架,会导致配置文件职责不明确,代码可读性差的问题出现,因此实现开发过程中,我们会将spring部分的操作放在spring的配置文件中,springmvc部分的操作配置在springmvc的配置文件中。

​ 如果以两个配置文件来分别描述两个框架的内容,此时需要注意父子容器(spring容器为父容器,springmvc为子容器)带来的问题:父子容器都去扫描了相同的包下的注解,此时不管是controller也好,service也好,会同时在父子容器中各存在一份,此时如果项目中使用了声明式事务,则声明式事务会失效。

​ 因此,在SSM整合时,需要去考虑父子容器带来的问题。我们需要让spring扫描所有的注解,但排除Controller注解不扫,让springmvc只扫描Controller注解,其他注解不扫,此时即可保证项目中所有被注解的类在容器中只有一个对象,从而可以解决声明式事务失效的问题。

整合
整合之XML
spring整合mybatis

applicationContext.xml

<!--表示spring应该扫描com.woniuxy包下的所有注解,但把@Controller注解给排除掉,让springmvc去扫-->
    <context:component-scan base-package="com.woniuxy">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
spring整合springmvc

springmvc.xml

	<!--表示指定springmvc只去扫描com.woniuxy.controller下的@Controller注解
    注意:base-package必须指向controller层,如果配置成com.woniuxy,springmvc一样的也会扫描对应包下的所有注解
    -->
    <context:component-scan base-package="com.woniuxy.controller">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
web.xml配置父子容器启动顺序

web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <!--spring提供了监听器,监听servletContext对象的创建,此时会读取context-param配置的初始化参数,从而读取spring的配置文件生成spring容器,保证了父容器创建在子容器之前-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

完整案例:实现转账业务,使用了声明式事务,使用了全局异常处理。

model层

@Data
public class Account {
    private Integer id;
    private String username;
    private Double balance;
}

mapper层

public interface AccountMapper {
    @Select("select * from account where username=#{username}")
    Account findByUserName(String username);

    @Update("update account set balance=#{balance} where username=#{username}")
    void updateAccount(Account account);
}

service层

public interface AccountService {
    Account findByUserName(String username);
    void transfer(String source,String target,Double money);
}

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Resource
    private AccountMapper accountMapper;
    @Override
    public Account findByUserName(String username) {
        return accountMapper.findByUserName(username);
    }

    @Override
    public void transfer(String source, String target, Double money) {
        Account sourceAccount = accountMapper.findByUserName(source);
        Account targetAccount = accountMapper.findByUserName(target);

        if (sourceAccount.getBalance() > money) {
            sourceAccount.setBalance(sourceAccount.getBalance()-money);
            targetAccount.setBalance(targetAccount.getBalance()+money);

            accountMapper.updateAccount(sourceAccount);
            System.out.println(1/0);
            accountMapper.updateAccount(targetAccount);

        }else{
            throw new RuntimeException("余额不足");
        }

    }
}

controller层

@RestController
@RequestMapping("account")
public class AccountController {
    @Resource
    private AccountService accountService;

    @RequestMapping("find")
    public Result find(String username){
        Account account = accountService.findByUserName(username);
        return new Result(true,"20000","查询用户成功",account);
    }

    @RequestMapping("transfer")
    public Result transfer(AccountVO accountVO){
        accountService.transfer(accountVO.getSource(),accountVO.getTarget(),accountVO.getMoney());
        return new Result(true,"20000","转账成功",null);
    }


}

参数对象VO

@Data
public class AccountVO {
    private String source;
    private String target;
    private Double money;
}

数据传输对象DTO

@Data
@AllArgsConstructor
public class Result<T> {
    private Boolean flag;
    private String code;
    private String message;
    private T data;
}

全局异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ArithmeticException.class)
    public Result handleArithmeticException(){
        return new Result(false,"20009","算术异常",null);
    }


    @ExceptionHandler(Exception.class)
    public Result handleException(){
        return new Result(false,"20010","服务器异常",null);
    }
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd 
                           http://www.springframework.org/schema/context 
                           https://www.springframework.org/schema/context/spring-context.xsd 
                           http://www.springframework.org/schema/tx 
                           http://www.springframework.org/schema/tx/spring-tx.xsd 
                           http://www.springframework.org/schema/aop 
                           https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--表示spring应该扫描com.woniuxy包下的所有注解,但把@Controller注解给排除掉,让springmvc去扫-->
    <context:component-scan base-package="com.woniuxy">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>


    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql:///bank?characterEncoding=utf-8&amp;useSSL=false"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.woniuxy.mapper"/>
    </bean>

    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
        <tx:attributes>
            <tx:method name="*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="find" read-only="true" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.woniuxy.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
    </aop:config>
    
    
</beans>

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd 
                           http://www.springframework.org/schema/context 
                           https://www.springframework.org/schema/context/spring-context.xsd 
                           http://www.springframework.org/schema/mvc 
                           https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!--表示指定springmvc只去扫描com.woniuxy.controller下的@Controller注解
    注意:base-package必须指向controller层,如果配置成com.woniuxy,springmvc一样的也会扫描对应包下的所有注解
    -->
    <context:component-scan base-package="com.woniuxy.controller">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <mvc:annotation-driven/>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/html/"/>
        <property name="suffix" value=".html"/>
    </bean>

    <mvc:resources mapping="/html/**" location="/html/"/>

</beans>

web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <!--spring提供了监听器,监听servletContext对象的创建-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  
  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  
  
</web-app>

测试页面

查询

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/account/find" method="get">
        <input type="text" name="username">
        <input type="submit" value="立即查询">
    </form>
</body>
</html>

转账

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>转账页面</title>
</head>
<body>
<form action="/account/transfer" method="get">
    转出账户:<input type="text" name="source"><br>
    转入账户:<input type="text" name="target"><br>
    转出金额:<input type="text" name="money"><br>
    <input type="submit" value="立即转账">
</form>
</body>
</html>
整合之注解
spring整合mybatis

使用配置类替换spring的配置文件

package com.woniuxy.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

/**
 * spring的配置类,用于代替applicationContext.xml
 */
@Configuration
@ComponentScan(basePackages = {"com.woniuxy"},useDefaultFilters = true,
    excludeFilters = {
        @ComponentScan.Filter(
                type = FilterType.ANNOTATION,
                classes = Controller.class
        )
    }
)//指定spring要去扫描哪些注解,排除了@Controller注解
@MapperScan("com.woniuxy.mapper")//用于替换mapperScannerConfigurer
@EnableTransactionManagement
@PropertySource(value = "classpath:jdbc.properties",ignoreResourceNotFound = true)
public class SpringConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        System.out.println("DataSource init...");
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        System.out.println("SqlSessionFactoryBean init...");
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource){
        System.out.println("DataSourceTransactionManager init...");
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }

}

spring整合springmvc

注意:先创建maven的web项目,再将webapp目录删掉。

使用配置类来替换springmvc的配置文件

package com.woniuxy.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@ComponentScan(basePackages = {"com.woniuxy.controller"},useDefaultFilters = false,
    includeFilters = {
        @ComponentScan.Filter(
                type = FilterType.ANNOTATION,
                classes = Controller.class
        )
    }
)//指定springmvc去扫描哪个包下的注解,只扫描了@Controller
@EnableWebMvc//
public class SpringMvcConfig implements WebMvcConfigurer {
    /**
     * 静态资源映射
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        System.out.println("addResourceHandlers");
        //<mvc:resources  mapping="/html/**"  location="/html/"/>
        registry.addResourceHandler("/html/**").addResourceLocations("classpath:/html/");
    }

    /**
     * 视图解析器配置
     * @param registry
     */
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        System.out.println("configureViewResolvers");
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/html/");
        resolver.setSuffix(".html");
        registry.viewResolver(resolver);
    }
}

替换web.xml

使用配置类来替换web.xml

package com.woniuxy.config;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.*;
import java.util.EnumSet;

/**
 * 该类用于替代web.xml
 * 该类的onStartup()中需要进行三个配置:
 * 1、代替监听器去读取spring的配置类
 * 2、代替前端控制器去读取springmvc的配置类
 * 3、注册字符编码过滤器
 */
public class WebConfig implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {

        /*************************spring相关配置**************************/
        //创建spring的web容器
        AnnotationConfigWebApplicationContext spring = new AnnotationConfigWebApplicationContext();
        //注册spring的配置类
        spring.register(SpringConfig.class);
        //使用监听器监听spring的web容器的创建
        servletContext.addListener(new ContextLoaderListener(spring));

        /*************************springmvc相关配置**************************/
        //创建springmvc的web容器
        AnnotationConfigWebApplicationContext springmvc =new AnnotationConfigWebApplicationContext();
        //注册springmvc的配置类
        springmvc.register(SpringMvcConfig.class);
        //创建前端控制器对象
        ServletRegistration.Dynamic dispatcherServlet = servletContext.addServlet("dispatcherServlet",
                new DispatcherServlet(springmvc));
        //设置前端控制器的初始化时机
        dispatcherServlet.setLoadOnStartup(1);
        //设置前端控制器匹配的url-pattern
        dispatcherServlet.addMapping("/");

        /*************************字符编码过滤器配置**************************/
        FilterRegistration.Dynamic charset = servletContext.addFilter("charset",
                new CharacterEncodingFilter("UTF-8"));

        EnumSet<DispatcherType> dispatcherTypes = EnumSet.noneOf(DispatcherType.class);

        dispatcherTypes.add(DispatcherType.REQUEST);
        dispatcherTypes.add(DispatcherType.FORWARD);

        charset.addMappingForUrlPatterns(dispatcherTypes,true,"/*");


    }
}

跨域

方法一:在控制层类上 @CrossOrigin

方法二:在SpringMVC注解开发上 重写addCorsMappings(CorsRegistry registry)

@Override
public void addCorsMappings(CorsRegistry registry){
    registry.addMapping("/**")    //匹配请求的url
            .allowedHeaders("*")  //允许那些头能够跨域
        	.allowedOrigins("*")  //允许哪些源能够跨域
        	.allowedMethods("*")  //允许哪些提交方法可以跨域访问
        	.allowCredentials(true) //允许请求中附带cookie信息
        	.maxAge(3600);         //跨域访问超时时间
}

de
public void onStartup(ServletContext servletContext) throws ServletException {

    /*************************spring相关配置**************************/
    //创建spring的web容器
    AnnotationConfigWebApplicationContext spring = new AnnotationConfigWebApplicationContext();
    //注册spring的配置类
    spring.register(SpringConfig.class);
    //使用监听器监听spring的web容器的创建
    servletContext.addListener(new ContextLoaderListener(spring));

    /*************************springmvc相关配置**************************/
    //创建springmvc的web容器
    AnnotationConfigWebApplicationContext springmvc =new AnnotationConfigWebApplicationContext();
    //注册springmvc的配置类
    springmvc.register(SpringMvcConfig.class);
    //创建前端控制器对象
    ServletRegistration.Dynamic dispatcherServlet = servletContext.addServlet("dispatcherServlet",
            new DispatcherServlet(springmvc));
    //设置前端控制器的初始化时机
    dispatcherServlet.setLoadOnStartup(1);
    //设置前端控制器匹配的url-pattern
    dispatcherServlet.addMapping("/");

    /*************************字符编码过滤器配置**************************/
    FilterRegistration.Dynamic charset = servletContext.addFilter("charset",
            new CharacterEncodingFilter("UTF-8"));

    EnumSet<DispatcherType> dispatcherTypes = EnumSet.noneOf(DispatcherType.class);

    dispatcherTypes.add(DispatcherType.REQUEST);
    dispatcherTypes.add(DispatcherType.FORWARD);

    charset.addMappingForUrlPatterns(dispatcherTypes,true,"/*");


}

}


#### 跨域

方法一:在控制层类上 @CrossOrigin

方法二:在SpringMVC注解开发上 重写addCorsMappings(CorsRegistry registry)

```java
@Override
public void addCorsMappings(CorsRegistry registry){
    registry.addMapping("/**")    //匹配请求的url
            .allowedHeaders("*")  //允许那些头能够跨域
        	.allowedOrigins("*")  //允许哪些源能够跨域
        	.allowedMethods("*")  //允许哪些提交方法可以跨域访问
        	.allowCredentials(true) //允许请求中附带cookie信息
        	.maxAge(3600);         //跨域访问超时时间
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值