• Java中的代理模式


    一、什么是代理模式?

           这里不做过多的理论解释,相关资料一大堆,只简单说下我理解的代理模式,代理模式的意思就是你想完成的工作不用自己完成,交给代理去帮你去完成。代理模式的案例生活中很常见,比如:毕业了在城市里工作,需要租房,大部分人都会接触到中介,通过中介租房,这里的中介在代理模式中就充当了代理的角色,中介代理真实房东带你去看房,有些权利大的中介,甚至会代理真实房东和你签合同。

    1.代理模式有什么好处?

    1).中介隔离

           还以上面租房为例:我是一位真实房东,家里有很多房产,打工是不可能打工的,平时就靠收收房租维持生活这样子。但是最近正好赶上毕业季,天天有租客联系我租房、看房,楼上楼下的跑是真累啊,而且还租不出去几套房子,这样的生活不能再持续下去了。于是我拨通了呗柯公寓的电话,希望呗柯公寓能够代理我的房子,帮我租出去,这样每天我就可以边收房租边好好呆在王者峡谷,不用每天去跟租客接触了。

    2).在遵循开闭原则的情况下,增加功能

           和呗柯公寓谈好价格后,我就等着房子租出去后,收租就行了,某一间房1500元每月,这是我跟呗柯公寓说好的价格。于是呗柯公寓的中介开始工作了,为了让租客对房子有一个很好的第一印象,负责的中介认真地打扫了整个房间,毕竟自己付出了劳动,也得给自己点回报,于是中介将房租价格加价200,打算以1700的价格出租出去,很快哈,房子就被租出去了,在我和租客签完合同后,中介又热心地帮助租客提运行李

           在上面租房的整个过程中,如果是我本人跟租客进行交涉,那么在签合同前,我不会打扫房间,也不会加价,也不会在合同签好之后,帮助租客提运行李,这些都是中介的附加行为,而我跟中介的唯一要求就是房子租出去签好合同,并且价格1500就行,中介的这些附加行为就相当于在不影响我的要求下(遵循开闭原则),增加的功能。

    二、静态代理

           静态代理是代理模式的一种,之所以会被称为静态代理,是因为还有个动态代理与之相对,下面直接以代码为例描述静态代理。

    两个单词
    Landlord:房东
    Agency:中介
    
    /**
     * 房东接口,不管每个月要1500还是每个月3000、5000的房东,都要签合同
     */
    public interface LandlordInterface {
    
        void sign();
    }
    
    /**
     * 每月要1500元的一个真实房东
     */
    public class Landlord implements LandlordInterface{
    
        @Override
        public void sign() {
            System.out.println("和租客签合同,1500元每月...");
        }
    }
    
    /**
     * 中介
     */
    public class Agency implements LandlordInterface{
    
        private Landlord landlord;
    
        public Agency(Landlord landlord) {
            this.landlord = landlord;
        }
    
        @Override
        public void sign() {
          	// 在和租客签合同前,中介的"附加行为"
            System.out.println("打扫房间...");
            System.out.println("将房间价格加价200...");
          	// 房东和租客签合同
            landlord.sign();
          	// 在和租客签合同后,中介的"附加行为"
            System.out.println("帮租客提运行李...");
        }
    }
    
    /**
     * 模拟租房过程的一个测试类
     */
    public class Test {
    
        public static void main(String[] args) {
            Landlord landlord = new Landlord();
            // 中介要完成房东的要求,要完成哪个房东要求?所以需要在构造方法中传入这个租金为1500元的房东
            Agency agency = new Agency(landlord);
            // 签合同
            agency.sign();
        }
    }
    

    运行测试类的结果:

    打扫房间...
    将房间价格加价200...
    真实房东和租客签合同,1500元每月...
    帮租客提运行李...
    

           以上代码描述的就是一个静态代理的过程,简单并且很容易理解。但是,如果这位房东不想自己收租了,于是把收租的权利也交给了中介,那么中介就要给自己添加一条收租行为;如果房东不想收水电费了,中介还要给自己添加一条收水电费的行为;如果房东...... 只要房东一有要求,那么中介类就要修改自己的行为(修改代码)。很显然,这种静态代理模式不是我们经常会用到的。

    三、动态代理

           动态代理分为JDK动态代理和Cglib动态代理,与静态代理相比较,从代码的角度看,代理类不再需要我们手动去创建了,而是根据被代理对象的需求动态生成一个代理对象。

    1.JDK动态代理

           JDK动态代理是Java提供的一种动态代理Api,要使用JDK动态代理,被代理对象需要实现一个接口,下面用代码演示一下动态代理的过程。

    /**
     * 房东接口
     */
    public interface LandlordInterface {
    
        void sign();
    }
    
    /**
     * 每月要1500元的一个真实房东
     */
    public class Landlord implements LandlordInterface{
    
        @Override
        public void sign() {
            System.out.println("和租客签合同,1500元每月...");
        }
    }
    
    /**
     * 使用JDK动态代理,需要编写一个类实现InvocationHandler接口
     */
    public class MyInvocationHandler implements InvocationHandler {
    
        /**
         * 被代理的对象(比如房东)
         */
        private Object target;
    
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          	// 中介每次在执行房东的任务前,都会加收租客费用
            System.out.println("加价收取租客费用...");
          	// 执行房东的任务(即执行房东对象对应的方法)
            Object result = method.invoke(target, args);
            return result;
        }
    }
    
    /**
     * 模拟租房过程的一个测试类
     */
    public class Test {
        public static void main(String[] args) {
    
            Landlord landlord = new Landlord();
            MyInvocationHandler invocationHandler = new MyInvocationHandler(landlord);
          	// 动态生成一个代理对象
            LandlordInterface proxy = (LandlordInterface) Proxy.newProxyInstance(
                landlord.getClass().getClassLoader(), 
                landlord.getClass().getInterfaces(), 
                invocationHandler);
          	// 执行签合同方法
            proxy.sign();
        }
    }
    
    

           如果房东只有一个签合同行为,动态代理和静态代理相比,虽然不用去写代理类了,但是需要编写一个InvocationHandler的实现类,这里并没有体现动态代理的动态性、便利性,但是,如果房东还将收水费、收电费、收房租也代理给中介,那么就能看到动态代理的便利性了。

    /**
     * 房东接口
     */
    public interface LandlordInterface {
    
        void sign();
        
        void waterCharge();
    }
    
    /**
     * 每月要1500元的一个真实房东
     */
    public class Landlord implements LandlordInterface{
    
        @Override
        public void sign() {
            System.out.println("和租客签合同,1500元每月...");
        }
      
      	@Override
        public void waterCharge() {
            System.out.println("收水费,每月10元...");
        }
    }
    

           这里,又给房东添加了一个收水费的行为,此时中介想要代理收水费行为并从中牟利,不需要修改任何代码,直接在测试类中使用即可。

    /**
     * 模拟租房过程的一个测试类
     */
    public class Test {
        public static void main(String[] args) {
    
            Landlord landlord = new Landlord();
            MyInvocationHandler invocationHandler = new MyInvocationHandler(landlord);
          	// 动态生成一个代理对象
            LandlordInterface proxy = (LandlordInterface) Proxy.newProxyInstance(
                landlord.getClass().getClassLoader(), 
                landlord.getClass().getInterfaces(), 
                invocationHandler);
          	// 执行签合同方法
            proxy.sign();
          	// 执行收水费方法
            proxy.waterCharge();
        }
    }
    

           如果使用静态代理的方式,还需要在中介类中添加对应的waterCharge()方法。

    2.Cglib动态代理

           通过上面JDK动态代理的实例可以看到,JDK动态代理依赖于被代理类对象(房东)需要实现一个接口(房东接口),如果被代理对象没有实现接口,那么就无法使用JDK动态代理了,这也是JDK动态代理和Cglib动态代理的区别之处。Cglib(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以动态地扩展Java类。Cglib动态代理示例:

    <!-- 使用cglib动态代理需要引入cglib依赖或者加入cglib相关jar包 -->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2</version>
    </dependency>
    
    /**
     * 每月要1500元的一个真实房东 需要注意:本类没有实现接口
     */
    public class Landlord {
    
        public void sign() {
            System.out.println("和租客签合同,1500元每月...");
        }
    }
    
    /**
     * 使用Cglib动态代理,需要编写一个类实现MethodInterceptor接口
     */
    public class MyMethodInterceptor implements MethodInterceptor {
    
        private Enhancer enhancer = new Enhancer();
    
      	/**
      	 * clazz是被代理对象的类类型
      	 */
        public Object getProxy(Class<?> clazz){
            enhancer.setSuperclass(clazz);
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable 		{
          	// 中介每次在执行房东的任务前,都会加收租客费用
            System.out.println("加价收取租客费用...");
          	// 执行房东的任务(即执行房东对象对应的方法)
            Object result = methodProxy.invokeSuper(obj, args);
            return result;
        }
    }
    
    /**
     * 模拟租房过程的一个测试类
     */
    public class Test {
        public static void main(String[] args) {
          	MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor();
          	// 动态生成一个代理对象
            Landlord proxy = (Landlord) myMethodInterceptor.getProxy(Landlord.class);
          	// 执行签合同方法
            proxy.sign();
        }
    }
    

    题外话

           本文拿中介租房作为一个代理模式的例子举例,对于中介加价的行为可能冒犯了中介从事者,我在此表示歉意。中介和我一样都是打工人,也很不容易,我第一次租房就是通过中介,接触到不少中介,并没有觉得租房过程有什么不舒服的地方,反倒他们的热情会让我愧疚我的租金给少了。。。还望看到这里的同学不要对中介有敌意。

  • 相关阅读:
    codevs 1432 总数统计
    codevs3500 快速幂入门题解
    #163. 【清华集训2015】新式计算机
    2989:糖果
    191:钉子和小球
    java 删除所有HTML工具类
    DateTools时间插件
    新的开始
    springBoot---端口,路径数据配置
    springBoot---多个配置文件读取
  • 原文地址:https://www.cnblogs.com/BoildWater/p/14103487.html
Copyright © 2020-2023  润新知