记录控制器请求的耗时处理通常有三种实现方式,分别是:过滤器、拦截器、aspect;下文将逐一实现。
@Component //表明作为spring的一个bean public class TimeFilter implements Filter { @Override public void destroy() { System.out.println("time filter destroy"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("time filter start"); long start = new Date().getTime(); //过滤器主要逻辑,整个处理流程 chain.doFilter(request, response); System.out.println("time filter 耗时:"+ (new Date().getTime() - start)); System.out.println("time filter finish"); } @Override public void init(FilterConfig arg0) throws ServletException { System.out.println("time filter init"); } }
@Component public class TimeInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle"); System.out.println(((HandlerMethod)handler).getBean().getClass().getName()); System.out.println(((HandlerMethod)handler).getMethod().getName()); request.setAttribute("startTime", new Date().getTime()); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); Long start = (Long) request.getAttribute("startTime"); System.out.println("time interceptor 耗时:"+ (new Date().getTime() - start)); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); Long start = (Long) request.getAttribute("startTime"); System.out.println("time interceptor 耗时:"+ (new Date().getTime() - start)); System.out.println("ex is "+ex); } }
@Configuration public class WebConfig extends WebMvcConfigurerAdapter { @SuppressWarnings("unused") @Autowired private TimeInterceptor timeInterceptor; // 拦截器的一个注册器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(timeInterceptor); } }这个配置类需要继承 WebMvcConfigurerAdapter,并重写添加拦截器的方法 addInterceptors,将自定义拦截器添加到应用中。 这时候拦截器就生效了。
通知分为五种类型: Before(前置通知):在目标方法被调用之前调用通知功能 After(后置通知):在目标方法完成后调用通知,无论方法是否执行成功,不会关心方法的输出是什么 After-returning(返回通知):在目标方法成功执行之后调用通知 After-throwing(异常通知):在目标方法抛出异常后调用通知 Around(通知环绕):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
连接点是一个应用执行过程中能够插入一个切面的点。 比如:方法调用、方法执行、字段设置/获取、异常处理执行、类初始化、甚至是for循环中的某个点。 理论上, 程序执行过程中的任何时点都可以作为作为织入点, 而所有这些执行时点都是Joint point, 但 Spring AOP 目前仅支持方法执行 (method execution)。
通知(advice)定义了切面何时,那么切点就是定义切面“何处” 描述某一类 Joint points, 比如定义了很多 Joint point, 对于 Spring AOP 来说就是匹配哪些方法的执行。
切面是切点和通知的结合。通知和切点共同定义了关于切面的全部内容 —— 它是什么时候,在何时和何处完成功能。
引用允许我们向现有的类添加新的方法或者属性
组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。 Spring和其他纯Java AOP框架一样,在运行时完成织入。
来看一下 Aspect 怎么写:
@Aspect @Component public class TimeAspect { @Around("execution(* club.sscai.security.web.controller.UserController.*(..))") public Object handleTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("time aspect is start."); for (Object object : proceedingJoinPoint.getArgs()) { System.out.println(object); } long startTime = System.currentTimeMillis(); Object obj = proceedingJoinPoint.proceed(); System.out.println("time aspect 耗时:" + (System.currentTimeMillis() - startTime)); System.out.println("time aspect finish."); return obj; } }@Around 定义了环绕通知,也就是定义了何时使用切面,表达式"execution(* club.sscai.security.web.controller.UserController.*(..))"定义了再哪里使用。 ProceedingJoinPoint 对象的 proceed() 方法表示执行被拦截的方法,它有一个 Object 类型的返回值,是原有方法的返回值,后期使用的时候往往需要强转。 对于上面三种拦截方式,他们的执行有一个基本的顺序,进入的顺序是: Filter-->Interceptor-->Aspect-->Controller-->Aspect-->Interceptor-->Filter(不考虑异常的发生)。 如下所示:
博客地址:http://www.cnblogs.com/niceyoo