代理模式

概述

代理模式是结构化设计模式中的一种,平时我们生活中都有类似的代理模式,比如代购,网上购物,打官司。
定义:为对象提供一种代理以控制对象的访问,这种方式就称为代理模式。
在这里插入图片描述
代理模式的结构图中,
Subject:抽象主题类,声明真实主题的共同接口方法。
RealSubject:真实主题类,定义了代理所表示的对象。客户端通过代理类间接的调用真实主题对象的方法。
Proxy:代理类,持有真实主题的类的对象的引用,在其实现的接口中,调用真实主题对象的相应接口的方法。
Client:客户端类。

代理模式分类

代理模式按照代理类的加载时机不同可以分为静态代理和动态代理。其中,动态代理按照,被代理的类是否实现了某个接口又可以划分为Jdk动态代理和Cglib动态代理。
1.静态代理
就是在编译前就已经明确了代理类,也就是在编译前就已经写好了代理类,并且代理类和被代理类要实现相同的接口或者继承相同的父类。
下面通过代码演示,如何使用静态代理:
1.首先定义一个接口,也就是抽象主题

public interface IUserDao {
    void save();
}

2.定义一个真实主题

public class UserDao implements IUserDao {
    @Override
    public void save() {
        LogUtil.i("保存数据");
    }
}

3.定义一个代理类

public class UserDaoProxy implements IUserDao {
    private IUserDao userDao;
    public UserDaoProxy(IUserDao userDao){
        this.userDao = userDao;
    }

    @Override
    public void save() {
        LogUtil.e("开始事务");
        userDao.save();
        LogUtil.i("提交事务");
    }
}

4.新建一个客户端类,并在客户端类中调用代理对象的方法

public class Client {
  public static void main(String[] args){
	//新建真实对象
	IUserDao userDao = new UserDao();
	//通过真实主题对象构造一个代理对象
	IUserDao userDaoProxy = new UserDaoProxy(userDao);
	userDaoProxy.save();
  }
}

最后的打印结果是:
10-11 test.cn.example.com.androidskill E/MY_LOG: UserDaoProxy.java::17::save-->>开始事务
10-11 test.cn.example.com.androidskill I/MY_LOG: UserDao.java::13::save-->>保存数据
10-11 test.cn.example.com.androidskill I/MY_LOG: UserDaoProxy.java::19::save-->>提交事务
  1. JDK动态代理
    代理类是在Proxy.newProxy()方法调用后创建的,也就是说是动态生成的,这里就体现了动态代理中的动态的含义。
    下面通过具体示例来演示,如何使用Jdk的动态代理
    1.还是使用上面静态代理的演示示例中的抽象主题,IUserDao接口和真实主题类UserDao。
    2.创建客户端
public class Client {
  public static void main(String[] args){
	//动态代理
	//首先创建真实主题
        IUserDao realUserDao = new UserDao();
	//创建InvocationHandler对象
        InvocationHandler h = new DynamicUserDaoHandler(realUserDao);
	//创建代理对象
        IUserDao dynamicProxy = (IUserDao) Proxy.newProxyInstance(h.getClass().getClassLoader(),realUserDao.getClass().getInterfaces(),h);
        //调用代理对象的方法
	dynamicProxy.save();
  }

  private class DynamicUserDaoHandler implements InvocationHandler{
        private Object realSubject;

        public DynamicUserDaoHandler(IUserDao subject){
            this.realSubject = subject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            LogUtil.i("动态代理---开始事务");
            method.invoke(realSubject,args);
            LogUtil.i("动态代理---提交事务");
            return null;
        }
    }
}
打印结果如下:
10-11 test.cn.example.com.androidskill I/MY_LOG: ProxyPatternActivity.java::51::invoke-->>动态代理---开始事务
10-11 test.cn.example.com.androidskill I/MY_LOG: UserDao.java::13::save-->>保存数据
10-11 test.cn.example.com.androidskill I/MY_LOG: ProxyPatternActivity.java::53::invoke-->>动态代理---提交事务

Jdk动态代理中,涉及到Proxy类的newProxyInstance方法,这个方法中,需要传入三个参数

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
	...				  
}

这个方法中,
第一个参数 ClassLoader loader:指定一个动态加载代理类的类加载器
第二个参数是要传入被代理对象的所有接口。可以通过被代理对象的getClass().getInterfaces()方法获取。
getInterfaces()方法和Java的反射机制有关。它能够获得这个对象所实现的所有接口。
第三个参数InvocationHandler,这是一个方法委托类,我们通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类。
关于InvocationHandler接口的方法的参数的解释

public interface InvocationHandler{
	public Object invoke(Object proxy,Method method,Object[] args){
		// 参数 proxy 是创建的代理对象
		// 参数 method,是代理对象调用的方法
		// 参数 args,是代理对象调用的方法传入的参数
	}
}

每一次代理对象调用一次方法,则InvocationHandler接口的invoke方法就会执行一次。

  1. Cglib动态代理
    Cglib底层采用ASM字节码生成框架,使用字节码技术生成代理类,也就是生成的.class文件,而我们在android中加载的是优化后的.dex文件,也就是说我们需要可以动态生成.dex文件代理类,cglib在android中是不能使用的。如果想要在Android中使用可以使用CGLib-for-Android这个库,下面演示如何在android项目中使用CGLib-for-Android这个库
    1.首先下载CGLib-for-Android这个库, 下载地址
    2.将下载的cglib-for-android.jar包导入项目
    android studio 中,当导入下载的jar包,导入后,无法使用时,或者导入好,右键,找不到 add as library选项时,看看下载的jar包是否完整,自己下载的cglib-for-android.jar包未下载完整,导致导入到android项目中后,一直无法使用,坑了自己半个小时
    3.编写代理拦截器类
import android.content.Context;

import leo.android.cglib.proxy.Enhancer;
import leo.android.cglib.proxy.MethodInterceptor;
import leo.android.cglib.proxy.MethodProxy;
import test.cn.example.com.util.LogUtil;

public class MyInterceptor implements MethodInterceptor {
    private final Context context;

    public MyInterceptor(Context context){
        this.context = context;
    }

    public Object getProxy(Class clazz){
        Enhancer enhancer = new Enhancer(context);
        enhancer.setSuperclass(clazz);
        enhancer.setInterceptor(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Object[] objects, MethodProxy methodProxy) throws Exception {
        LogUtil.i("cglib for android  动态代理 事物开始");
        Object result = methodProxy.invokeSuper(o, objects);
        LogUtil.i("cglib for android  动态代理 事物提交");
        return result;
    }
}

4.编写客户端

public class Client {
  public static void main(String[] args){
	//代理类是Cglib自动生成的,生成的代理类是目标类的子类
	UserDao proxy1 = (UserDao) new MyInterceptor(this).getProxy(UserDao.class);
	LogUtil.i(""+proxy1);
        proxy1.save();
  }
}
打印日志如下:
通过第一行的打印日志可以知道,生成的代理对象就是被代理对象的子类
10-11 I/MY_LOG: ProxyPatternActivity.java::89::onClick-->>test.cn.example.com.androidskill.model.UserDao$Enhancer$@8234b17
10-11 I/MY_LOG: MyInterceptor.java::26::intercept-->>cglib for android  动态代理 事物开始
10-11 I/MY_LOG: UserDao.java::13::save-->>保存数据
10-11 I/MY_LOG: MyInterceptor.java::28::intercept-->>cglib cglib for android  动态代理 事物提交

Cglib代理前提条件:
目标类不能为final
目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
以上便是三种代理模式的使用示例。

代理模式使用场景

看到这里,估计有些读者会好奇,到底什么时候使用代理模式呢?
a.动态代理在Android实际开发中用的并不是很多,但在设计框架的时候用的就比较多了,插件化和热更新就是使用的动态代理技术。

b.在J2EE中,像Spring,Hibernate等都有通过动态代理来实现方法增强、方法拦截等需要,通过代理的方式优雅的实现AOP编程。

c.一般在接手前面的人项目时,如果不想改变源码,可以通过代理类来进行增强。

d.也可以通过代理来进行日志记录功能、性能统计等等。

在使用动态代理模式时,要根据具体情况使用,一般按照如下规则进行抉择:
如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理

Jdk动态代理的实现原理

下面继续看看Jdk动态代理是如何生成代理类的
通过Proxy.newProxyInstance方法就可以创建一个代理对象,下面看看这方法

//Proxy.java

private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

@CallerSensitive
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 {
    //关键代码2
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            cons.setAccessible(true);
        }
	//关键代码3
        return cons.newInstance(new Object[]{h});
    } 
   ...
}

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

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}

这个方法中,通过getProxyClass0方法获取代理类的Class,具体是通过在proxyClassCache.get(loader, interfaces)方法获取的,这个proxyClassCache的赋值是这样的

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

所以proxyClassCache.get(loader, interfaces)方法回先通过传入的loader查找jvm是缓存了代理对象的Class,如果存在,则直接返回这个Class,如果不存在,则调用Proxy.ProxyClassFactory的apply方法去生成代理类的Class。由于proxyClassCache是WeakCache类型的,下面看看WeakCache的get方法:

//WeakCache.java
public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        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;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
	//关键代码
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            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 {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
 }

这个方法中,关键代码处,调用subKeyFactory.apply(key, parameter)方法来生成代理类的,由于这个subKeyFactory就是通过WeakCache类的构造方法传入的,通过上面proxyClassCache
的赋值,可以知道,传入了一个new ProxyClassFactory()对象赋值给了subKeyFactory,所以,subKeyFactory.apply方法其实是调用的Proxy.ProxyClassFactory类的apply方法,下面看看这个方法

//Proxy
public class Proxy implements java.io.Serializable {
    ...
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>{
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

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

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            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");
                }

                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
    
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;


            for (Class<?> intf : interfaces) {
                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) {
                proxyPkg = "";
            }

            {
               
                List<Method> methods = getMethods(interfaces);
                Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
                validateReturnTypes(methods);
                List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);

                Method[] methodsArray = methods.toArray(new Method[methods.size()]);
                Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);

  
                long num = nextUniqueNumber.getAndIncrement();
                String proxyName = proxyPkg + proxyClassNamePrefix + num;

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

}

这个方法内部,最终是通过一个native方法generateProxy来完成代理类的Class创建。
得到了代理类的Class对象后,就在Proxy类的newProxyInstance方法的方法,通过newIntance()方法完成代理对象的创建。以上过程便是Jdk动态代理对象生成过程的分析,具体更加细致的分析,大家可以看这篇文章 JDK动态代理实现原理----JDK1.8

总结:
1.如果想对原来的代码不做修改,在原有的代码中新增一些控制逻辑,可以考虑使用代理模式,但是选择使用哪种代理模式,需要根据实际情况来选择,对于动态代理,如果被代理的类实现了接口,则可以使用Jdk动态代理,如果被代理的类未实现接口,则需要使用Cglib动态代理。

2.静态代理和动态代理的差别:
静态代理,是在编译期就已经明确了代理类,也就是程序运行前,代理类的.class已经存在,
而动态代理在编译期还不能明确,需要在执行阶段通过反射动态的创建。
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道代理什么,而动态代理是不知道要代理的东西的,要在运行时才知道。

3.Jdk动态代理需要被代理的类实现接口,而Cglib动态代理则不需要被代理的类实现接口,它是通过派生被代理类的子类来实现的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值