一、何为AOP?
AOP(Aspect-Oriented Programming,面向切面编程)是对传统传统 OOP(Object-Oriented Programming,面向对象编程)的补充,属于一种横向扩展。其将与核心业务无关的代码,如日志记录、性能监控、事务处理等从业务逻辑代码中抽离出来,进行横向排列,从而实现低耦合,提高开发效率。
二、AOP相关术语
名称 | 说明 |
---|---|
Advice(通知) | 例如日志记录、性能监控、事务处理等增强的功能。 |
Joinpoint(连接点) | 具体执行增强的地方。例如方法调用前、调用后和方法捕获异常后。 |
Pointcut(切入点) | 匹配连接点的正则表达式,是实现增强的具体连接点的集合。 |
Aspect(切面) | 切入点和通知的结合。 |
Introduction(引介) | 一种特殊的通知,可以在运行期为类动态地添加一些方法或 Field。 |
Proxy(代理) | 一个类被 AOP 织入增强后,就产生一个结果代理类。 |
Target(目标) | 指代理的目标对象。 |
Weaving(织入) | 把切面应用到目标对象,并创建代理对象的过程。 |
三、AOP流行框架比较
当下最流行的AOP框架为Spring AOP 及 AspectJ
比较 | Spring AOP | AspectJ |
---|---|---|
能力和目标 | 旨在通过Spring IoC提供一个简单的AOP实现。 不是一个完整的AOP解决方案,只能用于被Spring容器管理的bean。 |
完整的AOP解决方案,比Spirng AOP复杂。 能够被应用于所有的领域对象。 |
织入时机 | 只能在运行时织入 | 可以在编译时,加载期,编译后(jar包和字节码文件)。 |
依赖 | 使用JDK动态代理或者VGLIB动态代理。 | 不依赖任何运行时环境,只需要引入编译器AspectJ compiler(ajc)完成织入。 |
连接点 | 使用代理模式,所以不能作用于final类、static方法、final方法。 只支持方法执行连接点。 |
没有限制。方法调用,方法执行,构造器调用,构造器执行,静态初始化执行,对象初始化,成员引用,成员赋值,处理器执行,通知执行。 |
复杂度 | 不需要额外的编译器,只能和Spirng管理的bean一起工作 AOP | 需要引入AJC编译器并重新打包 |
性能 | 基于代理的框架,运行时会有目标类的代理对象生成。 | 在应用执行前织入切面到代码中,没有额外的运行时开销。 |
四、动态代理
基于接口的动态代理
提供者:JDK官方的ava.lang.reflect.Proxy 类。
要求: 被代理类至少实现一个接口。
基于子类的动态代理
提供者:第三方的CGLib, CGLIB 要依赖于 ASM 的包。
要求:被代理类不能用final修饰的类(最终类)。
下面以一个例子来了解两者的差别:
1.创建接口UserDao
public interface UserDao {
public void add();// 添加
public void delete();// 删除
}
2.创建实现类 UserDaoImpl
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("添加用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
}
3.创建切面类MyAspect
public class MyAspect {
public void myBefore() {
System.out.println("方法执行之前...");
}
public void myAfter() {
System.out.println("方法执行之后...");
}
}
4.创建代理类 JdkBeanFactory
public class JdkBeanFactory {
public static UserDao getBean() {
// 准备目标类
final UserDaoImpl userDao = new UserDaoImpl();
// 创建切面类实例
final MyAspect myAspect = new MyAspect();
// 使用代理类,进行增强
return (UserDao) Proxy.newProxyInstance(JdkBeanFactory.class.getClassLoader(),
new Class[]{
UserDao.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
myAspect.myBefore(); // 前增强
Object obj = method.invoke(userDao, args);
myAspect.myAfter