Spring中AOP和拦截器

88 阅读2分钟

AOP: Aspect Oriented Programming 面向切面编程

这个概念听起来有点抽象,说通俗一点,就是在程序运行期间,在不修改原有代码的情况下,增强跟主要业务没有关系的公共功能代码(无侵入),到之前写好的方法中的指定位置.

1.添加pom依赖

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.5</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.3.RELEASE</version>
     </dependency> 

2.配置xml文件(因为我的这个是一个ssm整合的project,所以直接就在springmvc.xml中添加了)

image.png

3.编写切面类

package com.cctv.util;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
public class LogUtil {
    @Before("execution(* com.cctv.service..*.*(..))")
    public static void before(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        String name = joinPoint.getSignature().getName();

        System.out.println("param: "+ Arrays.asList(args));
        System.out.println("method: "+name);
        System.out.println("方法前");
    }

    // 后置通知
    @After("execution(* com.cctv.service..*.*(..))")
    public static void after(){

        System.out.println("方法后");
    }

    // 后置异常通知
    @AfterThrowing("execution(* com.cctv.service..*.*(..))")
    public static void afterException(){


        System.out.println("方法异常");
    }

    // 后置返回通知
    @AfterReturning("execution(* com.cctv.service..*.*(..))")
    public static void afterEnd() {

        System.out.println("方法返回");
    }
}

使用AOP必须添加@Component和@Aspect注解

@Before: 在目标方法之前运行

@After: 在目标方法之后运行

@AfterReturing: 在目标方法正常返回之后运行

@AfterThrowing: 在目标方法抛出异常之后运行

这里就有一个疑问,所谓目标方法是什么呢?其实就是execution表达式所指的.

image.png

image.png

这是运行了tomcat服务器,发送了某一个请求,首先会经过Controller层,然后到达Service层,由于execution表达式设置了会对Service层的所有类所有方法,所以会先触发@Before中的方法,接着就会走到Mapper层,在Mapper层把方法执行完毕之后,就会执行@After和@AfterReturing中的方法.

image.png

这里提一下AOP实现原理,是JDK动态代理和cglib代理.

如果是一个接口且有实现类,就会触发底层JDK动态代理,如果是一个类,没有子类,就会触发cglib代理.图中的Bean都是对应子类. image.png

SpringMVC拦截器

1.自定义一个拦截器,并且实现HandlerInterceptor,重写perHandle,postHandle,afterCompletion三个方法.

image.png

2.配置xml文件

image.png

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/login"/>
        <bean class="com.cctv.interceptor.CheckLoginInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

上图代表拦截所有的路径,除了/login.

执行顺序: 拦截器preHandle-->执行目标方法-->拦截器postHandle-->执行页面跳转-->拦截器afterCompletion方法

dispatcherServlet-->preHandle-->controller-->postHandle(在return ModelAndView之前执行,可以修改ModelAndView的值)

-->afterCompletion(在Controller的return之后,但是在filter之前)

如果preHandle方法返回false,意味着不放行,后续所有操作会中断,如果执行方法出现异常,后续流程不会执行,但是afterCompletion会执行.

如果有多个拦截器,执行顺序按照配置顺序:

preHandle按照顺序执行

postHandle按照逆序执行

afterCompletion按照逆序执行

关于 过滤器, 拦截器, AOP三者的执行顺序大致如下图

image.png