在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
相关术语:
- 横切关注点:诸如日志记录、权限校验等,这些影响多个模块,但是并不参与的模块的实现的定义。
- 切面:模块化的横切逻辑单元,包含切入点和通知。
- 目标:被代理的原始对象,包含核心业务逻辑。
- 代理:由 Spring 生成的增强对象,包装目标对象,负责插入切面逻辑。
- 连接点:程序执行过程中的具体节点。
- 切入点:通过表达式筛选连接点的规则,决定哪些连接点会被切面增强。
/**
* @auther macrozheng
* @description 用于生产MBG的代码
* @date 2018/4/26
* @github https://github.com/macrozheng
*/
public class Generator {
public static void main(String[] args) throws Exception {
//MBG 执行过程中的警告信息
List<String> warnings = new ArrayList<String>();
//当生成的代码重复时,覆盖原代码
boolean overwrite = true;
//读取我们的 MBG 配置文件
InputStream is = Generator.class.getResourceAsStream("/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(is);
is.close();
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
//创建 MBG
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
//执行生成代码
myBatisGenerator.generate(null);
//输出警告信息
for (String warning : warnings) {
System.out.println(warning);
}
}
}
通知
Spring AOP 支持以下 5 种通知类型,按执行顺序排列:
| 通知类型 | 执行时机 | 使用注解 | 是否可修改返回值 |
|---|---|---|---|
| 环绕 | 包裹目标方法(必须手动调用目标方法) | @Around | ✅ |
| 前置 | 目标方法执行前 | @Before | ❌ |
| 后置 | 目标方法执行后(无论是否异常) | @After | ❌ |
| 返回 | 目标方法正常返回后 | @AfterReturning | ❌ |
| 异常 | 目标方法抛出异常后 | @AfterThrowing | ❌ |
切点表达式
定义哪些方法需要被切面增强。
语法结构:
execution(修饰符 返回类型 包名.类名.方法名(参数类型) 异常类型)
代理
AspectJ
完整的 AOP 框架,提供比 Spring AOP 更强大的功能。
实现方式:
- 编译时织入(CTW):通过专用编译器(ajc)直接修改字节码。
- 加载时织入(LTW):在类加载时通过 Java Agent 修改字节码。
功能特性:
- 支持所有连接点(方法、构造器、字段访问等)。
- 支持
call(方法调用)和execution(方法执行)等更细粒度的切点。 - 支持注解驱动和原生语法两种配置方式。
JDK 动态代理
Java 标准库提供的代理技术,基于接口实现.
- 实现方式:
- 使用
java.lang.reflect.Proxy 类生成代理对象。 - 代理类实现目标接口,拦截所有接口方法调用。
- 使用
- 限制:
- 只能代理接口中定义的方法。
- 目标类必须实现至少一个接口。
CGLIB 动态代理
- 定位:第三方库(已集成到 Spring 中),基于继承实现。
- 实现方式:
- 通过 ASM 字节码框架生成目标类的子类。
- 重写父类方法实现拦截。
- 优势:
- 可代理无接口的普通类。
- 方法调用效率高于 JDK 动态代理(通过 FastClass 机制避免反射)。