动态代理(jdk/cglib)
什么是代理模式?
-
在开发中当我们要访问目标类的时候,不是访问目标类,而是
调用其代理类
,通过代理类调用目标类完成。简单来说就是直接调用变成间接调用。 -
那这样做最大的好处呢就是我们可以在代理类调用目标类之前和之后去添加一些预处理和后处理的操作,来扩展一些不属于目标类的功能。比如说我们可以在方法开始和结束前记录日志;在方法执行前进行额外的参数校验,进行事务管理,如手动提交,权限校验等
-
代理模式是一种设计思想,在实际实现方式上又会有静态和动态之分。
那静态代理和动态代理的区别是什么呢?
-
所谓静态代理,就是在程序运行之前,我们就给目标类编写了其代理类的代码然后编译其代理类
- 这样呢就是在程序运行之前,我们就已经生成了它代理类的字节码文件,具体到某个类了,没有动态性
-
所谓动态代理,我们就不需要事先给目标类去编写代理代码,而是在运行中呢它要通过反射自动生成代理对象
-
那比如如果我们使用JDK动态代理,只需要我们的代理来去实现invocation handler然后实现它的invoke方法就可以去代理任何你想控制访问类,而不是具体的某个类。如下
-
//jdk动态代理 class JdkDynamicProxy implements InvocationHandler { private Object targetObj; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始..........."); Object invoke = method.invoke(targetObj, args); System.out.println("结束..........."); return invoke; } /** * 产生代理对象实例 */ public Object getInstance(Object targetObj){ this.targetObj = targetObj; return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(), targetObj.getClass().getInterfaces(), this); } }
-
//cglib动态代理 class CglibDynamicProxy implements MethodInterceptor { //目标对象 private Object target; public CglibDynamicProxy(Object target) { this.target = target; } //给目标对象创建一个代理对象 public Object newProxyInstance(){ //1.工具类 Enhancer en = new Enhancer(); //2.设置父类 en.setSuperclass(target.getClass()); //3.设置回调函数 en.setCallback(this); //4.创建子类(代理对象) return en.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("CglibDynamicProxy,"+ method.getName()+"方法,执行之前"); Object returnValue = method.invoke(target, args); System.out.println("CglibDynamicProxy,"+ method.getName()+"方法,执行之后"); return returnValue; } }
-
那么jdk动态代理和cglib动态代理的区别是什么呢?
我们对上面的jdk动态代理代码进行使用
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
List<String> proxy = (List<String>) new JdkDynamicProxy().getInstance(list);
proxy.add("java");
}
对cglib进行动态代理
public static void main(String[] args) {
User userProxy = (User) new CglibDynamicProxy(new User()).newProxyInstance();
userProxy.addUser();
}
class User{
public void addUser(){
System.out.println("addUser");
}
}
JDK动态代理和CGLIB动态代理是Java中两种常见的实现动态代理的方式,它们之间有一些区别。
-
基于接口 vs 基于继承:
-
JDK动态代理只能代理接口,它是基于接口实现的。代理类实现了目标接口,并通过InvocationHandler来处理方法调用。利用拦截器(必须实现InvocationHandler接口)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
-
CGLIB动态代理可以代理类,它是基于继承实现的。代理类继承了目标类,并重写了其中的方法。利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来进行代理
-
-
目标对象类型:
-
JDK动态代理要求目标对象实现一个或多个接口,代理对象与目标对象实现了相同的接口。
-
CGLIB动态代理可以代理没有实现接口的类
-
如果想要实现JDK动态代理那么代理类必须实现接口,否则不能使用;如果想要使用CGlib动态代理,那么代理类不能使用final修饰类和方法
-
-
性能:
- JDK动态代理在生成代理对象时,需要使用反射调用目标对象的方法,这会导致一定的性能损耗。
- CGLIB动态代理通过生成目标类的子类来实现代理,避免了反射调用,因此在某些情况下,性能可能会更好(在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。)
-
依赖关系:
-
JDK动态代理是Java标准库的一部分,无需额外的依赖。
-
CGLIB动态代理需要引入CGLIB库作为依赖。
-
选择使用哪种动态代理方式取决于具体的需求和场景。如果目标对象实现了接口,且性能要求较高,可以选择JDK动态代理。如果目标对象没有实现接口,或者对性能要求不是特别苛刻,可以选择CGLIB动态代理。