欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > JDK动态代理与CGLib动态代理

JDK动态代理与CGLib动态代理

2024/10/26 7:26:51 来源:https://blog.csdn.net/weixin_74261199/article/details/140782280  浏览:    关键词:JDK动态代理与CGLib动态代理

一、什么是动态代理?

动态代理(Dynamic Proxy)是在程序运行时给目标对象创建代理对象,代理对象可以对方法进行增强、控制等。在生成代理对象的过程中,目标对象不变,代理对象中方法对目标对象方法进行增强。代理对象执行目标方法时会被拦截,转而调用写好的代理方法。

动态代理的实现方式有两种:JDK代理、CGLib代理

二、JDK动态代理

特点:基于 接口 实现(implement)的动态代理,即 生成的代理对象必须实现指定的接口

生成代理对象方法Proxy类中的静态方法newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h));
  • ClassLoader loader:要代理对象的类加载器

  • Class<?>[] interfaces:代理对象实现的接口

  • InvocationHandler h:调用处理器,代理对象每次执行方法都会被拦截,将相关信息传递给调用处理器的invoke方法

特别注意在调用处理器的 invoke方法中尽量别调用代理对象的任何方法 ,因为在任何地方执行代理对象的方法都会被拦截,然后将调用信息传递给调用处理器的invoke方法,在内部调用代理对象的方法,很容易造成无限递归(包括不要打印代理对象,因为会隐式调用代理对象的toString方法)

实现jdk动态代理核心步骤:

1)定义任务处理器,实现 InvocationHandler 接口,实现invoke方法

2)通过 Proxy.newProxyInstance(...) 创建代理对象

具体jdk动态代理实现:

① 声明接口类

import java.util.List;public interface IUserService {/* 获取所有用户的名字*/List<String> getUserNames() ;
}

② 定义实现类(即将被代理的类)

import java.util.Arrays;
import java.util.List;public class UserServiceImpl implements IUserService{@Overridepublic List<String> getUserNames() {return Arrays.asList("超人强", "GGB", "小呆呆");}
}

③ 实现调用处理器(定义代理行为)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class UserInvocationHandler implements InvocationHandler {//  目标对象private Object target;//    接收目标对象public UserInvocationHandler(Object target) {this.target = target;}/*** @param proxy  代理对象* @param method 执行方法* @param args   方法参数* @return  返回方法的执行结果*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//    ======     !!! 尽量不要再该方法中调用proxy对象的方法,不然很容易无限递归 !!!    ======//  1.记录方法的开始时间long startTime = System.currentTimeMillis();//  2.执行方法Object result = method.invoke(target, args);//  3.记录方法的结束时间long endTime = System.currentTimeMillis();//  4.打印方法耗时System.out.printf("======  %s 方法执行耗时:%d ms ======\n", method.getName(), endTime- startTime);//    5.返回方法的执行结果return result;}
}

④ 创建代理对象,调用代理方法

import java.lang.reflect.Proxy;
import java.util.List;public class Main {public static void main(String[] args) {//  生成代理对象IUserService userService = (IUserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), //  需要代理对象类的加载器UserServiceImpl.class.getInterfaces(),  //  代理类实现的接口new UserInvocationHandler(new UserServiceImpl())    //  调用处理器);//  通过代理对象执行方法List<String> userNames = userService.getUserNames();System.out.println(userNames);}
}

执行结果:

======  getUserNames 方法执行耗时:0 ms ======
[超人强, GGB, 小呆呆]进程已结束,退出代码为 0    

三、CGLib动态代理

特点:基于类 继承(extend)的动态代理,即代理类必须实现目标类,无需提前写好接口,属于第三方依赖,需要导入

工作原理

  1. 生成子类:CGLIB通过字节码技术在运行时生成目标类的子类(代理类)。

  2. 方法拦截:在生成的子类中,方法可以被重写以添加额外的逻辑,例如前置处理、后置处理等,这些逻辑组成了代理的行为。

  3. 创建代理对象:通过创建代理类对象,得到一个与目标对象有相同的方法名的对象

导入CGLib依赖

方式一:maven导入

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

方式二:从远程仓库下载

下载地址:Maven Repository: cglib » cglib (mvnrepository.com)

生成代理对象方法Enhancer类中的静态方法create

public static Object create(Class type, Callback callback);
  • Class type:要代理类的Class对象

  • Callback callback:代理类实现行为的接口,其主要实现是MethodInterceptor

实现CGLib动态代理的核心步骤:

1)定义代理类的行为,实现方法拦截器MethodInterceptor,实现其中的intercept方法

2)通过Enhancer.create(...)创建代理对象

具体GGLib动态代理实现:

① 定义目标类

public class Person {public void marry() {System.out.println("Person.marry()+++");}
}

② 实现方法拦截器(定义代理行为)

intercept方法介绍

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy);
  • Object proxy:代理对象,与jdk代理逻辑类似,不要在intercept方法内部直接调用代理对象的方法,不然容易形成无限递归

  • Method method:要执行的方法

  • Object[] args:方法参数

  • MethodProxy methodProxy:方法的代理,这个类中重点关注两个方法:

    • invoke(Object obj, Object[] args)

      与Method中的invoke方法效果一样,都是执行特定对象中的方法

    • invokeSuper(Object obj, Object[] args)

      执行obj父类对象中的方法,这里可以将代理对象proxy传递过去,这样就会直接执行proxy父类中的同名方法,

      invokeSuper(proxy, args) 等价于 ==> invoke(target, args) ,target的需要被代理的对象

方法拦截器具体实现

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/*代理对象方法拦截器*/
public class ProcedureHandler implements MethodInterceptor {//  目标对象private Object target;public ProcedureHandler(Object target) {this.target = target;}/*** @param proxy 代理对象* @param method  调用方法* @param args  方法参数* @param methodProxy   代理方法* @return  调用方法的返回值* @throws Throwable*/@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//  --- 记录开始时间long startTime = System.currentTimeMillis();System.out.println("方法开始时间:" + startTime);//  ======   执行方法,这里方便对比,将方法执行了三遍  ======//      执行当前类的当前方法,以下两行代码效果相同Object result = method.invoke(target, args);		Object result2 = methodProxy.invoke(target, args);//      执行父类同名方法Object result3 = methodProxy.invokeSuper(proxy, args);//  --- 记录结束时间long endTime = System.currentTimeMillis();System.out.println("方法开始时间:" + endTime);//  --  打印方法耗时System.out.printf("====== 方法总耗时:%d ms ======\n", endTime - startTime);return result;}
}

③ 创建代理对象,调用代理方法

import net.sf.cglib.proxy.*;public class Main {public static void main(String[] args) {Person person = (Person) Enhancer.create(Person.class, new ProcedureHandler(new Person()));person.marry();}
}

执行结果:

方法开始时间:1722225835558
Person.marry()+++
Person.marry()+++
Person.marry()+++
方法开始时间:1722225835568
====== 方法总耗时:10 ms ======进程已结束,退出代码为 0

动态代理使用推荐

当有代理类的接口时,可以选择jdk动态代理;没有接口就用CGLib动态代理,毕竟CGLib动态代理太好用了

四、问题解决

我这里使用的是jdk8版本,如果大于8可能会出现一些反射相关的异常(由于反射需要大量性能,所以一些使用频率不高的方法就被关闭了,所以从 JDK1.8 后大量包的可访问反射属性都被关闭了,lang 就是其中之一。)。

如:“module java.base does not “opens java.lang“ to unnamed module”。

只需要在启动虚拟机选项中加上以下参数即可:

--add-opens java.base/java.lang=ALL-UNNAMED

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com