• Java 静态代理和动态代理


    Java 静态代理和动态代理

    代理模式:A调用B,由于某些原因(解耦合、保护B、扩展B),B不想让A直接调用,创建一个中间代理者C,由A调用C,由C中调用B的功能和C封装的东西。

    优点:

    • 将调用者与被调用者分离,保护被调用者,降低耦合度
    • 扩展被调用者功能时比较方便

    缺点:

    • 增加较多的代理类,类的设计数量会上升,增加系统复杂度
    • 请求经过代理类,整体的运行响应速度会有影响

    静态代理只能指定单一的被代理类,动态代理可以在运行时使用反射机制动态生成代理类

    下面举三个例子,分别不使用代理使用静态代理使用动态代理来进行比较。

    People接口含有一个方法say(),每个实现此接口的类都要输出自己的姓氏,但是有一天需求扩展,还要在执行 say() 之后输出一句代表新增扩展功能的字符串。

    代码举例:

    公共接口:People

    // 人类接口
    public interface People {
        // 说话方法
        public void say();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    不使用代理

    // 不使用代理类,实现人类接口
    public class Ling implements People {
        public void say() {
            System.out.println("I am Ling ... ");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class MyTest {
    
        public static void main(String[] args) {
    
            // 1、不使用代理
            System.out.println("-- 不使用代理 --");
            // 创建基本类对象
            People ling = new Ling();
            // 新增扩展功能
            System.out.println("扩展功能 ... ");
            // 执行基本类基本方法
            ling.say();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    不使用代理的时候,就只能指定实现类Ling,且执行 say() 方法,再单独执行代表扩展功能的字符串。如果有很多个地方都要执行 ling.say() ,当扩展功能时,那么每个调用 ling.say() 的地方都要添加扩展功能代码,就会显得十分繁琐。

    优点: 简单明了
    缺点: 扩展复杂,冗余度高

    静态代理:

    // 被代理类,实现人类接口
    public class Wang implements People {
        public void say() {
            System.out.println("I am Wang ... ");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // 静态代理类,封装被代理类
    public class WangSayProxy implements People {
    
    	// 内部创建被代理类
        private Wang wang = new Wang();
    
        public void say() {
        	// 新增扩展功能
            System.out.println("扩展功能 ... ");
            // 执行被代理类基本功能
            wang.say();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    public class MyTest {
    
        public static void main(String[] args) {
    
            // 2、使用静态代理
            System.out.println("-- 使用静态代理 --");
            // 创建静态代理类对象
            People wangSayProxy = new WangSayProxy();
            // 执行静态代理类方法
            wangSayProxy .say();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    使用静态代理,规定了 WangSayProxy 类为 Wang 类的静态代理类,调用此静态代理类,不仅可以实现调用 wang.say() ,还可以在静态代理类中进行功能增强,最后展示到外层。只要本来调用 wang.say() 的地方,都可以直接调用 wangSayProxy.say() ,如果 wang.say() 有扩展功能,直接在 WangSayProxy 类中就修改即可,减少代码耦合度。

    优点: 减耦合,功能扩展方便
    缺点: 被代理的对象比较单一,不够灵活

    动态代理:

    // 被代理类,实现人类接口
    public class Zhan implements People {
        public void say() {
            System.out.println("I am Zhan ... ");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // 动态代理类,封装被代理类
    public class PeopleSayProxyHandler implements InvocationHandler {
    
    	// 内部创建Object对象
        private Object object;
    
    	// 有参构造(指定被代理的动态类型)
        public PeopleSayProxyHandler(Object object) {
            this.object = object;
        }
    
    	// 动态生成被代理类
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        	// 执行扩展功能
            System.out.println("扩展功能 ... ");
            // 执行动态被代理类的方法
            method.invoke(object, args);
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    public class MyTest {
    
        public static void main(String[] args) {
    
            // 3、使用动态代理
            System.out.println("-- 使用动态代理 --");
            // 创建希望被动态代理的类
            People zhan = new Zhan();
            // 创建动态代理类(此处指定被动态代理对象,此对象为参数,运行时才能确定创建对象)
            InvocationHandler peopleSayProxyHandler = new PeopleSayProxyHandler(zhan);
            // 通过Proxy类的静态方法newProxyInstance返回一个接口的代理实例
            People proxySay = (People) Proxy.newProxyInstance(zhan.getClass().getClassLoader(),
                    zhan.getClass().getInterfaces(), peopleSayProxyHandler);
            // 执行动态代理类方法
            proxySay.say();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    使用静态代理,灵活的将被代理类的功能进行扩展。

    可以发现,动态代理的关键点就是使用 Proxy 类的静态方法 newProxyInstance ,因此我们可以查看下源码:

        @CallerSensitive
        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
        	// 判断对象为不为空
            Objects.requireNonNull(h);
    
    		// 对象克隆
            final Class<?>[] intfs = interfaces.clone();
            // 创建安全管理器
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
            	// 此方法调用上述安全管理器对象检查是否存在恶意代码
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
             // 生成代理类对象
            Class<?> cl = getProxyClass0(loader, intfs);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                if (sm != null) {
                    checkNewProxyPermission(Reflection.getCallerClass(), cl);
                }
    
    			// 使用指定的调用处理程序获取代理类的构造函数对象
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                // 如果Class作用域为私有,通过 setAccessible 支持访问
                if (!Modifier.isPublic(cl.getModifiers())) {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            cons.setAccessible(true);
                            return null;
                        }
                    });
                }
                // 获取ProxyClass构造函数创建Proxy代理实例
                return cons.newInstance(new Object[]{h});
            } catch (IllegalAccessException|InstantiationException e) {
                throw new InternalError(e.toString(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    throw (RuntimeException) t;
                } else {
                    throw new InternalError(t.toString(), t);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString(), e);
            }
        }
    
    
    • 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

    更详细的源码解读可以参考此文章:动态代理源码解读

  • 相关阅读:
    用Python写一个猜数字的小游戏
    微信小程序图书管理系统
    CSS之垂直水平居中的背后
    数据中台的前世今生(一):数据仓库——数据应用需求的涌现
    解锁LLMs的“思考”能力:Chain-of-Thought(CoT) 技术推动复杂推理的新发展
    [MySQL]order by失效
    go语法入门2
    程序都不知道的代码
    gentool gen go自动生成表结构
    数智化转型的痛,谁能懂啊家人们!
  • 原文地址:https://blog.csdn.net/qq_39004632/article/details/127622506