• Java Apache Commons Collection3.2.1 理解Transformer 接口


    引言

    好久没写JAVA文章了,写一篇基础,序列化和反序列化基础应该都好理解,这次讲的是Java Apache Commons Collection中的Transformer

    Transformer 接口

    org.apache.commons.collections.Transformer 接口开始分析

    public interface Transformer {
        public Object transform(Object input);
    }
    
    • 1
    • 2
    • 3
    • Transforms the input object (leaving it unchanged) into some output object.
    • @param input the object to be transformed, should be left unchanged
    • @return a transformed object

    它说传入的对象会被转换,并返回转换后的对象

    记住了,转换器就是用来转换对象用的,在这里你可以先简单的当它是一个特殊的工厂

    实现此接口的类有下面这些:

    在这里插入图片描述

    比如有一些有趣的Transformer,我们看看它们的transform方法

    InvokerTransformer

     Class cls = input.getClass();
     Method method = cls.getMethod(iMethodName, iParamTypes);
     return method.invoke(input, iArgs);
    
    • 1
    • 2
    • 3

    它将返回invoke后的对象,其中iMethodName成员iParamTypes成员在其构造函数传入

    MapTransformer 和 ConstantTransformer

    //private final Map iMap;
    return iMap.get(input);
    
    • 1
    • 2

    它将返回iMap中input对象为key的值,其中iMap参数在其构造函数传入,有趣的是它的getInstance用到了ConstantTransforme

     public static Transformer getInstance(Map map) {
            if (map == null) {
                return ConstantTransformer.NULL_INSTANCE;
            }
            return new MapTransformer(map);
     } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ConstantTransformertransform方法,其实什么都没有做,它返回的就是一直都是iConstant成员,其中iConstant成员在其构造函数传入。

    public Object transform(Object input) {
           //根本没有用到input
           return iConstant;
    }
    
    • 1
    • 2
    • 3
    • 4

    ChainedTransformer

    ChainedTransformertransform方法,比起之前的稍微有些复杂

     public Object transform(Object object) {
            for (int i = 0; i < iTransformers.length; i++) {
                object = iTransformers[i].transform(object);
            }
            return object;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    iTransformers是一个Transformer数组,也就是说它根据数组中的Transformer(其中可能包含很多不同的Transformer),遍历得到每个Transformer然后依次转换为一个Object,并且将这个Object传入数组的下一个Transformer进行转换

    我们看看它的2个getInstance方法,代码贴在下面,我们可以看见要么传入一个数组,要么传入2个Transformer,它会帮我们拼接为数组,其实它还有一个public static Transformer getInstance(Collection transformers),我没有将它贴在下面,因为他本质上和传入数组干的事情没什么区别。

    private final Transformer[] iTransformers;
    
    public static Transformer getInstance(Transformer[] transformers) {
            FunctorUtils.validate(transformers);
            if (transformers.length == 0) {
                return NOPTransformer.INSTANCE;
            }
            transformers = FunctorUtils.copy(transformers);
            return new ChainedTransformer(transformers);
    }
    
    public static Transformer getInstance(Transformer transformer1, Transformer transformer2) {
            if (transformer1 == null || transformer2 == null) {
                throw new IllegalArgumentException("Transformers must not be null");
            }
            Transformer[] transformers = new Transformer[] { transformer1, transformer2 };
            return new ChainedTransformer(transformers);
    }
    
    
    //构造函数
    public ChainedTransformer(Transformer[] transformers) {
            super();
            iTransformers = transformers;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    Runtime

    Runtime运行时,每个语言都有,比如C/C++的CRT/GLIC,它的意思很简单,就是运行时,更细的解释应该说是运行时支持库和运行时环境,众所周知一般程序员使用C/C++开发应用层或者基于系统的驱动时,使用的Runtime大多都是标准库,它们是背靠操作系统的,而Java,C#,Python,Lua等等语言都是有自己的虚拟机的,我们想让系统执行一个命令,只有通过虚拟机来和操作系统打交道,因为操作系统本身根本不认识你的字节码。而虚拟机提供的Runtime中,存在了很多native方法,它们是由C/C++编写的,依靠操作系统的。

    执行命令

    如果我们想调用Runtime.getRuntime通过运行时执行命令,那么我们直观的代码应该是下面这样:

    Runtime.getRuntime().exec("calc"); //执行一个calc,弹出一个计算器
    
    • 1

    反射调用

    反射的基础就不在这里写了,我们如果要通过反射来调用,会写成下面这样:

    Method getRuntime = Runtime.class.getMethod("getRuntime");
    Object runtime = getRuntime.invoke(Runtime.class);
    if (runtime instanceof Runtime){
        ((Runtime) runtime).exec("calc");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    或者更为复杂的

     Method getRuntime = Runtime.class.getMethod("getRuntime");
     Method exec =  Runtime.class.getMethod("exec", String.class);
    
     Object runtime = getRuntime.invoke(Runtime.class);
     if (runtime instanceof Runtime){
         exec.invoke(runtime,"calc");
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    尝试使用Transformer调用

    InvokerTransformer小栗子

    举个最简单的例子,我们用InvokerTransformer来弹出一个计算器。

    InvokerTransformergetInstance方法上面并没有贴代码,简单的说就是

    第一个参数值methodName,第二个参数是对应methodName方法参数类型,第三个参数就是对应methodName方法参数本身

    Runtime runtime = Runtime.getRuntime();
    Transformer transformer = InvokerTransformer.getInstance("exec", new Class[]{String.class}, new String[]{"calc"});
    transformer.transform(runtime);
    
    • 1
    • 2
    • 3

    我们成功的弹了一个计算器。

    MapTransformer 小栗子

    只是举一个简单的例子

     HashMap<String,Object> map = new HashMap<>();
     map.put("key",Runtime.getRuntime());
     Transformer transform = MapTransformer.getInstance(map);
     Runtime runtime = (Runtime) transform.transform("key");
     runtime.exec("calc");
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ChainedTransformer 链式调用

    Transformer[] transformers = {
        InvokerTransformer.getInstance("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
    
        InvokerTransformer.getInstance("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
        InvokerTransformer.getInstance("exec", new Class[]{String.class}, new String[]{"calc"})
    };
    Transformer chainedTransformer = ChainedTransformer.getInstance(transformers);
    chainedTransformer.transform(Runtime.class);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这个稍微有些复杂,我们先来分析一下

    第一个InvokerTransformer,为什么我们不直接调用getRuntime,而是调用了getMethod?我们看InvokerTransformer#transform方法

     Class cls = input.getClass();
     Method method = cls.getMethod(iMethodName, iParamTypes);
     return method.invoke(input, iArgs);
    
    • 1
    • 2
    • 3

    它本身通过反射调用,如果其中method是通过input.getMethod调用而不是input.getClass().getMethod,是没有任何问题的,问题出在Class cls = input.getClass();

    其中input.getClass()也就是Runtime.class.getClass(),它会变成java.lang.Class,也就是直接获取getRuntime根本拿不到。

    所以第一个InvokerTransformer算是做了一个反射套娃,本身含义如下:

    Class cls = Runtime.class.getClass();
    Method method = cls.getMethod("getMethod", String.class, Class[].class);
    Method getRuntimeMethod = (Method) method.invoke(Runtime.class, "getRuntime", null);
    //注意此时还是一个Method对象,并不是Runtime
    
    • 1
    • 2
    • 3
    • 4

    第二个InvokerTransformer就等于拿到了runtime对象了,后面也就很简单了。

    //第二个InvokerTransformer做的事
    Class cls = getRuntimeMethod.getClass();
    Method method = cls.getMethod("invoke", Object.class, Object[].class);
    Runtime runtime = (Runtime) method.invoke(getRuntimeMethod,null,null);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    请求报错:javax.net.ssl.SSLHandshakeException: No appropriate protocol
    浅谈归并排序:合并 K 个升序链表的归并解法
    数据库整理知识
    httpclient工具类(支持泛型转换)
    【PAT 1033】 To Fill or Not to Fill 贪心算法&模拟
    机器学习之算法优化—Geatpy工具中案例分析
    leetCode 206.反转链表 图解
    线程池的异常处理机制
    跑步需要哪些运动装备?跑步爱好者者的装备推荐
    Redis实战 - 17 Redis事务和乐观锁实现缓存登录图片验证码功能
  • 原文地址:https://blog.csdn.net/slslslyxz/article/details/125454643