代理模式

306 阅读6分钟

前言

这两天复习Retrofit源码时,看到了一些设计模式,平时写业务逻辑或多或少会使用的,简单记录一下^^--^^

目录

一、普通代理

定义一个接口IGamePlayer,所有喜欢网络游戏的玩家,然后定义一个具体的实现类GamePlayer,实现每个游戏爱好者为了玩游戏要执行的操作。

public interface IGamePlayer{
    public void login(String user,String pwd);//登录
    public void killBoss();//打怪升级
}
//玩家
public class GamePlayer implements IGamePlayer{
    private String name = "";
    public GamePlayer(String name){
        this.name = name;
    }
    public void login(String user,String pwd){
        //登录游戏
    }
    public void killBoss(){
        //打怪
    }
}

玩家打怪升级

IGamePlayer player = new GamePlayer("张三");
player.login("张三","密码");
player.killBoss();

这时候玩家累了,去找了一个代练

public class GamePlayerProxy implements IGamePlayer{
    private IGamePlayer gamePlayer =  null;
    public GamePlayerProxy(IGamePlayer gamePlayer){
        this.gamePlayer = gamePlayer;
    }
    public void login(String user,String password){
        this.gamePlayer.login(user,password);
    }
    public void killBoss(){
        this.gamePlayer.killBoss();
    }
}

找到代练后,给他账号、密码,代练也要清楚知道是谁要我帮他打怪,然后开始

IGamePlayer  player = new GamePlayer("张三");
//player去找代练
IGamePlayer proxy = new GamePlayerProxy(player);
proxy.login("user","pwd");
proxy.killBoss();

代练代理了真正玩家角色,负责真正账号、角色的应用。实际结果都是作用于玩家的账号,可以理解为玩家。这种你清楚这个代练,代练也清楚你,代打起来你也放心。这里是怎么做到让代练也清楚你呢?通过构造函数指定实际玩家角色。

其中好处就是可以减轻你的负担。这也是代理模式的优点。但是上面这样写,有点问题,每次找代理打游戏的时候,你都要事先热身,再交给代练吗?也就是这里 的 new GamePlayer("张三"),这样的话,高层业务逻辑即调用者知道了真实角色是谁,为了使高层业务对真实角色的影响,可以将GamePlayerProxy的构造函数改成传账户、密码,然后内部new GamePlayer。

二、强制代理

强制代理比较另类,一般都是通过代理找到真实的角色,但是强制代理必须要通过真实角色才能找到代理角色,就像你去找明星合作,明星会给经纪人,也就是代理人的信息给你,让你们去沟通,强制代理类似如此。

public interface IGamePlayer{
    public void login(String user,String pwd);//登录
    public void killBoss();//打怪升级
    public IGamePlayer getProxy();//获得代理
}
​
public class GamePlayer implements IGamePlayer{
    private String name = "";
    private IGamePlayer proxy = null; //玩家的代理
    public GamePlayer(String name){
        this.name = name;
    }
    public IGamePlayer getProxy(){
        this.proxy = new GamePlayerProxy(this);
        return this.proxy;
    }
    public void killBoss(){
        if(this.isProxy()){
            //代理成功
        }else{
            //请找我的代理干活
        }
    }
    public void login(String user,String pwd){
        if(this.isProxy()){

        }else{
            //代理失败
        }
    }
    private boolean isProxy(){
        return this.proxy == null;
    }

}

其实和之前的普通代理刚开始写法差不多,唯一的区别就是做了角色限制,代理的管理由真实角色完成。

IGamePlayer  player = new GamePlayer("张三");
IGamePlayer proxy = player.getProxy();
proxy.login("user","pwd");
proxy.killBoss();

三、动态代理及源码解析

//动态代理类
public class GamePlayIH implements InvocationHandler{
    //被代理者
    Class cls = null;
    //被代理的实例
    Object obj = null;
    //我要代理谁
    public GamePlayIH(Object obj){
        this.obj = obj;
    }
    //调用被代理的方法
    public Object invoke(Object proxy,Mehtod method,Object[] args){
        Object result = method.invoke(this.obj,args);
        if(method.getName().equals("login")){
            //有人登录了你的账号
        }
        return result;
    }
}
//调用
IGamePlayer palyer = new GamePlayer("张三");
InvocationHandler handler = new GamePlayIH(player);
IGamePlayer proxy = (IGamePlayer)Proxy.newProxyInstance(palyer.getClass().getClassLoader(),new Class[]{IGamePlayer.class},handler);

动态代理都会用,底层是如何实现的呢?Proxy创建代理的源码如下

 @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);
        }
​
        /*
         *1\. 生成代理类Class对象
         */
        Class<?> cl = getProxyClass0(loader, intfs);
​
   ......
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 2.创建Proxy代理实例
            return cons.newInstance(new Object[]{h});
       ........
    }

**主要就是通过getProxyClass0生成代理类对象Class,然后反射创建代理类对象并返回。**getProxyClass0方法如下

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        return proxyClassCache.get(loader, interfaces);
    }

直接从proxyClassCache中根据ClassLoader和指定接口interfaces匹配取出,如果没有的话,则通过ProxyClassFactory创建,其中proxyClassCache是WeachCache类型对象,初始化如下

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

可以继续跟进get方法查看一下是如和取数据的

 public V get(K key, P parameter) {
        //代理类接口不能为空
        Objects.requireNonNull(parameter);
​
        expungeStaleEntries();
        //生成CacheKey对象
        Object cacheKey = CacheKey.valueOf(key, refQueue);
​
        //根据cacheKey获取键值对valuesMap,valuesMap的key是接口列表的包装类,value是动态代理类的包装类
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }
​
       //生成 代理类对应的 key
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        //代理类的包装类
        Supplier<V> supplier = valuesMap.get(subKey);
        //生成动态代理的工厂类
        Factory factory = null;
​
        while (true) {
            if (supplier != null) {
                // 关注这里  即使supplier为空 后面supplier会被创建
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
​
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }
​
            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                .....
            }
        }
    }

可以看到get方法内调用了Factory的get方法

   @Override
        public synchronized V get() { // serialize access
            // re-check
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {

                return null;
            }
            // else still us (supplier == this)// create new value
            V value = null;
            try {
            //通过valueFactory的apply方法创建动态代理类
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
          .....
            return value;
        }
    }

Factory的get方法里主要是通过ProxyClassFactory的apply方法来生成动态代理类的

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
​
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //1.获取代理类接口的Class对象,并校验是否是接口
            for (Class<?> intf : interfaces) {

                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                ......
            }
            //2.要生成的代理类的包名
            String proxyPkg = null;     // package to define proxy class in
            //类的修饰符
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
​
            for (Class<?> intf : interfaces) {
            //3.根据代理类接口生成包名和访问权限修饰符
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
​
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            // 4\. 准备代理类类名
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
​
            //5.生成字节码文件
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                // 6.生成代理类对象 native方法
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {

                throw new IllegalArgumentException(e.toString());
            }
        }

主要方法就是上述代码中的ProxyGenerator。generateProxyClass()和defineClass0()方法,一个是根据类名、接口、类访问权限,按照class文件格式生成字节流,一个是根据字节流生成代理类对象。

动态代理优点:

不修改源码的基础上对方法增强,在此过程中会建立一个新的类对象

动态代理缺点:

有时候我们写的类中,可能许多内部的方法会相互之间调用,但如果我想对它们进行同时增强的话,用动态代理是做不到的。因为动态代理只会增强最先调用的方法,后续内部之间的相互调用的方法,是不会被增强的。

比如

public Object invoke(Object proxy,Mehtod method,Object[] args){
        Object result = method.invoke(this.obj,args);
        if(method.getName().equals("login")){
            //有人登录了你的账号
        }else if(method.getName().equals("killBoss")){
        }
        return result;
    } public void login(String user,String pwd){
        if(this.isProxy()){
            killBoss();
        }else{
            //代理失败
        }
    }

在对login方法增强的时候,如果login方法内调用了killBoss()方法,动态代理是不会对这个killBoss()方法增强

四、代理模式的作用以及应用场景

现在增加一个需求,需要在所有类的方法前后增加校验或者是打印功能,那你会怎么做呢?肯定是利用代理模式,为现有的类增加代理类,在代理类中实现前后打印功能,这就是它的作用。

代理模式可以在不修改代理对象代码基础上,通过扩展代理类,进行一些功能的扩展,动态代理的话,只需要去实现代理的扩展InvocationHandler即可。

代理模式(除JDK提供外的动态代理方式还有 CGLB基于ASM技术的动态代理)常见于AOP切面编程,例如ASpectJ。