Java基础一篇过(十)Java静/动态代理

一、前言

  关于Java中的动态代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理动态代理

了解代理模式

  定义:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法、实际执行的是被代理类的方法。

  作用:代理模式最主要的就是有一个公共接口或者父类,一个具体的类,一个代理类,代理类持有具体类的实例,代为执行具体类实例方法。代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

  常见实现:Spring的AOP实现原理就是用到了代理模式。

被代理类

  一般指我们的业务类,实现具体业务方法的类。

代理类

  一般指对业务类进行“增强”的类,实现一些不影响主业务的增强方法,如添加日志等功能。

结构图

二、静态代理

  定义:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

场景分析

  场景:假如一个班的同学要向老师交班费,但是都是通过班长把自己的钱转交给老师。这里,班长就是代理学生上交班费,班长就是学生的代理。

  代码思路:首先,我们创建一个IStudent接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上交班费的行为。这样,学生上交班费就可以让班长来代理执行。

代码实现

 

 

使用代理类的好处

三、动态代理

  定义:代理类在程序运行时才被创建的代理方式被成为动态代理。

  优势:可以很方便的对代理类的方法进行统一的处理,而不用修改每个代理类中的方法。

代码实现

 

 

 

 

四、动态代理源码解析

 1     /**
 2      * Proxy生成动态代理类【源码解析】
 3      *
 4      * @param loader     类加载器
 5      * @param interfaces 需要生成动态代理器的接口集合
 6      * @param h          自定义的动态代理器对象
 7      * @return
 8      * @throws IllegalArgumentException
 9      * @author 有梦想的肥宅
10      */
11     @CallerSensitive
12     public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
13         //1、校验“自定义的动态代理器对象”不能为空
14         Objects.requireNonNull(h);
15 
16         //2、clone一份需要生成动态代理器的接口集合,用于下面的操作
17         final Class<?>[] intfs = interfaces.clone();
18 
19         //3、创建安全管理器
20         final SecurityManager sm = System.getSecurityManager();
21         if (sm != null) {
22             //权限检查
23             checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
24         }
25 
26         //4、查找并生成指定的代理类【获得一个代理类的对象】
27         Class<?> cl = getProxyClass0(loader, intfs);
28 
29         //5、调用构造函数去创建代理类对象
30         try {
31             if (sm != null) {
32                 //校验创建新代理类的权限
33                 checkNewProxyPermission(Reflection.getCallerClass(), cl);
34             }
35 
36             //5.1 从刚才生成的代理类对象中查找参数为InvocationHandler的构造器
37             final Constructor<?> cons = cl.getConstructor(constructorParams);
38             final InvocationHandler ih = h;
39 
40             //5.2 检测构造器是否是Public修饰,如果不是则强行转换为可以访问的。
41             // 【什么都不加 是0 ,public 是1,private 是2,protected 是4,static 是8 ,final 是16】
42             if (!Modifier.isPublic(cl.getModifiers())) {
43                 //5.3 把非public方法设置为可访问的
44                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
45                     public Void run() {
46                         cons.setAccessible(true);
47                         return null;
48                     }
49                 });
50             }
51             //6、通过反射机制,将h【我们自定义的代理类生成器】作为参数,实例化并返回代理类实例对象。
52             return cons.newInstance(new Object[]{h});
53         } catch (IllegalAccessException | InstantiationException e) {
54             throw new InternalError(e.toString(), e);
55         } catch (InvocationTargetException e) {
56             Throwable t = e.getCause();
57             if (t instanceof RuntimeException) {
58                 throw (RuntimeException) t;
59             } else {
60                 throw new InternalError(t.toString(), t);
61             }
62         } catch (NoSuchMethodException e) {
63             throw new InternalError(e.toString(), e);
64         }
65     }

  Proxy.newProxyInstance是Proxy的静态方法,代码并不难理解,除去权限相关的代码外,就剩下两步:

  • 1,获取代理类对象(27行)
  • 2,利用反射技术实例化代理类,并返回实例化对象(52行)

  详解查看参考文章的第一篇:JAVA设计模式-动态代理(Proxy)源码分析【强推👍】

五、JDK动态代理和CGLIB代理的区别

区别

  • JDK动态代理只能对实现了接口的类生成代理,而不能针对普通类【针对接口】
  • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)【针对父类】

选择依据

  • (1)当Bean实现接口时,Spring就会用JDK的动态代理
  • (2)当Bean没有实现接口时,Spring使用CGlib是实现
  • (3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)

效能区别【作了解即可】

  • (1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
  • (2)在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。

 

 

 

 

 参考文章:

JAVA设计模式-动态代理(Proxy)源码分析【强推👍】

Java动态代理实现与原理详细分析

动态代理:JDK动态代理和CGLIB代理的区别

 

posted @ 2021-01-13 11:15  有梦想的肥宅  阅读(178)  评论(0编辑  收藏  举报