目录
一、 JDK和CGLIB动态代理原理
二、InvocationHandler接口详解
三、Proxy类详解
四、 代码示例
五、JDK和CGLIB动态代理总结
简单来说:
JDK动态代理只能对实现了接口的类生成代理,而不能针对类;
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承);
1、JDK动态代理
利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
2、CGLiB动态代理
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
3、何时使用JDK还是CGLiB?
4、如何强制使用CGLIB实现AOP?
5、JDK动态代理和CGLIB字节码生成的区别?
6、CGlib比JDK快?
7、Spring在选择用JDK还是CGLiB的依据:
InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。
看下官方文档对InvocationHandler接口的描述:
{@code InvocationHandler} is the interface implemented by
the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler.
When a method is invoked on a proxy instance, the method
invocation is encoded and dispatched to the {@code invoke}
method of its invocation handler.
每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用,看如下invoke方法:
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
Proxy类就是用来创建一个代理对象的类,它提供了很多方法,但是我们最常用的是newProxyInstance方法。
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
这个方法的作用就是创建一个代理类对象,它接收三个参数,我们来看下几个参数的含义:
接口:
package com.proxy;
/**
* 用户管理接口
*/
public interface UserManager {
//新增用户抽象方法
void addUser(String userName,String password);
//删除用户抽象方法
void delUser(String userName);
}
实现类:
package com.proxy;
/**
* 用户管理实现类,实现用户管理接口(被代理的实现类)
*/
public class UserManagerImpl implements UserManager{
//重写用户新增方法
@Override
public void addUser(String userName, String password) {
System.out.println("调用了用户新增的方法!");
System.out.println("传入参数:\nuserName = " + userName +", password = " + password);
}
//重写删除用户方法
@Override
public void delUser(String userName) {
System.out.println("调用了删除的方法!");
System.out.println("传入参数:\nuserName = "+userName);
}
}
JDK代理实现:
package com.proxy.JDKProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK动态代理实现InvocationHandler接口
*/
public class JdkProxy implements InvocationHandler {
private Object targetObject; //需要代理的目标对象
//定义获取代理对象的方法(将目标对象传入进行代理)
public Object getJDKProxy(Object targetObject){
//为目标对象target赋值
this.targetObject = targetObject;
//JDK动态代理只能针对实现了接口的类进行代理,newProxyInstance 函数所需参数就可看出
Object proxyObject = Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
//返回代理对象
return proxyObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK动态代理,监听开始!");
// 调用invoke方法,result存储该方法的返回值
Object result = method.invoke(targetObject,args);
System.out.println("JDK动态代理,监听结束!");
return result;
}
// public static void main(String[] args) {
// JdkProxy jdkProxy = new JdkProxy(); //实例化JDKProxy对象
// UserManager user = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl()); //获取代理对象
// user.addUser("admin","123456");
// }
}
CGLIB代理实现:
package com.proxy.CglibProxy;
import com.proxy.UserManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Cglib动态代理:
* (需要导入两个jar包,asm-5.0.3.jar,cglib-3.1.jar 版本可自行选择)
*/
//Cglib动态代理,实现MethodInterceptor接口
public class CglibProxy implements MethodInterceptor {
private Object target;//需要代理的目标对象
//重写拦截方法
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib动态代理,监听开始!");
Object result = method.invoke(target,args);//方法执行参数:target 目标对象 arr参数数组
System.out.println("Cglib动态代理,监听结束!");
return result;
}
//定义获取代理对象的方法
public UserManager getCglibProxy(Object targetObject) {
this.target = targetObject;//为目标对象target赋值
Enhancer enhancer = new Enhancer();
//设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
enhancer.setSuperclass(targetObject.getClass()); //UserManagerImpl
enhancer.setCallback(this);//设置回调
Object result = enhancer.create();//创建并返回代理对象
return (UserManager) result;
}
// public static void main(String[] args) {
// CglibProxy cglibProxy = new CglibProxy(); //实例化CglibProxy对象
// UserManager user = cglibProxy.getCglibProxy(new UserManagerImpl());//获取代理对象
// user.delUser("admin");
// }
}
客户端测试类:
package com.proxy;
import com.proxy.CglibProxy.CglibProxy;
import com.proxy.JDKProxy.JdkProxy;
public class ClientTest {
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy(); //实例化JDKProxy对象
UserManager userJdk = (UserManager) jdkProxy.getJDKProxy(new UserManagerImpl()); //获取代理对象
userJdk.addUser("admin","123456");
CglibProxy cglibProxy = new CglibProxy(); //实例化CglibProxy对象
UserManager userCglib = cglibProxy.getCglibProxy(new UserManagerImpl());//获取代理对象
userCglib.delUser("admin");
}
}
运行结果:
JDK动态代理,监听开始!
调用了用户新增的方法!
传入参数:
userName = admin, password = 123456
JDK动态代理,监听结束!
Cglib动态代理,监听开始!
调用了删除的方法!
传入参数:
userName = admin
Cglib动态代理,监听结束!
1. JDK动态代理只能对实现了接口的类生成代理,而不能针对类 ,使用的是 Java反射技术实现,生成类的过程比较高效。
2. CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 ,使用asm字节码框架实现,相关执行的过程比较高效,生成类的过程可以利用缓存弥补,因为是继承,所以该类或方法最好不要声明成final
3. JDK代理是不需要第三方库支持,只需要JDK环境就可以进行代理,使用条件:
4. CGLib必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法,是一种继承但是针对接口编程的环境下推荐使用JDK的代理;
参考:
JDK和CGLIB动态代理区别
Spring的两种动态代理:JDK和CGLIB的区别和实现