spring拦截器 与统一格式

文章介绍了Spring拦截器的使用,包括创建UserInterceptor类实现HandlerInterceptor接口,配置拦截器规则。还探讨了动态代理的概念,对比了静态代理和动态代理的区别,并详细讲解了基于接口的JDK动态代理和基于类的CGLib动态代理。此外,提到了JDKProxy和CGlib的性能差异。最后,讨论了如何添加统一访问前缀,实现统一异常处理和数据返回格式。
摘要由CSDN通过智能技术生成

前言

之前博客讲述了 , 关于SpringAOP如何实现, 但是在实际开发中, 我们大多数不会遇到SpringAOP, 而是使用Spring拦截器代替了SpringAOP的功能, 因为Spring拦截器 比起AOP来更简单, 更好上手

模拟拦截器

  1. 创建一个拦截器 - 定义一个类,实现HandlerInterceptor 接口, 并重写preHandle方法 , 在这个类中 ,写业务的判断方法, (注: 这个类只是一个普通类)
package com.example.demo.config;

import com.example.demo.common.AppVariable;
import org.springframework.web.servlet.HandlerInterceptor;

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

public class UserInterceptor implements HandlerInterceptor {
    /**
     *
     * @param request
     * @param response
     * @param handler
     * @return  返回TRUE的时候, 表示拦截器验证成功, 返回FALSE 表示拦截器验证失败
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {

        // 业务方法
        HttpSession session = request.getSession(false); // 默认值是TRUE ,没有会自动创建一个session会话
        if (session != null && session.getAttribute(AppVariable.SESSION_KEY) != null){
            // 用户已登录
            return true;//如果是TRUE就继续执行后续方法
        }
        return false;// FALSE 不执行后续方法
    }
}

  1. 将拦截器配置到系统的配置文件中, 并配置拦截器的拦截规则
    也就是, 定义一个全局类, 加上@Configuration 注解 , 然后实现WebMvcConfigurer接口, 重写addInterceptors 方法, 在方法中定义具体拦截那些资源
    注: 一个项目中可以有多个拦截规则
package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration //一定要加一个Configuration

public class AppConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserInterceptor()) // 这儿也可以使用注解的方式来注入 , 表示具体拦截的类 
                .addPathPatterns("/**") // 一刀切, 拦截所有的请求
                .excludePathPatterns("/user/reg") // 配置不拦截的接口
                .excludePathPatterns("/user/login") // 不拦截的有更多的就写更多的方法

        ;
    }
}

  1. 定义一个测试类, 来测试拦截的功能
package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getuser")
    public String getUser(){
        return "do getUser";
    }
    @RequestMapping("/reg")
    public String reg(){
        return "do reg";
    }
    @RequestMapping("/login")
    public String login(){
        return "do login";
    }

}

拦截器的实现原理

之前的Spring调用流程
在这里插入图片描述
加入拦截器的调用流程
在这里插入图片描述
可以看出, 拦截器就是在访问控制层之前, 先将用户的请求处理一遍, 如果正确就给与后序层去调用, 如果不正确就拦截下来,让其实现对应的业务逻辑

拦截器也是通过动态代理的方式来实现的,

什么是动态代理? 什么是静态代理

代理: 就是将我们不愿意做的事情,交给别人去做
我们先来说什么是静态代理: 在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。简单来说静态代理就是在不改变源代码的基础上增加新的功能

比方说: 俩个类都实现了一个接口, 这样类A就可以通过类B 来加强一些自己说不具备的功能, 这种就可以说是静态代理, 因为你是根据代码提前写好去调用其他对象,这些对象在程序运行前就已经加载上了

有了静态代理的概念: 那么动态代理也就很清晰了
我们从上述可以看出静态代理受限于接口的实现。动态代理就是通过使用反射,动态地获取抽象接口的类型,从而获取相关特性进行代理

代理类在程序运行期间,创建的代理对象称之为动态代理对象。这种情况下,创建的代理对象,并不是事先在Java代码中定义好的。而是在运行期间,根据我们在动态代理对象中的“指示”,动态生成的

静态代理与动态代理的区别

静态代理需要自己写代理类并一一实现目标方法,且代理类必须实现与目标对象相同的接口。

动态代理不需要自己实现代理类,它是利用 JDK或CGLib,动态地在内存中构建代理对象(需要我们传入被代理类),并且默认实现所有目标方法。

两种常用的动态代理方式

基于接口的动态代理

是JDK提供的: 必须使用JDK官方的Proxy类创建代理对象,代理的目标对象必须实现接口

基于类的动态代理

是 CGLib提供的: 使用CGLib的Enhancer类创建代理对象

JDK Proxy 与 CGlib的区别

  1. 出生不同
  2. 实现不同 , JDK Proxy 要求代理类实现接口才能实现代理 , 而CGLib是实现代理类的子类完成动态代理
  3. 性能不同, JDK 7 Proxy 性能高于 CGLib , 而JDK 7 之前CGLib性能高于Proxy

其他 统⼀访问前缀添加

所有请求地址添加 api 前缀 , 在实现WebMvcConfigure 接口的类中 ,重写configurePathMatch方法 , 其中第⼆个参数是⼀个表达式,设置为 true 表示启动前缀

@Configuration
public class AppConfig implements WebMvcConfigurer {
 // 所有的接⼝添加 api 前缀
 @Override
 public void configurePathMatch(PathMatchConfigurer configurer) {
 configurer.addPathPrefix("api", c -> true);
 }
}

统⼀异常处理

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件,具体实现代码如下:
⽅法名和返回值可以⾃定义,其中最重要的是@ExceptionHandler(Exception.class) 注解 , 注解中放具体的异常类型, 也可以直接使用Exception来处理所有的异常,
调用流程如下: 定义了Exception异常的前提下, 如果找到对应异常就使用对应异常处理, 找不到就是用Exception异常处理

import java.util.HashMap;
@ControllerAdvice
public class ErrorAdive {
	 @ExceptionHandler(Exception.class)
	 @ResponseBody
	 public Object handler(Exception e) {
		 HashMap<String, Object> map = new HashMap<>();
		 map.put("state", 0);
		 map.put("data", null);
		 map.put("msg", e.getMessage());
		 return map;
	 }
}

统⼀数据返回格式

统⼀的数据返回格式可以使⽤ @ControllerAdvice + ResponseBodyAdvice 的⽅式实现,具体实现代
码如下
如果想要使用统一数据的返回格式, 首先在类上加上@ControllerAdvice注解, 然后在类上实现 ResponseBodyAdvice 接口, 并重写俩个方法, support 与beforeBodywrite , 其中support 必须返回TRUE 否则不调用beforeBodywrite 方法, 在beforeBodywrite中写数据返回类型

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.util.HashMap;
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
	 /**
	 * 内容是否需要重写(通过此⽅法可以选择性部分控制器和⽅法进⾏重写)
	 * 返回 true 表示重写
	 */
	 @Override
	 public boolean supports(MethodParameter returnType, Class converterTyp
	e) {
		 return true;
	 }
	 /**
	 * ⽅法返回之前调⽤此⽅法
	 */
	 @Override
	 public Object beforeBodyWrite(Object body, MethodParameter returnType,
	MediaType selectedContentType,
	 Class selectedConverterType, ServerHttpR
	equest request,
	 ServerHttpResponse response) {
		 // 构造统⼀返回对象
		 HashMap<String, Object> result = new HashMap<>();
		 result.put("state", 1);
		 result.put("msg", "");
		 result.put("data", body);
		 return result;
	 }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值