打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Java设计模式——代理模式

代理模式

学习目标

  • 掌握代理模式的应用场景和实现原理。
    • 应用场景
      • 保护目标对象:客户并不知道对象是如何实现具体业务功能的,只要调用代理对象的接口就行了。
      • 增强目标对象:在实现目标对象的业务功能之前或者之后做一些业务实现。Spring-AOP
    • 实现原理
      • 静态代理:代理对象持有被代理对象的引用。由被代理对象实现目标业务。
      • 动态代理:代理对象持有被代理对象的引用,代码运行产生一个新的java类,并被编译重新加载,然后在新的类中执行目标业务实现动态代理。
  • 了解静态代理和动态代理的区别。
    • 静态代理:被代理对象在代码中被写死了,不易扩展,适用于简单的业务,代码复用性不高,违背开闭原则。
    • 动态代理:只要是实现了同一个接口的被代理类,都可以代理实现,实现了业务扩展。代码复用,遵循开闭原则。
  • 了解CGLib和JDK-proxy的区别。
    • JDK-proxy动态代理:实现了被代理对象的接口,生成新的class字节码,利用反射调用实现代理。无法代理不实现代理业务接口的方法。
    • CGLib动态代理:继承被代理对象,生成新的class字节码,通过fastclass机制调用实现代理。无法代理被final关键字修饰的方法

代理模式的定义

为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和服务端起到中介的作用。是一种结构型设计模式。

静态代理

/**
 * @description: 被代理对象需要实现的目标接口
 * @author: lmc
 * @create: 2019-06-12 15:38
 **/
public interface ITarget {
    /**
     * @description: 被代理对象的行为
     * @return void
     * @date 2019/6/12 15:39
     * @author lmc
     */
    void behavior();
}
/**
 * @description: 真实的被代理的目标对象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class TargetImpl implements ITarget {

    public void behavior() {
        System.out.println("执行真实的被代理对象的行为。");
    }
}

下面的类也是一个被代理的目标对象,但是没有实现ITarget接口

/**
 * @description: 真实的被代理的目标对象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class Target{

    public void behavior() {
        System.out.println("执行被代理对象target的行为。");
    }
}

/**
 * @description: 静态代理类
 * @author: lmc
 * @create: 2019-06-12 15:45
 **/
public class StaticProxy {

    /**
     *  持有被代理对象的引用
     */
    private ITarget targetImpl;

    /**
     *  持有被代理对象的引用
     */
    private Target target;//一个没有实现接口的类



    /**
     *  构造方法初始化值
     * @param targetImpl
     */
    public StaticProxy(ITarget targetImpl,Target target){
        this.targetImpl=targetImpl;
        this.target=target;
    }

    /**
     * @description: 被代理之前的增强行为
     * @date 2019/6/12 15:56
     * @author lmc
     */
    private void beforeBehavior(){
        System.out.println("执行代理之前需要做的一些事情。");
    }

    /**
     * @description: 被代理之后的增强行为
     * @date 2019/6/12 15:57
     * @author lmc
     */
    private void afterBehavior(){
        System.out.println("执行代理之后需要做的一些事情。");
    }

    /**
     * @description: 开始执行代理
     * @date 2019/6/12 15:59
     * @author lmc
     */

    public void startProxy(){
        beforeBehavior();
        targetImpl.behavior();
        target.behavior();
        afterBehavior();
    }
}
/**
 * @description: 静态代理客户端 懒汉式单例
 * @author: lmc
 * @create: 2019-06-12 16:01
 **/
public class StaticProxyClient implements Serializable {

    private final static StaticProxyClient staticProxyClient=new StaticProxyClient();

    private StaticProxyClient(){
        if(null != staticProxyClient){
            throw new RuntimeException("单例类,不允许被反射实例化");
        }
    };

    public static StaticProxyClient getInstance(){
        return staticProxyClient;
    }

    /**
     * @description: 开始静态代理
     * @date 2019/6/12 16:20
     * @author lmc
     */
    public void startStaticProxy(){
        ITarget targetImpl=new TargetImpl();
        Target target=new Target();
        StaticProxy staticProxy=new StaticProxy(targetImpl,target);
        staticProxy.startProxy();
    }

    /**
     * @description: 重写readResolve,防止序列化破坏单例
     * @return java.lang.Object
     * @date 2019/6/12 16:18
     * @author lmc
     */
    private Object readResolve(){
        return staticProxyClient;
    }
}
/**
 * @description: 静态代理测试
 * @author: lmc
 * @create: 2019-06-12 16:10
 **/
public class StaticProxyTest {

    public static void main(String[] args) {

        StaticProxyClient.getInstance().startStaticProxy();
    }
}
静态代理测试结果

只要代理对象持有被代理对象的引用就可以实现静态代理了。

JDK-proxy动态代理

/**
 * @description: 真实的被代理的目标对象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class TargetImpl1 implements ITarget {

    public void behavior() {
        System.out.println("执行被代理对象1的行为。");
    }
}
/**
 * @description: 真实的被代理的目标对象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class TargetImpl2 implements ITarget {

    public void behavior() {
        System.out.println("执行被代理对象2的行为。");
    }
}
/**
 * @description: 真实的被代理的目标对象
 * @author: lmc
 * @create: 2019-06-12 15:41
 **/
public class TargetImpl3 implements ITarget {

    public void behavior() {
        System.out.println("执行被代理对象3的行为。");
    }
}
/**
 * @description: JDK动态代理对象
 * @author: lmc
 * @create: 2019-06-12 17:00
 **/

public class JdkProxy implements InvocationHandler {

    /**
     * 被代理对象的引用
     */
    private ITarget target;

    /**
     * @description: 获取代理之后的实例对象
     * @param target 被代理对象
     * @return com.lmc.gp12380.pattern.proxy.ITarget
     * @date 2019/6/12 19:55
     * @author lmc
     */
    public ITarget getProxyInstance(ITarget target){
        this.target = target;
        Class<?> clazz = target.getClass();
        return (ITarget) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeBehavior();
        //通过反编译工具可以查看源代码
        byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{ITarget.class});
        FileOutputStream os = new FileOutputStream("E://$Proxy0.class"); os.write(bytes); os.close();
        Object obj = method.invoke(this.target,args);
        afterBehavior();
        return obj;
    }

    /**
     * @description: 被代理之前的增强行为
     * @date 2019/6/12 15:56
     * @author lmc
     */
    private void beforeBehavior(){
        System.out.println("执行代理之前需要做的一些事情。");
    }

    /**
     * @description: 被代理之后的增强行为
     * @date 2019/6/12 15:57
     * @author lmc
     */
    private void afterBehavior(){
        System.out.println("执行代理之后需要做的一些事情。");
    }
}

下面这个类是测试没有实现ITarget接口的代理

/**
 * @description: JDK动态代理对象
 * @author: lmc
 * @create: 2019-06-12 17:00
 **/
public class JdkProxy1 implements InvocationHandler {

    /**
     * 被代理对象的引用
     */
    private Target target;

    /**
     * @description: 获取代理之后的实例对象
     * @param target 被代理对象
     * @return com.lmc.gp12380.pattern.proxy.ITarget
     * @date 2019/6/12 19:55
     * @author lmc
     */
    public Target getProxyInstance(Target target){
        this.target = target;
        Class<?> clazz = target.getClass();
        return (Target) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeBehavior();
        //通过反编译工具可以查看源代码
        byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{ITarget.class});
        FileOutputStream os = new FileOutputStream("E://$Proxy0.class"); os.write(bytes); os.close();
        Object obj = method.invoke(this.target,args);
        afterBehavior();
        return obj;
    }

    /**
     * @description: 被代理之前的增强行为
     * @date 2019/6/12 15:56
     * @author lmc
     */
    private void beforeBehavior(){
        System.out.println("执行代理之前需要做的一些事情。");
    }

    /**
     * @description: 被代理之后的增强行为
     * @date 2019/6/12 15:57
     * @author lmc
     */
    private void afterBehavior(){
        System.out.println("执行代理之后需要做的一些事情。");
    }
}

下面这个类是Jdk-proxy代理生成的class字节码反编译之后的java

public final class $Proxy0 extends Proxy
        implements ITarget {

    public $Proxy0(InvocationHandler invocationhandler) {
        super(invocationhandler);
    }

    public final boolean equals(Object obj) {
        try {
            return ((Boolean) super.h.invoke(this, m1, new Object[]{
                    obj
            })).booleanValue();
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
        return false;
    }

    public final String toString() {
        try {
            return (String) super.h.invoke(this, m2, null);
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
        return null;
    }

    public final void behavior() {
        try {
            super.h.invoke(this, m3, null);
            return;
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return ((Integer) super.h.invoke(this, m0, null)).intValue();
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
        return 0;
    }

    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    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.lmc.gp12380.pattern.proxy.ITarget").getMethod("behavior", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException nosuchmethodexception) {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        } catch (ClassNotFoundException classnotfoundexception) {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

这里我们知道了这个反编译生成的java类实现了ITarget接口

/**
 * @description: JDK代理实现测试
 * @author: lmc
 * @create: 2019-06-12 19:49
 **/
public class JdkProxyTest {

    public static void main(String[] args) {

        ITarget target1 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl1());
        target1.behavior();

        ITarget target2 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl2());
        target2.behavior();

        ITarget target3 = (ITarget) new JdkProxy().getProxyInstance(new TargetImpl3());
        target3.behavior();

        /**
         *  Target 类没有实现接口 无法被jdkproxy代理
         */
        ITarget target4 = (ITarget) new JdkProxy1().getProxyInstance(new Target());
        target4.behavior();
    }
}
JDK-proxy动态代理测试结果

嘿嘿,可以看到target4代理出错啦。原因就是Target类没有实现ITarget接口。

CGLib动态代理

/**
 * @description: CGLib动态代理对象
 * @author: lmc
 * @create: 2019-06-12 17:00
 **/
public class CGLibProxy implements MethodInterceptor {

    /**
     * @description: 被代理之前的增强行为
     * @date 2019/6/12 15:56
     * @author lmc
     */
    private void beforeBehavior(){
        System.out.println("执行代理之前需要做的一些事情。");
    }

    /**
     * @description: 被代理之后的增强行为
     * @date 2019/6/12 15:57
     * @author lmc
     */
    private void afterBehavior(){
        System.out.println("执行代理之后需要做的一些事情。");
    }


    /**
     * @description: 获取代理之后的实例对象
     * @param target 被代理对象
     * @return com.lmc.gp12380.pattern.proxy.ITarget
     * @date 2019/6/12 19:55
     * @author lmc
     */
    public Object getProxyInstance(Object target){

        Class<?> clazz = target.getClass();
        //相当于Proxy,代理的工具类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeBehavior();
        /**
         *  利用 cglib 的代理类可以将内存中的 class 文件写入本地磁盘
         */
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib_proxy_class/");

        Object obj = methodProxy.invokeSuper(o,objects);
        afterBehavior();
        return obj;
    }
}

/**
 * @description: CGLib代理实现测试
 * @author: lmc
 * @create: 2019-06-12 20:26
 **/
public class CGLibProxyTest {

    public static void main(String[] args) {

        ITarget target1= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl1());
        target1.behavior();

        ITarget target2= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl2());
        target2.behavior();

        ITarget target3= (ITarget) new CGLibProxy().getProxyInstance(new TargetImpl3());
        target3.behavior();

        Target target4= (Target) new CGLibProxy().getProxyInstance(new Target());
        target4.behavior();
    }
}
CGLib动态代理测试结果

嘿嘿,可以看到Target没有实现ITarget接口,也能实现动态代理。

代码里面是可以生成CGLib动态代理的字节码文件的,也可以反编译过来看看,因为我是看不懂,我就不展示了。

代理模式的优缺点

优点
  1. 代理模式能将代理对象与真实被调用的目标对象分离。
  2. 一定程度上降低了系统的耦合度,扩展性好。
  3. 可以起到保护目标对象的作用。
  4. 可以对目标对象的功能增强。
缺点
  1. 代理模式会造成系统设计中类的数量增加。
  2. 在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。
  3. 增加了系统的复杂度。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【设计模式】牛掰格拉斯的代理模式
Spring AOP 本质系列之动态代理
java反射机制与动态代理(三)
解读JDK的动态代理
JAVA的反射机制与动态代理
java动态代理模式初解
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服