代理模式

2020-06-11  本文已影响0人  more2023

一、什么是代理模式

代理模式(Proxy pattern):代理模式又叫委托模式,是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。代理模式通俗来讲就是生活中的中介。

二、为什么要使用代理模式

1、中介隔离作用
在某些情况下,一个客户类不想或者不能直接直接引用委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
2、开闭原则,增加功能
代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来拓展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类、以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是通过调用委托类的相关方法,来提供特定的方法。真正的业务还是由委托类来实现,但是可以在业务功能执行前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要修改已经封装好的委托类。

三、代理模式的分类

我们有多种不同的方式来实现代理,如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。

3.1、静态代理

静态代理是由程序员创建或特定工具自动生成源码,再对其进行编译。在程序运行之前,代理类.class文件就已经被创建了。

3.2、动态代理

动态代理是在程序运行时,代理类.class文件是通过反射动态创建的。
动态代理实现的两种方式:

四、代理模式的结构

代理模式结构图.png

Subject(抽象主题角色):可以是抽象类,也可以是接口。抽象主题是一个普通的业务类型。
RealSubject(具体主题角色):也叫做被委托角色或被代理角色,是业务逻辑的具体执行者。
Proxy(代理主题角色):也叫做委托类或代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后处理工作。

五、代码示例

5.1、静态代理

5.1.1、抽象主题类

public interface Subject {
    /**
     * 接口方法
     */
    public void req();
}

5.1.2、具体主题类

public class RealSubject implements Subject {
    /**
     * 具体的业务逻辑实现
     */
    @Override
    public void req() {
        //业务处理逻辑
    }
}

5.1.3、代理类

public class Proxy implements Subject {
    /**
     * 要代理的实现类
     */
    private Subject subject = null;

    public Proxy(Subject subject) {
        this.subject = subject;
    }
    /**
     * 实现接口方法
     */
    @Override
    public void req() {
        this.preReq();
        this.subject.req();
        this.postReq();
    }
    /**
     * 预处理
     */
    private void preReq() {
        //do something
    }
    /**
     * 后处理
     */
    private void postReq() {
        //do something
    }
}

5.1.4、客户端类

public class Client {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        Proxy proxy = new Proxy(subject);
        proxy.req();
    }
}

静态代理的优缺点
1、优点

5.2、动态代理(JDK动态代理)

5.2.1、java.lang.reflect.InvocationHandler接口

public interface InvocationHandler {
    /**
     * @param   proxy   被代理对象
     * @param   method  要调用的方法
     * @param   args    方法调用时所需要的参数
     * @return  
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

5.2.2、java.lang.reflect.Proxy类

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;

    /** parameter types of a proxy class constructor */
    private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

    /**
     * Prohibits instantiation.
     */
    private Proxy() {
    }

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

    /**
     * 获取被代理对象实例
     * @param   被代理对象的类加载器
     * @param   被代理对象实现的全部接口
     * @param   InvocationHandler接口的子类的实例(即代理类本身)
     * @return  
     */
    @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;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            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);
        }
    }
}

5.2.3、抽象主题类

public interface Subject {
    /**
     * 接口方法
     */
    public void req();
}

5.2.4、具体主题类

public class RealSubject implements Subject {
    /**
     * 具体的业务逻辑实现
     */
    @Override
    public void req() {
        //业务处理逻辑
    }
}

5.2.5、代理类

@Slf4j
public class ProxyHandler implements InvocationHandler {
    /**
     * 目标对象
     */
    private Object target;

    /**
     * 绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
     * @param target 绑定具体的代理实例
     * @return 动态代理类实例
     */
    public Object newProxyInstance(Object target) {
        this.target = target;
        Object result = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
        return result;
    }

    /**
     * 关联的这个实现类的方法被调用时将被执行。InvocationHandler接口的方法。
     * @param proxy  代理
     * @param method 原对象被调用的方法
     * @param args   方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //TODO 原对象方法调用前添加的预处理逻辑
        Object ret = null;
        try {
            //调用目标方法
            ret = method.invoke(target, args);
        } catch (Exception e) {
            log.error("调用{}.{}发生异常", target.getClass().getName(), method.getName(), e);
            throw e;
        }
        //TODO 原对象方法调用后添加的后处理逻辑
        return ret;
    }
}

5.2.6、客户端类

@Slf4j
public class Client {
    public static void main(String[] args) {
        log.info("开始");
        ProxyHandler handler = new ProxyHandler();
        Subject subject = (Subject) handler.newProxyInstance(new RealSubject ());
        subject.req();
        log.info("结束");
    }
}

5.2.7、生成的代理类

public final class Subject$proxy0 extends Proxy implements Subject {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $proxy4(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void req() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.fyb.myProxy.Subject").getMethod("req");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

动态代理(JDK方式)的优缺点
1、优点

5.3、动态代理(CGlib动态代理)

(待补充)

上一篇下一篇

猜你喜欢

热点阅读