Java设计模式之代理模式

1、前期回顾

上一篇文章《JAVA设计模式之模板方法模式和建造者模式》谈到了设计模式中建造类的模式,咱们来回顾下。模板方法模式定义了核心的算法结构,而后子类能够实现某些特定结构的算法内容,建造者模式彻底把核心的算法内容交给子类去实现。咱们这篇博文来学习下最经常使用的设计模式,代理模式。java

2、代理模式的定义与实践

定义:Provide a surrogate or placeholder for another object to control access to it.算法

翻译:为其余对象提供一种代理以控制对这个对象的访问。设计模式

代理模式仍是比较好理解,就是委托别人作事情,好比咱们要租房子,委托中介去找房子,这就是代理模式。代理模式分为静态代理模式动态代理模式,咱们来结合代码看看代理模式如何实现的。bash

2.1 静态代理框架

/***
定义消费者接口,查找指定区域,指订价格如下的房子*/
public interface ICustomer {
    /**查找房子,指定区域,指订价格*/
    String findHouse(String location,int price);
}
/**真正的找房消费者,因此只关注找到这个目标**/
public class YungCustomer implements ICustomer {
    @Override
    public String findHouse(String location,int price) {
        System.out.println("找到了位于["+location+"],价格"+price+"如下的房子");
        return "找到了位于["+location+"],价格"+price+"如下的房子";
    }
}

/**找房子中介类,找到合适房子再反馈给指消费者**/
public class AgencyProxy implements ICustomer{
    private ICustomer coustomer;

    public AgencyProxy(ICustomer coustomer) {
        this.coustomer = coustomer;
    }


    @Override
    public String findHouse(String location,int price) {
        String result="没有符合条件的房子";
        before();
        if (null != coustomer){
            result= coustomer.findHouse(location,price);
        }
        after();
        return result;
    }

    private void before(){
        System.out.println("开始找房子");
    }
    private void after(){
        System.out.println("结束找房子");
    }
}
/***场景类*/
public class Client {
    public static void main(String[] args) {
        ICustomer customer = new YungCustomer();
        ICustomer proxy = new AgencyProxy(customer);
        String result = proxy.findHouse("钓鱼岛附近", 2000);
        System.out.println(result);
    }
}

复制代码

输出结果:maven

开始找房子

找到了位于[钓鱼岛附近],价格2000如下的房子

结束找房子
复制代码

静态代理模式比较简单,总结就是代理类中包含实际调用者的引用,当符合条件时候,在去调用真正的对象方法。分布式

2.2 JDK动态代理ide

动态代理比较复杂一点,相对于静态代理的指定代理对象,动态代理是在运行时候才知道实际代理对象。动态代理应用比较普遍,咱们用的最多的框架Spring中 AOP就采用了动态代理。咱们来把上面的代码改形成动态代理。post

/**动态代理handler**/
public class MyInvocationHandler implements InvocationHandler {
    private Object object;

    public MyInvocationHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result= method.invoke(object,args);
        after();
        return result;
    }
    private void before(){
        System.out.println("开始找房子");
    }
    private void after(){
        System.out.println("结束找房子");
    }
}

public class Client {
    public static void main(String[] args) {
        // 获取动态代理对象。
        ICustomer customer = (ICustomer) Proxy.newProxyInstance(Client.class.getClassLoader()
                , new Class[]{ICustomer.class}
                , new MyInvocationHandler(new YungCustomer()));
        customer.findHouse("钓鱼岛附近", 2000);
    }
}
复制代码

输出结果:性能

开始找房子

找到了位于[钓鱼岛附近],价格2000如下的房子

结束找房子

复制代码

动态代理要求代理的类必需要有接口,同时实现InvocationHandler的接口,实现接口的invoke方法便可,在invoke方法内能够在真正执行方法的先后添加想作的事情,好比说记录日志,通知消息等等,这就是AOP的思想,在不改变原有业务代码的状况下,添加功能。可是jdk动态代理的缺点就是代理的类必需要有接口才行,为了弥补这个缺点,出现了一款不须要接口就能被代理的第三方库,CGLIB库。

2.3.CGLIB动态代理

2.3.1 CGLIB介绍

CGLIB是一个强大的、高性能的代码生成库。它被普遍使用在基于代理的AOP框架(例如Spring AOP和dynaop)提供方法拦截。在实现内部,CGLIB库使用了ASM这一个轻量但高性能的字节码操做框架来转化字节码,产生新类。

2.3.2 CGLIB的使用

既然是第三方库,咱们须要添加maven依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>3.2.9</version>
		</dependency>

复制代码

CGLIB也和jdk动态代理同样,须要设置回调方法,在JDK动态代理中咱们要实现 InvocationHandler,而在CGLIB中咱们须要实现net.sf.cglib.proxy.MethodInterceptor接口做为回调接口。咱们先看代码

/**实现回调接口**/
public class MyMthondInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(o,objects);
        after();
        return result;
    }

    private void before(){
        System.out.println("开始找房子");
    }
    private void after(){
        System.out.println("结束找房子");
    }
}

public class Client {
    public static void main(String[] args) {
        //建立回调实例
        MyMthondInterceptor interceptor=new MyMthondInterceptor();
        //CGLIB建立实例
        Enhancer enhancer = new Enhancer();
        //设置须要代理的类
        enhancer.setSuperclass(YungCustomer.class);
        //设置回调类
        enhancer.setCallback(interceptor);
        //获取代理对象
        YungCustomer customer= (YungCustomer) enhancer.create();
        //执行方法
        customer.findHouse("钓鱼岛",1000);
    }
}
复制代码

输出结果:

开始找房子
找到了位于[钓鱼岛],价格1000如下的房子
结束找房子

复制代码

这里CGLIB实现结果和JDK动态代理彻底同样。上面实现回调方法的时候须要注意,推荐使用methodProxy.invokeSuper(o,objects);,这里调用的是CJLIB库的方法,若是使用method.invoke(o,args);须要注意的是,这里使用的就是JDK的动态代理了,同时invoke的object必须是传入的代理实例,而不是方法中形参object,不然会致使死循环调用。同时考虑到性能,仍是建议使用第一种调用方式。

CGLIB库还提供了不少其余的特性,好比回调方法过滤等等。有兴趣的同窗能够自行研究。

3、代理模式的优势与缺点

优势

1.职责清晰

真实的角色实现实际的业务逻辑,不用关心其余非核心的业务逻辑。业务是业务,辅助功能是辅助功能,职责很是清晰。好比要实现日志功能,不用耦合在实际的业务代码中,只要作一个代理便可。

2.良好的扩展性

因为核心的业务逻辑已经封装好了,后面要加强业务功能,也可使用代理模式代理增长功能便可。

缺点

1.类的臃肿

对于静态代理来讲,若是过多的使用静态代理会带来类臃肿。通常会在接口协议转换中使用比较多代理模式。 2.复杂性 对于动态代理来讲,设计到回调方法的实现,特别是CGLIB中的使用,仍是带来了必定的复杂性。

3.性能

对于动态代理来讲,都是运行期进行字节码操做,因此仍是带来了一些性能损耗,可是这不能做为不使用动态代理的理由,任何东西都是有两面性的。

4、代理模式的使用场景

1.方法加强;好比增长日志,事务等功能。

2.远程RPC调用;如今不少分布式系统RPC调用都是采用了代理模式。

3.协议等的转换;好比须要适用另一套协议接口,会使用代理模式,先转换为老协议,而后再调用实际的类去执行。

4.懒加载;有些框架会在开始的时候使用代理类来替换实际类,等到真正要使用该类的时候才进行加载,从而达到了懒加载的效果。

5、总结

本篇博客介绍了代理模式,代理模式分为静态代理和动态代理,动态代理又分为jdk动态代理和CGLIB动态代理。JDK动态代理必需要有接口才能使用,CGLIB弥补了这个缺陷,能够直接对类进行代理。同时CGLIB动态代理性能相对于JDK动态代理要优秀。

6、参考

《设计模式之禅》

7、推荐阅读

JAVA设计模式之模板方法模式和建造者模式

Java设计模式之工厂方法模式与抽象工厂模式

Java设计模式之单例模式

JAVA设计模式之开篇

带你走进java集合之ArrayList

带你走进java集合之HashMap

Java锁之ReentrantLock(一)

Java锁之ReentrantLock(二)

相关文章
相关标签/搜索