当前位置:   article > 正文

拦截机制中Aspect、ControllerAdvice、Interceptor、Fliter之间的区别详解_controller aspect interceptor

controller aspect interceptor

在项目的开发中,在某些情况下,我们需要对客户端发出的请求进行拦截,常用的API拦截方式有Fliter,Interceptor,ControllerAdvice以及Aspect。

Spring中的拦截机制,如果出现异常的话,异常的顺序是从里面到外面一步一步的进行处理,如果到了最外层都没有进行处理的话,就会由tomcat容器抛出异常.

1.过滤器:Filter :可以获得Http原始的请求和响应信息,但是拿不到相应方法的信息

2.拦截器:Interceptor:可以获得Http原始的请求和响应信息,也拿得到相应方法的信息,根据httpServeltRquest也能拿到请求参数

3.ControllerAdvice(Controller增强,自spring3.2的时候推出):主要是用于全局的异常拦截和处理,这里的异常可以使自定义异常也可以是JDK里面的异常

4.切片:Aspect:主要是进行公共方法的,可以拿得到方法响应中参数的值,但是拿不到原始的Http请求和相对应响应的方法

Filter(过滤器)

可以获得Http原始的请求和响应信息,但是拿不到响应方法的信息,filter是属于Servlet规范的,不属于Spring。

springboot中的配置方法:
自定义一个Filter

  1. import javax.servlet.*;
  2. import java.io.IOException;
  3. public class TimeFilter implements Filter {
  4. /**Filter接口中的部分方法添加了default关键字,这样的方法就不是一定要重写*/
  5. @Override
  6.     public void init(FilterConfig filterConfig)throws ServletException {
  7. System.out.println("Time Filter init");
  8.     }
  9. @Override
  10.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
  11. System.out.println("time filter start");
  12.         /**这里说明一下,在JDK8以及之后的JDK版本中都不再建议使用new Date().getTime()这种方式来获得时间*/
  13.         long start = System.currentTimeMillis();
  14.         chain.doFilter(request,response);
  15.         System.out.println("time filter:"+(System.currentTimeMillis()-start));
  16.         System.out.println("time filter finish");
  17.     }
  18. @Override
  19.     public void destroy() {
  20. System.out.println("time filter destroy");
  21.     }
  22. }

方式一:通过Bean注入的方式

注册Filter,springboot当中提供了FilterRegistrationBean类来注册Filter

  1. import com.imooc.Filter.TimeFilter;
  2. import com.imooc.Interceptor.TimeInterceptor;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  8. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. @Configuration
  12. public class WebConfigimplements WebMvcConfigurer {
  13. /**WebMvcConfigurerAdapter在JDK8中这个类已经过时,我们直接继承这个类所继承的接口*/
  14. @Bean
  15.     public FilterRegistrationBeantimeFilter(){
  16. FilterRegistrationBean registrationBean =new FilterRegistrationBean();
  17.         TimeFilter timeFilter =new TimeFilter();
  18.         registrationBean.setFilter(timeFilter);
  19.         /**添加拦截的地址*/
  20.         List urls =new ArrayList<>();
  21.         urls.add("/*");
  22.         return registrationBean;
  23.     }
  24. }

方式二:通过@WebFilter注解实现

  1. mport org.springframework.stereotype.Component;
  2. import javax.servlet.*;
  3. import javax.servlet.annotation.WebFilter;
  4. import java.io.IOException;
  5. @Component
  6. @WebFilter(filterName ="TimeFilter",urlPatterns ="/*")
  7. public class TimeFilterimplements Filter {
  8. @Override
  9.     public void init(FilterConfig filterConfig)throws ServletException {
  10. System.out.println("Time Filter init");
  11.     }
  12. @Override
  13.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
  14. System.out.println("time filter start");
  15.         /**这里说明一下,在JDK8以及之后的JDK版本中都不再建议使用new Date().getTime()这种方式来获得时间*/
  16.         long start = System.currentTimeMillis();
  17.         chain.doFilter(request,response);
  18.         System.out.println("time filter:"+(System.currentTimeMillis()-start));
  19.         System.out.println("time filter finish");
  20.     }
  21. @Override
  22.     public void destroy() {
  23. System.out.println("time filter destroy");
  24.     }
  25. }

Interceptor (拦截器) 

可以获得Http原始的请求和响应信息,也拿得到响应方法的信息,但是拿不到方法响应中参数的值。

在web开发中,拦截器是经常用到的功能。它可以帮我们验证是否登陆、预先设置数据以及统计方法的执行效率等。在spring中拦截器有两种,第一种是HandlerInterceptor,第二种是MethodInterceptor。

HandlerInterceptor是SpringMVC中的拦截器,它拦截的是Http请求的信息,优先于MethodInterceptor。

而MethodInterceptor是springAOP的。

前者拦截的是请求的地址,而后者是拦截controller中的方法,因为下面要讲Aspect,就不详细讲述MethodInterceptor。

在springboot中HandlerInterceptor的配置
1.首先定义自己的Interceptor

  1. import org.springframework.stereotype.Component;
  2. import org.springframework.web.method.HandlerMethod;
  3. import org.springframework.web.servlet.HandlerInterceptor;
  4. import org.springframework.web.servlet.ModelAndView;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. /**
  8. * 拦截器会拦截所有的控制器,不管是spring的还是自定义的
  9. */
  10. @Component
  11. public class TimeInterceptorimplements HandlerInterceptor {
  12. /**
  13.     *控制器方法调用之前会进行
  14.     *和上面的Filter一样,继承的某些接口方法中也加了default关键字,可以不用重写,这里为了演示就都写了
  15. */
  16.     @Override
  17.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
  18. System.out.println("proHandle");
  19.         System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
  20.         System.out.println(((HandlerMethod)handler).getMethod().getName());
  21.         request.setAttribute("startTime",System.currentTimeMillis());
  22. return true;
  23.         /**true的话 就是选择可以调用后面的方法  也就是controller中的getInfo方法*/
  24.     }
  25. /**控制后方法执行之后会进行,抛出异常则不会被执行*/
  26.     @Override
  27.     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception {
  28. System.out.println("postHandle");
  29.         Long start = (Long)request.getAttribute("startTime");
  30.         System.out.println("time interceptor 耗时:"+(System.currentTimeMillis()-start));
  31.     }
  32. /**方法被调用或者抛出异常都会被执行*/
  33.     @Override
  34.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
  35. System.out.println("afterCompletion");
  36.         Long start = (Long)request.getAttribute("startTime");
  37.         System.out.println("time interceptor 耗时:"+(System.currentTimeMillis()-start));
  38.     }
  39. }

2.在配置类中配置自定义的Interceptor

  1. import org.springframework.context.annotation.Configuration;
  2. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  3. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  4. import com.imooc.Interceptor.TimeInterceptor;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. @Configuration
  7. public class WebConfigimplements WebMvcConfigurer {
  8. /**WebMvcConfigurerAdapter在JDK8中这个类已经过时,我们直接继承这个类所继承的接口*/
  9.     @Autowired
  10.     private TimeInterceptortimeInterceptor;
  11.     @Override
  12.     public void addInterceptors(InterceptorRegistry registry) {
  13. registry.addInterceptor(timeInterceptor);
  14.     }
  15. }

 ControllerAdvice(Controller增强,自spring3.2的时候推出)

 用于处理当数据库事务业务和预期不同的时候抛出封装后的异常,进行数据库事务回滚,并将异常的显示给用户

1.定义自己的异常类

  1. import lombok.Data;
  2. @Data
  3. public class UserNotExistException extends RuntimeException{
  4. private static final long serialVersionUID =4820951478405122770L;
  5.     private Stringid;
  6.     public UserNotExistException(String id) {
  7. super("user not exist.......");
  8.         this.id = id;
  9.     }
  10. }

 2.编写全局异常处理类

  1. import com.imooc.Exception.UserNotExistException;
  2. import org.springframework.http.HttpStatus;
  3. import org.springframework.web.bind.annotation.ControllerAdvice;
  4. import org.springframework.web.bind.annotation.ExceptionHandler;
  5. import org.springframework.web.bind.annotation.ResponseBody;
  6. import org.springframework.web.bind.annotation.ResponseStatus;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. @ControllerAdvice
  10. public class ControllerExceptionHandler {
  11. /**指定抛出的异常类*/
  12. @ExceptionHandler(UserNotExistException.class)
  13. /**如果全部异常处理返回json格式,那么可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,这样在方法上就可以不需要添加 @ResponseBody.@ResponseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。*/
  14. @ResponseBody
  15.     @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  16. /**当出现该Http状态码的时候抛出异常*/
  17.     public MaphandlerUserNotExistException(UserNotExistException exception){
  18. Map result =new HashMap<>();
  19.         result.put("id",exception.getId());
  20.         result.put("message",exception.getMessage());
  21.         return result;
  22.     }
  23. }

3、controller中抛出异常进行测试

  1. import com.imooc.Exception.UserNotExistException;
  2. import com.imooc.dto.User;
  3. import com.imooc.dto.UserQueryCondition;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.validation.BindingResult;
  6. import org.springframework.web.bind.annotation.*;
  7. @RestController
  8. public class UserController {
  9.     @GetMapping(value ="/user/{id}")
  10.     public UsergetInfo(@PathVariable String id)throws Exception{
  11.     throw new UserNotExistException(id);
  12.         }
  13. }

 Aspect(切面)

 可以拿得到方法响应中参数的值,但是拿不到原始的Http请求和相对应响应的方法,基于Controller层。对于统一异常处理和日志记录非常方便,有效地减少了代码的重复率。
可以参照https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html,spring的官方文档

1.创建Controller

2.创建一个Aspect切面

  1. import lombok.extern.slf4j.Slf4j;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.springframework.stereotype.Component;
  6. @Aspect
  7. @Component
  8. @Slf4j
  9. public class TimeAspect {
  10. @Around("execution(* com.imooc.controller.UserController.*(..))")
  11. public ObjecthandleControllerMethod(ProceedingJoinPoint proceedingJoinPoint)throws Throwable {
  12. log.info("Time aspect start");
  13.         Long start = System.currentTimeMillis();
  14.         Object[] args = proceedingJoinPoint.getArgs();
  15.         for(Object object:args)
  16. {
  17. log.info("arg is:"+String.valueOf(object));
  18.         }
  19. Object object =proceedingJoinPoint.proceed();
  20.         System.out.println("time filter:"+(System.currentTimeMillis()-start));
  21.         log.info("Time aspect finish");
  22.         return object;
  23.     }
  24. }

 切面的方法说明:
@Aspect
作用是把当前类标识为一个切面供容器读取


@Before
标识一个前置增强方法,相当于BeforeAdvice的功能


@AfterReturning
后置增强,相当于AfterReturningAdvice,方法退出时执行


@AfterThrowing
异常抛出增强,相当于ThrowsAdvice


@After
final增强,不管是抛出异常或者正常退出都会执行


@Around
环绕增强,相当于MethodInterceptor

除了@Around外,每个方法里都可以加或者不加参数JoinPoint,如果有用JoinPoint的地方就加,不加也可以,JoinPoint里包含了类名、被切面的方法名,参数等属性,可供读取使用。@Around参数必须为ProceedingJoinPoint,pjp.proceed相应于执行被切面的方法。@AfterReturning方法里,可以加returning = “XXX”,XXX即为在controller里方法的返回值,本例中的返回值是“first controller”。@AfterThrowing方法里,可以加throwing = "XXX"

关于切面PointCut的切入点

execution切点函数

execution函数用于匹配方法执行的连接点,语法为:

execution(方法修饰符(可选)  返回类型  方法名  参数  异常模式(可选)) 

例如:execution(* com.imooc.controller.UserController.*(..))

第一个*代表的是所有的返回值类型,com.imooc.controller.UserController.*代表的是com.imooc.controller包下UserController类的所有方法,(..)代表的是所有的参数

参数部分允许使用通配符:

*  匹配任意字符,但只能匹配一个元素

.. 匹配任意字符,可以匹配任意多个元素,表示类时,必须和*联合使用

+  必须跟在类名后面,如Horseman+,表示类本身和继承或扩展指定类的所有类

除了execution(),Spring中还支持其他多个函数,这里列出名称和简单介绍,以方便根据需要进行更详细的查询

 @annotation()

表示标注了指定注解的目标类方法

例如 @annotation(org.springframework.transaction.annotation.Transactional) 表示标注了@Transactional的方法

args()

通过目标类方法的参数类型指定切点

例如 args(String) 表示有且仅有一个String型参数的方法

@args()

通过目标类参数的对象类型是否标注了指定注解指定切点

如 @args(org.springframework.stereotype.Service) 表示有且仅有一个标注了@Service的类参数的方法

within()

通过类名指定切点

如 with(examples.chap03.Horseman) 表示Horseman的所有方法

target()

通过类名指定,同时包含所有子类

如 target(examples.chap03.Horseman)  且Elephantman extends Horseman,则两个类的所有方法都匹配

@within()

匹配标注了指定注解的类及其所有子类

如 @within(org.springframework.stereotype.Service) 给Horseman加上@Service标注,则Horseman和Elephantman 的所有方法都匹配

@target()

所有标注了指定注解的类

如 @target(org.springframework.stereotype.Service) 表示所有标注了@Service的类的所有方法

 this()

大部分时候和target()相同,区别是this是在运行时生成代理类后,才判断代理类与指定的对象类型是否匹配

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/342086
推荐阅读
相关标签
  

闽ICP备14008679号