一、前言

根据创建代理类的时间点,又可以分为静态代理动态代理

了解代理模式

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

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

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

被代理类

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

代理类

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

结构图

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

二、静态代理

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

场景分析

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

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

代码实现

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

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

Java基础一篇过(十)Java静/动态代理_代理模式_04

 

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

 

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

使用代理类的好处

Java基础一篇过(十)Java静/动态代理_代理模式_07

三、动态代理

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

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

代码实现

Java基础一篇过(十)Java静/动态代理_代理模式_08

 

Java基础一篇过(十)Java静/动态代理_代理模式_09

 

Java基础一篇过(十)Java静/动态代理_代理模式_10

 

Java基础一篇过(十)Java静/动态代理_代理模式_11

Java基础一篇过(十)Java静/动态代理_代理模式_12

 

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

四、动态代理源码解析

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     }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 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代理的区别