参考&详细资料:
java 代理 理解原理及实现
JDK的Proxy技术实现AOP,InvocationHandler和Proxy详解——Spring AOP(三)
JAVA JDK的动态代理反射实现
Spring AOP中的JDK和CGLib动态代理哪个效率更高?
一、静态代理
由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
代理模式最主要的就是有一个公共接口,一个具体的类,一个代理类,代理类持有具体类的实例,代为执行具体类实例方法。
上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指不直接调用实际对象的方法,那么我们在代理过程中就可以加上一些其他用途。
二、动态代理
代理类在程序运行时创建的代理方式被称为动态代理。
1、JDK代理
要实现动态代理首先要求代理对象需要实现接口,即需要有一个接口类
和一个实现类
。其次,需要一个类实现InvocationHandler接口
用来进行代理事务的处理。该类需要通过构造方法获得目标类对象的引用
,同时实现invoke方法
。之后利用Proxy的工厂方法newProxyInstance()创建一个代理对象
,这个对象同样可以实现对具体类的代理功能。
实现原理:
JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy
和 InvocationHandler
。InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。 Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象。
实现过程:
- 通过实现InvocationHandler接口创建自己的
调用处理器
; - 通过为Proxy类指定ClassLoader对象和一组interface来
创建动态代理
; - 通过
反射机制
获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型; - 通过构造函数
创建动态代理类实例
,构造时调用处理器对象作为参数传入;
程序示例:
//接口类 计算接口
interface ICal {
public int Cal(int a, int b);
}
//加法实现
class ClassAdd implements ICal {
@Override
public int Cal(int a, int b) {
System.out.println("ClassAdd!");
return a+b;
}
}
//减法实现
class ClassSub implements ICal {
@Override
public int Cal(int a, int b) {
System.out.println("ClassSub!");
return a-b;
}
}
//动态代理类,实现InvocationHandler接口
class MyInvocationHandler implements InvocationHandler {
private Object target;
public DynamicProxy(Object ac) {
this.target = ac;
}
//动态生成代理对象
public Object getDynamicProxyObject() {
return Proxy.newProxyInstance(this.target.getClass().getClassLoader(),this.target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy,Method method,Object[] arg) throws Throwable {
//调用之前可以做一些处理
System.out.println("Method before!");
Object result = method.invoke(target,arg);
//调用之后也可以做一些处理
System.out.println("Method after!");
return result;
}
}
参数说明:
newProxyInstance()方法的三个参数
:
第一个参数loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
第二个参数interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了;
第三个参数:这个动态代理在调用方法时所关联的InvocationHandler对象。
invoke()方法参数
:
proxy:所代理的真实对象;
method:所要调用真实对象的某个方法的Method对象;
args:调用真实对象某个方法时接收的参数。
可以看出在动态生成代理对象时用到了反射机制。
测试代码:
class DynamicProxyTest {
//创建加法类的代理对象
ICal calProxyObject = (ICal) new MyInvocationHandler(new ClassAdd()).getDynamicProxyObject();
//调用加法类的计算方法
int add = calProxyObject.Cal(5,3);
System.out.println(add);
}
补充:
实际上,代理对象的创建需要类加载器,目标接口类以及InvocationHandler对象,整个过程可以用一行代码完成
ICal calProxyObject = (ICal)Proxy.newInstance(ClassAdd.class.getClassLoader(),Class<?>[] {ICal.class},MyInvocationHandler(new ClassAdd));
Proxy.newInstance()方法之所以需要以上三个参数是和代理对象的生成流程有关。
newInstance()方法会调用Proxy的静态方法getProxyClass(),这个方法需要类加载器
和目标接口类
,用来生成代理对象的class文件
,并将其加载到虚拟机,这个过程中还给这个代理类起了个类名和包名。实际上完成这个过程后已经完成了代理类的生成,每个代理类都继承自Proxy
,Proxy中包含有InvocationHandler接口的引用,接下来只需要通过代理类的构造方法将InvocationHandler对象传进去即可完成代理对象的生成
。
注意!!:
要利用Proxy产生某个对象的代理对象,这个对象必须实现一个接口,Proxy动态代理技术只能基于接口进行代理。
2、CGLIB代理
CGLIB相比于JDK原生动态代理,不要求代理对象必须实现某个接口。
CGLIB采用继承
的方式完成代理,即可理解为代理类是原类的子类
。
CGLIB代理主要分为两步,首先定义一个类实现MethodInterceptor接口,并实现intercept()方法。然后再使用待代理对象时通过CGLIB获取代理对象。
包括如下代码:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloConcrete.class); //设置enhancer对象的父类
enhancer.setCallback(new MyMethdInterceptor()); //设置enhancer的回调对象
HelloConcrete proxy = (HelloConcrete) enhancer.create(); //创建代理对象
proxy.sayHello(); //通过代理对象调用目标方法
代理对象的所有非final方法的调用都会转发给MethodInterceptor.intercept()方法
。
public Object intercept (Object obj, java.lang.reflect.Method method,Object[] args,MethodProxy proxy) throws Throwable;
Intercept方法的4个参数
:
1)obj表示增强的对象,即实现这个接口类的一个对象;(即生成的代理对象)
2)method表示要被拦截的方法;
3)args表示要被拦截方法的参数;
4)proxy表示要触发父类的方法对象(即代理方法)。
使用:
利用CGLIB中的Enhancer字节码生成器生成代理对象,它是被代理类的子类。代理对象的所有非final方法的调用都会回调给intercept()方法进行拦截,该方法由自定义MethodInterceptor接口实现类实现,并加入自己的代理逻辑。
原理:
cglib封装了ASM,可以在运行期动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
底层:
使用字节码处理框架ASM来转换字节码并生成新的类。
缺点:
对于final方法,无法进行代理。
详细可参考:
CGLIB动态代理实现原理
三、总结与对比
1、三种方式对比:
2、JDK与CGLIB性能对比
Spring AOP中的JDK和CGLib动态代理哪个效率更高?
3、3. Spring AOP中的动态代理:
Spring里面有一个AOP编程。即面向切面编程,其实就是动态代理
。当我们交给Spring一个对象,它就会返回代理
给我们,它在返回代理对象的时候,首先会检查我们这个对象有没有实现一个接口,如果我们这个类有接口,它使用Java的动态代理
技术来帮我们构建出代理对象;如果我们这个类没有实现接口,它会使用CGLIB
这套API,采用创建子类的方式来创建代理对象。
所以我们所实现AOP动态代理的两种方式就是JDK的Proxy和CGLIB。