Skip to content

1. AOP

  • 概念:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术
  • 作用:不改变源代码基础上为方法增加功能
  • 核心概念:
    • 代理:SpringAOP 的核心本质是采用代理模式实现的
    • 连接点:任意方法的执行
    • 切入点:匹配连接点的式子,具有共性功能的方法描述
    • 通知:若干个方法的共性功能,在切入点处执行,最终体现为一个方法
    • 切面:描述通知与切入点的对应关系
    • 目标对象:被代理的原始对象

2.切入点表达式

  • 格式:动作关键字(访问修饰符 返回值 包名 . 类 / 接口名 . 方法名(参数) 异常名)
java
    execution(void com.my.dao.UserDao.update())
  • 切入点表达式通配符(以上为例)
    • *:匹配任意符号(UserDao 可以描述 *Service)
    • . . :匹配多个连续的任意符号(update(..))
    • +:匹配子类类型

3. AOP的使用

  1. 添加依赖
xml
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.22</version>
        </dependency>

<!--        aop-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
  1. 实现类(接口省略)
java
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("user dao save......");
    }

    @Override
    public void update() {
        System.out.println("user dao update......");
    }

    @Override
    public int select() {
        System.out.println("user dao select......");
        return 100;
    }
}
  1. 配置类,@EnableAspectJAutoProxy 开启 AOP
java
@Configuration
@ComponentScan("com.my")
@EnableAspectJAutoProxy
public class SpringConfig {
}
  1. 通知类(添加功能类),@Aspect :把当前类标识为一个切面类
java
@Component
@Aspect
public class MyAdvice {
    //切入点,将 save()方法添加功能
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }
    //在哪一个方法前,执行此方法,添加新功能
    @Before("pt()")
    public void method(){
        System.out.println("before......");
    }
}
  1. 测试类
java
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);

        UserDao userDao = (UserDao) applicationContext.getBean("userDao");
        userDao.save();
        //输出结果为:before......
        //user dao save......
    }

4. 通知类型和通知类型的使用

4.1 前置通知 Before
  • 前置通知 @Before
java
    //切入点
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }   
    //在哪一个方法前,执行此方法
    @Before("pt()")
    public void before(){
        System.out.println("before......");
    }
  • 前置通知获取数据(参数、返回值、异常)
java
    @Before("pt()")
    public void before(JoinPoint jp){
        Object[] args = jp.getArgs();
        System.out.println(Arrays.toString(args));
        System.out.println("before advice......");
    }
4.2 后置通知 After
  • 后置通知 @After
java
    //切入点
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }
    //在哪一个方法后,执行此方法
    @After("pt()")
    public void after(){
        System.out.println("after......");
    }
  • 后置通知获取数据(参数、返回值、异常)
java
    //切入点
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }   
    @After("pt()")
    public void after(JoinPoint jp){
        Object[] args = jp.getArgs();
        System.out.println(Arrays.toString(args));
        System.out.println("after advice......");
    }
4.3 环绕通知 Around

环绕通知:

  1. 依赖形参 ProceedingJoinPoint 才能实现对原始方法的调用

  2. 可以隔离原始方法的调用执行

  3. 返回值设为 Object 类型

  4. 可以对原始方法调用过程中出现的异常进行处理

  • 环绕通知 @Around
java
    //切入点
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }       
    //环绕
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("before......");
        Object proceed = pjp.proceed();
        System.out.println("after......");
        return proceed;
    }
  • 环绕通知获取数据(参数、返回值、异常)
java
    //切入点
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }       
    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        System.out.println(Arrays.toString(args));
        
        //此处可对数据进行修改
        
        Object ret = pjp.proceed(args);
        return ret;
    }
4.4 返回后通知 AfterReturning
  • 返回后通知 @AfterReturning
java
    //切入点
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }    
    @AfterReturning("pt()")
    public void afterReturning() {
        System.out.println("afterReturning advice......");
    }
  • 返回后通知获取数据(参数、返回值、异常)
java
    //切入点
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }   
    @AfterReturning(value = "pt()", returning = "o")
    public void afterReturning(Object o) {
        System.out.println("afterReturning advice......" + o);
    }
4.5 抛出异常后通知 AfterThrowing
  • 抛出异常后通知 @AfterThrowing
java
    //切入点
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }   
    @AfterThrowing("pt()")
    public void afterThrowing() {
        System.out.println("afterThrowing advice......");
    }
  • 抛出异常后通知获取数据(参数、返回值、异常)
java
       //切入点
    @Pointcut("execution(void com.my.dao.UserDao.save())")
    private void pt() {
    }   
    @AfterThrowing(value = "pt()",throwing = "t")
    public void afterThrowing(Throwable t){
        System.out.println("afterThrowing advice......"+t);
    }