系列十一、AOP

一、概述

1.1、官网       

        AOP的中文名称是面向切面编程或者面向方面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.2、通俗描述

        不通过修改源代码的方式,在主干功能里面添加新功能。

1.3、案例说明

系列十一、AOP_第1张图片

二、底层原理 

        AOP底层使用动态代理技术。有两种情况的动态代理:

  • 有接口情况:JDK动态代理
  • 无接口情况:CGLIB动态代理,基于类的继承,使用代理子类的形式,对父类的方法进行重写,完成方法的增强

2.1、JDK动态代理案例代码

2.1.1、UserDao

package org.star.dao;

public interface UserDao {

    /**
     * 两数相加
     * @param num1
     * @param num2
     * @return
     */
    int add(int num1,int num2);

    /**
     * 根据id修改
     * @param id
     * @return
     */
    int update(long id);

}

2.1.2、UserDaoImpl

package org.star.dao.impl;

import org.star.dao.UserDao;

public class UserDaoImpl implements UserDao {

    @Override
    public int add(int num1, int num2) {
        return num1 + num2;
    }

    @Override
    public int update(long id) {
        // do your business...
        return 1;
    }
}

 2.1.3、MyInvocationHandler

package org.star.handler;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

@Slf4j
public class MyInvocationHandler implements InvocationHandler {

    private Object obj;

    public MyInvocationHandler(Object obj) {
        this.obj = obj;
    }

    /**
     * 增强逻辑
     *
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 目标方法执行之前
        log.info("目标方法执行之前执行,目标方法名称:{},入参:{}", method.getName(), Arrays.toString(args));
        // 目标方法执行
        Object result = method.invoke(obj, args);
        // 目标方法执行之后
        log.info("目标方法执行之后执行,result:{},obj:{}",result,obj.getClass());
        return result;
    }
}

2.1.4、测试类

package org.star;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.star.dao.UserDao;
import org.star.dao.impl.UserDaoImpl;
import org.star.handler.MyInvocationHandler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

/**
 * Unit test for simple App.
 */
@Slf4j
public class AppTest {

    @Test
    public void testJDKProxy1() {
        Class[] interfaces = {UserDao.class};
        UserDaoImpl userDaoImpl = new UserDaoImpl();
        UserDao userDao = (UserDao) Proxy.newProxyInstance(AppTest.class.getClassLoader(),interfaces,new MyInvocationHandler(userDaoImpl));
        int result = userDao.add(3,5);
        log.info("result:{}",result);
    }

    @Test
    public void testJDKProxy2() {
        Class[] interfaces = {UserDao.class};
        UserDao userDao = (UserDao) Proxy.newProxyInstance(AppTest.class.getClassLoader(), interfaces, new InvocationHandler() {
            UserDaoImpl userDaoImpl = new UserDaoImpl();
            @Override
            public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
                // 目标方法执行之前
                log.info("目标方法执行之前执行,目标方法名称:{},入参:{}", method.getName(), Arrays.toString(args));
                // 目标方法执行
                Object result = method.invoke(userDaoImpl, args);
                // 目标方法执行之后
                log.info("目标方法执行之后执行,obj:{}",userDaoImpl.getClass());
                return result;
            }
        });
        int result = userDao.add(5,5);
        log.info("result:{}",result);
    }

}

2.2、Cglib动态代理案例代码

2.2.1、pom


    
      cglib
      cglib
      3.1
    
    
      aopalliance
      aopalliance
      1.0
    
    
      org.aspectj
      aspectjweaver
      1.9.19
    
    
      org.springframework
      spring-aop
      5.2.5.RELEASE
    
    
      commons-logging
      commons-logging
      1.1.1
    
    
      com.alibaba
      druid
      1.2.16
    
    
      org.springframework
      spring-beans
      5.2.5.RELEASE
    
    
      org.springframework
      spring-context
      5.2.5.RELEASE
    
    
      org.springframework
      spring-core
      5.2.5.RELEASE
    
    
      org.springframework
      spring-expression
      5.2.5.RELEASE
    


    
      junit
      junit
      4.13.2
    
    
    
      org.projectlombok
      lombok
      1.18.22
    
    
      org.slf4j
      slf4j-api
      1.7.32
    
    
      ch.qos.logback
      logback-classic
      1.2.10
    

2.2.2、Panda

package org.star.cglib;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author: 
 * @Date: 2023/8/28 14:32
 * @Description:
 */
@Slf4j
public class Panda {

    public void eat() {
        log.info("熊猫吃竹子");
    }

}

2.2.3、App

package org.star;

import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.star.cglib.Panda;

import java.lang.reflect.Method;

@Slf4j
public class App {
    public static void main( String[] args ) {
        Panda panda = new Panda();
        // 创建代理类
        Panda proxyPanda = (Panda) Enhancer.create(Panda.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                log.info("target method before...");
                Object result = method.invoke(panda, args);
                log.info("target method after...");
                return result;
            }
        });
        // 通过代理类调用目标方法实现增强
        proxyPanda.eat();
    }
}

三、AOP术语

3.1、连接点

        类里面哪些方法可以被增强,这些被增强的方法就被称为连接点。

3.2、切入点

        类里面实际真正被增强的方法,称为切入点。

3.3、通知(增强)

        实际增强的逻辑部分称为通知(增强),通知按照作用于目标方法的位置不同,可以分为如下5种类型。

3.3.1、前置通知

        前置通知因在目标方法之前执行,故名前置通知,注解为@Before。

3.3.2、后置通知

        后置通知又叫正常结束通知,即目标方法正常结束后执行,常用于做一些善后的操作,注解为@AfterReturning。

注意事项:如果方法有返回值,可以在通知中获取到方法的返回值;

3.3.3、异常通知

        异常通知故名思议是指当目标方法运行期间出现异常时才会执行,注解为@AfterThrowing。

3.3.4、最终通知

        最终通知有点儿类似于try..catch...finally中的finally代码块,不管目标方法运行期间是否出现异常,都会执行,用于兜底。注解为@After

3.3.5、环绕通知

        环绕通知是上述四个通知的集大成者,一个通知等价于上述四个通知。注解为@Around。

注意事项:

        (1)需要通过代码调用方法,在方法执行的周围进行增强;

        (2)使用代码调用连接点方法:proceedingJoinPoint.proceed();

        (3)环绕通知需要有返回值,此返回值就是方法调用的结果;

3.4、切面

        把通知应用到切入点的过程称为切面,是动作。

四、AOP案例

4.1、基于xml配置文件实现(maven)

4.1.1、pom


    
      cglib
      cglib
      3.1
    
    
      aopalliance
      aopalliance
      1.0
    
    
      org.aspectj
      aspectjweaver
      1.9.19
    
    
      org.springframework
      spring-aop
      5.2.5.RELEASE
    
    
      commons-logging
      commons-logging
      1.1.1
    
    
      com.alibaba
      druid
      1.2.16
    
    
      org.springframework
      spring-beans
      5.2.5.RELEASE
    
    
      org.springframework
      spring-context
      5.2.5.RELEASE
    
    
      org.springframework
      spring-core
      5.2.5.RELEASE
    
    
      org.springframework
      spring-expression
      5.2.5.RELEASE
    


    
      junit
      junit
      4.13.2
    

    
      com.alibaba
      fastjson
      1.2.76
    

    
    
      org.projectlombok
      lombok
      1.18.22
    
    
      org.slf4j
      slf4j-api
      1.7.32
    
    
      ch.qos.logback
      logback-classic
      1.2.10
    

4.1.2、ATM

package org.star.component;

import lombok.extern.slf4j.Slf4j;

/**
 * 目标
 */
@Slf4j
public class ATM {

    public int take(int money) {
        log.info("取钱方法正在执行...");
        if (money == 100) {
            throw new RuntimeException("自定义的异常");
        }
        return 10000;
    }

}

4.1.3、LogAspect

package org.star.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 增强类
 */
@Slf4j
public class LogAspect {

    /**
     * 前置通知:方法执行之前执行
     */
    public void beforeLog() {
        log.info("前置通知执行...");
    }

    /**
     * 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行
     */
    public void afterReturningLog(Object result) {
        log.info("后置通知(正常结束通知)执行...,返回值:{}",result);
    }

    /**
     * 异常通知
     */
    public void afterThrowingLog(Exception ex) {
        log.info("异常通知(目标方法出现异常时执行)...,异常信息:{}",ex.getMessage());
    }

    /**
     * 最终通知:不管目标方法执行期间是否有异常,都会执行
     *
     */
    public void afterLog() {
        log.info("最终通知(不管目标方法执行期间是否有异常,都会执行)...");
    }

    /**
     * 环绕通知
     * @param joinPoint
     * @return
     */
    public Object aroundLog(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            log.info("around before");
            result = joinPoint.proceed(); // 调用目标方法返回的值
            log.info("around afterReturning");
        } catch (Throwable e) {
            log.info("around afterThrowing");
        } finally {
            log.info("around after");
        }
        return result;
    }

}

4.1.4、applicationContext




    
    
    

    
    
        
        
        
        
            
            
            

            
            

            
            

            
            

            
            

        
    

4.1.5、AppTest

package org.star;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.star.component.ATM;

/**
 * Unit test for simple App.
 */
@Slf4j
public class AppTest {

    @Test
    public void aopTest() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        ATM atm = context.getBean(ATM.class);
        int take = atm.take(100);
        log.info("aopTest take:{}",take);
    }

}

4.2、基于全注解方式实现(maven)

4.2.1、pom


	
	  cglib
	  cglib
	  3.1
	
	
	  aopalliance
	  aopalliance
	  1.0
	
	
	  org.aspectj
	  aspectjweaver
	  1.9.19
	
	
	  org.springframework
	  spring-aop
	  5.2.5.RELEASE
	
	
	  commons-logging
	  commons-logging
	  1.1.1
	
	
	  com.alibaba
	  druid
	  1.2.16
	
	
	  org.springframework
	  spring-beans
	  5.2.5.RELEASE
	
	
	  org.springframework
	  spring-context
	  5.2.5.RELEASE
	
	
	  org.springframework
	  spring-core
	  5.2.5.RELEASE
	
	
	  org.springframework
	  spring-expression
	  5.2.5.RELEASE
	


	
	  junit
	  junit
	  4.13.2
	

	
	  com.alibaba
	  fastjson
	  1.2.76
	

	
	
	  org.projectlombok
	  lombok
	  1.18.22
	
	
	  org.slf4j
	  slf4j-api
	  1.7.32
	
	
	  ch.qos.logback
	  logback-classic
	  1.2.10
	

4.2.2、MySpringConfig

package org.star.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {"org.star"})
@EnableAspectJAutoProxy(proxyTargetClass = true) // 启用CGLB动态代理
public class MySpringConfig {

}

4.2.3、ATM

package org.star.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class ATM {

    public int take(int money) {
        log.info("取钱方法正在执行...");
        if (money == 100) {
            throw new RuntimeException("自定义的异常");
        }
        return 10000;
    }

}

4.2.4、LogAspect

package org.star.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@Aspect // 生成代理对象
public class LogAspect {

    /**
     * 相同的切入点进行抽取
     */
    @Pointcut(value = "execution(* org.star.component.ATM.take(..))")
    public void commonPoint() {

    }

    /**
     * 前置通知:方法执行之前执行
     */
    // @Before(value = "execution(* org.star.component.ATM.take(..))")
    // @Before(value = "commonPoint()")
    public void beforeLog() {
        log.info("前置通知执行...");
    }

    /**
     * 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行
     */
    // @AfterReturning(value = "execution(* org.star.component.ATM.take(..))",returning = "result")
    // @AfterReturning(value = "commonPoint()",returning = "result")
    public void afterReturningLog(Object result) {
        log.info("后置通知(正常结束通知)执行...,返回值:{}",result);
    }

    // @AfterThrowing(value = "execution(* org.star.component.ATM.take(..))",throwing = "ex")
    // @AfterThrowing(value = "commonPoint()",throwing = "ex")
    public void afterThrowingLog(Exception ex) {
        log.info("异常通知(目标方法出现异常时执行)...,异常信息:{}",ex.getMessage());
    }

    /**
     * 最终通知:不管目标方法执行期间是否有异常,都会执行
     * 思考:目标方法正常执行结束,控制台打印的日志如下:
     *      07:55:40.702 [main] INFO org.star.aspect.LogAspect - 前置通知执行...
     *      07:55:40.717 [main] INFO org.star.component.ATM - 取钱方法正在执行...
     *      07:55:40.717 [main] INFO org.star.aspect.LogAspect - 最终通知(不管目标方法执行期间是否有异常,都会执行)...
     *      07:55:40.717 [main] INFO org.star.aspect.LogAspect - 后置通知(正常结束通知)执行...,返回值:10000
     *  为什么最终通知先于后置通知执行?
     *  答:后置通知是在方法执行return后执行的,是不可能修改方法的返回值的,而最终通知是在目标方法返回前执行的,即便目标方法出现抛出异常,最终通知
     *  也会执行,但当抛出异常时,后置通知将不会执行。
     */
    // @After(value = "execution(* org.star.component.ATM.take(..))")
    // @After(value = "commonPoint()")
    public void afterLog() {
        log.info("最终通知(不管目标方法执行期间是否有异常,都会执行)...");
    }

    /**
     * 环绕通知
     * @param joinPoint
     * @return
     */
    // @Around(value = "execution(* org.star.component.ATM.take(..))")
    @Around(value = "commonPoint()")
    public Object aroundLog(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            log.info("around before");
            result = joinPoint.proceed(); // 调用目标方法返回的值
            log.info("around afterReturning result:{}",result);
        } catch (Throwable e) {
            log.info("around afterThrowing");
        } finally {
            log.info("around after");
        }
        return result;
    }

}

4.2.5、AppTest

package org.star;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.star.component.ATM;
import org.star.config.MySpringConfig;

/**
 * Unit test for simple App.
 */
@Slf4j
public class AppTest {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        ATM atm = context.getBean(ATM.class);
        log.info("atm:{}",atm);
        if (atm != null) {
            atm.take(1000);
        }
    }

}

4.3、基于自定义注解方式实现(springboot)

4.3.1、pom


	
		org.springframework.boot
		spring-boot-starter-web
	
	
		org.springframework.boot
		spring-boot-starter-test
		test
	

	
		org.springframework.boot
		spring-boot-starter-aop
	

	
		org.projectlombok
		lombok
		true
	
	
		org.apache.commons
		commons-collections4
		4.4
	
	
		org.apache.commons
		commons-lang3
	
	
		com.alibaba
		fastjson
		2.0.6
	
	
		cn.hutool
		hutool-all
		5.7.22
	

4.3.2、LogAnnotation

package org.star.annotation;

import java.lang.annotation.*;

/**
 * @Author: liuhaibo
 * @Date: 2023/8/28 10:16
 * @Description:
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {

    String value() default "";

}

4.3.3、UserVO

package org.star.entity.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @Author: liuhaibo
 * @Date: 2023/8/28 10:22
 * @Description:
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserVO implements Serializable {

    /**
     * 编号
     */
    private Integer id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private Integer age;

}

4.3.4、UserService

package org.star.service;

import org.star.entity.vo.UserVO;

import java.util.List;

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/8/28 7:55
 * @Description:
 */
public interface UserService {

    /**
     * 查询所有用户
     * @return
     */
    List listAllUser();

    /**
     * 根据id查询用户
     * @param id
     * @return
     */
    UserVO getUserById(Integer id);

    /**
     * 添加用户
     * @param param
     * @return
     */
    boolean saveUser(UserVO param);

    /**
     * 修改用户
     * @param param
     * @return
     */
    boolean editUser(UserVO param);

    /**
     * 根据id删除用户
     * @param id
     * @return
     */
    boolean delUserById(Integer id);
}

4.3.5、UserServiceImpl

package org.star.service.impl;

import org.springframework.stereotype.Component;
import org.star.entity.vo.UserVO;
import org.star.service.UserService;

import java.util.Arrays;
import java.util.List;

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/8/28 7:55
 * @Description:
 */
@Component
public class UserServiceImpl implements UserService {

    @Override
    public List listAllUser() {
        List users = Arrays.asList(
                new UserVO(1, "张三", 23),
                new UserVO(2, "李四", 24),
                new UserVO(3, "王五", 25)
        );
        return users;
    }

    @Override
    public UserVO getUserById(Integer id) {
        return new UserVO(1, "张三", 23);
    }

    @Override
    public boolean saveUser(UserVO param) {
        return true;
    }

    @Override
    public boolean editUser(UserVO param) {
        return true;
    }

    @Override
    public boolean delUserById(Integer id) {
        return true;
    }
}

4.3.6、UserController

package org.star.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.star.annotation.LogAnnotation;
import org.star.entity.vo.UserVO;
import org.star.service.UserService;

import java.util.List;

/**
 * @Author: liuhaibo
 * @Date: 2023/8/28 10:19
 * @Description:
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @LogAnnotation(value = "查询所有用户")
    @GetMapping("/listAllUser")
    public List listAllUser() {
        return userService.listAllUser();
    }

    @GetMapping("/getUserById/{id}")
    public UserVO getUserById(@PathVariable("id") Integer id) {
        return userService.getUserById(id);
    }

    @LogAnnotation(value = "添加用户")
    @PostMapping("/saveUser")
    public boolean saveUser(@RequestBody UserVO param) {
        return userService.saveUser(param);
    }

    @LogAnnotation(value = "修改用户")
    @PutMapping("/editUser")
    public boolean editUserById(@RequestBody UserVO param) {
        return userService.editUser(param);
    }

    @LogAnnotation(value = "根据ID删除用户")
    @DeleteMapping("/delUserById/{id}")
    public boolean delUserById(@PathVariable("id") Integer id) {
        return userService.delUserById(id);
    }

}

4.3.7、测试

        启动服务,在Postman中访问接口,观察控制台的日志输出。

五、切入点表达式

5.1、概述

5.2、语法

execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))

5.3、案例

表达式 含义
execution(* org.star.ArithmeticCalculator.*(..)) (1)ArithmeticCalculator接口中声明的所有方法
(2)第一个*代表任意修饰符及任意返回值
(3)第二个*代表任意方法
(4)..代表匹配任意数量、任意类型的参数。若目标类、接口与该切面类在同一个包中可以省略包名。
execution(public * ArithmeticCalculator.*(..)) ArithmeticCalculator接口的所有公有方法
execution(public double ArithmeticCalculator.*(..)) ArithmeticCalculator接口中返回值类型为double的方法
execution(public double ArithmeticCalculator.*(double,..)) ArithmeticCalculator接口中第一个参数为double类型的方法。第二个参数".."代表匹配任意数量、任意类型的参数
execution(public double ArithmeticCalculator.*(double, double)) ArithmeticCalculator接口中参数类型为double,double类型的方法

5.4 、高级应用

        在AspectJ中,切入点表达式还可以与逻辑运算符结合起来使用

# 任意类中第一个参数为int类型的add方法或sub方法
execution (* org.star.ArithmeticCalculator.add(int,..)) || execution(* org.star.ArithmeticCalculator.sub(int,..))

# 匹配不是任意类中第一个参数为int类型的add方法
!execution (* org.star.ArithmeticCalculator.add(int,..)) 

六、切面的优先级

6.1、概述

        如果系统定义了多个切面,如何让某些切面先运行?可以通过设置切面的优先级来改变切面的执行顺序,注解为@Order(数字),其中数字越小,优先级越高。

6.2、案例代码

6.2.1、pom


    
      cglib
      cglib
      3.1
    
    
      aopalliance
      aopalliance
      1.0
    
    
      org.aspectj
      aspectjweaver
      1.9.19
    
    
      org.springframework
      spring-aop
      5.2.5.RELEASE
    
    
      commons-logging
      commons-logging
      1.1.1
    
    
      com.alibaba
      druid
      1.2.16
    
    
      org.springframework
      spring-beans
      5.2.5.RELEASE
    
    
      org.springframework
      spring-context
      5.2.5.RELEASE
    
    
      org.springframework
      spring-core
      5.2.5.RELEASE
    
    
      org.springframework
      spring-expression
      5.2.5.RELEASE
    


    
      junit
      junit
      4.13.2
    

    
      com.alibaba
      fastjson
      1.2.76
    

    
    
      org.projectlombok
      lombok
      1.18.22
    
    
      org.slf4j
      slf4j-api
      1.7.32
    
    
      ch.qos.logback
      logback-classic
      1.2.10
    

6.2.2、ATM

package org.star.component;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class ATM {

    public int take(int money) {
        log.info("取钱方法正在执行...");
        if (money == 100) {
            throw new RuntimeException("自定义的异常");
        }
        return 10000;
    }

}

6.2.3、MySpringConfig

package org.star.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {"org.star"})
@EnableAspectJAutoProxy(proxyTargetClass = true) // 启用CGLB动态代理
public class MySpringConfig {

}

6.2.4、LogAspect

package org.star.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @Aspect : 生成代理类
 */
@Slf4j
@Component
@Aspect
@Order(1)
public class LogAspect {

    /**
     * 相同的切入点进行抽取
     */
    @Pointcut(value = "execution(* org.star.component.ATM.take(..))")
    public void commonPoint() {

    }

    /**
     * 前置通知:方法执行之前执行
     */
    // @Before(value = "execution(* org.star.component.ATM.take(..))")
    @Before(value = "commonPoint()")
    public void beforeLog() {
        log.info("log前置通知执行...");
    }

    /**
     * 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行
     */
    // @AfterReturning(value = "execution(* org.star.component.ATM.take(..))",returning = "result")
    @AfterReturning(value = "commonPoint()",returning = "result")
    public void afterReturningLog(Object result) {
        log.info("log后置通知(正常结束通知)执行...,返回值:{}",result);
    }

}

6.2.5、ArgsAspect

package org.star.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @Aspect : 生成代理类
 */
@Slf4j
@Component
@Aspect
@Order(2)
public class ArgsAspect {

    /**
     * 相同的切入点进行抽取
     */
    @Pointcut(value = "execution(* org.star.component.ATM.take(..))")
    public void commonPoint() {

    }

    /**
     * 前置通知:方法执行之前执行
     */
    // @Before(value = "execution(* org.star.component.ATM.take(..))")
    @Before(value = "commonPoint()")
    public void beforeLog() {
        log.info("args前置通知执行...");
    }

    /**
     * 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行
     */
    // @AfterReturning(value = "execution(* org.star.component.ATM.take(..))",returning = "result")
    @AfterReturning(value = "commonPoint()",returning = "result")
    public void afterReturningLog(Object result) {
        log.info("args后置通知(正常结束通知)执行...,返回值:{}",result);
    }

}

6.2.6、AppTest

package org.star;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.star.component.ATM;
import org.star.config.MySpringConfig;

/**
 * Unit test for simple App.
 */
@Slf4j
public class AppTest {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
        ATM atm = context.getBean(ATM.class);
        log.info("atm:{}",atm);
        if (atm != null) {
            atm.take(1000);
        }
    }

}

// 控制台打印结果
15:39:18.209 [main] INFO org.star.AppTest - atm:org.star.component.ATM@223aa2f7
15:39:18.213 [main] INFO org.star.aspect.LogAspect - log前置通知执行...
15:39:18.213 [main] INFO org.star.aspect.ArgsAspect - args前置通知执行...
15:39:18.224 [main] INFO org.star.component.ATM - 取钱方法正在执行...
15:39:18.224 [main] INFO org.star.aspect.ArgsAspect - args后置通知(正常结束通知)执行...,返回值:10000
15:39:18.224 [main] INFO org.star.aspect.LogAspect - log后置通知(正常结束通知)执行...,返回值:10000

七、使用AOP注意事项

(1)连接点方法不能是private,否则AOP不能进行增强;

(2)连接点在其他方法内部被调用时,不会被增强;

你可能感兴趣的:(Spring5系列,java,spring)