飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

动态代理及java演示

时间:2022-01-18  作者:chen943354  

代理模式的理解

    首先代理二字的含义,程序中代理与字面意思的代理并无区别。比如现实生活中办理车辆审车,我们经常会听说花钱找代理(又称黄牛)办手续,即办手续这个事,不是我们亲自执行,而是通过代理(即黄牛)去车管所办理。再比如联系明星商业出演,那明星一般也不会直接和商家对线,而是通过明星的经纪人协商时间地点出场费等。     换到程序中代理二字也是上述含义:即调用一个对象时,不是直接与此对象示例交互,而是通过对象的代理来完成交互。这就是代理最基本的含义。     那增加这层代理的好处也可类比现实生活进行理解:
  • 直观的,可以避免调用方和被调用的对象产生直接的联系。也就是程序设计中耦合度问题。
  • 其次,通过代理可以实现目标对象本身所不具备的行为功能。比如明星只负责演出,而经纪人则负责安排档期、经费、缴税等工作。
基于上述的现实案例做铺垫,我们梳理并识别代理模式涉及的几个组成部分:
  • 目标对象:车主、明星
  • 代理人:黄牛、经纪人
  • 行为:办审核手续、唱歌跳舞
  • 提出方、调用方:警察、商人

说回到程序中,代理主要有以下两种形式:

代理的形式之一:静态代理

这里采用明星商演的场景来演示:

public interface IStar {
    void SingAsong(String songName);
}
public class Star implements IStar {
    private String _name;
    public Star(String name) {
        域名e = name;
    }
    @Override
    public void SingAsong(String songName) {
        域名tln("i am " + _name + ",I\'m singing....");
    }
}
public class StaticStarProxy implements IStar {
    Star zhangsanStar = new Star("zhangsan");
    @Override
    public void SingAsong(String songName) {
        域名SingDate();
        域名Asong(songName);
        域名ngTax();
    }
    private String PlanSingDate() {
        Calendar calendar = 域名nstance();
        域名ime(new Date());
        域名(域名H, 2);
        Date singDate = 域名ime();
        域名tln("sing date =" + singDate);
        return 域名ring();
    }
    private void PayingTax() {
        域名tln("payint tex....");
    }
}

public class Main {
    public static void main(String[] args) {

        域名tln("starting....");

        //直接调用,直接与明星对线
        IStar zhangsanStar = new Star("zhangsan");
        域名Asong("molihua");
        域名tln();
        域名tln("stoping");
    }
上述示例代码中,Star就是明星本人,StarProxy就是代理人,IStar就是行为,Main就是调用方。 程序中静态一般是代表稳定不变的、相对固定的描述。静态代理也就是说代理是相对不变化的,代理程序(StarProxy)怎么写的一般就不会变化。 很显然,明星不止一个,并且明星的行为也不仅仅是唱歌。采用静态代理就会产生几个问题:
  • 如果采用多个明星对应多个代理类的话,那么就会产生大量的代理类出现。【虽然在现实生活中是合理的,但是程序中一般追求精简】
  • 如果采用多个明星对应一个代理类,那么这个代理类的实现就会很复杂,且代理类变更频繁。
  • 还有一个问题,如果明星增加技能(比如跳舞),那么代理类就需要同步的变更。
于是,程序中提出了动态代理来解决这些问题。

代理的形式之二:动态代理

所谓动态代理,直观理解就是代理是动态生成的,而不需要提前编写好。


public class DnyamicStarProxy implements InvocationHandler {
    IStar _targetStar;

    public DnyamicStarProxy(IStar star) {
        域名getStar = star;
    }

    @Override
    public Object invoke(Object proxyInstance, Method method, Object[] objects) throws Throwable {
        域名SingDate();
        Object invokeResult = 域名ke(_targetStar, objects);
        域名ngTax();
        return invokeResult;
    }

    private String PlanSingDate() {
        Calendar calendar = 域名nstance();
        域名ime(new Date());
        域名(域名H, 2);
        Date singDate = 域名ime();
        域名tln("sign date =" + singDate);
        return 域名ring();
    }

    private void PayingTax() {
        域名tln("the agent is paying tex....");
    }
}

public class Main {
    public static void main(String[] args) {

        域名tln("starting....");

        //直接调用,直接与明星对线
        IStar zhangsanStar = new Star("zhangsan");
        域名Asong("molihua");
        域名tln();

        //代理模式,通过经纪人对线
        IStar zhangsanProxy = (IStar) new StaticStarProxy();
        域名Asong("molihua");
        域名tln();

        //代理模式,动态代理调用
        域名tln("邀请李四演出:");
        IStar lisiStar = new Star("lisi");
        IStar lisiStarProxy = (IStar) 域名roxyInstance(域名lassLoader()
                , new Class[]{域名s}
                , new DnyamicStarProxy(lisiStar));
        域名Asong("molihua");
        域名tln();

        域名tln("邀请王五演出:");
        IStar wangwuStar = new Star("wangwu");
        IStar wangwuStarProxy = (IStar) 域名roxyInstance(域名lassLoader()
                , new Class[]{域名s}
                , new DnyamicStarProxy(wangwuStar));
        域名Asong("molihua");
        域名tln();
     
        域名tln("stoping");
    }
输出结果:
starting....
i am zhangsan,I\'m singing....

sing date =Wed Mar 16 18:14:17 CST 2022
i am zhangsan,I\'m singing....
payint tex....

邀请李四演出:
sign date =Wed Mar 16 18:14:17 CST 2022
i am lisi,I\'m singing....
the agent is paying tex....

邀请王五演出:
sign date =Wed Mar 16 18:14:17 CST 2022
i am wangwu,I\'m singing....
the agent is paying tex....

stoping

进程已结束,退出代码为 0
  首先对代码中陌生的类和方法做一个说明: InvocationHandler类的Object invoke(Object o, Method method, Object[] objects)
  • 入参o代表:动态生成的代理类对象
  • 入参method代表;调用方调用的哪个方法,演示代码中即代表SingAsong方法
  • 入参objects代表:调用方法传入的参数,即实参
  Proxy类的static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  • 参数loader:代表java反射中的类加载器
  • 参数interfaces:代表依赖的接口
  • 参数h:代表动态代理类的的对象,即DnyamicStarProxy
  站在调用方角度来说,可以这么理解:调用SingAsong时不是直接new一个Star实例就调用了,而是通过域名roxyInstance动态的创建一个代理类的对象,然后通过此代理类的对象来调用。   通过演示代码,可以看出以下特征:
  • 只有一个DnyamicStarProxy类,而省掉了具体的各个代理人的类;
  • 对商人(调用方)来说,只需要提前知道想要邀请的明星(李四、王五),然后通过特定的代码就生成了明星经纪人对象(lisiStarProxy、wangwuStarProxy)
  • 得到经纪人对象后,就可以通过经纪人对象调用执行具体的行为(唱歌)。
同时,我们梳理分析看看动态代理方式的各个组成部分的依赖情况:
  • 目标对象:Star,没有额外的变化
  • 代理人:省掉了具体的xxx代理人,转为DnyamicStarProxy类,从而依赖InvocationHandler类。
  • 行为:IStar,没有额外的变化
  • 调用方:依赖IStar接口、特定的Star类对象、Proxy类、DnyamicStarProxy类
  现在,我们考虑一个问题:演示代码中是如何调用了Star类的Sing方法? 根据程序执行输出,可以判断是在域名ke中调用的。于是可以看到反射技术的应用,即域名ke()处利用反射真正执行了Star的SingAsong()方法。  

动态代理的一些常见思考题:

Java常用的动态代理技术有哪些?

    本次演示程序虽然用到反射技术来实现动态代理,但也有其它技术来实现比如CGLIB等

演示代码中,调用方还是new了具体的明星类(lisiStar、wangwutar),这就产生了严重的依赖。有什么办法可以解除这个依赖?

域名ke中通过反射执行了目标对象的方法(域名Asong),此时执行的代码是本地的代码。如果这里通过socket将方法名、入参发送到另一台server上执行,然后把结果在再通过网络返回,这就是RPC框架中常用的动态代理了。

虽然是动态代理,但根据前面的铺垫,按说也是会生成代理类的吧,那动态生成的代理长什么样?

    确实是运行期间动态生成了代理类,如下所示:
注意动态代理类的SingAsong方法;
查看代码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import 域名r;
import 域名域名cationHandler;
import 域名域名od;
import 域名域名y;
import 域名域名claredThrowableException;

public final class DynamicGeneratedStarProxyClass extends Proxy implements IStar {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public DynamicGeneratedStarProxyClass(InvocationHandler var1) throws  {
        super(var1);
    }

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

    public final void SingAsong(String var1) throws  {
        try {
            域名ke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

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

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

    static {
        try {
            m1 = 域名ame("域名ct").getMethod("equals", 域名ame("域名ct"));
            m3 = 域名ame("域名r").getMethod("SingAsong", 域名ame("域名ng"));
            m2 = 域名ame("域名ct").getMethod("toString");
            m0 = 域名ame("域名ct").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(域名essage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(域名essage());
        }
    }
}
 

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。