之前写过一篇关于JDK动态代理分析。juejin.cn/post/685736…
今天想聊一下JDK动态代理和CGLIB动态代理区别
JDK动态代理
public class MyProxy<T> implements InvocationHandler {
/**
* 被代理的对象
*/
private T t;
public Object getProxyObj(T t){
this.t = t;
return Proxy.newProxyInstance(t.getClass().getClassLoader(),
t.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("begin");
method.invoke(t,args);
return null;
}
}
CGLI动态代理
public class MyMethodInterceptor implements MethodInterceptor {
public Object getInstance(Class clazz) {
// 在指定目录下生成动态代理类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java\\java_workapace");
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 设置目标类的字节码文件
enhancer.setSuperclass(clazz);
// 设置回调
enhancer.setCallback(this);
// 创建代理类
// 第一步、生成源代码
// 第二步、编译成class文件
// 第三步、加载到JVM中,并返回被代理对象
return enhancer.create();
}
/**
* obj:cglib生成的代理对象
* method:被代理对象方法
* objects:方法入参
* methodProxy: 代理方法
*/
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======begin======");
Object object = methodProxy.invokeSuper(obj, objects);
System.out.println("======end======");
return object;
}
}
JDK动态代理代理
1)实现InvocationHandler接口,重写invoke()
2)使用Proxy.newProxyInstance()产生代理对象
3)被代理的对象必须要实现接口
CGLib 必须依赖于CGLib的类库,需要满足以下要求:
1)实现MethodInterceptor接口,重写intercept()
2)使用Enhancer对象.create()产生代理对象
测试
public class Test {
public static void main(String[] args) throws Exception {
MyProxy mp = new MyProxy();
IUserService u = (IUserService)mp.getProxyObj(new UserServiceImpl());
u.addUser();
MyMethodInterceptor myt = new MyMethodInterceptor();
Dog proxyDog = (Dog)myt.getInstance(Dog.class);
proxyDog.eat();
}
}
通过2段代码
IUserService u = (IUserService)mp.getProxyObj(new UserServiceImpl());
Dog proxyDog = (Dog)myt.getInstance(Dog.class);
我们发现JDK的动态代理是基于接口的,要求被代理的类要实现一个接口而CGLIB的动态代理是基于类的,它不要求被代理的类实现接口,通过派生出子类实现代理。并对父类方法进行增强,因此被代理的类不能有final关键字所修饰
这里的动态是针对静态代理而言的,静态代理是在编译阶段就确定的,二动态代理在运行阶段才确定,这样就大大的增加了程序的灵活性。使用过Spring的人都知道,AOP就是动态代理的运用。
不管是JDK的动态代理还是CGLIB的动态代理都是字节码技术,通过操作字节码,从而生成代理对象。
提到字节码大家应该很熟悉吧,Java是跨平台语言,之所以跨平台是因为每个平台都有自己的jvm,jvm是用来执行字节码解释工作。一个.Java源文件经过编译后成.class文件.class文件可以被不同平台的jvm解释执行。.java文件经过javac命令变成.class文件,javac命令就是字节码技术,CGLIB是第三方字节码类库
强制使用CGLIB实现AOP的方法
1)添加CGLIB库
2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>