SpringMVC框架(2)

简介: SpringMVC框架(2)

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

20211231154858979.png&pos_id=img-XwiWAXIf-1706540147877)

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(){
    

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标签:局部
  1. 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配置类中的组件

取出、增加、放回

@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配置类中的组件

目录
相关文章
|
1月前
|
JSON Java 数据格式
SpringMVC框架(1)
SpringMVC框架
11 1
|
1月前
|
XML 存储 Java
SpringMVC常见组件之HandlerMapping分析
SpringMVC常见组件之HandlerMapping分析
84 0
|
6月前
|
设计模式 前端开发 Java
SpringMVC框架
SpringMVC框架
|
6月前
|
算法 Java API
SpringMVC常见组件之HandlerMethodArgumentResolver解析-1
SpringMVC常见组件之HandlerMethodArgumentResolver解析-1
53 0
|
10月前
|
设计模式 前端开发 Java
SpringMVC框架(详解)上
SpringMVC框架(详解)
|
10月前
|
JSON 前端开发 Java
SpringMVC框架(详解)下
SpringMVC框架(详解)
|
JSON 前端开发 Java
HandlerMethodArgumentResolver(四):自定参数解析器处理特定场景需求,介绍PropertyNamingStrategy的使用【享学Spring MVC】(下)
HandlerMethodArgumentResolver(四):自定参数解析器处理特定场景需求,介绍PropertyNamingStrategy的使用【享学Spring MVC】(下)
HandlerMethodArgumentResolver(四):自定参数解析器处理特定场景需求,介绍PropertyNamingStrategy的使用【享学Spring MVC】(下)
|
XML JSON 前端开发
springmvc框架
springmvc框架
75 0
|
XML JSON 缓存
SpringMVC框架理解2
SpringMVC框架理解
SpringMVC框架理解2
|
存储 XML 前端开发
SpringMVC框架理解1
SpringMVC框架理解
SpringMVC框架理解1