• Java之动态代理的详细解析


    2. 动态代理

    2.1 好处:

    无侵入式的给方法增强功能

    2.2 动态代理三要素:

    1,真正干活的对象

    2,代理对象

    3,利用代理调用方法

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

    2.3 代码实现:

    1. public class Test {
    2.    public static void main(String[] args) {
    3.    /*
    4.        需求:
    5.            外面的人想要大明星唱一首歌
    6.             1. 获取代理的对象
    7.                代理对象 = ProxyUtil.createProxy(大明星的对象);
    8.             2. 再调用代理的唱歌方法
    9.                代理对象.唱歌的方法("只因你太美");
    10.     */
    11.        //1. 获取代理的对象
    12.        BigStar bigStar = new BigStar("鸡哥");
    13.        Star proxy = ProxyUtil.createProxy(bigStar);
    14.        //2. 调用唱歌的方法
    15.        String result = proxy.sing("只因你太美");
    16.        System.out.println(result);
    17.   }
    18. }
    19. /*
    20. *
    21. * 类的作用:
    22. *       创建一个代理
    23. *
    24. * */
    25. public class ProxyUtil {
    26.    /*
    27.    *
    28.    * 方法的作用:
    29.    *       给一个明星的对象,创建一个代理
    30.    *
    31.    * 形参:
    32.    *       被代理的明星对象
    33.    *
    34.    * 返回值:
    35.    *       给明星创建的代理
    36.    *
    37.    *
    38.    *
    39.    * 需求:
    40.    *   外面的人想要大明星唱一首歌
    41.    *   1. 获取代理的对象
    42.    *     代理对象 = ProxyUtil.createProxy(大明星的对象);
    43.    *   2. 再调用代理的唱歌方法
    44.    *     代理对象.唱歌的方法("只因你太美");
    45.    * */
    46.    public static Star createProxy(BigStar bigStar){
    47.       /* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:
    48.        public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
    49.        参数一:用于指定用哪个类加载器,去加载生成的代理类
    50.        参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
    51.        参数三:用来指定生成的代理对象要干什么事情*/
    52.        Star star = (Star) Proxy.newProxyInstance(
    53.                ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
    54.                new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
    55.                //参数三:用来指定生成的代理对象要干什么事情
    56.                new InvocationHandler() {
    57.                    @Override
    58.                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    59.                        /*
    60.                        * 参数一:代理的对象
    61.                        * 参数二:要运行的方法 sing
    62.                        * 参数三:调用sing方法时,传递的实参
    63.                        * */
    64.                        if("sing".equals(method.getName())){
    65.                            System.out.println("准备话筒,收钱");
    66.                       }else if("dance".equals(method.getName())){
    67.                            System.out.println("准备场地,收钱");
    68.                       }
    69.                        //去找大明星开始唱歌或者跳舞
    70.                        //代码的表现形式:调用大明星里面唱歌或者跳舞的方法
    71.                        return method.invoke(bigStar,args);
    72.                   }
    73.               }
    74.       );
    75.        return star;
    76.   }
    77. }
    78. public interface Star {
    79.    //我们可以把所有想要被代理的方法定义在接口当中
    80.    //唱歌
    81.    public abstract String sing(String name);
    82.    //跳舞
    83.    public abstract void dance();
    84. }
    85. public class BigStar implements Star {
    86.    private String name;
    87.    public BigStar() {
    88.   }
    89.    public BigStar(String name) {
    90.        this.name = name;
    91.   }
    92.    //唱歌
    93.    @Override
    94.    public String sing(String name){
    95.        System.out.println(this.name + "正在唱" + name);
    96.        return "谢谢";
    97.   }
    98.    //跳舞
    99.    @Override
    100.    public void dance(){
    101.        System.out.println(this.name + "正在跳舞");
    102.   }
    103.    /**
    104.     * 获取
    105.     * @return name
    106.     */
    107.    public String getName() {
    108.        return name;
    109.   }
    110.    /**
    111.     * 设置
    112.     * @param name
    113.     */
    114.    public void setName(String name) {
    115.        this.name = name;
    116.   }
    117.    public String toString() {
    118.        return "BigStar{name = " + name + "}";
    119.   }
    120. }

    2.4 额外扩展

    动态代理,还可以拦截方法

    比如:

    在这个故事中,经济人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。

    但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。

    1. /*
    2. * 类的作用:
    3. *       创建一个代理
    4. * */
    5. public class ProxyUtil {
    6.    public static Star createProxy(BigStar bigStar){
    7.        public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
    8.        Star star = (Star) Proxy.newProxyInstance(
    9.                ProxyUtil.class.getClassLoader(),
    10.                new Class[]{Star.class},
    11.                new InvocationHandler() {
    12.                    @Override
    13.                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    14.                        if("cleanWC".equals(method.getName())){
    15.                            System.out.println("拦截,不调用大明星的方法");
    16.                            return null;
    17.                       }
    18.                        //如果是其他方法,正常执行
    19.                        return method.invoke(bigStar,args);
    20.                   }
    21.               }
    22.       );
    23.        return star;
    24.   }
    25. }

    2.5 动态代理的练习

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

    1. public class MyProxyDemo1 {
    2.    public static void main(String[] args) {
    3.        //动态代码可以增强也可以拦截
    4.        //1.创建真正干活的人
    5.        ArrayList list = new ArrayList<>();
    6.        //2.创建代理对象
    7.        //参数一:类加载器。当前类名.class.getClassLoader()
    8.        //                 找到是谁,把当前的类,加载到内存中了,我再麻烦他帮我干一件事情,把后面的代理类,也加载到内存
    9.        //参数二:是一个数组,在数组里面写接口的字节码文件对象。
    10.        //                 如果写了List,那么表示代理,可以代理List接口里面所有的方法,对这些方法可以增强或者拦截
    11.        //                 但是,一定要写ArrayList真实实现的接口
    12.        //                 假设在第二个参数中,写了MyInter接口,那么是错误的。
    13.        //                 因为ArrayList并没有实现这个接口,那么就无法对这个接口里面的方法,进行增强或拦截
    14.        //参数三:用来创建代理对象的匿名内部类
    15.        List proxyList = (List) Proxy.newProxyInstance(
    16.                //参数一:类加载器
    17.                MyProxyDemo1.class.getClassLoader(),
    18.                //参数二:是一个数组,表示代理对象能代理的方法范围
    19.                new Class[]{List.class},
    20.                //参数三:本质就是代理对象
    21.                new InvocationHandler() {
    22.                    @Override
    23.                    //invoke方法参数的意义
    24.                    //参数一:表示代理对象,一般不用(了解)
    25.                    //参数二:就是方法名,我们可以对方法名进行判断,是增强还是拦截
    26.                    //参数三:就是下面第三步调用方法时,传递的参数。
    27.                    //举例1:
    28.                    //list.add("阿玮好帅");
    29.                    //此时参数二就是add这个方法名
    30.                    //此时参数三 args[0] 就是 阿玮好帅
    31.                    //举例2:
    32.                    //list.set(1, "aaa");
    33.                    //此时参数二就是set这个方法名
    34.                    //此时参数三 args[0] 就是 1 args[1]"aaa"
    35.                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    36.                        //对add方法做一个增强,统计耗时时间
    37.                        if (method.getName().equals("add")) {
    38.                            long start = System.currentTimeMillis();
    39.                            //调用集合的方法,真正的添加数据
    40.                            method.invoke(list, args);
    41.                            long end = System.currentTimeMillis();
    42.                            System.out.println("耗时时间:" + (end - start));
    43.                            //需要进行返回,返回值要跟真正增强或者拦截的方法保持一致
    44.                            return true;
    45.                       }else if(method.getName().equals("remove") && args[0] instanceof Integer){
    46.                            System.out.println("拦截了按照索引删除的方法");
    47.                            return null;
    48.                       }else if(method.getName().equals("remove")){
    49.                            System.out.println("拦截了按照对象删除的方法");
    50.                            return false;
    51.                       }else{
    52.                            //如果当前调用的是其他方法,我们既不增强,也不拦截
    53.                            method.invoke(list,args);
    54.                            return null;
    55.                       }
    56.                   }
    57.               }
    58.       );
    59.        //3.调用方法
    60.        //如果调用者是list,就好比绕过了第二步的代码,直接添加元素
    61.        //如果调用者是代理对象,此时代理才能帮我们增强或者拦截
    62.        //每次调用方法的时候,都不会直接操作集合
    63.        //而是先调用代理里面的invoke,在invoke方法中进行判断,可以增强或者拦截
    64.        proxyList.add("aaa");
    65.        proxyList.add("bbb");
    66.        proxyList.add("ccc");
    67.        proxyList.add("ddd");
    68.        proxyList.remove(0);
    69.        proxyList.remove("aaa");
    70.        //打印集合
    71.        System.out.println(list);
    72.   }
    73. }

  • 相关阅读:
    ds项目管理
    Linux使用tab补全时报没有空间的错
    MySQL数据库cpu飙升的话,要怎么处理呢?
    【云原生】Docker操作命令大全
    港卡开户感想(2023-8)
    cvf_使用lora方法增强能力
    一零二零、C语言白菜入门--流程控制
    1.数据库的连接、创建会话与模型
    RPC和REST
    C/C++ 有 1 、 2 、 3 、 4 个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
  • 原文地址:https://blog.csdn.net/qq_69748833/article/details/133800981