SpringMVC - 高级

1. 拦截器

SpringMVC拦截器定义如下:

public interface HandlerInterceptor {
    /**
     * 在调用自定义的controller之前会调用这个方法,若返回false,
     * 将跳过controller方法的调用,否则将进入到controller的方法中。
     */
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    /**
     * 调用自定义controller中的方法之后会调用这个方法,此时还没有渲染视图,
     * 也就是还没有将结果输出到客户端
     */
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    /**
     * 整个请求处理完毕之后,即结果以及输出到客户端之后,调用这个方法,
     * 此时可以做一些清理的工作,注意这个方法最后一个参数是Exception类型的,
     * 说明这个方法不管整个过程是否有异常,这个方法都会被调用。
     */
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}

自定义一个拦截器,实现HandlerInterceptor接口

public class MyInterceptor implements HandlerInterceptor {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor.preHandle");
        return true;
    }
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor.postHandle");
    }
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor.afterCompletion");
    }
}

配置拦截器

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/example/**"/>
        <bean class="com.example.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

访问执行顺序如下:

多个拦截器如何执行?

当请求的url匹配到多个拦截器的时候,执行顺序如下图

  • preHandle方法是顺序执行的,即和定义的顺序是一致的
  • 而拦截器中的其他2个方法postHandle、afterCompletion是逆序执行的,和preHandle的顺序相反

 2. Spring定义的Advice,对@Controller, @RequestBody, @ResponseBody的增强

1. 对Controller的增强,@ControllerAdvice, @RestControllerAdvice两个注解,来看一下使用方式

@ControllerAdvice
public class MyControllerAdvice {

    /**
     * 全局异常处理方法
     */
    @ExceptionHandler
    public ModelAndView doException(Exception ex){
        // 处理异常的逻辑
        ModelAndView modelAndView = new ModelAndView("error");
        modelAndView.addObject("errorMessage", ex.getMessage());
        return modelAndView;
    }
}

定义的error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false"%>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h2 style="color:red">${errorMessage}</h2>
</body>
</html>

Controller

@GetMapping("/exceptionTest")
public String exceptionTest(Model model) {
    throw new RuntimeException("测试异常");
}

访问,得到error.jsp页面如下:

 2. 对@RequestBody增强,有一个RequestBodyAdvice接口,还有一个默认的实现类RequestBodyAdviceAdapter,我们一般继承RequestBodyAdviceAdapter, 然后对request进行处理,比如得到一串加密的数据,我们需要统一在入口处进行解密。

@ControllerAdvice
public class MyRequestBodyAdvice extends RequestBodyAdviceAdapter {
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    /**
     * 在body中的数据读取之前可以做一些处理,我们在这个方法中来做解密的操作
     */
    @Override
    public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        String body = IOUtils.toString(inputMessage.getBody(), "utf-8");
        // 对body解密
        String decryptBody = decrypt(body);
        final InputStream inputStream = IOUtils.toInputStream(decryptBody, "utf-8");
        return new HttpInputMessage() {
            public InputStream getBody() throws IOException {
                return inputStream;
            }

            public HttpHeaders getHeaders() {
                return inputMessage.getHeaders();
            }
        };
    }
}

3. ResponseBodyAdvice对@ResponseBody进行增强,我们来看一个事例,设计一个通用返回结果

@RestControllerAdvice
public class MyResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return returnType.getParameterType() == User.class;
    }

    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return Result.success(body);
    }
}

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

    public static <T> Result<T> success(T data){
        return new Result<T>("成功", 200, data);
    }
}
@RestController
@RequestMapping("/hello")
public class HelloController {
    @GetMapping("/getUser")
    public User getUser(){
        User user = new User();
        user.setName("Mike");
        user.setAge("18");
        return user;
    }
}

访问输出结果如下:

 3. 自定义参数解析器Converter

比如我们请求http://localhost:8080/hello/testUser?user=Jack,20把user参数转换成对象该怎么做呢

@GetMapping("/testUser")
public User testUser(@RequestParam("user") User user){
    System.out.println(user);
    return user;
}

自定义一个Converter实现接口Converter

public class MyConverter implements Converter<String, User> {
    public User convert(String source) {
        if(!StringUtils.hasText(source)){
            return null;
        }
        String[] split = source.split(",");
        return new User(split[0], split[1]);
    }
}

xml当中配置converter

<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.example.MyConverter" />
        </set>
    </property>
</bean>

运行输出:

 4. CORS跨域问题详解

1. Spring定义了@CrossOrigin注解来实现跨域访问问题,这个注解可以使用在类或方法上,定义如下

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CrossOrigin {
    /** @deprecated */
    @Deprecated
    String[] DEFAULT_ORIGINS = new String[]{"*"};
    /** @deprecated */
    @Deprecated
    String[] DEFAULT_ALLOWED_HEADERS = new String[]{"*"};
    /** @deprecated */
    @Deprecated
    boolean DEFAULT_ALLOW_CREDENTIALS = false;
    /** @deprecated */
    @Deprecated
    long DEFAULT_MAX_AGE = 1800L;

    /**
    * 指定允许请求源列表
    */ 
    @AliasFor("origins")
    String[] value() default {};

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

    String[] originPatterns() default {};

    /**
    * 指定允许实际请求标头列表
    */ 
    String[] allowedHeaders() default {};

    /**
    * 指定允许客户端(如浏览器)访问的响应标头
    */
    String[] exposedHeaders() default {};

    /**
    * 指定允许请求的HTTP方法。
    */ 
    RequestMethod[] methods() default {};

    /**
    * 指定其值,表示客户端(如浏览器)是否应将凭证(如Cookies)和跨域请求一起发送到服务器。
    */
    String allowCredentials() default "";

    String allowPrivateNetwork() default "";

    /**
    * 指定预处理响应的最大缓存期限,单位为秒。
    */
    long maxAge() default -1L;
}

2. 全局配置CORS

@EnableWebMvc
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        //每次调用registry.addMapping可以添加一个跨域配置,需要多个配置可以多次调用registry.addMapping
        registry.addMapping("/**")
                .allowedOrigins("*") //放行哪些原始域
                .allowedMethods("PUT", "DELETE","POST", "GET") //放行哪些请求方式
                .allowedHeaders("header1", "header2", "header3") //放行哪些原始请求头部信息
                .exposedHeaders("header1", "header2") //暴露哪些头部信息
                .allowCredentials(false) //是否发送 Cookie
                .maxAge(3600);

    }
}

3. 全局添加CorsFilter

@Configuration
public class CorsConfig {
    private CorsConfiguration buildConfig(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setMaxAge(3600L);
        //corsConfiguration.setAllowCredentials(true);
        return corsConfiguration;
    }
    @Bean
    public CorsFilter corsFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig());
        return new CorsFilter(source);
    }
}

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值