草庐IT

静态代理和动态代理

何冽冽同学 2023-06-06 原文

java中的代理是什么:

代理就是通过代理对象去访问实际的目标对象,比如买火车票,我们可以去火车站买票,也可以从代售点买票,通过代售点的方式就是代理。在java中,代售点就是就是代理类,不仅可以实现目标对象,还可以增加一些额外的功能。据我所知java中的代理方式有两种,一种是静态代理,一种是动态代理。

什么是静态代理:

静态代理就是代码运行之前,这个代理类就已经存在了,还是以买火车票为例子,在代码中先创建一个通用的买票接口

/**
 * 卖票接口
 *
 * @Dte:2022/9/3
 */
public interface SellTickets {
    /**
     * 卖票方法
     */
    void sell();
}

然后需要一个真实的类

/**
 * 火车站 具有卖票功能,所以需要实现SellTickets接口
 *
 * @Dte:2022/9/3
 */
public class TrainStation implements SellTickets {

    @Override
    public void sell() {
        System.out.println("火车站卖票......");
    }
}

最后需要一个代理类

/**
 * 代售点
 *
 * @Dte:2022/9/3
 */
public class ProxyPoint implements SellTickets {

    private TrainStation station = new TrainStation();

    @Override
    public void sell() {
        System.out.println("代售点收取一些费用......");
        station.sell();
    }
}

代理类可以在不更改被代理对象的情况下去增加功能

/**
 * @Dte:2022/9/3
 */
public class Client {
    public static void main(String[] args) {
        //创建代售点对象
        ProxyPoint proxyPoint = new ProxyPoint();   
        //调用方法进行买票
        proxyPoint.sell();
    }
}

/**
代售点收取一些费用......
火车站卖票......
**/

静态代理的缺点

因为静态代理在代码运行之前就已经存在代理类了,所以每一个代理对象都需要去建一个代理类去代理,当需要代理的对象有很多时,就需要创建很多的代理类,降低了程序可维护性。所以有了动态代理。

什么是动态代理

动态代理指代理类不写在代码中,而是在运行过程中产生,java提供了两种动态代理,分别是jdk的动态代理和基于Cglib的动态代理。

JDK动态代理

要想用jdk动态代理,可以先写一个ProxyHandler类,这个类实现了InvocationHandler接口,并重写里面的invoke方法,invoke方法里有被代理对象的需要被增强的方法,到这里只是定义了被代理类的通用增强方法,要想真正使用的话,需要通过Proxy.newProxyInstance,它的目的是在运行期间生成代理类,它的参数是类加载器,目标类实现的接口,InvocationHandler对象,要注意的是Proxy.newProxyInstance的返回值需要用被代理类所实现的接口来接,接住的就是代理对象,如果代理对象中的方法被调用,就会调用InvocationHandler实现类中的invoke方法,这里面有需要增强的内容。

通用接口

/**
 * 卖票接口
 *
 */
public interface SellTickets {
    /**
     * 卖票方法
     */
    void sell();
}

具体类

/**
 * 火车站 具有卖票功能,所以需要实现SellTickets接口
 *
 */
public class TrainStation implements SellTickets {

    @Override
    public String sell() {
        System.out.println("火车站卖票......");
        return "我正在被打印";
    }
}

ProxyHandler类

public class ProxyHandler implements InvocationHandler {
    Object object;

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

    /**
     * @param proxy  代理对象
     * @param method 要实现的方法
     * @param args   方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("注册账号......");
        Object invoke = method.invoke(object, args);
        return invoke;
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        TrainStation trainStation = new TrainStation();
        InvocationHandler proxyHandler = new ProxyHandler(trainStation);
        SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(trainStation.getClass().getClassLoader()
                , trainStation.getClass().getInterfaces()
                , proxyHandler);
        String sell = sellTickets.sell();
        System.out.println(sell);
    }
}

/**
注册账号......
火车站卖票......
**/

动态代理的优势

静态代理的话需要对每一个被代理对象都创建一个代理对象,因为静态代理是在项目运行前就写好的。但是动态代理不是,动态代理是在运行期间才创建代理类,所以只需要创建一个动态代理类就可以。比如我在写一个被代理的对象招聘员工。

首先先写一个通用接口

/**
 * 招聘接口
 *
 */
public interface Recruit {
    /**
     * 招聘方法
     */
    void find();
}

再写一个被代理对象的类

public class TrainStationCenter implements Recruit {

    @Override
    public void find() {
        System.out.println("火车站总部在招人......");
    }
}

不写代理类源代码,直接用动态代理生成

public class ProxyTest {
    public static void main(String[] args) {
        TrainStationCenter trainStationCenter = new TrainStationCenter();
        InvocationHandler proxyHandler1 = new ProxyHandler(trainStationCenter);
        Recruit proxyInstance = (Recruit) Proxy.newProxyInstance(trainStationCenter.getClass().getClassLoader()
                , trainStationCenter.getClass().getInterfaces()
                , proxyHandler1);
        proxyInstance.find();
    }
}

/**
注册账号......
火车站总部在招人......
**/

通过动态代理,就可以使用一个动态代理类,去代理多个对象。

动态代理只能代理接口,要想代理类,可以使用cglib的动态代理类

首先写一个真实的类,比如火车站类,里面有卖票的方法

public class TrainStation {
    /**
     * 卖票方法
     */
    public void sell() {
        System.out.println("火车站卖票......");
    }
}

然后写一个ProxyHandler,它实现了MethodInterceptor接口,重写了intercept方法,类似于jdk中的invoke方法

public class ProxyHandler implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("注册......");
        Object o1 = methodProxy.invokeSuper(o, objects);
        return o1;
    }
}

不写代理类源代码,直接用cglib动态生成字节码

public class ProxyTest {
    public static void main(String[] args) {
        //创建Enhancer对象,类似于JDK动态代理中的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(TrainStation.class);
        //设置回调函数
        enhancer.setCallback(new ProxyHandler());
        //创建代理对象
        TrainStation trainStation = (TrainStation) enhancer.create();
        trainStation.sell();
    }
}
/**
注册账号......
火车站卖票......
**/

哪些地方用到了动态代理

spring中的AOP思想就是使用了动态代理,AOP通过动态代理对目标方法进行了增强,比如拦截器。

有关静态代理和动态代理的更多相关文章

  1. ruby - 在 Ruby 中动态创建数组 - 2

    有没有办法在Ruby中动态创建数组?例如,假设我想遍历用户输入的书籍数组:books=gets.chomp用户输入:"TheGreatGatsby,CrimeandPunishment,Dracula,Fahrenheit451,PrideandPrejudice,SenseandSensibility,Slaughterhouse-Five,TheAdventuresofHuckleberryFinn"我把它变成一个数组:books_array=books.split(",")现在,对于用户输入的每一本书,我想用Ruby创建一个数组。伪代码来做到这一点:x=0books_array.

  2. ruby - 是否可以将 IRB 提示配置为动态更改? - 2

    我想在IRB中浏览文件系统并让提示更改以反射(reflect)当前工作目录,但我不知道如何在每个命令后进行提示更新。最终,我想在日常工作中更多地使用IRB,让bash溜走。我在我的.irbrc中试过这个:require'fileutils'includeFileUtilsIRB.conf[:PROMPT][:CUSTOM]={:PROMPT_N=>"\e[1m:\e[m",:PROMPT_I=>"\e[1m#{pwd}>\e[m",:PROMPT_S=>"FOO",:PROMPT_C=>"\e[1m#{pwd}>\e[m",:RETURN=>""}IRB.conf[:PROMPT_MO

  3. ruby - HTTP 请求中的用户代理,Ruby - 2

    我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)

  4. ruby-on-rails - capybara poltergeist - 覆盖用户代理 - 2

    有人知道如何将capybarapoltergeist的用户代理覆盖到移动用户代理以进行测试吗?我发现了一些有关为seleniumwebdriver配置它的信息:http://blog.plataformatec.com.br/2011/03/configuring-user-agents-with-capybara-selenium-webdriver/这在capybara闹鬼中怎么可能? 最佳答案 请参阅poltergeistgithub页面上的链接:https://github.com/teampoltergeist/polte

  5. ruby - 如何配置 Ruby Mechanize 代理以通过 Charles Web 代理工作? - 2

    我正在使用Ruby/Mechanize编写一个“自动填写表格”应用程序。它几乎可以工作。我可以使用精彩CharlesWeb代理以查看服务器和我的Firefox浏览器之间的交换。现在我想使用Charles查看服务器和我的应用程序之间的交换。Charles在端口8888上代理。假设服务器位于https://my.host.com。.一件不起作用的事情是:@agent||=Mechanize.newdo|agent|agent.set_proxy("my.host.com",8888)end这会导致Net::HTTP::Persistent::Error:...lib/net/http/pe

  6. ruby-on-rails - carrierwave:在序列化动态属性上安装 uploader - 2

    首先,我使用的是rails3.1.3和来自master的carrierwavegithub仓库的分支。我使用after_init钩子(Hook)来确定基于属性的字段页面模型实例并为这些字段定义属性访问器将值存储在序列化哈希中(希望它清楚我是什么谈论)。这是我正在做的事情的精简版:classPage省略mount_uploader命令让我可以访问我想要的属性。但是当我安装uploader时出现错误消息说“nil类的未定义新方法”我在源代码中读到有方法read_uploader和扩展模块中的write_uploader。我如何必须覆盖这些来制作mount_uploader命令使用我的“虚拟

  7. ruby - 如何捕获所有 HTTP 流量(本地代理) - 2

    我希望访问我机器上的所有HTTP流量(我的Windows机器-不是服务器)。据我了解,拥有一个本地代理是所有流量路线的必经之路。我一直在谷歌搜索但未能找到任何资源(关于Ruby)来帮助我。非常感谢任何提示或链接。 最佳答案 WEBrick中有一个HTTP代理(Rubystdlib的一部分)和here's一个实现示例。如果你喜欢生活在边缘,还有em-proxy伊利亚·格里戈里克。这postIlya暗示它似乎确实需要一些调整来解决您的问题。 关于ruby-如何捕获所有HTTP流量(本地代理)

  8. ruby - 在 Ruby 中动态生成多维数组 - 2

    我正在尝试动态构建一个多维数组。我想要的基本上是这样的(为简单起见写出来):b=0test=[[]]test[b]这给了我错误:NoMethodError:undefinedmethod`test=[[],[],[]]而且它工作正常,但在我的实际使用中,我不会事先知道需要多少个数组。有一个更好的方法吗?谢谢 最佳答案 不需要像您正在使用的索引变量。只需将每个数组附加到您的test数组:irb>test=[]=>[]irb>test[["a","b","c"]]irb>test[["a","b","c"],["d","e","f"]]

  9. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

  10. ruby - 动态方法链? - 2

    如何在对象上调用方法名称的嵌套哈希?例如,给定以下哈希:hash={:a=>{:b=>{:c=>:d}}}我想创建一个方法,给定上面的散列,执行以下操作:object.send(:a).send(:b).send(:c).send(:d)我的想法是我需要从一个未知的关联中获取一个特定的属性(这个方法不知道,但程序员知道)。我希望能够指定一个方法链来以嵌套哈希的形式检索该属性。例如:hash={:manufacturer=>{:addresses=>{:first=>:postal_code}}}car.execute_method_hash(hash)=>90210

随机推荐