首页 > 编程语言 >Java中的动态代理

Java中的动态代理

时间:2024-08-24 17:50:44浏览次数:11  
标签:Java name 对象 代理 接口 动态 方法 public

今天来复习一下动态代理(无侵入的增强或改变某些方法),在学springAop的时候有点蒙,因为底层是用动态代理来实现的。

1.动态代理介绍

假设现在有一个大明星叫杨超越,它有唱歌和跳舞的本领,作为大明星是要用唱歌和跳舞来赚钱的,但是每次做节目,唱歌的时候要准备话筒、收钱,再唱歌;跳舞的时候也要准备场地、收钱、再唱歌。杨超越越觉得我擅长的做的事情是唱歌,和跳舞,但是每次唱歌和跳舞之前或者之后都要做一些繁琐的事情,有点烦。于是杨超越就找个一个经济公司,请了一个代理人,代理杨超越处理这些事情,如果有人想请杨超越演出,直接找代理人就可以了。如下图所示:

杨超越的代理是中介公司派的,那中介公司怎么知道,要派一个有唱歌和跳舞功能的代理呢?解决这个问题,Java使用的是接口,杨超越想找代理,在Java中需要杨超越实现了一个接口,接口中规定要唱歌和跳舞的方法。Java就可以通过这个接口为杨超越生成一个代理对象,只要接口中有的方法代理对象也会有。

 动态代理三要素:

1,真正干活的对象

2,代理对象

3,利用代理调用方法

切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。

1.2 生成代理对象

Java为开发者提供的一个生成代理对象的类叫Proxy类通过Proxy类的newInstance(...)方法可以为实现了同一接口的类生成代理对象。 调用方法时需要传递三个参数,该方法的参数解释可以查阅API文档,如下:

/*
*
* 类的作用:
*       创建一个代理
*
* */
public class ProxyUtil {
    /*
    *
    * 方法的作用:
    *       给一个明星的对象,创建一个代理
    *
    *  形参:
    *       被代理的明星对象
    *
    *  返回值:
    *       给明星创建的代理
    * 需求:
    *   外面的人想要大明星唱一首歌
    *   1. 获取代理的对象
    *      代理对象 = ProxyUtil.createProxy(大明星的对象);
    *   2. 再调用代理的唱歌方法
    *      代理对象.唱歌的方法("只因你太美");
    * */
    public static Star createProxy(BigStar bigStar){
       /* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:

        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        参数一:用于指定用哪个类加载器,去加载生成的代理类
        参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
        参数三:用来指定生成的代理对象要干什么事情*/

        //解释:Proxy.newProxyInstance(...)创建一个代理对象,该对象的类同样也实现了Star接口,所以下面写法属于多态写法。
        Star star = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
                new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
                //参数三:用来指定生成的代理对象要干什么事情
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                        * 参数一:代理的对象
                        * 参数二:要运行的方法 sing
                        * 参数三:调用sing方法时,传递的实参
                        * */
                        if("sing".equals(method.getName())){
                            System.out.println("准备话筒,收钱");
                        }else if("dance".equals(method.getName())){
                            System.out.println("准备场地,收钱");
                        }
                        //去找大明星开始唱歌或者跳舞
                        //代码的表现形式:调用大明星里面唱歌或者跳舞的方法
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}
public interface Star {
    //我们可以把所有想要被代理的方法定义在接口当中
    //唱歌
    public abstract String sing(String name);
    //跳舞
    public abstract void dance();
}
public class BigStar implements Star {
    private String name;


    public BigStar() {
    }

    public BigStar(String name) {
        this.name = name;
    }

    //唱歌
    @Override
    public String sing(String name){
        System.out.println(this.name + "正在唱" + name);
        return "谢谢";
    }

    //跳舞
    @Override
    public void dance(){
        System.out.println(this.name + "正在跳舞");
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "BigStar{name = " + name + "}";
    }
}

测试代码:

public class Test {
    public static void main(String[] args) {
    /*
        需求:
            外面的人想要大明星唱一首歌
             1. 获取代理的对象
                代理对象 = ProxyUtil.createProxy(大明星的对象);
             2. 再调用代理的唱歌方法
                代理对象.唱歌的方法("只因你太美");
     */
        //1. 获取代理的对象
        BigStar bigStar = new BigStar("鸡哥");
        Star proxy = ProxyUtil.createProxy(bigStar);

        //2. 调用唱歌的方法
        String result = proxy.sing("只因你太美");
        System.out.println(result);
    }
}

执行流程:

最后来进行一个练习来加深印象

需求:对add方法进行增强,对remove方法进行拦截,对其他方法不拦截也不增强。

代码:

public class MyProxyDemo1 {
    public static void main(String[] args) {
        //1.创建真正干活的人
        ArrayList<String>list = new ArrayList<>();

        //2.创建代理对象
        //参数一:类加载器。当前类名.class.getClassLoader()
        //                 找到是谁,把当前的类,加载到内存中了,我再麻烦他帮我干一件事情,把后面的代理类,也加载到内存

        //参数二:是一个数组,在数组里面写接口的字节码文件对象。
        //                  如果写了List,那么表示代理,可以代理List接口里面所有的方法,对这些方法可以增强或者拦截
        //                  但是,一定要写ArrayList真实实现的接口
        //                  假设在第二个参数中,写了MyInter接口,那么是错误的。
        //                  因为ArrayList并没有实现这个接口,那么就无法对这个接口里面的方法,进行增强或拦截
        //参数三:用来创建代理对象的匿名内部类

     List proxyList =(List) Proxy.newProxyInstance(
               //参数一:类加载器
                MyProxyDemo1.class.getClassLoader(),
               //参数二:是一个数组,表示代理对象能代理的方法范围
                new Class[]{List.class},
               //参数三:本质就是代理对象
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //对add方法做一个增强,统计耗时
                        if (method.getName().equals("add")){
                            long start = System.currentTimeMillis();
                            //调用集合的方法,真正的添加数据
                            method.invoke(list,args);
                            long end = System.currentTimeMillis();
                            System.out.println("耗费时间:" + (end - start));
                            return true;
                        }else if (method.getName().equals("remove") && args[0] instanceof Integer){
                            System.out.println("拦截了按照索引删除的方法");
                            return null;
                        }else if (method.getName().equals("remove")){
                            System.out.println("拦截了按照对象删除的方法");
                            return false;
                        }else {
                            //如果当前调用的是其他方法,我们既不增强也不拦截
                            method.invoke(list,args);
                            return null;
                        }
                    }
                }
        );
      //3.调用方法
        //如果调用者是list,就好比绕过了第二步的代码,直接添加元素
        //如果调用者是代理对象,此时代理才能帮我们增强或者拦截

        //每次调用方法的时候,都不会直接操作集合
        //而是先调用代理里面的invoke,在invoke方法中进行判断,可以增强或者拦截
        proxyList.add("aaa");
        proxyList.add("bbb");
        proxyList.add("ccc");
        proxyList.add("ddd");

        proxyList.remove(0);
        proxyList.remove("aaa");

        System.out.println(list);
    }
}

运行结果:

可以看到对add确实进行了增强,对remove方法进行了拦截。

标签:Java,name,对象,代理,接口,动态,方法,public
From: https://blog.csdn.net/m0_50345460/article/details/141501142

相关文章

  • java+vue计算机毕设汽车租赁系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着城市化进程的加速和人们出行需求的多样化,汽车租赁行业近年来迎来了前所未有的发展机遇。传统租车模式已难以满足现代人对便捷性、灵活性及个性化......
  • java+vue计算机毕设山西工程技术学院任务流程管理系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在当今信息化快速发展的时代,高校及各类组织机构的日常运营与管理日益依赖于高效的任务流程管理系统。山西工程技术学院作为一所培养工程技术人才的高......
  • java+vue计算机毕设流浪猫救助平台【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着城市化进程的加速,流浪猫问题日益凸显,成为城市管理和社会关注的焦点。流浪猫不仅面临着食物短缺、生存环境恶劣等生存挑战,还可能对公共卫生安全构......
  • Java毕业设计-基于springboot+vue的在线电商个性化推荐平台,基于Springboot的商城商品
    博主介绍:✌️码农一枚,专注于大学生项目实战开发、代码讲解和毕业......
  • java+vue计算机毕设软件工程类课程实验项目管理系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着教育信息化的不断深入,高校软件工程类课程的教学管理面临着日益复杂的需求与挑战。传统的手工管理模式已难以满足现代教学对效率、准确性和实时性......
  • java+vue计算机毕设社区服务管理系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着城市化进程的加速和居民生活水平的提高,社区作为城市的基本单元,其管理与服务的重要性日益凸显。传统的社区管理模式已难以满足居民日益增长的多样......
  • java+vue计算机毕设老人养老社区服务平台【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着社会的快速发展与人口老龄化的加剧,老年人口数量持续增长,对高质量养老服务的需求日益迫切。传统家庭养老模式面临着资源有限、照顾能力不足等挑战,......
  • java+vue计算机毕设排球比赛管理系统的设计与实现【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着体育事业的蓬勃发展,排球比赛作为广受欢迎的运动项目之一,其组织管理的复杂性和效率要求日益提升。传统的排球比赛管理方式往往依赖于人工记录、整......
  • [Javascript + Performance] How to run a large number of time-consuming tasks and
    Tryoption1:Promise PromiserunninginMicrotaskqueue,andrenderingshouldwaituntilthequeueisempty;Ifyouhavealargenumberoftime-consuminginmicrotask,itwillalsoblockrenderingfunctionrunTask(task){Promise.resolve().then(()=&g......
  • JAVA表达式(包括IDEA的介绍)
    JavaIDEA介绍:IDE:集成开发环境(IDE,IntegratedDevelopmentEnvironment)是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。Eclipse,IntelliJIDEA..........