资讯专栏INFORMATION COLUMN

java设计模式之动态代理

piglei / 2200人阅读

摘要:场景描述病从口入这句成语告诉我们注意饮食健康,小六同学想吃苹果,在吃苹果之前需要清洗一下苹果和洗一下手,吃完苹果后,需要洗一下手保持个人卫生十分钟后。。。动态代理小六委托管家来代理洗食物和洗手,小六属于委托对象,管家属于代理对象。

前言

为了更好的理解代理模式,首先根据生活中实际场景进行模拟,让我们在生活中去体验设计思想的美妙。

场景描述

“病从口入”这句成语告诉我们注意饮食健康,小六同学想吃苹果,在吃苹果之前需要清洗一下苹果和洗一下手,吃完苹果后,需要洗一下手保持个人卫生;十分钟后。。。小六同学又想吃一个大鸭梨,清洗鸭梨--洗手--吃鸭梨--吃完洗手。

代码模拟

苹果和鸭梨都属于食物,创建一个食物接口

public interface Foods {
    void eatApple();
    void eatpear();
}

小六同学吃苹果和鸭梨的动作,相当于实现类

public class People implements Foods {
    @Override
    public void eatApple() {
        System.out.println("eat apple");
    }
    @Override
    public void eatpear() {
        System.out.println("eat pear");
    }
}

小六同学谨记“病从口入”这句成语,所以在吃食物之前需要清洗食物洗手,吃完食物后需要洗手。so easy~~直接在People实现类上加上这两个动作就可以,但是小五同学说我吃苹果和鸭梨之前只洗手不洗食物,为了实现小五这个动作需要重新写接口,重写实现类。那可不可以在不改变实现类的前提下实现呢,答案是肯定的,那就用到静态代理来实现。

 public static void main(String []args){
        People people = new Perople();
        System.out.println("吃前:洗食物洗手");
        people.eatApple();
        System.out.println("吃后:洗手");
        System.out.println("吃前:洗食物洗手");
        people.eatpear();
        System.out.println("吃后:洗手");
    }
小六变胖了

小六同学最近变胖了,原因是越来越能吃了,一天需要吃苹果、鸭梨、香蕉、樱桃、橘子、橙子。。。等一百种水果才能吃饱!虽然饮食控制不住,但小六同学还是每次吃食物之前都洗食物洗手,吃完食物后洗手的好习惯,随之食量的增大,每次都需要洗食物洗手,费力费时间,小六心生一计,不如干脆找个管家,每次想吃东西时只需喊一声,管家帮忙洗食物洗手,自己只负责吃,棒极了。

动态代理的两种实现方式

Java 实现动态代理有两种方式,一种是 Java 自带的 JDK 动态代理,还有一种是使用字节码增强技术实现的 CGLIB 库动态代理。

两种方法同时存在,各有优劣。jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

JDK动态代理

小六委托管家来代理洗食物和洗手,小六属于委托对象,管家属于代理对象。

JDK动态代理主要两个相关类:

Proxy(java.lang.reflect包下的),主要负责管理和创建代理类的工作。

InvocationHandler 接口,只拥有一个invoke方法,主要负责方法调用部分,是动态代理中我们需要实现的方法

每一个代理实例都必须要实现InvocationHandler这个接口,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用,所以要想对方法(吃食物)加强(洗食物洗手)就需要在invoke方法中实现。

public class FoodsHandler implements InvocationHandler{
    private Object object;//委托对象(小六同学)
    public FoodsHandler(Object object){
        this.object = object;
    }
    /*invoke方法的三个参数:
    proxy:  指代我们所代理的那个真实对象
    method:  指代的是我们所要调用真实对象的某个方法的Method对象
    args:  指代的是调用真实对象某个方法时接受的参数
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        System.out.println("吃前:洗食物洗手");
          //当代理对象调用真实对象的方法时,会自动跳转到代理对象关联的handler对象的invoke方法来进行调用
        Object result = method.invoke(object, args);
        System.out.println("吃后:洗手");
        return result;
    }
}
public static void main(String []args){
        //委托对象(小六同学)
        People people = new People();
          //我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
        FoodsHandler inter = new FoodsHandler(people);
        //加上这句将会产生一个$Proxy0.class文件,这个文件即为动态生成的代理类文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        //获取代理类实例foods
        Foods foods = (Foods)(Proxy.newProxyInstance(Foods.class.getClassLoader(), new                 Class[] {Foods.class}, fh));

        //通过代理类对象调用代理类方法,实际上会转到invoke方法调用
        foods.eatApple();
        foods.eatpear();
    }

/*newProxyInstance方法三个参数的解释如下
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException 
loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
*/
Cglib动态代理

上面的 JDK 动态代理需要定义一个接口,实现类实现接口中的方法,如果实现类不能实现接口时,我们就需要 CGLIB 动态代理。

使用 CGLIB 动态代理之前需要导入相关 jar 包,可以多带带导入 cglib-.jar 包和 asm-.jar 包,也可以只导入一个 cglib-nodep-.jar 包(包含了 asm)。下载链接

public class People {
    public void eatApple() {
        System.out.println("eat apple");
    }

    public void eatpear() {
        System.out.println("eat pear");
    }
}
public class PeopleCglib implements MethodInterceptor {
    @Override
    // object 代表要增强的对象,method代表要拦截的方法,objects 代表方法中的参数,methodProxy 代表对方法的代理
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable{
        System.out.println("吃前:洗食物洗手");
        methodProxy.invokeSuper(object,objects);
        System.out.println("吃后:洗手");
        return object;
    }
}
public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer(); // 增强类对象
        enhancer.setSuperclass(People.class); // 设置要增强的类(People)
        PeopleCglib peopleCglib = new PeopleCglib();
        enhancer.setCallback(peopleCglib); // 设置要增强的方法(peopleCglib)
        People people = (People) enhancer.create(); // 生成增强过的子类对象
        people.eatApple(); // 调用方法实际为增强过的方法
        people.eatApple();
    }
}

输出结果

吃前:洗食物洗手
eat apple
吃后:洗手
吃前:洗食物洗手
eat apple
吃后:洗手

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75363.html

相关文章

  • Java设计模式代理模式

    摘要:设计模式之代理模式今天学到的动态代理实现,对代理这个概念很模糊,看了一篇文章发现这是一种设计模式,于是学习记录一下。简介代理模式是一种对象结构型的模式,主要为其他对象提供一种代理以控制对这个对象的访问。下面依次讲解着三种代理。 设计模式之代理模式 今天学到Spring的动态代理实现AOP,对代理这个概念很模糊,看了一篇文章发现这是一种设计模式,于是学习记录一下。 简介 代理模式是一种对...

    ZHAO_ 评论0 收藏0
  • 慕课网_《模式的秘密代理模式》学习总结

    时间:2017年08月28日星期一说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学源码:https://github.com/zccodere/s...学习源码:https://github.com/zccodere/s... 第一章:代理模式 1-1 概念介绍 学习本课程基础 面向对象的设计思维 了解多态的概念 了解反射机制 课程目标 代理模式基本概念及分类...

    wow_worktile 评论0 收藏0
  • Java动态代理InvocationHandler最简单的入门教程

    摘要:网上关于的动态代理,和这些概念有讲解得非常高深的文章。现在咱们通过一个最简单的例子认识什么是。创建一个简单的类,实现这个接口。看看用如何优雅实现吧希望这个例子能让大家对的动态代理之有了最基本的了解。 网上关于Java的动态代理,Proxy和InvocationHandler这些概念有讲解得非常高深的文章。其实这些概念没有那么复杂。现在咱们通过一个最简单的例子认识什么是Invocatio...

    lingdududu 评论0 收藏0
  • Java动态代理InvocationHandler最简单的入门教程

    摘要:网上关于的动态代理,和这些概念有讲解得非常高深的文章。现在咱们通过一个最简单的例子认识什么是。创建一个简单的类,实现这个接口。看看用如何优雅实现吧希望这个例子能让大家对的动态代理之有了最基本的了解。 网上关于Java的动态代理,Proxy和InvocationHandler这些概念有讲解得非常高深的文章。其实这些概念没有那么复杂。现在咱们通过一个最简单的例子认识什么是Invocatio...

    Joonas 评论0 收藏0
  • 给女朋友讲解什么是代理模式

    摘要:受知乎文章和设计模式之禅的启发,我也来搞一篇脑洞小开的文章由标题可知,这篇文章是写给我女朋友看的。于是这就让经纪人对粉丝说只有万,我才会写代码。 前言 只有光头才能变强 回顾前面: ThreadLocal就是这么简单 多线程三分钟就可以入个门了! 多线程基础必要知识点!看了学习多线程事半功倍 Java锁机制了解一下 AQS简简单单过一遍 Lock锁子类了解一下 线程池你真不来了解一下...

    stormgens 评论0 收藏0

发表评论

0条评论

piglei

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<