学习链接:https://www.liaoxuefeng.com/wiki/1252599548343744/1266265125480448

大部分内容来自此教程,以下只是我的学习笔记。

概念

AOP(Aspect Oriented Programming),面向切面编程。与OOP(Object Oriented Programming) 不同的是,OOP把系统视为不同对象之间的交互,而AOP将系统分解为不同的关注点(即,切面)。

使用AOP:有一些业务的公共方法(如权限检查等),每次都要写入业务逻辑中很繁琐。将这些公共逻辑视为切面,以某种自动化的方式,把切面织入到核心逻辑中,实现Proxy模式。

将切面织入Service中,有三种方式:

  • 编译器
  • 类加载器
  • 运行期:通过JVM的动态代理或者第三方库实现运行期动态织入

最简单的方式是第三种,Spring的AOP实现就是基于JVM的动态代理

术语解释

关于AOP的一些概念解释:

  • Aspect(切面): 一个横跨多个核心逻辑的功能
  • JointPoint(连接点):定义何处插入切面
  • PointCut(切入点):一组连接点的集合
  • Advice(增强):特定连接点上执行的动作
  • Introduction(引介):为一个已有的Java对象动态添加新的接口
  • Weaving(织入):将切面整合到执行流程中
  • Interceptor(拦截器):一种实现增强的方式
  • Target Object(目标对象):真正执行业务的核心逻辑对象
  • AOP Proxy(AOP代理):客户端持有的增强后的对象引用

使用AspectJ实现AOP

  • 定义代理方法,通过AspectJ的注解标明在何时调用
  • 加上@Component和@Aspect注解
  • 在@Configuration类上标注@EnableAspectJAutoProxy

使用Annotation装配AOP

上面说的AspectJ装配方法比较复杂,而且因为是通过在切面方法中加作用范围的注解来控制,容易造成误伤且作用范围对于新的方法来说不清晰。因此较少用。实际上更广泛使用的方法是在需要使用AOP的方法或类上加注解来装配。类似于数据库的事务注解@Transactional。

文章中举了这个例子:

  1. 定义注解
1
2
3
4
5
@Target(METHOD)
@Retention(RUNTIME)
public @interface MetricTime {
    String value();
}
  1. 在需要装配的方法上加此注解
1
2
3
4
5
6
7
8
9
@Component
public class UserService {
    // 监控register()方法性能:
    @MetricTime("register")
    public User register(String email, String password, String name) {
        ...
    }
    ...
}
  1. 定义切面实现
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Aspect
@Component
public class MetricAspect {
    @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");
        }
    }
}

其中,@Around("@annotation(metricTime)")表示该切面实现是以around方法注入定义了MetricTime注解的方法中。这样就更清晰了,通过主动设置注解来定义使用的切面。