java--动态代理技术

柔光的暖阳◎ 2022-03-18 07:14 302阅读 0赞

动态代理技术

在学习动态代理之前,需要了解的是一种常用的设计模式—代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。

代理模式

代理模式是常用的java设计模式,他的特点是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等,它们之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

静态代理

静态代理是由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成,缺点是接口与代理类是1v1,有多个接口需要代理,就需要新建多个代理类,比较繁琐。

静态代理的列子

显示方法运行的时间

UserServiceTarget 目标类 实现了UserService
TimeHandler 包含了重复代码,间接调用目标
UserServiceProxy 代理类 实现了UserService

接口

  1. public interface UserService {
  2. public void b();
  3. public void c();
  4. }

委托类实现接口

  1. // 目标类
  2. public class UserServiceTarget implements UserService {
  3. public void b() {
  4. System.out.println("b");
  5. }
  6. public void c() {
  7. System.out.println("c");
  8. }
  9. }

代理类实现接口

  1. // 静态代理 - 自己编码实现的代理类
  2. // 动态代理 - jdk可以在运行期间,动态地生成这个代理类
  3. // proxy 代理 (封装复杂逻辑,它去间接调用 TimeHandler 和 业务代码), 让使用者觉得简单
  4. public class UserServiceProxy implements UserService {
  5. public void b() {
  6. try {
  7. TimeHandler timeHandler = new TimeHandler();
  8. //得到方法 类名.class.getMethod(方法名)
  9. Method b = UserServiceTarget.class.getMethod("b");
  10. //反射调用,传递方法,下面的计算类里就可以调用此方法
  11. timeHandler.invoke(b);
  12. } catch (NoSuchMethodException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. public void c() {
  17. try {
  18. TimeHandler timeHandler = new TimeHandler();
  19. Method c = UserServiceTarget.class.getMethod("c");
  20. timeHandler.invoke(c);
  21. } catch (NoSuchMethodException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }

计算时间的代码

  1. public class TimeHandler {
  2. private UserServiceTarget userService = new UserServiceTarget();
  3. // b() c()
  4. public void invoke(Method method) {
  5. System.out.println("==================================");
  6. long start = System.nanoTime();
  7. // 调用这个方法 method
  8. // 正常调用: 对象.方法名(参数)
  9. // 反射调用: 方法.invoke(对象, 参数);
  10. try {
  11. //调用 UserServiceTarget 中的方法
  12. method.invoke(userService);
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. }
  16. long end = System.nanoTime();
  17. System.out.println("花费了: " + (end - start));
  18. }
  19. }

测试类

  1. public class TestTimeHandler {
  2. public static void main(String[] args) {
  3. UserServiceProxy proxy = new UserServiceProxy();
  4. proxy.b();
  5. proxy.c();
  6. }
  7. }

结果

  1. ==================================
  2. b
  3. 花费了: 201800
  4. ==================================
  5. c
  6. 花费了: 65691

动态代理(Dynamic Proxy )

原始编写java 代码的方式是从java文件编译为class文件,然后class文件经过翻译后变为机器语言,而动态代理就是体现了class文件到机器语言这一过程。

  • 原始编写java 代码的方式

.java -> javac -> *.class -> 类加载

  • 动态代理

.class -> 类加载

动态代理例子

显示方法运行的时间

UserServiceTarget 目标类 实现了UserService
TestDynamicProxy 包含了计算时间代码,间接调用目标
UserServiceProxy 代理类 实现了UserService

接口

  1. public interface UserService {
  2. public void a(int x, int y);
  3. public void b();
  4. public void c();
  5. }

目标类(委托类)

  1. // 目标类
  2. public class UserServiceTarget implements UserService {
  3. public void a(int x, int y) {
  4. System.out.println("a");
  5. }
  6. public void b() {
  7. System.out.println("b");
  8. }
  9. public void c() {
  10. System.out.println("c");
  11. }
  12. }

测试类

  1. import java.io.FileOutputStream;
  2. import java.io.IOException;
  3. import java.lang.reflect.Constructor;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.InvocationTargetException;
  6. import java.lang.reflect.Method;
  7. // dynamic 动态的
  8. // proxy 代理
  9. public class TestDynamicProxy {
  10. public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
  11. // 1. 生成了类的字节码
  12. // 参数1 string 表示代理类的名字,随意起
  13. // 参数2 class[] 表示代理类要实现的接口
  14. // 返回的结果就是 代理类 的字节码
  15. byte[] bytes = ProxyGenerator.generateProxyClass("UserServiceProxy2", new Class[]{UserService.class});
  16. // 2. 执行类加载
  17. // 类加载的最终结果体现为 Class 类对象, 要借助类加载器
  18. ClassLoader cl = new ClassLoader() {
  19. @Override
  20. protected Class<?> findClass(String name) throws ClassNotFoundException {
  21. return defineClass(name, bytes, 0, bytes.length);
  22. }
  23. };
  24. Class c = cl.loadClass("UserServiceProxy2"); // 进行类加载, 获得了 UserServiceProxy 类对象
  25. // 3. 创建代理类实例对象
  26. // 不能正常创建它的实例对象, new UserServiceProxy2();
  27. // 获取代理类的构造方法
  28. Constructor constructor = c.getConstructor(InvocationHandler.class);
  29. UserServiceTarget target = new UserServiceTarget();
  30. // 创建实例对象, 强制转换为它的接口类型
  31. UserService proxy = (UserService)constructor.newInstance(new InvocationHandler() {
  32. @Override
  33. // method 就是 a , b, c 等方法对象
  34. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  35. System.out.println("进入了 invoke");
  36. long start = System.nanoTime();
  37. // 方法.invoke(目标, 参数); 通过这行代码调用目标类的方法
  38. method.invoke(target, args);
  39. long end = System.nanoTime();
  40. System.out.println("花费了:" + (end - start));
  41. return null;
  42. }
  43. });
  44. // 4. 使用代理对象
  45. proxy.b();
  46. proxy.c();
  47. proxy.a(10,20);
  48. }
  49. }

结果

  1. 进入了 invoke
  2. b
  3. 花费了:682903
  4. 进入了 invoke
  5. c
  6. 花费了:233935
  7. 进入了 invoke
  8. a
  9. 花费了:267017

以上两种代理方法可以看出,静态代理中的方法实现太过繁琐(业务逻辑多),相比之下,动态代理的业务逻辑就简洁多了。

总结

简而言之,动态代理也就是间接的调用委托的方法,并完善方法的过程,这样使代码简洁,并且在编码时,代理逻辑与业务逻辑相互独立,个不影响,没有侵入,没有耦合(耦合也就是一处代码修改,另一处代码也要修改,如果偶合性强,那么修改一处代码时,需要改多处代码,才可以把程序修改完毕,反之则修改处少)。

发表评论

表情:
评论列表 (有 0 条评论,302人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java中的动态代理技术

    动态代理在日常的开发中还是比较重要的,下面来总结一下。 首先,什么是动态代理技术呢?当项目中有几个类,每一个类都需要做一些类似的操作,比方说打印日志,现在需求可能有更改,那么

    相关 java--动态代理技术

    动态代理技术 > 在学习动态代理之前,需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。 代理模式 >

    相关 java 动态代理技术

    1、要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累! 2、JVM可以在运行期动态生成