一、AOP
过滤器、拦截器、Aspect都属于面向切面编程的具体实现。
AOP不是一种技术,而是一种编程思想。在面向对象编程的过程中,我们可以根据继承、多态来纵向扩展。 而横向的功能(比如权限检查、记录日志等),仅用面向对象是无法解决的。所以,AOP(面向切面编程)其实是面向对象编程思想的一个补充。
① 对于自定义Servlet对请求分发流程:Tomcat容器 —> Filter(过滤请求) —> Servlet(处理请求) —> Filter(过滤响应)
② 对于自定义 Controller 的请求分发流程:Tomcat容器 —> Filter(过滤请求) —> Interceptor(拦截请求、HandlerAdapter处理请求、拦截响应) —> Filter(过滤响应) —> Controller
二、Filter
在web开发中,过滤器就是过滤一些指定的url。
Filter可以对用户请求进行预处理,然后将请求交给 Servlet 进行处理并生成响应,最后Filter再对服务器响应进行后处理。Filter是可以复用的代码片段,常用来转换 HTTP 请求、响应和头信息。Filter不像 Servlet,它不能产生 响应,而只是修改对某一资源的请求或者响应。
通过过滤器来实现记录请求执行时间的功能,具体的逻辑就是在方法执行前先记录时间戳,然后通过过滤器链完成请求的执行,最后在返回结果之前计算执行的时间。具体的实现过程如下:
1、第一种方法:@WebFilter
注解方式
注解@WebFilter
是Servlet3.0的规范,并不是SpringBoot提供的。这里的Filter必须要有名字,所有的过滤器执行顺序是根据Filter的名字字母顺序来执行的。
@WebFilter(urlPatterns = "/*", filterName = "myFilter2")
public class MyFilter2 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {long start = System.currentTimeMillis();chain.doFilter(request,response);System.out.println("MyFilter2记录请求执行时间:"+(System.currentTimeMillis()-start));}@Overridepublic void destroy() {}
}
此外,需要在启动类配置注解@ServletComponentScan
进行扫描指定的包。@ServletComponentScan
注解虽然名字带了 Servlet,但是实际上它不仅仅可以扫描项目中的 Servlet 容器,也可以扫描Filter和Listener。
@SpringBootApplication
@MapperScan("com.demo.mapper")
@ServletComponentScan("com.demo.filters")
public class Application {public static void main(String[] args) throws Exception {SpringApplication.run(Application.class, args);}
}
但是这种方法无法指定Filter的优先级,如果存在多个Filter时,无法通过@Order指定优先级。
2、第二种方法:配置类方法
public class MyFilter1 implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {long start = System.currentTimeMillis();chain.doFilter(request,response);System.out.println("MyFilter1记录请求执行时间:"+(System.currentTimeMillis()-start));}@Overridepublic void destroy() {}
}
同时需要Filter配置类,使用SpringBoot提供的FilterRegistrationBean来注册Filter
@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<MyFilter1> registFilter() {FilterRegistrationBean<MyFilter1> registration = new FilterRegistrationBean<>();registration.setFilter(new MyFilter1());registration.addUrlPatterns("/*");// order越小优先级越高registration.setOrder(-1);return registration;}
}
三、Interceptor
拦截器的实现比过滤器稍微复杂一点。
1、通过拦截器来实现记录请求执行时间的功能,需要实现HandlerInterceptor接口,这个接口包括三个方法(① preHandle表示请求前执行,即Controller方法调用之前,返回值为true表示继续流程,返回值为false表示流程中断,不会继续调用其他的拦截器或处理器;② postHandler表示请求结束后执行,即Controller方法调用之后,但是在视图被渲染之前;③ afterCompletion表示整个请求处理完毕回调方法,即在视图渲染完毕时回调,通常用于清理资源等工作)。具体实现如下:
@Component
public class MyInterceptor implements HandlerInterceptor {long start = System.currentTimeMillis();@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {start = System.currentTimeMillis();return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {System.out.println("MyInterceptor记录请求执行时间:"+(System.currentTimeMillis()-start));}@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {}
}
此外,还要配置config管理类
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@AutowiredMyInterceptor myInterceptor ;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 添加拦截器myInterceptor,拦截所有路径/**,排除路径为/loginregistry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/login");}
}
WebMvcConfigurerAdapter已不推荐使用,可以使用WebMvcConfigurer接口。继承抽象类WebMvcConfigurerAdapter是适配器模式(不是一定要实现所有方法),而实现接口WebMvcConfigurer则预定要实现所有方法(以Adapter结尾的类一般为抽象类)。
2、Servlet的Filter和Spring的Interceptor区别
① Filter在Servlet中定义,依赖于Servlet容器,只能用于Web程序中;而Interceptor在Spring中定义,是独立存在的,可以在任何情况下使用。
② Filter可以修改request,而Interceptor不能。
③ Filter 对几乎所有的请求起作用,而 Interceptor只对Controller 对请求起作用。
④ Filter的实现基于Servlet容器的回调函数,而Interceptor(代理模式)的实现基于反射,代理分静态代理和动态代理,动态代理是拦截器的简单实现。
⑤ Filter的生命周期由Servlet容器管理,而Interceptor可以通过IOC容器来管理,因此可以通过注入等方式来获取其他Bean的实例。
⑥ Filter只在Servlet前后起作用,而Interceptor能够在方法前后、异常抛出前后等起作用,因此在Spring框架中优先使用Interceptor。
3、何时使用拦截器?何时使用过滤器?
① 如果是非spring项目,那么拦截器不能用,只能使用过滤器。
② 如果是处理controller前后,既可以使用拦截器也可以使用过滤器。
③ 如果是处理dispaterServlet前后,只能使用过滤器。
4、不过这里还要说明一点的是,其实这个实现是有问题的,因为preHandle和postHandle是两个方法,所以我们这里不得不设置一个共享变量start来存储开始值,但是这样就会存在线程安全问题。当然,我们可以通过其他方法来解决,比如通过ThreadLocal就可以很好的解决这个问题,有兴趣的同学可以自己实现。不过通过这一点我们其实可以看到,虽然拦截器在很多场景下优于过滤器,但是在这种场景下,过滤器比拦截器实现起来更简单。
四、Aspect
Aspect是一种更灵活的AOP实现技术,可以通过Aspect来完成更多更强大的功能。
@Aspect
@Component
public class TimeAspect {@Around("execution(* com.demo.controller.UserController.*(..))")public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {System.out.println("进入TimeAspect");Object[] args = pjp.getArgs();for (Object arg : args) {System.out.println("arg is "+arg);}long start = new Date().getTime();Object object = pjp.proceed();System.out.println("TimeAspect耗时:"+ (new Date().getTime() - start));System.out.println("TimeAspect结束");return object;}
}
扩展、监听器
Listener:用于监听servletContext、HttpSession和servletRequest等域对象的创建和销毁事件,可以在这些事件发生前和发生后进行处理。
用途:① 用于统计在线人数和在线用户;② 系统启动时加载初始化信息;③ 统计网站访问量;④ 记录用户访问路径。
@WebListener()
public class MyListener implements HttpSessionListener{@Overridepublic void sessionCreated(HttpSessionEvent se) {System.out.println("创建session 请求Url" + se.getSession().getServletContext().getContextPath());}@Overridepublic void sessionDestroyed(HttpSessionEvent se) {System.out.println("销毁session");}
}
这里的controller类
@Controller
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("findAll")public String findAll(Model model, HttpServletRequest request) throws Exception {List<User> list = userService.getAllUser();model.addAttribute("list",list); request.getSession().setAttribute("55", "55");return "index"; }
}
启动类
@SpringBootApplication
@ServletComponentScan("com.demo.listener")
public class SpringBoot1Application {public static void main(String[] args) {SpringApplication.run(SpringBoot1Application.class, args);}
}
其他
① ServletContextListener:监听servletContext对象的创建以及销毁
② HttpSessionListener:监听session对象的创建以及销毁
③ ServletRequestListener:监听request对象的创建以及销毁
④ ServletContextAttributeListener:监听servletContext对象中属性的改变(添加属性、修改属性、删除属性)
⑤ HttpSessionAttributeListener:监听session对象中属性的改变(添加属性、修改属性、删除属性)
⑥ ServletRequestAttributeListener:监听request对象中属性的改变(添加属性、修改属性、删除属性)