设计模式->结构型模式->代理模式

来源:互联网 发布:iphone8支持5g网络吗 编辑:程序博客网 时间:2024/06/05 00:33

1.定义:为其他对象提供一种代理以控制对该对象的访问

2.UML(通用)


3.理论基础:里氏替换原则,继承,多态

4.涉及角色:

        抽象主题:定义一组和业务相关的操作,一般为接口或者抽象类

        真实主题:被代理者或者委托者,真正的业务处理类,

        代理:该模式的核心,负责对真实主题的应用,把所有抽象主题中定义的操作委托给真正主题处理,同时在真实主题处理前作预处理以及在真实主题处理后作后续的操作

5.优点

1.职责清晰.业务处理都是有真实主题角色负责,也就是说真实角色只关注于业务,预处理以及后续的操作都可以放在代理中完成。eg:数据插入数据库时,必要的参数检查以及对事务的控制都可以放在代理中,而实际的数据插入直接交给真实主题角色

2.高扩展性。真实主题随时都可以变化,但是无论如何变化都会实现抽象主题角色,不管真实主题如何变化只要依赖的抽象没有变化那么代理类无须修改

3.高灵活性,这个主要针对动态代理

6.缺点

1.一个代理只能代理一个真实主题,随着代理类的增加系统的复杂行也会增加

2.当抽象修改后,同时需要修改真实主题以及代理

7.使用场景

        1.隐藏对象存在于不同地址空间的事实。对象在不同地址空间提供局部代表eg远程代理,Android AIDL,webservice

2.虚拟代理需要创建开销很大的对象。通过该实例来存储实例化需要很长时间的真实对象。如web页面图片展示。页面加载图片时有的图片没有显示但是却预留了位置,此时采用的就是代理。代理中存放了图片的尺寸

3.安全代理,用来控制真实对象的访问权限。

4.智能指引调用真实对象时代理处理另外一些事务。eg:计算真实对象的调用次数。

5.增强现有代码逻辑功能

代理分为如下几种:

1.普通模式:只能通过代理访问真实对象不能直接访问真实对象,也就是说该模式下我们需要知道代理不需要知道真实对象


//抽象主题

public interface IUserDao {    public long insertUser(User user);    //强制使用代理时使用    public IUserDao getProxy();}
public class User {    private long id;    private String name;    public User(String name) {        this.name = name;    }    public User() {    }    public long getId() {        return id;    }    public void setId(long id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

//代理

//做相关的预处理,以及后续处理,让真正的功能单元更关注实际的业务public class UserDaoProxy implements IUserDao{    //持有最上层对象实例    private IUserDao userDao = null;    //通过构造函数封装具体对象的创建    public UserDaoProxy() {        this.userDao = new UserDao(this);    }    public IUserDao getUserDao() {        this.userDao = new UserDao(this);        return this.userDao;    }    @Override    public long insertUser(User user) {        System.out.println("前置操作");        //前置操作eg.属性有效性等        if(null == user.getName() || user.getName().length() == 0) {            throw new IllegalArgumentException("参数无效");        }        this.userDao.insertUser(user);        //后置操作        System.out.println("后置操作");        return user.getId();    }    @Override    public IUserDao getProxy() {        return null;    }}
//真实主题

//只专注于实际的业务public class UserDao implements IUserDao{    //通过自身代理构建真实主题对象    public UserDao(IUserDao userDao) {        if(null == userDao) {            throw new NullPointerException("my proxy is null");        }    }    @Override    public long insertUser(User user) {        //插入数据到物理空间        user.setId(10);        System.out.println("插入 OK");        return user.getId();    }    @Override    public IUserDao getProxy() {        return null;    }}
客户端

public class Client {    public static void main(String[] args){        User user = new User("Jack");        IUserDao proxy = new UserDaoProxy();        System.out.println(proxy.insertUser(user));    }}
如果在真实对象中添加无参构造方法,那么该代理可以通过该构造方法初始化真实对象的实例,此时可以绕过代理直接访问真实对象。也就是说无法进行预处理以及后续处理。也就是说在代理中创建真实主题的实例用多种方式。Team中需要通过编程规范来约束。

2.强制代理模式:只能调用真实对象,不用关心具体的代理。该模式下的代理只能有真实对象产生。


真实主题

//只专注于实际的业务public class UserDao implements IUserDao{    private IUserDao proxy;    @Override    public long insertUser(User user) {        if(null == proxy) {            throw new NullPointerException("代理为null");        }        //插入数据到物理空间        user.setId(10);        System.out.println("插入 OK");        return user.getId();    }    //该模式的核心    //在真实主题中构建自己的代理,将正式主题代理绑定,    //该方式下客户端不需要知道代理的存在,    //但是真实主题需要知道自己的代理    @Override    public IUserDao getProxy() {        this.proxy = new UserDaoProxy(this);        return this.proxy;    }}
代理

//做相关的预处理,以及后续处理,让真正的功能单元更关注实际的业务public class UserDaoProxy implements IUserDao{    private IUserDao userDao = null;    public UserDaoProxy(IUserDao userDao) {        this.userDao = userDao;    }    @Override    public long insertUser(User user) {        if(null == userDao) {            throw new NullPointerException("target object is null");        }        System.out.println("前置操作");        //前置操作eg.属性有效性等        if(null == user.getName() || user.getName().length() == 0) {            throw new IllegalArgumentException("参数无效");        }        this.userDao.insertUser(user);        //后置操作        System.out.println("后置操作");        return user.getId();    }    @Override    public IUserDao getProxy() {        return null;    }}

客户端

public class Client {    public static void main(String[] args){        User user = new User("Jack");        UserDao userDao = new UserDao();        System.out.println(userDao.getProxy().insertUser(user));    }}


不管是普通模式还是强制模式,都需要解决的问题是,代理和真实主题之间的绑定问题,及代理需要知道真实主题,真实主题需要知道代理,一般都采用构造函数的方式满足以上两点。但是普通模式需要通过编码规范约束,有时候会绕过代理导致风险。

3.动态代理:

3.1采用JDK提供的接口以及类(InvocationHandler/Proxy


需要实现InvocationHandler接口并且重写invoke

public class UserInvocationHandler implements InvocationHandler {    private Object realSubject;    public UserInvocationHandler(Object realSubject) {        this.realSubject = realSubject;    }    /**     *     * @param proxy 代理类实例     * @param method 真实主题的某个方法     * @param args 真实主题方法参数     * @return     * @throws Throwable     */    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("before");        Object obj = method.invoke(realSubject, args);        System.out.println("after");        return obj;    }}
真实主题

//只专注于实际的业务public class UserDao implements IUserDao{    private IUserDao proxy;    @Override    public long insertUser(User user) {        //插入数据到物理空间        user.setId(10);        System.out.println("插入 OK");        return user.getId();    }}

客户端

public class Client {    public static void main(String[] args) {        IUserDao userDao = new UserDao();        UserInvocationHandler userInvocationHandler = new UserInvocationHandler(userDao);        //目标对象的代理对象        IUserDao proxy = (IUserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),                userDao.getClass().getInterfaces(),                userInvocationHandler);        proxy.insertUser(new User());    }}

其中invkoe是接口InvocationHandler定义必须实现的,他完成了对真实主题方法的调用。

IUserDao proxy = (IUserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),                userDao.getClass().getInterfaces(),                userInvocationHandler);

重新生成一个对象,该对象就是代理实例,其中userDao.getClass.getInterface();查找该类的所有接口。也就是说采用JDK自带机制实现动态代理的前置条件是必须实现接口。那么有谁负责真正的逻辑?是通过new UserInvocationHandler(userDao);对象,一个动态代理类是由IncocationHandler的实现类实现所有的方法,由invoke方法接管所有方法的实现。调用顺序为Client.insert(...)-->Proxy.invoke(...)-->UserIncocationHandler.invoke(...)-->UserDao

静态代理中代理类是知道真实主题对象的,可以这么理解动态代理中代理类和真实主题是绑定在一起的。但是动态代理中没有具体的代理类,因为动态代理中代理类是动态创建的,此外InvocationHandler是如何被调用的

1.代理类实例创建 

loader:类加载器interfaces:真实主题实现的所有接口h:InvocationHandler子类public static Object newProxyInstance(ClassLoader loader,                                      Class<?>[] interfaces,                                      InvocationHandler h)    throws IllegalArgumentException{    ……    final Class<?>[] intfs = interfaces.clone();    /*     * Look up or generate the designated proxy class.     */    查找或者生成指定的代理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;        if (!Modifier.isPublic(cl.getModifiers())) {            AccessController.doPrivileged(new PrivilegedAction<Void>() {                public Void run() {                    cons.setAccessible(true);                    return null;                }            });        }        将InvocationHandler对象作为参数创建代理类实例        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);    }}
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    在缓存中查找代理类实例,如果存在直接返回否则通过ProxyClassFactory创建    return proxyClassCache.get(loader, interfaces);}private static final WeakCache<ClassLoader, Class<?>[], Class<?>>    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());private static final class ProxyClassFactory    implements BiFunction<ClassLoader, Class<?>[], Class<?>>{    ……    @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) {            }           ……        /*         * Choose a name for the proxy class to generate.         */        代理类文件名称        long num = nextUniqueNumber.getAndIncrement();        String proxyName = proxyPkg + proxyClassNamePrefix + num;        /*         * Generate the specified proxy class.         */        生成代理类字节码文件到内存        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(            proxyName, interfaces, accessFlags);        try {            根据代理字节码文件生成代理类            return defineClass0(loader, proxyName,                                proxyClassFile, 0, proxyClassFile.length);        } catch (ClassFormatError e) {            ……        }    }}

private final class Factory implements Supplier<V>public V get(K key, P parameter) {    Objects.requireNonNull(parameter);    expungeStaleEntries();    根据classloder从缓存中获取对象    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创建一个给定类型的结果    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);            }        }    }}
2.InvocationHandler如何使用

在代理实例创建是将自定义的handler作为构造函数的参数,通过反编译代理字节文件

public final class $proxy  extends Proxy  implements IUserDao{  private static Method m1;  private static Method m2;  private static Method m3;  private static Method m4;  private static Method m0;  采用自定义handler构建代理类实例  public $proxy(InvocationHandler paramInvocationHandler)  {    super(paramInvocationHandler);  }    public final boolean equals(Object paramObject)  {    try    {      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }    public final String toString()  {    try    {      return (String)this.h.invoke(this, m2, null);    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }    public final long insertUser(User paramUser)  {    try    {      调用自定义handler中的Object invoke(Object proxy, Method method, Object[] args)      return ((Long)this.h.invoke(this, m3, new Object[] { paramUser })).longValue();    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }    public final IUserDao getProxy()  {    try    {      return (IUserDao)this.h.invoke(this, m4, null);    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }    public final int hashCode()  {    try    {      return ((Integer)this.h.invoke(this, m0, null)).intValue();    }    catch (Error|RuntimeException localError)    {      throw localError;    }    catch (Throwable localThrowable)    {      throw new UndeclaredThrowableException(localThrowable);    }  }    static  {    try    {      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);     通过反射加载接口中的方法      m3 = Class.forName("com.patterns.structure.proxy.common.IUserDao").getMethod("insertUser", new Class[] { Class.forName("com.patterns.structure.proxy.common.User") });      强制使用代理      m4 = Class.forName("com.patterns.structure.proxy.common.IUserDao").getMethod("getProxy", new Class[0]);      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);      return;    }    catch (NoSuchMethodException localNoSuchMethodException)    {      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());    }    catch (ClassNotFoundException localClassNotFoundException)    {      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());    }  }}

代理类字节码输出

   public static void writeProxyClassToHardDisk(String path) {        // 第一种方法,这种方式在刚才分析ProxyGenerator时已经知道了//         System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true);        // 第二种方法        // 获取代理类的字节码        byte[] classFile = ProxyGenerator.generateProxyClass("$proxy", UserDao.class.getInterfaces());        FileOutputStream out = null;        try {            out = new FileOutputStream(path);            out.write(classFile);            out.flush();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                out.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

JDK动态代理总结

1.必须要实现InvocationHandler接口重写invoke

2.被代理类必须实现接口

3..代理类是动态生成的,需要采用字节码的方式获得代理类实例

4..在代理类中,构造函数采用自定义的handler创建代理类实例,同时采用静态块以及反射的的方式加载接口中方法以及Object的部分方法。

5.采用代理对象调用api时,实际上调用的是自定义handler中的invoke,在自定义中的handler中构造方法采用了真实主题对象

采用JDK现有机制被代理类必须实现接口,在不实现接口的情况下使用CGLib的方式创建

public class UserService {    public int update(int id){        try {            TimeUnit.MILLISECONDS.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("update" + id);        return id;    }}
//定义自己的方法拦截器public class UserServiceProxy implements MethodInterceptor {    /**     * All generated proxied methods call this method instead of the original method.     * The original method may either be invoked by normal reflection using the Method object,     * or by using the MethodProxy (faster).     * @param obj "this", the enhanced object 实现MethodInterceptor接口的类的实例     * @param method intercepted Method    需要拦截的方法     * @param args argument array; primitive types are wrapped 拦截方法参数列表     * @param proxy used to invoke super (non-intercepted method); may be called     * as many times as needed 触发父类方法的对象     * @throws Throwable any exception may be thrown; if so, super method will not be invoked     * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.     * @see MethodProxy     */    @Override    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {        System.out.println("before");        Object ret = proxy.invokeSuper(obj, args);        System.out.println("after");        return ret;    }}
public class Client {    public static void main(String[] args) {        Enhancer enhancer = new Enhancer();        //设置生成类的父类        enhancer.setSuperclass(UserService.class);        //设置回调方法器参数为自定义拦截器器,最后增强目标类调用的是代理类对象intercept方法        enhancer.setCallback(new UserServiceProxy());        System.out.println(enhancer.create().getClass().getSuperclass());        //创建代理对象        UserService us = (UserService) enhancer.create();        //调用该方法是会触发拦截器,控制权先交给拦截器,连接器执行完成后将控制权交给当前进程        //也就是说在拦截器中实现了目标类的调用        us.update(100);        System.out.println("End");    }}

CGLib动态代理总结

1.必须要实现MethodInterceptor接口

2.被代理类可以不实现接口,不管是方法还是类的定义不能是是final修饰

3.其中方法拦截器以及Enhancer是关键


0 0
原创粉丝点击