AOP 编程

AOP 编程

专业名词介绍:

  • Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点;
  • Joinpoint:连接点,即定义在应用程序流程的何处插入切面的执行;
  • Pointcut:切入点,即一组连接点的集合;
  • Advice:增强,指特定连接点上执行的动作;
  • Introduction:引介,指为一个已有的Java对象动态地增加新的接口;
  • Weaving:织入,指将切面整合到程序的执行流程中;
  • Interceptor:拦截器,是一种实现增强的方式;
  • Target Object:目标对象,即真正执行业务的核心逻辑对象;
  • AOP Proxy:AOP代理,是客户端持有的增强后的对象引用。

1. 定义切面

  1. 引入相关支持
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 开启注解支持

配置类或启动类添加 @EnableAspectJAutoProxy

  1. 定义切面并添加增强方法
@Aspect
@Component
public class LogingAspect {
    // 在执行PersonService的每个方法前执行:
    @Before("execution(public * com.zbm.test.controller.PersonService.*(..))")
    public void doAccessCheck() {
        System.err.println("[Before] do access check...");
    }

    // 在执行PersonService的每个方法前后执行:
    @Around("execution(public * com.zbm.test.controller.PersonService.*(..))")
    public Object doLogging(ProceedingJoinPoint pjp) throws Throwable {
        System.err.println("[Around] start " + pjp.getSignature());
        Object retVal = pjp.proceed();
        System.err.println("[Around] done " + pjp.getSignature());
        return retVal;
    }
}

以上就是 Spring 实现 AOP 编程的注解方法,这是其中最为简单的一种方法,其底层主要是 JDK 动态代理或者 CGLIB 动态代理来实现,使用哪一种取决于委托类是否可继承。

  1. 其他增强类型
  • @Before:这种拦截器先执行拦截代码,再执行目标代码。如果拦截器抛异常,那么目标代码就不执行了;
  • @After:这种拦截器先执行目标代码,再执行拦截器代码。无论目标代码是否抛异常,拦截器代码都会执行;
  • @AfterReturning:和@After不同的是,只有当目标代码正常返回时,才执行拦截器代码;
  • @AfterThrowing:和@After不同的是,只有当目标代码抛出了异常时,才执行拦截器代码;
  • @Around:能完全控制目标代码是否执行,并可以在执行前后、抛异常后执行任意拦截代码,可以说是包含了上面所有功能。

2. 使用注解装配 AOP

  1. 定义注解
/**
 * 监控时间
 *
 * @author Andrew
 * @date 2022/6/22
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MetricTime {
    String value();
}
  1. 定义切面
/**
 * 监控时间切面
 *
 * @author Andrew
 * @date 2022/6/22
 */
@Aspect
@Component
public class MetricAspect {

    /**
     * 环绕通知
     *
     * @param joinPoint  连接点
     * @param metricTime 注解
     * @return Object
     * @throws Throwable 异常
     */
    @Around("@annotation(metricTime)")
    public Object metric(ProceedingJoinPoint joinPoint, MetricTime metricTime) throws Throwable {
        String name = metricTime.value();
        long start = System.currentTimeMillis();
        try {
            return joinPoint.proceed();
        } finally {
            long t = System.currentTimeMillis() - start;
            // 写入日志或发送至JMX:
            System.err.println("[Metrics] " + name + ": " + t + "ms");
        }
    }
}
  1. 使用注解
/**
 * @author Andrew
 * @date 2022/6/16
 */
@Component
public class PersonService {


    @MetricTime("register")
    public void outputPersonInfo(Person person) {
        if (person != null) {
            System.out.println(person.toString());
        }
    }
}

类似于 @Translation 开启事务注解一样,在方法上加了此注解将会开启方法增强,比上一种通过表达式来模糊匹配更为精准一些。

3. 使用注意事项

  • 访问被注入的Bean时,总是调用方法而非直接访问字段;
  • 编写Bean时,如果可能会被代理,就不要编写public final方法。

引用:
https://www.liaoxuefeng.com/wiki/1252599548343744/1266265125480448

https://zhuanlan.zhihu.com/p/161705262

posted @ 2022-07-01 21:38  生活是很好玩的  阅读(287)  评论(0编辑  收藏  举报