一个请求先后经历Filter,Interceptor,AOP的过程:
- 一个请求过来 ,先进行过滤器处理,看程序是否受理该请求 。
- 过滤器放过后 , 程序中的拦截器进行处理
- 处理完后进入被 AOP动态代理重新编译过的主要业务类进行处理
===========before doFilter1
===========before doFilter2
===========HandlerInterceptorAll preHandle
===========HandlerInterceptor1 preHandle
===========HandlerInterceptor2 preHandle
执行Controller
Controller return前
===========HandlerInterceptor2 postHandle
===========HandlerInterceptor1 postHandle
===========HandlerInterceptorAll preHandle
Controller return后,Jsp加载完成
===========HandlerInterceptor2 afterCompletion
===========HandlerInterceptor1 afterCompletion
===========HandlerInterceptorAll preHandle
===========before doFilter2
===========before doFilter1
Filter
Filter:依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,一个过滤器实例只能在容器初始化时调用一次,和框架无关,过滤器可以拦截到方法的请求和响应(ServletRequest request, ServletResponse response),并对请求响应做出过滤操作,比如设置字符编码,鉴权操作等
Filter 是在 Servlet 规范中定义的,是 Servlet 容器支持的,Filter只在 Servlet 前后起作用
- 在HttpServletRequest 到达Servlet 之前,拦截客户的HttpServletRequest
- 根据需要检查HttpServletRequest ,也可以修改HttpServletRequest 头和数据
- 根据需要检查HttpServletResponse ,可以修改HttpServletResponse 头和数据
代码实现示例:对请求接口的过滤
@Component
public class AuthFilter implements Filter {
private static final String URL_PREFIX = "/sample/api/v1";@Overridepublic void init(FilterConfig filterConfig) throws ServletException {
}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;String requestURI = httpServletRequest.getRequestURI();if (!requestURI.contains(URL_PREFIX)) {
log.warn("请求地址无效");return;}System.out.println("Filter过滤器 开始");filterChain.doFilter(servletRequest, servletResponse);System.out.println("Filter过滤器 结束");}@Overridepublic void destroy() {
log.debug("destroy");}
Interceptor
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,可以控制请求的控制器和方法,但控制不了请求方法里的参数(用于处理页面提交的请求响应并进行处理,例如做国际化,做主题更换,过滤等)。
拦截器是在 Spring容器内的,是Spring框架支持的,拦截器能够深入到方法前后、异常抛出前后等。
- 通过实现HandlerInterceptor接口或者继承HandlerInterceptorAdapter抽象类,
- 复写preHandle()、postHandle()和afterCompletion()来对用户的请求进行拦截处理
代码实现示例:
@Component
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
/*** 处理器实际执行 之前 会被执行** @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();Auth annotation = method.getAnnotation(Auth.class);if (Objects.nonNull(annotation) && !annotation.value()) {
throw new RuntimeException("用户没有操作权限");}System.out.println("拦截器 preHandle 执行");return true;}/*** 处理器执行 完毕 以后被执行** @param request* @param response* @param handler* @param modelAndView* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器 postHandle 执行");}/*** 整个请求处理完成 之后被执行** @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("拦截器 afterCompletion 执行");}
}// WebConfig拦截器注册中心:
@Configuration
public class WebInterceptorConfig extends WebMvcConfigurationSupport {
@Autowiredprivate AuthInterceptor authInterceptor;@Overrideprotected void addInterceptors(InterceptorRegistry registry) {
// 多个拦截器组成一个拦截器链// addPathPatterns 用于添加拦截规则,/**表示拦截所有请求// excludePathPatterns 用户排除拦截registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns();super.addInterceptors(registry);}
}
AOP
是一种编程思想,对OOP的补充,常见实现:Filter和拦截器,AOP使用的主要是动态代理,AOP操作可以对操作进行横向的拦截,最大的优势在于他可以获取执行方法的参数,对方法进行统一的处理.常见使用日志,事务,请求参数安全验证等
AOP的一些概念:
-
通知(增强)Advice:定义具体要插入的内容,以及在连接点插入的时机,通过
before
、after
等语义关键词表示。- Advice通知的类型。Spring 中可以分为以下几种:
- before: 前置通知,在连接点前调用。
- after: 后置通知,在连接点后调用。
- afterReturning: 返回通知,在连接点方法执行并正常返回后调用
- afterThrowing: 异常通知,当连接点方法异常时调用
- Around: 环绕通知
- Advice通知的类型。Spring 中可以分为以下几种:
-
连接点 Join point:连接点,表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等。也就是我们要插入代码的地方。
-
切点 Pointcut: 连接点的集合。用来描述连接点。如通过通配符、正则表达式等语法来定义连接点。实际执行时代码是插入到某个具体的连接点。
-
切点匹配规则
-
任意公共方法的执行:
execution(public * *(..))
public可以省略, 第一个* 代表方法的任意返回值 第二个参数代表任意包+类+方法 (…)任意参数 -
任何一个以“set”开始的方法的执行:
execution(* set*(..))
-
UserService接口的任意方法:
execution(* com.coffee.service.UserService.*(..))
-
定义在com.coffee.service包里的任意方法的执行:
execution(* com.coffee.service.*.*(..))
第一个 .* 代表任意类, 第二个 .* 代表任意方法 -
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.coffee.service..*.*(..))
…* 代表任意包或者子包
-
定义在com.coffee包和所有子包里的UserService类的任意方法的执行:
execution(* com.coffee..UserService.*(..))")
-
-
-
切面 Aspect: 连接点,切点,通知的载体。由这三者组合起来,才能称为一个完整的切面。面向切面编程,也即是将我们想要新增的不影响主逻辑的代码抽出,并将其插入到原有目标代码的过程。
-
引入 Introduction: 向现有的类添加新方法属性
-
织入 Weaving : 切面应用到目标对象来创建新的代理对象的过程
代码实现示例:
@Aspect
@Component
public class AopExec {
/*** 定义切入点,切入点为com.barry.sample.controller.example中的所有函数* 通过@Pointcut注解声明频繁使用的切点表达式*/@Pointcut("execution(* com.barry.sample.controller..*.*(..)))")public void brokeAspect() {
}/*** 前置通知 在连接点执行之前执行的通知*/@Before(value = "brokeAspect()")public void beforeExec() {
System.out.println("AOP 前置通知 before");}/*** @description 在连接点执行之后执行的通知*/@After(value = "brokeAspect()")public void afterExec() {
System.out.println("AOP 后置通知 after");}/*** @description 在连接点方法执行并正常返回后调用*/@AfterReturning(value = "brokeAspect()")public void afterReturningExec() {
System.out.println("AOP 正常返回通知 AfterReturning");}/*** @description 当连接点方法异常时调用*/@AfterThrowing(value = "brokeAspect()")public void afterThrowingExec() {
System.out.println("AOP 异常返回通知 AfterThrowing");}@Around(value = "brokeAspect()")public void aroundExec(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前置通知");joinPoint.proceed();System.out.println("环绕后置通知");}
}
过滤器 拦截器 AOP 实测执行顺序
Filter过滤器 开始
拦截器 preHandle 执行
环绕前置通知
AOP 前置通知 before
目标方法执行
环绕后置通知
AOP 后置通知 after
AOP 正常返回通知 AfterReturning
拦截器 postHandle 执行
拦截器 afterCompletion 执行
Filter过滤器 结束
说明:文中的图是从网上pull的