【Java核心技术】springmvc框架

SpringMVC

核心流程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.客户端发送请求

2.DispatcherServlet统一接收请求,需要维护一个Spring容器(ApplicationContext),使用init方法完成ApplicationContext的初始化,首先需要看servletContext中是否已有容器,如果有就直接用,如果没有就初始化(有可能会在listener中维护),容器维护Controller组件,这些组件中包含Handler方法

3.访问到doGet和doPost方法,经过doService然后再合并到doDispatch中

4.由doDispatch来获得url,通过HandlerMapping建立映射关系

5.经过HandlerAdapter,最终执行到HandlerMethod(容器中的组件的方法)

init方法

public final void init() throws ServletException {

    this.initServletBean();
}
protected final void initServletBean() throws ServletException {

    try {
        this.webApplicationContext = this.initWebApplicationContext();
    }

}

WebApplicationContext也是容器,是ApplicationContext的子接口;还增加对Web应用的支持,在WebApplicationContext中可以使用ServletContext

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

doDispatch

分发请求

protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}

protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.doService(request, response);
}
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.doDispatch(request, response);
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

    HandlerExecutionChain mappedHandler = null;
    try {
        try {
            ModelAndView mv = null;
            Object dispatchException = null;

            try {
				// HandlerMapping通过url获得对应的Handler方法
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }
				// 适配器HandlerAdapter
                HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
				// 执行Handler方法 → 根据HandlerMapping的执行结果去执行的
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                this.applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception var20) {
                dispatchException = var20;
            } catch (Throwable var21) {
                dispatchException = new NestedServletException("Handler dispatch failed", var21);
            }
			// 处理执行后的结果
            this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        } catch (Exception var22) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
        } catch (Throwable var23) {
            this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
        }

    } finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            if (mappedHandler != null) {
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            }
        } else if (multipartRequestParsed) {
            this.cleanupMultipart(processedRequest);
        }

    }
}

创建一个完整的SpringMVC应用

引入依赖

spring-web、spring-webmvc、spring相关依赖(5+1)

servlet-api(provided)

jackson-annotations、core、databind(json)

只需要引入标红的即可

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>3.0-alpha-1</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.15.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.4</version>
</dependency>

配置DispatcherServlet

<?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">
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <!--全局(根路径下的jsp → 类似于html 👉 会将jsp编译为Servlet)-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

还需要提供Spring的配置文件所在的位置

public void setContextConfigLocation(@Nullable String contextConfigLocation) {
    this.contextConfigLocation = contextConfigLocation;
}

在servlet标签中使用init-param标签 👉 set方法

<init-param>
    <!--set方法是谁 👉 setContextConfigLocation-->
    <param-name>contextConfigLocation</param-name>
    <!--set方法的形参是什么-->
    <param-value>classpath:application.xml</param-value>
</init-param>

配置文件

<context:component-scan base-package="com.cskaoyan"/>
<!--springmvc的注解驱动 👉 类型转换、参数校验、Json-->
<mvc:annotation-driven/>

运行流程

访问hello请求进入了helloController里的hello方法

我们打断点查看doDispatch方法的流程,断点位置如下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

搭建应用的步骤

1.pom.xml→3行依赖

2.web.xml→创建模板

3.application.xml→创建模板

常见的依赖:

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
</properties>
<packaging>war</packaging>
<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>3.0-alpha-1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.15.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.4</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.7</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.6</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.15.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.7</version>
    </dependency>
</dependencies>

@RequestMapping

建立URL和Handler方法之间的映射关系

与EE中的Servlet类似,可以处理分发过来的请求

// 可以窄化请求,可以写在类上或方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";

    // value属性是字符串数组,可以映射多个URL,也可以使用通配符
    @AliasFor("path")
    String[] value() default {};

    @AliasFor("value")
    String[] path() default {};

    // 请求方法限定 → 关系是or,引申@GetMapping、@PostMapping
    RequestMethod[] method() default {};
	// 请求参数限定 → 关系是and
    String[] params() default {};
	// 请求头限定 → 关系是and
    String[] headers() default {};
	// Content-Type请求头的值 → 关系是and
    String[] consumes() default {};
	// Accept请求头的值 → 关系是and
    String[] produces() default {};
}

属性值中有s的代表的就是and的关系

***URL路径映射 value属性 → String[]

value属性是字符串数组

多个URL映射到同一个Handler方法上

/index 、/home

/hello、/helloworld

//注解的属性值为数组,如果数组中只有一个值,可以省略掉{}
@RequestMapping({"hello", "helloworld"})
@ResponseBody
public String hello() {
    return "hello world 2.0";
}

窄化请求

将请求中共有的部分提取到类上,达到窄化的效果

/user/login

/user/register

/user/modify

最终方法中映射的url就是:类上的@RequestMapping的value属性+方法上的@RequestMapping的value属性

分隔符会自动补充

@Controller
@RequestMapping("user")
public class UserController {

    //@RequestMapping("user/login")
    @RequestMapping("login")  //  → user/login
    @ResponseBody
    public String login() {
        return "login";
    }
}

使用通配符*

/goodbye/songge

/goodbye/ligenli

/goodbye/xueqie

/goodbye/nanfeng

/goodbye/*

@RequestMapping("goodbye/*")
@ResponseBody
public String goodbye() {

    return "byebye";
}

请求方法限定 method属性 → RequestMethod[]

限定了Handler方法只能处理特定的请求方法的请求

两个方法之间是or的关系

@Controller//("method")
@RequestMapping("method")
public class MethodController {

    @RequestMapping(value = "get",method = RequestMethod.GET) //method/get
    @ResponseBody
    public String methodGet() {
        return "Method GET";
    }

    @RequestMapping(value = "post",method = RequestMethod.POST)
    @ResponseBody
    public String methodPost() {
        return "Method POST";
    }

    //增加两个值,这两个值之间的关系 → or → 也就是满足其中一项即可
    @RequestMapping(value = "double",method = {RequestMethod.GET,RequestMethod.POST})
    @ResponseBody
    public String methodDouble() {
        return "Method double";
    }

}	

引申注解

@GetMapping、@PostMapping

增强了方法限定的功能

@RequestMapping(
    method = {RequestMethod.GET}
)
public @interface GetMapping {}
@RequestMapping(
    method = {RequestMethod.POST}
)
public @interface PostMapping {}

使用注解改造

//@RequestMapping(value = "get",method = RequestMethod.GET) //method/get
@GetMapping("get")
@ResponseBody
public String methodGet() {
    return "Method GET";
}

//@RequestMapping(value = "post",method = RequestMethod.POST)
@PostMapping("post")
@ResponseBody
public String methodPost() {
    return "Method POST";
}

请求参数限定 params属性 → String[]

限定的是请求参数要有哪一些,只能多,不能少

//params → 多个值是and的关系,全都要
@RequestMapping(value = "register",params = {"username","password"})
@ResponseBody
public String register() {
    return "register";
}

如果状态码出现400,那么一定要检查请求参数的封装有没有出问题

请求头限定 headers属性 → String[]

限定的是请求头要有哪一些

//多个值之间的关系是and,全都要
@RequestMapping(value = "limit",headers = {"aaa","bbb"})
@ResponseBody
public String headerLimit() {
    return "header limit";
}

如果不符合要求,这里报的是404

consumes → Content-Type

限定的值Content-Type这个请求头对应的值,值的格式是xxx/xxx

@RequestMapping(value = "consumes", consumes = "aaa/bbb")
@ResponseBody
public String contentTypeLimit() {
    return "ContentType limit";
}

produces → Accept

限定的Accept这个请求头对应的值,值的格式是xxx/xxx

@RequestMapping(value = "produces", produces = "ccc/ddd")
@ResponseBody
public String acceptLimit() {
    return "Accept limit";
}

Handler方法的返回值

ModelAndView

单体应用中会去使用的结果:里面包含了视图信息,以及数据信息

@RequestMapping("hello") //没有写@ResponseBody
public ModelAndView hello() {
    //想要访问webapp路径下hello.jsp 并且给里面的username赋值
    ModelAndView modelAndView = new ModelAndView();
    // 通过ModelAndView要设定访问视图名为hello.jsp
    modelAndView.setViewName("/hello.jsp");
    // 通过ModelAndView要设定username对应的值
    modelAndView.addObject("username", "songge");

    return modelAndView;
}

String

没有@ResponseBody:把返回值的字符串作为ModelAndView的viewName

有@ResponseBody:直接响应字符串

//返回值字符串作为视图名
@RequestMapping("hello2")
public String hello2(Model model) {
    //相当于
    //ModelAndView modelAndView = new ModelAndView();
    //modelAndView.setViewName("/hello.jsp");
    model.addAttribute("username", "ligenli"); //和上面的hello方法做的事情是一样的
    return "/hello.jsp";
}

***响应Json

  1. jackson依赖
  2. mvc:annotation-driven
  3. Handler方法返回值写为需要转换为Json字符串的实例,即提供一个对象,@ResponseBody

这里要注意:返回的实例要提供无参构造方法和get\set方法

@RequestMapping("hello/json")
@ResponseBody
public User helloJson() {
    User user = new User();
    user.setUsername("松哥");
    user.setPassword("李艮隶");
    return user;
}

引申注解:@RestController = @ResponseBody + @Controller

意味着该类下所有的方法响应的都是Json(该类不响应视图)

//@Controller
//@ResponseBody
@RestController
public class JsonController {
}

这里会由Handler自动将实例转换为json数据

Handler方法的形参

主要做的是请求参数的封装,请求参数使用Handler方法的形参来封装

***请求参数接收

直接接收

在形参中直接接收:请求的参数名必须和Handler方法的形参名一致

字符串

任何参数都可以直接用字符串来接收

// 直接接收请求参数
@RequestMapping("register")
public BaseRespVo register(String username,String password,String age,String gender) {
    return BaseRespVo.ok(username + ":" + password);
}
基本类型和对应的包装类

直接写在形参中即可,MVC提供了类型转换器

// 直接接收请求参数
// 在形参中可以直接使用基本类型,或者其包装类来接收 String → 你接收的类型
// SpringMVC给我们提供了类型转换器
@RequestMapping("register2")
//public BaseRespVo register2(String username,String password,int age,String gender) {
public BaseRespVo register2(String username,String password,Integer age,String gender) {
    //
    return BaseRespVo.ok(username + ":" + password + ";age = " + age);
}

接收这些值建议使用包装类来接收,避免接收的过程中出现null值的转换错误

数组

即多个同名的参数,在接收的时候可以用一个数组来接收

localhost:8080/user/register3?username=songge&password=ligenli&age=28&gender=male

&hobbys=sing&hobbys=dance&hobbys=rap&hobbys=basketball

// 接收数组 → 请求参数名和Handler方法的形参名一致,数组的类型根据你的参数的类型选择
@RequestMapping("register3")
public BaseRespVo register3(String username,String password,
                            Integer age,String gender,
                            String[] hobbys) {
    //
    return BaseRespVo.ok(hobbys);
}
Date日期

&birthday=1991-06-13

// 接收日期 → 请求参数名和Handler方法的形参名一致
@RequestMapping("register4")
public BaseRespVo register4(String username, String password,
                            Integer age, String gender,
                            String[] hobbys, Date birthday) {

    return BaseRespVo.ok(hobbys);
}

自动提供的日期类型转换器只支持yyyy/MM/dd的格式

&birthday=1991/06/13

@RequestMapping("reg3")
public BaseRespVo reg3(Date date){
    return BaseRespVo.ok(date);
}
SpringMVC提供的类型转换器
// 接收日期 → 请求参数名和Handler方法的形参名一致
//指定接收的日期格式
//birthday=1991-06-13
@RequestMapping("register5")
public BaseRespVo register5(String username, String password,
                            Integer age, String gender,
                            String[] hobbys,
                            @DateTimeFormat(pattern = "yyyy-MM-dd") Date birthday) {

    return BaseRespVo.ok(hobbys);
}
自定义的类型转换器

1.写一个自定义的类型转换器

提供了接口,直接实现即可

//第一个泛型:作为convert方法的形参类型;第二个泛型作为convert方法的返回值类型
//其实做的就是一个由S类型转换为T类型的转换器
//在convert方法中就要写自定义的转换业务
@FunctionalInterface
public interface Converter<S, T> {
    @Nullable
    T convert(S var1);
}
// 接收日期
//自定义的类型转换器 → 请求参数名和Handler方法的形参名一致,来看类型转换器列表里是否包含形参类型的转换器
//birthday=1991-06-13
@Component
public class String2DateConvert implements Converter<String, Date> {

    @Override
    public Date convert(String s) {
        SimpleDateFormat simpleDateFormat = null;
        Date date = null;
        if (s.contains("-")&&s.length()==10){
            simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        }else if (s.length()==19){
            simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }else {
            simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
        }
        try {
            date = simpleDateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}

2.类型转换器的配置

<!--conversionService还要提供给SpringMVC-->
<mvc:annotation-driven conversion-service="conversionService"/>

<!--可以使用注解来注册这个组件-->
<!--<bean id="string2DateConverter" class="com.cskaoyan.converter.String2DateConverter"/>-->

<!--注册一个组件conversionService → 提供新增的converter-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <!--注册局部组件-->
            <!--<bean class="com.cskaoyan.converter.String2DateConverter"/>-->
            <!--引用全局组件 bean属性引用组件id-->
            <ref bean="string2DateConverter"/>
        </set>
    </property>
</bean>

3.@JsonFormat

将返回值标记为需要的日期格式

  @JsonFormat(pattern = "yyyy-MM-dd")
    T data;

写需要作为返回数据的属性上,同时也需要使用pattern属性。

要注意这里是返回参数,要和接收进行区别。这里转换也只会转换date类型,不会影响其他类型的数据的转换

文件 MultipartFile

文件上传

MultipartFile提供了一个方法 → 保存文件到指定位置

1.引入依赖commons-io、commons-fileupload

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

2.注册MultipartResolver组件

<!--组件id为固定值,不能写为其他值-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="512000000"/> //这里是限制文件上传的大小
</bean>

3.构造一个文件上传的请求,并且接收文件

<form action="upload/file" method="post" enctype="multipart/form-data">
    <%--name属性值就是请求参数名--%>
    <input type="file" name="file"><br>
    <input type="submit">
</form>

4.提供一个Handler方法,接收上传的文件参数

// 类型使用MultipartFile,形参名要和请求参数名一致
@RequestMapping("file")
public BaseRespVo uploadFile(MultipartFile file) throws IOException {

    //保存在指定位置
    // transferTo方法的形参提供的就是保存的位置以及名字
    //如果这个destFile已经存在,会覆盖 → 解决这个问题,就是new一个不存在的file
    // → 使用一个不会重复的文件名 UUID

    //也可以做这样一件事:上传的时候是啥名字,就以啥名字来接收
    String originalFilename = file.getOriginalFilename();//获得原始文件名
    String name = file.getName();                        //获得是请求参数名 → 这里是file,也就是形参名
    String contentType = file.getContentType();          //正文类型 → 文件类型
    long size = file.getSize();                          //文件大小
    //InputStream inputStream = file.getInputStream();     //输入流

    File destFile = new File("D:\\tmp", originalFilename);
    file.transferTo(destFile);
    return BaseRespVo.ok();
}

//上传多个文件
@RequestMapping("files")
public BaseRespVo uploadFile(MultipartFile[] files) throws IOException {
    //可以通过遍历的方式保存起来
    for (MultipartFile file : files) {
        String originalFilename = file.getOriginalFilename();
        File destFile = new File("D:\\tmp", originalFilename);
        file.transferTo(destFile);
    }
    return BaseRespVo.ok();
}
注意

不管接收到的是什么类型的参数,请求参数名必须和Handler方法的形参名一致

JavaBean

将请求参数封装到JavaBean的实例中,相当于是将形参名写成了JavaBean中的属性名,这时候也要注意属性名与请求参数名必须一致

并且需要提供无参构造和set方法,因为转换工具是通过set方法来赋值的

@Data
public class User {
    // 原先Handler方法的形参变更为了成员变量
    // 成员变量的类型和原先在Handler方法的形参中的写法是一样的
    String username;
    String password;

    Integer age;
    String gender;

    String[] hobbys;

    //@DateTimeFormat(pattern = "yyyy-MM-dd")
    Date birthday;
    
}
// 以JavaBean来接收请求参数 → 请求参数名和JavaBean的成员变量名(set方法)一致
@RequestMapping("register7")
public BaseRespVo register7(User user) {
    //以JavaBean来进行接收 → 实际上使用的JavaBean的无参构造方法和set方法来封装的
    //User user1 = new User();
    //user1.setUsername();
    //user1.setPassword();

    return BaseRespVo.ok();//做baseRespVo实例转换为字符串的过程中
}

接收方式的选择

有些情况下直接用形参来接收,有些情况下用JavaBean来接收,也有可能两者都用

混用

一部分参数是经常使用的,一部分参数使用不频繁

@RequestMapping("user/query")
public BaseRespVo queryUsers(Integer age, String gender, PageInfo pageInfo){

 return BaseRespVo.ok();
}
@Data
class PageInfo{
 Integer page;
 Integer limit;
}
继承JavaBean

如果需要增加请求参数,且这部分也是常有的,在不修改原来的代码的情况下,可以使用一个新的JavaBean来继承原来的对象

@RequestMapping("user/query")
public BaseRespVo queryUsers( UserPageInfo pageInfo){

 return BaseRespVo.ok();
}
// 改编方式1
@Data
class UserPageInfo{
 Integer age;
 String gender;
 Integer page;
 Integer limit;
}
// 改编方式2
@Data
class UserPageInfo extends PageInfo{
 Integer age;
 String gender;
}

Json

Json请求

请求方法:Post

Content-Type: application/json

data: Json字符串

构造Json请求

使用Postman来构造

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

引入依赖

jackson

JavaBean或Map来接收

//{"username":"admin123","password":"admin123"}
@RequestMapping("login")
//public BaseRespVo login(JsonUser user) {//localhost:8080/json/login?username=admin123&password=admin123
public BaseRespVo login(@RequestBody JsonUser user) {
    return BaseRespVo.ok();
}

@RequestMapping("login2")
public BaseRespVo login2(@RequestBody Map user) {
    return BaseRespVo.ok();
}

场景

使用Map来接收,主要场景是参数比较少。参数类型不容易固定

其他场景都用JavaBean

@RequestMapping("login2")
public BaseRespVo login2(@RequestBody Map user) {
    Object age = user.get("age");
    //age究竟是什么类型呢?
    return BaseRespVo.ok();
}

其他参数

Request/Response

如果你需要使用request和response可以直接放到Handler方法的形参中,可以直接使用,原因是会经过doGet或doPost,这个时候就会拿到request和response

//localhost:8080/other/hello?username=songge
@RequestMapping("hello")
public BaseRespVo hello(HttpServletRequest request, HttpServletResponse response) {
    String username = request.getParameter("username");
    return BaseRespVo.ok(username);
}

Model

Handler方法的返回值为字符串,如果没有增加@ResponseBody,意味着要处理ModelAndView,返回值字符串作为视图名,Model直接写在Handler方法的形参上

Cookie

不能直接放在形参中,可以通过request获得cookie

@RequestMapping("cookie")
public BaseRespVo cookie(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();
    for (Cookie cookie : cookies) {
        System.out.println("cookie:" + cookie.getName() + " → " + cookie.getValue());
    }
    return BaseRespVo.ok();
}
通过浏览器构造cookie

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

postman构造cookie

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Session

session也可以通过request来获得;另外session可以直接写在形参中 → HttpSession

//localhost:8080/other/session/put?username=songge
@RequestMapping("session/put")
public BaseRespVo put(HttpServletRequest request,String username) {
    HttpSession session = request.getSession();
    session.setAttribute("username", username);
    return BaseRespVo.ok();
}

//localhost:8080/other/session/get
@RequestMapping("session/get")
public BaseRespVo get(HttpSession session) {
    Object username = session.getAttribute("username");
    return BaseRespVo.ok(username);
}

RESTful

表述性状态传递Representational State Transfer

使用Json数据做交互,更侧重于响应

建议大家请求URL:资源(名词)+操作(动词)

/user/query

/user/insert

/user/update

/user/delete

注解:获得值给到Handler方法的形参,这些值是和请求相关的

即使用注解完成相关的编程流程

请求URL→ @PathVariable

localhost:8080/article/details?username=xxx&id=xxx

提供占位符,一个占位符在article之前,一个占位符在details之后;占位符写{xxx},xxx是占位符的名称

这样就可以根据传入的参数来动态的生成url,类似于vue中的插值表达式,{}中的内容是和注解对应的属性绑定的

//localhost:8080/article/details
//提供占位符,一个占位符在article之前,一个占位符在details之后;占位符写{xxx},xxx是占位符的名称
//使用的是@PathVariable注解的value属性来获得对应占位符的值
@RequestMapping("{aaa}/article/details/{bbb}")
public BaseRespVo articleDetails(@PathVariable("aaa")String username,
                                 @PathVariable("bbb")Integer id) {
    return BaseRespVo.ok(username + ":" + id);
}

根据userid做用户信息的查询的url → /user/query/5

@RequestMapping("query/{id}")
public BaseRespVo queryUser(@PathVariable("id") Integer id){
    User user = userMapper.selectUserById(id);

    return BaseRespVo.ok(user);
}

请求参数 → @RequestParam

无用

//@RequestParam注解的value属性和形参名对应起来,形参就可以接收到对应的值
// 同时会限定要携带对应的请求参数
//而这个过程我们直接让形参名和请求参数名一致就可以做到
//localhost:8080/user/login?username=songge&password=niupi
@RequestMapping("user/login")
public BaseRespVo login(@RequestParam("username") String usernamex,
                        @RequestParam("password") String passwordy) {
    return BaseRespVo.ok();
}

请求头 → @RequestHeader

获得指定请求头的值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

指定的冒号左边的部分,获得是冒号右边的部分

//可以通过String或String[]来接收对应的值
//如果以数组来接收,其实就是将字符串根据逗号作为分隔符转换数组
@RequestMapping("header")
public BaseRespVo header(@RequestHeader("Accept")String[] accept,
                         @RequestHeader("Host") String host) {
    //String usernamesString = "songge,ligenli,xuejia";
    //String[] split = usernamesString.split(",");
    return BaseRespVo.ok();
}

注解value属性的值需要与请求头对应

Cookie → @CookieValue

如果要获得指定name所对应的value,你要拿到所有的Cookie去遍历

可以通过注解直接拿到指定name所对应的value

@RequestMapping("cookie/value")
public BaseRespVo cookieValue(@CookieValue("songge")String value) {
    return BaseRespVo.ok(value);
}

Session → @SessionAttribute

直接拿到指定attributeName对应的value

//放入的时候放入的是什么类型,接收的时候就是什么类型
//简化的是session.getAttribute
@RequestMapping("session/get")
public BaseRespVo sessionGet(@SessionAttribute("username")String username) {
    return BaseRespVo.ok(username);
}

静态资源处理

在整合了MVC之后,web资源路径下的静态资源就无法正常访问了

原因:静态资源本应该由default的Servlet来处理,但在SpringMVC中都会交给DispatcherServlet来处理,由于没有写对应的处理静态资源的Servlet,就会造成访问不到的情况

解决的办法:

  • 配置default servlet

    • <servlet-mapping>
          <servlet-name>default</servlet-name>
          <url-pattern>*.jpg</url-pattern>
      </servlet-mapping>
      
  • 使用DispatcherServlet给出的默认配置

    • x <mvc:default-servlet-handler/>
      
    • 以上两种方式只能访问到web资源根路径下的静态资源,不能访问其他地方的,有局限性

  • 静态资源映射

    • <!--ResourceHandler的配置-->
      <!--mapping属性:映射的url-->
      <!--location属性:文件所处的真实路径-->
      <!--访问静态资源的请求URL:分为了两部分,mapping的属性值 + 静态资源相对于location的位置-->
      <mvc:resources mapping="/pic/**" location="/"/><!--web资源根路径-->
      <mvc:resources mapping="/pic2/**" location="classpath:/def/"/><!--classpath路径-->
      <mvc:resources mapping="/pic3/**" location="file:D:/spring/"/><!--文件路径,建议大家使用的路径-->
      <!--注意事项:location最后位置是/-->
      

前面的/pic类似于替代后面location中的目录,接下来只需要写相对于location目录下的资源路径即可

示意图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上传并访问

使用配置文件来管理文件相对路径,便于管理

<context:property-placeholder location="classpath:param.properties"/>

加载上传的驱动,使用配置文件配置路径映射

<mvc:resources mapping="/pic4/**" location="file:${file.path}"/><!--文件路径,建议大家使用的路径-->
<!--注意事项:location最后位置是/-->

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

编写上传servlet

@RestController
@RequestMapping("upload")
public class uploadController {

    @Value("${file.path}")
    String path;

    @RequestMapping("file")
    public BaseRespVo upload(MultipartFile file) throws IOException {

        file.transferTo(new File(path,file.getOriginalFilename()));
        return BaseRespVo.ok();
    }

}

异常处理

向上抛出异常,可以通过异常处理器进行统一的异常处理,也提供了个性化的处理,个性化的处理是根据异常的类型做个性化。

HandlerExceptionResolver

对异常进行统一的处理

接口,接口中提供了resolveException方法

范围:全部类型

如何生效:只需要注册到容器中就生效了

如果要做个性化的处理需要你自己来写业务

返回值:ModelAndView

如果需要返回Json,就需要调用response

public interface HandlerExceptionResolver {
    /**
    * var3: handler
    * var4: 抛出的异常
    */
    @Nullable
    ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, @Nullable Object var3, Exception var4);
}
@Component
public class CustomHandlerExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("/exception.jsp");
        if (e instanceof  ArithmeticException){
            modelAndView.setViewName("/arithmetic.jsp");
        }
        return modelAndView;
    }
}

***@ExceptionHandler

注解,注解的value属性Class<? extends Throwable>[],写的是异常类的Class

范围:value属性中的异常类型

如何生效:ElementType.method,方法要处于ControllerAdvice组件中

返回值:ModelAndView、Json

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
    Class<? extends Throwable>[] value() default {};
}

ModelAndView

直接返回String就是视图名

//@ExceptionHandler(value = {ArithmeticException.class})
/*@ExceptionHandler(ArithmeticException.class)
public ModelAndView arithmetic() {
    ModelAndView modelAndView = new ModelAndView("/arithmetic.jsp");
    return modelAndView;
}*/

@ExceptionHandler(ArithmeticException.class)
public String arithmetic(){
    return "/arithmetic.jsp";//viewName
}

Json

增加@ResponseBody注解

@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public BaseRespVo arithmetic() {
    return BaseRespVo.fail("算术异常3.0");
}

@ResponseBody可以写在类上,意味着该类下所有的方法响应都是Json

引申注解:@RestControllerAdvice = @ResponseBody + @ControllerAdvice

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {}
//@ControllerAdvice
//@ResponseBody
@RestControllerAdvice
public class ExceptionControllerAdvice {
    @ExceptionHandler(ArithmeticException.class)
    //@ResponseBody
    public BaseRespVo arithmetic() {
        return BaseRespVo.fail("算术异常3.0");
    }
}

可以将异常写在形参中,获取抛出异常的对象

@ExceptionHandler(ArithmeticException.class)
//@ResponseBody
public BaseRespVo arithmetic(ArithmeticException exception) {
    //return BaseRespVo.fail("算术异常3.0");
    return BaseRespVo.fail(exception.getMessage());//by zero
}

写业务时,可以自定义异常,通过抛出自定义的异常,做对应的异常处理,就可以响应对应的json数据

拦截器

filter

主要是解决Post请求乱码的问题

配置自带的CharacterEncodingFilter

它的作用就是判断forceRequestEncoding和forceResponseEncoding是否为true,如果为true则设置字符集为encoding对应的值

配置的过程

<!--Filter配置 class、映射范围-->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--使用其set方法,用到了init-param标签-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
    <init-param>
        <param-name>forceEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <!--<init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>-->
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <!--全部请求都来设置字符集-->
    <url-pattern>/*</url-pattern>
</filter-mapping>

HandlerInterceptor

是一个拦截器接口,其中提供了3个方法

public interface HandlerInterceptor {
    /**
    * 返回值为boolean:返回值决定是否继续执行
    * handler:HandlerMethod
    */
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }
	/**
	* 是在Handler方法执行之后 
	* ModelAndView 获得Handler方法的执行结果
	* 响应给用户之前
	*/
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    /**
    * 处理结果之后
    * ex:可以获得Handler方法抛出的异常,即使抛出异常,也可以为true   
    * 如果当前的preHandle执行到了,并且呢结果为true,则一定可以执行到对应的afterCompletion
    */
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

HandlerInterceptor和Handler之间的关系是什么样子?

通过HandlerExecutionChain(链)来维护的,并且Handler和chain之间是一对一的关系

public class HandlerExecutionChain {
    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
    private final Object handler;
    @Nullable
    private HandlerInterceptor[] interceptors;
    @Nullable
    private List<HandlerInterceptor> interceptorList;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

HandlerExcutionChain如何来的 → doDispatch → getHandler → 是通过HandlerMapping来获得的

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        Iterator var2 = this.handlerMappings.iterator();

        while(var2.hasNext()) {
            HandlerMapping mapping = (HandlerMapping)var2.next();
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }

    return null;
}

获得HandlerExecutionChain,接下来做了啥事情

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerExecutionChain mappedHandler = this.getHandler(processedRequest);
    //执行到了HandlerExecutionChain的applyPreHandle,如果返回值为false,则结束这个流程
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
    }
	// 执行的是Handler方法
    ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    // 在Handler方法执行之后执行的 → 并且第三个参数传入的Handler方法的执行结果mv
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    //处理最终结果
    this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
    finally{
        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    }
}

如果applyPreHandle返回值为false,则结束这个流程,我们来看下它做了什么

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //获得该Handler对应的多个HandlerInterceptor
    HandlerInterceptor[] interceptors = this.getInterceptors();
    //非空判断,如果为空,则直接return true
    if (!ObjectUtils.isEmpty(interceptors)) {
        // interceptors不为空,则遍历,i++ → 正序的遍历 → 给interceptorIndex赋值
        for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
            // 获得当前正在遍历的HandlerInterceptor
            HandlerInterceptor interceptor = interceptors[i];
            //如果HandlerInterceptor的preHandle为false,则满足判断条件
            if (!interceptor.preHandle(request, response, this.handler)) {
                //如果preHandle返回值为false → 这里return了false 
                //→ 导致了applyPreHandle返回值为false
                this.triggerAfterCompletion(request, response, (Exception)null);
                //意味着HandlerInterceptors中只要有一个Interceptor的preHandle为false
                //则中断doDispatch
                //我们又发现在中断doDispatch之前,执行了第14行代码 → 执行的afterCompletion
                return false;
            }
        }
    }

    return true;
}

在结束之前执行到了14行this.triggerAfterCompletion(request, response, (Exception)null);

执行的是对应的afterCompletion

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
    //先去获得HandlerInterceptors
    HandlerInterceptor[] interceptors = this.getInterceptors();
    //做了非空判断
    if (!ObjectUtils.isEmpty(interceptors)) {
        //执行遍历 
       	//从interceptorIndex开始执行的,执行到哪一个HandlerInterceptor的preHandle
        // --i 倒序
        for(int i = this.interceptorIndex; i >= 0; --i) {
            HandlerInterceptor interceptor = interceptors[i];

            try {
                interceptor.afterCompletion(request, response, this.handler, ex);
            } catch (Throwable var8) {
                logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
            }
        }
    }

}

如果有5个HandlerInterceptor,如果第4个preHandle为false,执行情况是什么样子的

preHandle1234、afterCompletion321

如果applyPreHandle所有的HandlerInterceptor的preHandle都返回true,则继续流程

执行applyPostHandle

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
    HandlerInterceptor[] interceptors = this.getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        //非空判断,--i 倒序遍历,遍历的是全部的HandlerInterceptor的postHandle
        for(int i = interceptors.length - 1; i >= 0; --i) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
        }
    }

}

注意:一旦preHandle为false则一个postHandle也执行不到。所有的preHandle都为true,则会执行所有的postHandle

后面的流程接着保证afterCompletion的执行

完整的执行流程

1.通过执行doDispatch中的getHandler方法,在其中通过HandlerMapping建立映射关系并执行getHandler方法获得了HandlerExecutionChain,Handler和Chain之间是一对一的关系,在Chain中维护了一个interceptor的list,所以handler和interceptor是一对多的关系。

2.执行HandlerExecutionChain的applyPreHandle方法,如果返回值是false的话则结束这个流程,在这个方法中遍历了interceptor数组,并执行每一个interceptor的preHandle方法,如果这个preHandle方法的返回值是fasle那么最终就会导致applyPreHandle的返回值是false,这样就会终止整个流程,也就是说如果遍历的数组中只要有一个的preHandle方法的返回值是false,那么就会中断doDispatch的流程。这里在中断之前,执行了triggerAfterCompletion方法,这里会根据interceptorIndex进行倒序的遍历,这个Index就是执行到的数组中的interceptor的个数,如果此时执行到第五个返回了false,那么就会执行4321个AfterCompletion方法,因为只有返回值是true才能保证AfterCompletion一定执行,如果返回false那么当前的就不会执行。

3.如果applyPreHandle所有的HandlerInterceptor的preHandle都返回true,则继续流程,执行applyPostHandle,执行全部Interceptor的postHandle方法,这里也是倒序执行。这里要注意只有所有的preHandle返回true才会执行到这里

4.最后有各种手段来执行AfterCompletion方法

如果要使用HandlerInterceptor我们需要做什么?

  1. HandlerInterceptor是谁
  2. HandlerInterceptor的作用范围
    • 直接写bean标签或ref标签:全局
    • mvc:interceptor标签:局部
  3. HandlerInterceptor的顺序
    • 书写顺序

首先定义一个类来实现接口,实现自己需要的方法即可

@Component
public class UserInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object username = session.getAttribute("username");
        if (username==null){
            return false;
        }
       return true;
    }
}

然后在配置文件中配置

<mvc:interceptors>
    <!--bean标签和ref标签配置HandlerInterceptor时,其实指定了作用范围:全局-->
    <!--注意:这个全局是DispatcherServlet作用范围下的全局-->
    <!--bean标签意味着局部组件-->
    <!--<bean class="com.cskaoyan.interceptor.CustomHandlerInterceptor"/>-->
    <!--ref标签意味着引用组件的id-->
    <!--<ref bean="customHandlerInterceptor"/>-->

    <!--也可以指定局部范围-->
    <mvc:interceptor>
        <!--指定作用范围 **代表多级url-->
        <mvc:mapping path="/hello/**"/>
        <!--bean标签或ref标签指定HandlerInterceptor-->
        <ref bean="customHandlerInterceptor"/>
    </mvc:interceptor>
    <ref bean="customHandlerInterceptor2"/>
    <ref bean="customHandlerInterceptor3"/>
</mvc:interceptors>

执行的顺序就是注册组件的顺序

HibernateValidation

校验框架,请求参数的合法性校验

以JavaBean来接收请求参数。请求参数名和JavaBean的成员变量名是对应起来,对请求参数做校验,转移到对成员变量做校验。

通过注解来建立关系 ,如果不同的校验逻辑提供了不同的注解,就是对成员变量(请求参数)增加不同的功能的校验注解,就意味着增加了校验功能

引入依赖

hibernate-validator使用6版本的

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

注册组件

validator

LocalValidatorFactoryBean,但是它是虚假的FactoryBean,没有实现FactoryBean接口

<mvc:annotation-driven validator="validator"/>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
    <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
</bean>

使用注解

参数校验声明@Valid或@Validated

//localhost:8080/user/register2?username=songge&password=niupi&age=30
@RequestMapping("register2")
public BaseRespVo register2(@Validated User user) {
    return BaseRespVo.ok();
}

校验功能注解

@Data
public class User {
    @Length(min = 6)
    String username;
    @Length(min = 6,max = 10)
    String password;
    @Max(100)
    @Min(18)
    Integer age;
}

常见的注解 (Bean Validation 中内置的 constraint)

@Null 被注释的元素必须为 null

@NotNull 被注释的元素必须不为 null

@Size(max=, min=) 被注释的元素的大小必须在指定的范围内

@AssertTrue 被注释的元素必须为 true

@AssertFalse 被注释的元素必须为 false

@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内

@Past 被注释的元素必须是一个过去的日期 Date

@Future 被注释的元素必须是一个将来的日期

@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式

Hibernate Validator 附加的 constraint

@NotBlank(message =) 验证字符串非null,且长度必须大于0

@Email 被注释的元素必须是电子邮箱地址

@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内

@NotEmpty 被注释的字符串的必须非空

@Range(min=,max=,message=) 被注释的元素必须在合适的范围内

没有通过校验的警告信

http://localhost:8080/user/register2?username=songge&password=niupile&age=120

Field error in object ‘user’ on field ‘age’: rejected value [120]; codes [Max.user.age,Max.age,Max.java.lang.Integer,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [user.age,age]; arguments []; default message [age],100]; default message [最大不能超过100]]

自行处理校验结果

//在形参中增加一个BindingResult
//localhost:8080/user/register3?username=songge&password=niupi&age=30
@RequestMapping("register3")
public BaseRespVo register3(@Validated User user, BindingResult bindingResult) {
    //如何知道校验没有通过
    if (bindingResult.hasFieldErrors()) {//只要有一个成员变量没有通过校验,返回true
        //如果校验没有通过是谁导致的 → 获得校验错误
        FieldError fieldError = bindingResult.getFieldError();
        //校验没有通过我们能获得哪些信息
        String field = fieldError.getField();//获得成员变量名 → 请求参数名
        Object rejectedValue = fieldError.getRejectedValue();//导致校验失败的值
        String defaultMessage = fieldError.getDefaultMessage();
        String msg = "请求参数" + field + "没有通过校验:" + rejectedValue + "; " + defaultMessage;
        return BaseRespVo.fail(msg);
    }
    return BaseRespVo.ok();
}

这里要注意,如果获取bindingResult的同时又要使用Session,那么要将bindingResult写在前面,否则会出现问题

修改默认的消息

校验功能注解的属性中,message属性

@Length(min = 6,max = 10,message = "length between 6 and 10")
String password;

JavaConfig

使用Java代码和注解完成对应的配置

application.xml → 配置类

***MVC配置类

@ComponentScan(“com.cskaoyan”) → context:component-scan

@EnableWebMvc → mvc:annotation-driven

SpringMVC这里我们做过哪些配置

  1. mvc:resources
  2. mvc:interceptors
  3. mvc:annotation-driven conversion-service
  4. mvc:annotation-driven validator
  5. multipartResolver

WebMvcConfigurer接口提供了这些方法 → 配置类实现这个接口

@ComponentScan("com.cskaoyan")
@EnableWebMvc
public class MvcConfiguration implements WebMvcConfigurer {
}

mvc:resources静态资源映射

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //静态资源映射mapping location
    registry.addResourceHandler("/pic1/**").addResourceLocations("/");
    registry.addResourceHandler("/pic2/**").addResourceLocations("classpath:/");
    registry.addResourceHandler("/pic3/**").addResourceLocations("file:d:/spring/");
}

mvc:interceptors HandlerInterceptor配置

@Override
public void addInterceptors(InterceptorRegistry registry) {
    //HandlerInterceptor是谁、作用范围是什么、顺序
    registry.addInterceptor(new CustomHandlerInterceptor());//作用范围是全局
    registry.addInterceptor(new CustomHandlerInterceptor2()).addPathPatterns("/hello/**");
    registry.addInterceptor(new CustomHandlerInterceptor3()).addPathPatterns("/goodbye/**");
}

类型转换服务

取出、增加、放回
@Autowired
ConfigurableConversionService conversionService;

//利用生命周期 → 来执行方法
@PostConstruct
public void addCustomConverter() {
    conversionService.addConverter(new String2DateConverter());
}

@Bean
@Primary
public ConfigurableConversionService conversionService() {
    return conversionService;
}
简单的方式
//添加Converter可以使用addFormatters
@Override
public void addFormatters(FormatterRegistry registry) {
    registry.addConverter(new String2DateConverter());
}

校验器

mvc:annotation-driven validator

@Override
public Validator getValidator() {
    LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
    validator.setProviderClass(HibernateValidator.class);
    return validator;
}

文件上传的处理器

//要么通过方法名指定组件id,要么通过@Bean的value属性指定;组件id是固定值
@Bean
public MultipartResolver multipartResolver() {
    return new CommonsMultipartResolver();
}

web.xml → 配置类

AACDSI

public class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() { //spring的配置文件
        return new Class[]{SpringConfiguration.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() { //mvc配置文件
        return new Class[]{MvcConfiguration.class};
    }

    @Override 
    protected String[] getServletMappings() { //dispatchServlet作用范围
        return new String[]{"/"};
    }
}

Spring配置类

//额外扫描到SpringMVC的配置类和Controller组件
//Filter里的type可以不写,默认值就是FilterType.ANNOTATION
@ComponentScan(value = "com.cskaoyan",
        excludeFilters = {@ComponentScan.Filter(/*type = FilterType.ANNOTATION,*/
                value = {Controller.class, EnableWebMvc.class})})
@Configuration
public class SpringConfiguration {
}

SpringMVC的配置类中可以使用Spring配置类中的组件

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值