动态代理相对于静态代理的优势

整理自知乎;整理自知乎;整理自知乎

静态代理与动态代理

代理模式是一种设计模式,典型的分为静态代理和动态代理。

先讲静态代理

首先给个场景,现有个FontProvider接口,用于获取字体

public interface FontProvider {

    Font getFont(String name);
}

具体的实现类有三种,分别是FontProviderFromDisk,FontProviderFromNet,FontProviderFromSystem

public class FontProviderFromDisk implements FontProvider {

    @Override
    public Font getFont(String name) {
        System.out.printf("get %s from disk\n",name);
        return null;
    }
}

public class FontProviderFromNet implements FontProvider {

    @Override
    public Font getFont(String name) {
        System.out.printf("get %s from net\n",name);
        return null;
    }
}

public class FontProviderFromSystem implements FontProvider {

    @Override
    public Font getFont(String name) {
        System.out.printf("get %s from system\n",name);
        return null;
    }
}

然后我们使用上面FontProvider的实现类,获取字体,但是我突然想增加个缓存的功能以提高效率!怎么办呢?第一种就是在各个FontProvider实现类中添加缓存的代码,这样显然不理想,万一我这有一百个FontProvider实现类呢,那岂不是得修改一百多个FontProvider的实现类?第二种就是利用静态代理控制方法的调用(从缓存中获取成功,则不调用委托类的getFont()方法,获取失败则老老实实调用委托类的getFont()方法),这样即使有一百个FontProvider实现类也可以保证就仅有一份实现字体缓存的代码。

public class CachedFontProvider implements FontProvider {

    private FontProvider fontProvider; //委托类
    private Map<String, Font> cached;
    public CachedFontProvider(FontProvider fontProvider) {
        this.fontProvider = fontProvider;
        cached = new HashMap<>();
    }

    /**
     * 绑定委托类,依靠这个方法可以运行时更换委托类
     * @param fontProvider
     */
    public void bindTo(FontProvider fontProvider) {
        this.fontProvider = fontProvider;
        cached.clear(); //委托类更换,缓存清除
    }

    @Override
    public Font getFont(String name) {

        Font font = cached.get(name);
        if(font == null) { //从缓存中获取失败,调用委托类的getFont()方法
            font = fontProvider.getFont(name);
            cached.put(name, font);
            return font;
        } else { //从缓存中获取成功,无需调用委托类的getFont()方法
            return font;
        }
    }
}

自此,静态代理的作用就体现出来了,它可以控制方法的调用,处理方法调用的结果。

现在,我又添加了ColorProvider,ShapeProvider两个结果,各自也拥有若干实现类,现在我也想为各个XXXProvider添加缓存功能,咋办?

这还不简单,静态代理啊,好不好?!

public class CachedColorProvider implements ColorProvider {

    private ColorProvider colorProvider; //委托类
    private Map<String, Color> cached;
    public CachedColorProvider(ColorProvider colorProvider) {
        this.colorProvider = colorProvider;
        cached = new HashMap<>();
    }

    /**
     * 绑定委托类,依靠这个方法可以运行时更换委托类
     * @param ColorProvider
     */
    public void bindTo(ColorProvider colorProvider) {
        this.colorProvider = colorProvider;
        cached.clear(); //委托类更换,缓存清除
    }

    @Override
    public Color getColor(String name) {

        Color color = cached.get(name);
        if(Color == null) { //从缓存中获取失败,调用委托类的getColor()方法
            Color = ColorProvider.getColor(name);
            cached.put(name, color);
            return color;
        } else { //从缓存中获取成功,无需调用委托类的getColor()方法
            return color;
        }
    }
}

public class CachedShapeProvider implements ShapeProvider {

    private ShapeProvider shapeProvider; //委托类
    private Map<String, Shape> cached;
    public CachedShapeProvider(ShapeProvider shapeProvider) {
        this.shapeProvider = shapeProvider;
        cached = new HashMap<>();
    }

    /**
     * 绑定委托类,依靠这个方法可以运行时更换委托类
     * @param shapeProvider
     */
    public void bindTo(ShapeProvider shapeProvider) {
        this.shapeProvider = shapeProvider;
        cached.clear(); //委托类更换,缓存清除
    }

    @Override
    public Shape getShape(String name) {

        Shape shape = cached.get(name);
        if(shape == null) { //从缓存中获取失败,调用委托类的getShape()方法
            shape = shapeProvider.getShape(name);
            cached.put(name, shape);
            return shape;
        } else { //从缓存中获取成功,无需调用委托类的getShape()方法
            return shape;
        }
    }
}

上面的代码乍看实现了功能,但是仍不理想,代码冗余太高,还是那个“万一”,万一我这还有ImageProvider、MusicProvider等等数百个XXXProvider呢?为每个XXXPrivder提供一个CachedXXXProvider自然不现实。

这就是静态代理的不足之处,它的实现方式以接口实现的形式与委托类绑定在了一起,所以决定了一个委托类对应一个代理类的模式(FontProvider对应CachedFontProvider,ShapeProvider对应CachedShapeProvider,ColorProvider对应CachedColorProvider),如果在委托类很多的应用场景,静态代理显然力不从心。这个时候,使用动态代理就可以打破这种限制。

public class CachedProviderHandler implements InvocationHandler {
    private Map<String, Object> cached = new HashMap<>();
    private Object target;

    public CachedProviderHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
        Type[] types = method.getParameterTypes();
        if (method.getName().matches("get.+") && (types.length == 1) &&
                (types[0] == String.class)) { //控制getFont(),getColor(),getShape()等方法的访问
            String key = (String) args[0];
            Object value = cached.get(key);
            if (value == null) {
                value = method.invoke(target, args);
                cached.put(key, value);
            }
            return value;
        }
        return method.invoke(target, args);
    }
}

//生成FontProvider的代理对象
Proxy.newProxyInstance(FontProvider.class.getClassLoader(),new Class[]{FontProvider.class},new CachedProviderHandler(new FontProviderFromDisk()));

//生成ShapeProvider的代理对象
Proxy.newProxyInstance(ShapeProvider.class.getClassLoader(),new Class[]{ShapeProvider.class},new CachedProviderHandler(new ShapeProviderFromDisk()));

...

一个 CachedProviderHandler就可以代理 FontProvider,ShapeProvider,ColorProvider多个XXXProvider,岂不美哉?

总结

至此,我们知道动态代理相对于静态代理的优势:就静态代理而言,在委托类特别多的应用场景,就要相应的添加许多的代理类,这显然增加了应用程序的复杂度,而使用动态代理就可以减少代理类的数量,相对降低了应用程序的复杂度。

引用

1.https://www.zhihu.com/question/20794107

时间: 2024-08-24 19:18:52

动态代理相对于静态代理的优势的相关文章

Spring框架_代理模式(静态代理,动态代理,cglib代理)

共性问题: 1. 服务器启动报错,什么原因? * jar包缺少.jar包冲突 1) 先检查项目中是否缺少jar包引用 2) 服务器: 检查jar包有没有发布到服务器下:                                      用户库jar包,需要手动发布到tomcat. (每次新建项目) 3) 重新发布项目 * 配置文件错误 (web.xml / struts.xml /bean.xml /hibernate.xml / *.hbm.xml) 明确的提示 * 端口占用 * we

代理设计模式之静态代理与动态代理(超..)详解

在学习Spring框架的时候,有一个重要的思想就是AOP,面向切面编程,利用AOP的思想结合Spring的一些API可以实现核心业务与辅助业务的分离,即可以在执行核心业务时,将一些辅助的业务加进来,而辅助业务(如日志,权限控制等)一般是一些公共业务,这样就实现了两者的分离,使得核心业务的代码更加纯粹,而且辅助业务也能得到复用,这一篇笔记是当时学习spring的时候写的,使用springAPI以及自定义类 实现AOP的一个例子 ,.AOP底层就是通过动态代理来实现的,最近专门学习了一下代理模式,反

代理模式(静态代理+动态代理)——JAVA

代理模式是常用的java设计模式,他的特征是代理类与目标类有同样的接口,代理类主要负责为目标类预处理消息.过滤消息.把消息转发给目标类,以及事后处理消息等.代理类与目标类之间通常会存在关联关系,一个代理类的对象与一个目标类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用目标类的对象的相关方法,来提供特定的服务. 结构图如下: 按照代理的创建时期,代理类可以分为静态代理和动态代理. 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译.在程序运行前,代理类(Proxy)的.clas

Java代理之(jdk静态代理/jdk动态代理/cglib动态代理/aop/aspectj)

一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法.如果公司卖多少钱,代理商也卖多少钱,那么代理商就赚不了钱.所以代理商在调用公司的卖方法后,加上自己的利润然后再把产品卖给客户.而客户部直接跟公司打交道,或者客户根本不知道公司的存在,然而客户最终却买到了产品. 专业点说:代理模式是对象的结构型模式,代码模式给某一个对象提供代理,并由代理对象控制原对象

Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)

一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法.如果公司卖多少钱,代理商也卖多少钱,那么代理商就赚不了钱.所以代理商在调用公司的卖方法后,加上自己的利润然后再把产品卖给客户.而客户部直接跟公司打交道,或者客户根本不知道公司的存在,然而客户最终却买到了产品. 专业点说:代理模式是对象的结构型模式,代码模式给某一个对象提供代理,并由代理对象控制原对象

【设计模式】代理模式:静态代理,动态代理,spring aop

代理模式分为静态代理和动态代理.我们拿链家来举例子,我们本人是真实的对象,有真实的业务需求:需要去找房子:链家是中介,是代理类,他来帮我执行找房子的这个操作. 静态代理: 1.实现一个接口 public interface SearchHome { public void search(); } 2.构建实现接口的委托类 public class Master implements SearchHome { @Override public void search() { System.out.

java 代理模式(静态代理、动态代理、Cglib代理) 转载

Java的三种代理模式 1.代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法 代理模式最大的特点就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代理对象的内部实现中又会去调

GOF23代理模式之静态代理模式理解之经典

 设计模式之代理模式之静态代理模式      代理模式(Proxy pattern)          核心作用:               通过代理,控制对对象的访问.                    可以通过详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理.(即AOP的微观实现)               AOP(面向切面编程.Aspect Oriented Programming)的核心实现的机制          举个例子来理解这种模

Java设计模式-代理模式之静态代理

Java设计模式-代理模式之静态代理 概念 为另一个对象提供一个替身或占位符以提供对这个对象的访问,使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理对象可以是远程的对象.创建开销大的对象或需要安全控制的对象 远程代理控制访问远程对象 虚拟代理控制访问创建开销大的资源 保护代理基于权限控制对资源的访问 看如下的类图: 仔细看上面的类图,首先是Subject它为RealSubject和Proxy提供了接口,通过实现同一个接口,Proxy在RealSubject出现的地方取代它,这点和适配