你真的懂AOP吗?

1 AOP概念

AOP(Aspect Oriented Programming) 面向切面编程,是OOP(Object Oriented Programming,面向对象编程)的补充和完善。AOP技术利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,即切面。所谓"切面"可减少系统的重复代码,降低模块之间的耦合度 ,并有利于未来的可操作性和可维护性。横切关注点经常发生在核心关注点的多处,且各处基本相似,比如权限认证、日志、事物。


2 代理设计模式

要想明白aop实现原理,一定要掌握代理模式,其中代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。下面是代理模式的简单类图与调用过程。


3 代理实现技术

Java编程中的实现代理模式分为静态代理、动态代理两种,静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了,而动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象,这就需要代码具备能动态创建一个类的能力。首先,我们先温习下Java从编译到运行的整个过程,编译Java文件之后产生.class 文件在磁盘中, JVM虚拟机读取字节码文件,取出二进制数据加载到内存中,解析.class 文件内的信息,生成对应的 Class对象,然后再实例化的过程的例子。那么,如果我们在运行期系统中遵循Java1编译系统组织.class文件的格式和结构,生成相应的二进制数据,然后再把这个二进制数据加载转换成对应的类,这样就具有了在代码中动态创建一个类的能力。

采用JDK自带的Proxy类方法,其基本流程为:主函数-->代理-->目标对象。对于Proxy类(被代理类)有一个使用前提:目标对象必须要实现接口,利用Proxy实现AOP的主要步骤如下:创建接口->创建接口实现类->创建代理工厂类。通过查看生成的代理类的字节码文件可以看到,生成的代理类是继承了Proxy主类和实现了指定的Dao接口,在初始化的时候将会加载被代理类的方法生成Method对象,Proxy父类中保存了实际处理的invoke方法, 所以外部调用代理类定义的方法时,Method元数据与方法参数都将会转发到方法代理类的定义好的invoke方法中。
// 1.首先实现一个InvocationHandler,方法调用会被转发到该类的invoke()方法
public class DaoInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)  {
       return method.invoke(object, args);
    }
}
//2.然后在需要使用Dao的时候,通过JDK动态代理获取Dao的代理对象
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //开启debug模式,保存动态生成的代理类字节码文件
Dao dao = new DaoImpl();
Dao proxyDao = (Dao) Proxy.newProxyInstance(
getClass().getClassLoader(),// 1. 类加载器
        new Class[]{Dao.class}, // 2. 代理需要实现的接口,可以有多个
        new DaoInvocationHandler(dao));// 3. 方法调用的实际处理者
复制代码



Cglib(Code Generation Library)是一个优秀的动态代理框架,是一个强大的,高性能的,高质量的Code生成类库,可以在运行期扩展Java类与实现Java接口。它的底层使用ASM在内存中动态的生成被代理类的子类,使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。CGLIB具有简单易用的特点,它的运行速度要远远快于JDK的Proxy动态代理。asm.jar–CGLIB的底层实现,cglib.jar – CGLIB的核心jar包。CGLIB的核心类:
net.sf.cglib.proxy.Enhancer–主要的增强类
net.sf.cglib.proxy.MethodIntercepto –主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy–JDK的Java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用;
实现AOP功能步骤如下所示:引入相关jar文件->创建实体类->创建CGLib代理类->创建入口类进行测试。
// 1. 首先实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
class DaoMethodInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy).{
        return proxy.invokeSuper(obj, args);
    }
}
// 2. 然后在需要使用HelloConcrete的时候,通过CGLIB动态代理获取代理对象。
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./$DaoProxy.class");
Enhancer enhancer = new Enhancer(); //1.新建代理增强对象
enhancer.setSuperclass(DaoImpl.class); //2.设置被代理类
enhancer.setCallback(new DaoMethodInterceptor());  //3.插入方法拦截对象
Dao dao = (DaoImpl)enhancer.create(); //4.获取被代理对象
复制代码

通过查看生成的代理类的字节码文件可以看到,cglib生成了三个类,有兴趣的可以自行了解一下cglib的机制。生成的代理类是通过继承被代理类实现的,所以需要指定父类。在初始化的时候也一样会加载被代理类的方法生成Method对象,所以外部调用代理类定义方法时,会先去执行拦截方法然后才是真正的被代理类的方法。

4.总结

本篇介绍了AOP的概念,代理的设计模式与实现技术,使用了jdk proxy与cglib的接口用来做些简单的代理实现并分析了原理,格物而致知,积小步而致千里。

下一期我们将模拟spring-aop的实现,手撸个小型的aop框架出来。敬请期待!







猜你喜欢

转载自juejin.im/post/5eef189c51882565cd468da7