【Spring AOP】统一处理过程 代码实现

上一节我们讲的是Spring AOP的原理部分,介绍了切面,切点,连接点,通知,织入,以及简单的代码实现.

我们上一节是使用@Aspect来进行切点函数的范围和通知方法的设定的.

但是使用上面的方法有两个问题是无法解决的;

  1. 无法获取HttpSession
  2. 切点的execution函数的拦截语法还是有限的,不可以清楚的定位

所以,我们就需要使用新的方法.

SpringAop给我们提供了新的框架------Spring 拦截器

Spring拦截器

Spring 中提供了具体的实现拦截器:HandlerInterceptor,
拦截器的实现分为以下两个步 骤:

  1. 创建⾃定义拦截器,实现 HandlerInterceptor 接⼝的 preHandle(执⾏具体⽅法之前的预处理)⽅ 法。
  2. 将⾃定义拦截器加⼊ WebMvcConfigurer 的 addInterceptors ⽅法

1. 自定义拦截器

  1. 创建一个拦截器类
  2. 实现HandlerInterceptor接口
  3. 重写preHandle方法

如果通过了拦截器的拦截认证,就会返回true,放行,继续执行之后的代码
但是如果没有通过,就根据业务的需要,执行响应的代码,并返回false

@Component  
public class LoginIntercepter implements HandlerInterceptor {  
    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
        HttpSession session=request.getSession();  
        if(session!=null&&session.getAttribute("user")!=null)  
            return true;  
        response.sendRedirect("/Blog_Main.html");  
        return false;  
    }  
}

2. 将⾃定义拦截器加⼊到系统配置

  1. 定义一个配置类
  2. 使用WebMvcConfigurer类
  3. 重写addInterceptors方法
  4. 将拦截器放入到addInterceptor方法中并指定拦截范围和不拦截范围
  
@Configuration  
public class AppConfig implements WebMvcConfigurer {  
    @Autowired  
    LoginIntercepter loginIntercepter;  
  
    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
		//加入拦截器
		registry.addInterceptor(loginIntercepter).  
                //指定拦截所有的接口
                addPathPatterns("/**").  
                //排除不拦截的接口
                excludePathPatterns("/resources/**").  
                excludePathPatterns("/**/login").  
                excludePathPatterns("/**/**.png").  
                excludePathPatterns("/**/**.jpg").  
                excludePathPatterns("/Blog_Main.html").  
                excludePathPatterns("/**/**.css").  
                excludePathPatterns("/**/**.js");  
    }  
}

可以通过多次调用registry.addInterceptor方法来添加多个拦截器

原理分析

那Spring拦截器是怎么样做到的呢?

主要是通过DispatcherServlet来进行实现的.
这个DispathcherServlet里面的doDispath方法里面主要有一个方法:

if (!mappedHandler.applyPreHandle(processedRequest, response)) {  
    return;  
}

这个applyPreHandle方法会处理所有的拦截器方法,下面是它的实现:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {  
    for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) {  
        HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);  
        if (!interceptor.preHandle(request, response, this.handler)) {  
            this.triggerAfterCompletion(request, response, (Exception)null);  
            return false;  
        }  
    }  
  
    return true;  
}

它会将拦截器列表的所有拦截器都遍历一遍,只要不符合其中任意一个拦截器,就会报错,返回false,拦截不通过.

添加路径前缀

还可以在系统配置中将所有的路径都加上前缀.

  1. 创建一个配置类实现WebMvcConfigurer接口
  2. 重写configurePathMatch方法
  3. 第一个参数是新加入的前缀,后一个参数是描述参数的范围
package com.example.demo.config;  
  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;  
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;  
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  
  
  
@Configuration  
public class AppConfig implements WebMvcConfigurer {  
    @Autowired  
    LoginIntercepter loginIntercepter;  
    @Override  
    public void configurePathMatch(PathMatchConfigurer configurer) {  
        configurer.addPathPrefix("hhh",c->true);  
    }  
}

原来:http://127.0.0.1:8080/user/test
现在:http://127.0.0.1:8080/hhh/user/test

加上前缀之后,就必须要带上前缀了

统一异常处理

  1. 创建一个类
  2. 在这个类前面加上@RestControllerAdvice或@ControllerAdvice注解,两者都可以
  3. 在处理异常的方法前面加上@ExceptionHandler,括号里面写异常的类型
  4. 处理异常的方法的参数是对应的异常类型
@RestControllerAdvice  
public class MyExceptionAdvice {  
  
    @ExceptionHandler(Exception.class)    
    public HashMap<String,Object> handleException(Exception e){  
        HashMap<String,Object> hashMap=new HashMap<>();  
        hashMap.put("state",-1);  
        hashMap.put("msg","出现异常");  
        hashMap.put("log",e.getMessage());  
        return hashMap;  
    }  
}

统一返回处理

  1. 创建一个类,在类的前面加上@ControllerAdvice注解
  2. 实现ResponseBodyAdvice接口,并是实现它的两个方法
  3. 对于supports方法来说,如果返回true,表示需要进行统一的返回处理.如果返回false,表示不需要统一的返回处理.可以使用这个函数进行过滤
  4. beforeBodyWrite就是对那些supports方法返回true的接口进行处理.一般都是写成前端可以识别的json格式
@ControllerAdvice  
public class MyResponseAdvice implements ResponseBodyAdvice {  
  
    @Override  
    public boolean supports(MethodParameter returnType, Class converterType) {  
        if("com.example.demo.controller.UserController".equals(returnType.getMethod().getDeclaringClass().getName()))  
            return true;  
        else  
            return false;  
    }  
  
    @Override  
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {  
        HashMap<String, Object> result = new HashMap<>();  
        result.put("success", 1);  
        result.put("message", "");  
        result.put("data", body);  
        return result;  
    }  
}

原理分析

我们发现@ControllerAdvice可以处理异常和返回值.但是这个的原理是什么呢?

将所有被@ControllerAdvice注解的类方法一个容器中.就是当发生事件的时候,会去调用对应的类的advice方法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值