从代理模式开始学习动态代理

开篇复习一下代理模式,什么是代理模式?菜鸟教程上写的是,在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

举个最简单的例子,写个静态代理的demo

// 抽象角色:开发者
public interface ICoder {
    void code();
}

// 代理角色
public class Person implements ICoder {

    // 名称
    private String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public void code() {
        System.out.println(name + " write code");
    }
}

// 真实角色:开发者代理类
public class CoderAgent implements ICoder {

    public ICoder coder;

    @Override
    public void code() {
        coder.code();
    }
}
    
public void main() {
    Person person = new Person("vod");

    CoderAgent agent = new CoderAgent();
    agent.coder = person;
    agent.code();
}

代理模式的demo写完了,但是总觉得哪里怪怪的…

我明明可以直接调用person.code,为什么还要再增加一层代理。

这个嘛,大部分时候代理模式会增加一些前置或后置逻辑,还有判空等操作。新的代理类如下

public class CoderAgent implements ICoder {

    public ICoder coder;

    // 前置逻辑
    private void before() {
        System.out.println("coder is design");
    }

    // 后置逻辑
    private void after() {
        System.out.println("coder is testing");
    }

    @Override
    public void code() {
        // 判空处理
        if (coder != null) {
        
            before();
            
            coder.code();
            
            after();
        }
    }
}

这时候,我们发现,以上代理模式的实现都是静态的代码实现,如果Person新实现一个接口叫 IEmployee,这时候代理应该怎么处理?

public interface IEmployee {

    void salary(int moneyCent);
}

一般来说,一个代理类代理一个接口,当代理对象实现多个接口的情况下,要么有多个代理类的实现,要么有个臃肿且难以复用的代理类出现,而这两情况无疑是我们不愿意看到的。

那么,这时候我们就会用到动态代理, Proxy

注意,动态代理类路径为 java.lang.reflect.Proxy,不要引net包中的Proxy

源码先放在这里

    /**
     * @param   loader 类加载器
     * @param   interfaces 代理接口
     * @param   h 代理逻辑处理器
     */
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();
        
        // 生成代理类
        Class<?> cl = getProxyClass0(loader, intfs);

        try {
            // 获取构造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            
            // 通过构造方法生成代理类的对象
            if (!Modifier.isPublic(cl.getModifiers())) {
                cons.setAccessible(true);
            }
            
            // 将代理类的对象返回出去
            return cons.newInstance(new Object[]{h});
        } catch (Exception e) {
            // catch 省略
        }
    }

源码放完了,那就再来一个demo

public void main2() {
    // 生成代理角色
    Person person = new Person("vod");

    // 生成代理类,入参分别为类加载器、需要代理的接口、处理器
    Object obj = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
            new Class[]{ICoder.class, IEmployee.class},
            new InvocationHandler() {

                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 当代理类的方法被调用时,就会触发一下逻辑
                    if (person != null) {
                        return method.invoke(person, args);
                    }
                    return null;
                }
            });

    // 写写代码
    ((ICoder)obj).code();
    // 发发工资
    ((IEmployee)obj).salary(50000000);
}

这时候,我们发现,动态代理生成的代理类,它所实现的接口完全可以通过在运行时,通过改变调用newProxyInstance的接口数组的参数,去动态改变,这让代理类更灵活了。

那么问题又来了,它是怎么实现的?

        // 生成代理类
        Class<?> cl = getProxyClass0(loader, intfs);

看方法中,这句代码返回的代理类,咱们深挖下去

    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            // 接口数大于六万五
            throw new IllegalArgumentException("interface limit exceeded");
        }

        return proxyClassCache.get(loader, interfaces);
    }

从这里可以看出,每次生成的代理类,都会缓存在 proxyClassCache这个缓存池中,这个缓存池的get方法源码太长不看,简单说就是有就取,没有就通过工厂类生成,而这个工厂类就是new缓存池时候的入参,ProxyClassFactory


    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        
        // 其余方法参数略

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            // 中间略

            return generateProxy(proxyName, interfaces, loader, methodsArray,
                                 exceptionsArray);
        }
    }

其实,这时候android的jar包和java的有一点区别,java的源码是

        public Class<?> apply(ClassLoader var1, Class<?>[] var2) {
            // 前,略
            byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);

            try {
                return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
            } catch (ClassFormatError var14) {
                throw new IllegalArgumentException(var14.toString());
            }
        }
    }

但是这并不影响重点,然而重点的方法 generateProxyClass

    private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
                                                 ClassLoader loader, Method[] methods,
                                                 Class<?>[][] exceptions);

好了native层就不看了,看了也看不懂,看懂了也不会用,会用了也请告诉我一下哈哈。

至于这个方法生成的类具体是什么样子,我在其他博客上找了一个demo

 1 public class Proxy0 extends Proxy implements UserDao {
 2 
 3     //第一步, 生成构造器
 4     protected Proxy0(InvocationHandler h) {
 5         super(h);
 6     }
 7 
 8     //第二步, 生成静态域
 9     private static Method m1;   //hashCode方法
10     private static Method m2;   //equals方法
11     private static Method m3;   //toString方法
12     private static Method m4;   //...
13     
14     //第三步, 生成代理方法
15     @Override
16     public int hashCode() {
17         try {
18             return (int) h.invoke(this, m1, null);
19         } catch (Throwable e) {
20             throw new UndeclaredThrowableException(e);
21         }
22     }
23     
24     @Override
25     public boolean equals(Object obj) {
26         try {
27             Object[] args = new Object[] {obj};
28             return (boolean) h.invoke(this, m2, args);
29         } catch (Throwable e) {
30             throw new UndeclaredThrowableException(e);
31         }
32     }
33     
34     @Override
35     public String toString() {
36         try {
37             return (String) h.invoke(this, m3, null);
38         } catch (Throwable e) {
39             throw new UndeclaredThrowableException(e);
40         }
41     }
42     
43     @Override
44     public void save(User user) {
45         try {
46             //构造参数数组, 如果有多个参数往后面添加就行了
47             Object[] args = new Object[] {user};
48             h.invoke(this, m4, args);
49         } catch (Throwable e) {
50             throw new UndeclaredThrowableException(e);
51         }
52     }
53     
54     //第四步, 生成静态初始化方法
55     static {
56         try {
57             Class c1 = Class.forName(Object.class.getName());
58             Class c2 = Class.forName(UserDao.class.getName());    
59             m1 = c1.getMethod("hashCode", null);
60             m2 = c1.getMethod("equals", new Class[]{Object.class});
61             m3 = c1.getMethod("toString", null);
62             m4 = c2.getMethod("save", new Class[]{User.class});
63             //...
64         } catch (Exception e) {
65             e.printStackTrace();
66         }
67     }
68     
69 }

如果感觉以上代码太长不看,一句话概括就是

生成的类继承所有接口与Object的方法,实现方式统一为调用动态代理的入参 InvocationHandler的 invoke 方法进行处理

OK,那么动态代理与代理模式到这就讲完了,祝大家

0 warnning, 0 error, 0 bug

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值