Spring里面的工具类

581 阅读10分钟

1.BeanInfo

可以用来快速展示类的方法签名等信息

public static void testDescriptor() throws IntrospectionException, InvocationTargetException, IllegalAccessException {
    // 1、通过内省api获取BeanDescriptor
    BeanInfo beanInfo = Introspector.getBeanInfo(Order.class);
    BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
    log.info("beanDescriptor -> {}", beanDescriptor.getName());

    // 2、直接获取属性描述器中的读方法、写方法等
    PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
    for (PropertyDescriptor descriptor : descriptors) {
        log.info("name -> {}", descriptor.getName());
        log.info("readMethod -> {}", descriptor.getReadMethod());
        log.info("writeMethod -> {}", descriptor.getWriteMethod());
        log.info("descriptorString -> {}", descriptor.getPropertyType().toString());
    }
}


beanDescriptor -> Order
name -> class
readMethod -> public final native java.lang.Class java.lang.Object.getClass()
writeMethod -> null
descriptorString -> class java.lang.Class
name -> orderId
readMethod -> public java.lang.String com.bytedance.tccPlus.configClient.manager.Order.getOrderId()
writeMethod -> public void com.bytedance.tccPlus.configClient.manager.Order.setOrderId(java.lang.String)
descriptorString -> class java.lang.String
name -> userId
readMethod -> public java.lang.String com.bytedance.tccPlus.configClient.manager.Order.getUserId()
writeMethod -> public void com.bytedance.tccPlus.configClient.manager.Order.setUserId(java.lang.String)
descriptorString -> class java.lang.String

2. PropertyDescriptor

通过反射 read, write 值

public static void testWriteRead() throws IntrospectionException, InvocationTargetException, IllegalAccessException {
    Order order = new Order();
    PropertyDescriptor propertyDescriptor = new PropertyDescriptor("orderId", Order.class);
    Method writeMethod = propertyDescriptor.getWriteMethod();
    writeMethod.invoke(order, "d3213213123");
    log.info("order-> {}", order);
    Method readMethod = propertyDescriptor.getReadMethod();
    Object result = readMethod.invoke(order);
    log.info("result -> {}", result);
}


// order-> Order(orderId=d3213213123, userId=null)
// result -> d3213213123

3. BeanWrapper

1. bean的创建

public  static  void  testBeanWrapper () {
    // 在构造的过程中就会实例化
    BeanWrapper  beanWrapper  =  new  BeanWrapperImpl (Order.class);
    // 通过beanWrapper设置属性
    beanWrapper.setPropertyValue( "orderId" , "第一次订单ID" );
    beanWrapper.setPropertyValue( "userId" , "第一次用户ID" );
    log .info( "bean -> {}" , beanWrapper.getWrappedInstance());

    // 通过beanWrapper使用map批量设置属性
    Map<String, Object> map = new  HashMap <>();
    map.put( "userId" , "第二次用户ID" );
    map.put( "orderId" , "第二次订单ID" );
    beanWrapper.setPropertyValues(map);
    log .info( "bean -> {}" , beanWrapper.getWrappedInstance());

    // 通过beanWrapper使用MutablePropertyValues批量设置属性
    MutablePropertyValues  pvs  =  new  MutablePropertyValues ();
    pvs.addPropertyValue( "userId" , "第三次用户ID" );
    pvs.addPropertyValue( "orderId" , "第三次订单ID" );
    beanWrapper.setPropertyValues(pvs);
    log .info( "bean -> {}" , beanWrapper.getWrappedInstance());


    //bean -> Order(orderId=第一次订单ID, userId=第一次用户ID)
    //bean -> Order(orderId=第二次订单ID, userId=第二次用户ID)
    //bean -> Order(orderId=第三次订单ID, userId=第三次用户ID)
} 

2. 批量创建Bean

@Test
public  static  void  testBatchCreate ()  throws IntrospectionException, ClassNotFoundException {
    // 1、创建一个beanDefinitionRegistry
    BeanDefinitionRegistry  registry  =  new  SimpleBeanDefinitionRegistry ();
    // 2、创建一个beanDefinitionReader
    BeanDefinitionReader  beanDefinitionReader  =  new  XmlBeanDefinitionReader (registry);
    beanDefinitionReader.loadBeanDefinitions( "bean.xml" );

    // 3、遍历beanDefinitionRegistry中的beanDefinition
    String[] beanDefinitionNames = registry.getBeanDefinitionNames();
    for (String beanDefinitionName : beanDefinitionNames) {
        log.info( "beanDefinitionName -> {}" , beanDefinitionName);
        BeanDefinition  beanDefinition  = registry.getBeanDefinition(beanDefinitionName);
        // 4、对beanDefinition进行包装,进行实例化
        Class<?> beanClass = Class.forName(beanDefinition.getBeanClassName());
        BeanWrapper  beanWrapper  =  new  BeanWrapperImpl (beanClass);
        // 5、进行属性填充(异常,xml中读取的是统一的"1" "0.1" "aaa"  --> 各自的真实属性类型)
        beanWrapper.setPropertyValues(beanDefinition.getPropertyValues());
        log.info( "bean -> {}" , beanWrapper.getWrappedInstance());

    }
} 

4. ResolvableType

该类可以封装Java类型,提供对【超类类型、接口和泛型参数】的访问,以及最终解析为类的能力,这是非常常见的一个类,他能及其方便的简化对反射api的调用,该类在spring中的使用率非常高。

ResolvableType可以从字段、方法参数、方法返回类型或类中获得。这个类上的大多数方法本身都会返回一个ResolvableType,以便于链式调用

1. 官方案例

private Map<Integer, List<String>> myMap;

public  void  example ()  throws NoSuchFieldException {
    // 当前类
    System. out .println( "当前类相关======================" );
    ResolvableType  t  = ResolvableType. forField (getClass().getDeclaredField( "myMap" ));
    System.out.println(t);
    System.out.println( "Map<Integer, List<String>> 的 Integer : " + t.getGeneric( 0 ).resolve().getSimpleName());
    System.out.println( "Map<Integer, List<String>> 的 List : " + t.getGeneric( 1 ).resolve().getSimpleName());
    System.out.println( "Map<Integer, List<String>> 的 List<String> 里的 String : " + t.getGeneric( 1 ).getGeneric( 0 ).resolve().getSimpleName());

    // 父类
    System. out .println( "父类相关======================" );
    ResolvableType  superType  = t.getSuperType();
    System.out.println(superType);
    System.out.println( "AbstractMap<Integer, List<String>> 的 Integer : " + superType.getGeneric( 0 ).resolve().getSimpleName());
    System.out.println( "AbstractMap<Integer, List<String>> 的 List : " + superType.getGeneric( 1 ).resolve().getSimpleName());
    System.out.println( "AbstractMap<Integer, List<String>> 的 List<String> 里的 String : " + superType.getGeneric( 1 ).getGeneric( 0 ).resolve().getSimpleName());

    //当前类相关======================
    //java.util.HashMap<java.lang.Integer, java.util.List<java.lang.String>>
    //Map<Integer, List<String>> 的 Integer : Integer
    //Map<Integer, List<String>> 的 List : List
    //Map<Integer, List<String>> 的 List<String> 里的 String : String
    //父类相关======================
    //java.util.AbstractMap<java.lang.Integer, java.util.List<java.lang.String>>
    //AbstractMap<Integer, List<String>> 的 Integer : Integer
    //AbstractMap<Integer, List<String>> 的 List : List
    //AbstractMap<Integer, List<String>> 的 List<String> 里的 String : String
} 

5. ConversionService

  • 类型转化服务,本身可以判断两个类型之间是否可以进行转化,转化的工作交给具体的Converter处理。

  • 类型转化服务中包含了大量的默认的转化器,同时也可以自由的添加和删除。

  • 给定他两个类型的值,通过类型进行匹配,匹配不成功则不能转化,匹配到合适的converter,就使用他进行转化

我们首先看看ConversionService接口,该接口相对简单,可以根据【源类型和目标类型】进行判断是否可以转换,并执行转换:

public  interface  ConversionService {

    boolean  canConvert ( @Nullable Class<?> sourceType, Class<?> targetType) ;

    boolean  canConvert ( @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) ;

    @Nullable
    <T> T convert ( @Nullable Object source, Class<T> targetType) ;

    // 将给定的{@code source}转换为指定的{@code targetType}。
    Object convert ( @Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) ;

} 

ConversionService有一个默认的实现DefaultConversionService,我们不妨大略看看其源码,更多核心的功能是在其父类中实现的,如在父类构造器中,或调用子类的会默认传入大量可用转化器:

public  class  DefaultConversionService  extends  GenericConversionService {

    @Nullable
    private  static  volatile DefaultConversionService sharedInstance;


    public  DefaultConversionService () {
        // 添加大量的默认的转换器
        addDefaultConverters( this );
    }
    // 类似单例的获取方式
    public  static ConversionService getSharedInstance () {
        DefaultConversionService  cs  = sharedInstance;
        if (cs == null ) {
            synchronized (DefaultConversionService.class) {
                cs = sharedInstance;
                if (cs == null ) {
                    cs = new  DefaultConversionService ();
                    sharedInstance = cs;
                }
            }
        }
        return cs;
    }

    // 添加适合大多数环境的转换器
    public  static  void  addDefaultConverters (ConverterRegistry converterRegistry) {
        addScalarConverters(converterRegistry);
        addCollectionConverters(converterRegistry);

        converterRegistry.addConverter( new  ByteBufferConverter ((ConversionService) converterRegistry));
        converterRegistry.addConverter( new  StringToTimeZoneConverter ());
        converterRegistry.addConverter( new  ZoneIdToTimeZoneConverter ());
        converterRegistry.addConverter( new  ZonedDateTimeToCalendarConverter ());

        //...还有好多
    }

    // 增加通用的转换器,例如集合、数组、对象等
    public  static  void  addCollectionConverters (ConverterRegistry converterRegistry) {
        ConversionService  conversionService  = (ConversionService) converterRegistry;

        converterRegistry.addConverter( new  ArrayToCollectionConverter (conversionService));
        converterRegistry.addConverter( new  CollectionToArrayConverter (conversionService));

        converterRegistry.addConverter( new  StringToCollectionConverter (conversionService));

        converterRegistry.addConverter( new  CollectionToObjectConverter (conversionService));
        converterRegistry.addConverter( new  ObjectToCollectionConverter (conversionService));

        //...还有好多
    }

    // 新增标量的转化器,主要是字符串数字类型
    private  static  void  addScalarConverters (ConverterRegistry converterRegistry) {
        converterRegistry.addConverterFactory( new  NumberToNumberConverterFactory ());
        converterRegistry.addConverterFactory( new  StringToNumberConverterFactory ());
        converterRegistry.addConverter(Number.class, String.class, new  ObjectToStringConverter ());
        converterRegistry.addConverter( new  StringToPropertiesConverter ());
        converterRegistry.addConverter( new  PropertiesToStringConverter ());
        converterRegistry.addConverter( new  StringToUUIDConverter ());

        //...还有好多
    }

} 

1. 使用示例

public  static  void  convertStr2Integer () {
    // DefaultConversionService中包含了大量的converter
    ConversionService  service  =  new  DefaultConversionService ();
    String  source  =  "123" ;
    // 判断两个类型是否可以转化
    boolean  canConvert  = service.canConvert(String.class, Integer.class);
    if (canConvert) {
        // 转换
        Integer  result  = service.convert(source, Integer.class);
        log .info( "result:{}" , result);
    }
}

public  static  void  convertStr2Double () {
    ConversionService  service  =  new  DefaultConversionService ();
    String  source  =  "100" ;
    if (service.canConvert(String.class, Double.class)) {
        Double  result  = service.convert(source, Double.class);
        System. out .println( "str -> double : res : " + result);
    }
}

public  static  void  convertStr2List () {
    String  source  =  "100,12,23,54,56" ;
    ConversionService  conversionService  =  new  DefaultConversionService ();
    if (conversionService.canConvert(String.class, List.class)) {
        List  target  = conversionService.convert(source, List.class);
        log .info( "The number is {}." , target);
    }
} 

2. 基于ConversionService封装工具类

public  class  ConvertUtils {

    private  static  final  ConversionService  CONVERSION_SERVICE  =  new  DefaultConversionService ();

    @SuppressWarnings("unchecked")
    public  static <T> T convert (Object source, Class<T> targetType) {
        if (source == null || targetType == null ) {
            throw  new  IllegalArgumentException ( "source or targetType can not be null" );
        }
        if (targetType.isAssignableFrom(source.getClass())) {
            return (T) source;
        }
        if ( CONVERSION_SERVICE .canConvert(source.getClass(), targetType)) {
            return  CONVERSION_SERVICE .convert(source, targetType);
        }
        else {
            throw  new  IllegalArgumentException ( "Can not convert " + source.getClass() + " to " + targetType);
        }
    }
} 

使用:

public  static  void  testConvertUtils () {
    String  source  =  "100" ;
    System. out .println(ConvertUtils. convert (source, Double.class));
} 

3. 编写自己的类型转换器

我们也可以实现自己的转化器,我们不妨写一个【从字符串到User类】的转化器,我们的转换器需要实现GenericConverter接口,这个可以仿照其他的转换器写:

我们可以先看看GenericConverter接口:

public  interface  GenericConverter {

    // 返回目标类型和源类型的一个set
    Set<ConvertiblePair> getConvertibleTypes () ;
    // 新的方法
    Object convert ( @Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) ;


    // 定义了一个 source-to-target class pair.
    final  class  ConvertiblePair {

        private  final Class<?> sourceType;
        private  final Class<?> targetType;

        ... 其他内容省略
    }

} 

我们需要实现GenericConverter的两个方法:

public  class  UserConvert  implements  GenericConverter {

    @Override
    public Set<ConvertiblePair> getConvertibleTypes () {
        // 返回一个Set集合,其中的元素为一个只包含object(obj)的不可变集合
        return Collections.singleton( new  GenericConverter .ConvertiblePair(String.class, User.class));
    }

    @Override
    public Object convert (Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        // 我们假设字符串的类型如下: name | age
        assert source != null ;
        String[] nameAndAge = source.toString().split( "\|" );
        if (nameAndAge.length == 2 ){
            return  new  User (nameAndAge[ 0 ].trim(),Integer.valueOf(nameAndAge[ 1 ].trim()));
        } else {
            throw  new  RuntimeException ( "转化出现异常." );
        }
    }
} 

测试

@Test
public  void  testConvertUser () {
    String  source  =  "Tom | 23" ;
    // 这里必须使用子类的类型,接口并不提供addConverter方法
    DefaultConversionService  conversionService  =  new  DefaultConversionService ();
    conversionService.addConverter( new  UserConvert ());
    if (conversionService.canConvert(String.class, User.class)){
        User  target  = conversionService.convert(source, User.class);
        logger.info( "The user is {}." , target);
    }
}
// 结果
19 : 51 : 03.210 [main] INFO com.qyh.ToolsTest - The user is User{name= 'Tom' , age= 23 }. 

更简单的转换

我们发现DefaultConversionService有很多重载的addConverter方法,我们甚至可以传入一个实现了Converter接口的实例,如下:

@Override
public  void  addConverter (Converter<?, ?> converter) {
    // 这个方法可以获得实现类的泛型
    ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
    if (typeInfo == null && converter instanceof DecoratingProxy decoratingProxy) {
        typeInfo = getRequiredTypeInfo(decoratingProxy.getDecoratedClass(), Converter.class);
    }
    if (typeInfo == null ) {
        throw  new  IllegalArgumentException ( "Unable to determine source type <S> and target type <T> for your " + "Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?" );
    }
    // Converter<?, ?>和GenericConverter做了适配
    addConverter( new  ConverterAdapter (converter, typeInfo[ 0 ], typeInfo[ 1 ]));
} 

按照这个方法的定义,我们重新编写我们的UserConverter2,他需要实现Converter接口,如下:

public  class  UserConvertor  implements  Converter <String, User> {
    @Override
    public User convert (String source) {
        String[] split = source.split( "|" );
        if (split.length == 2 ) {
            return  new  User (Integer. parseInt (split[ 0 ]), split[ 1 ]);
        }
        return  null ;
    }
} 

他的实现逻辑是,运行时获取泛型的类型,并且与GenericConverter做适配。

使用 ResolvableType

public  class  UserConvertor  implements  Converter <String, User> {
    @Override
    public User convert (String source) {
        String[] split = source.split( "|" );
        if (split.length == 2 ) {
            return  new  User (Integer. parseInt (split[ 0 ]), split[ 1 ]);
        }
        return  null ;
    }
}

public  static  void  testConverter () {
    ResolvableType  resolvableType  = ResolvableType. forClass (UserConvertor.class);

    ResolvableType  superType  = resolvableType.getInterfaces()[ 0 ];
    System. out .println(superType);

    System. out .println(superType.getGeneric( 0 ).resolve().getSimpleName());
    System. out .println(superType.getGeneric( 1 ).resolve().getSimpleName());

    //org.springframework.core.convert.converter.Converter<java.lang.String, com.bytedance.tccPlus.configClient.manager.User>
    //String
    //User
} 

有细心的朋友可能会发现,java中不是有泛型擦除的机制吗?为什么这里还可以获取呢?

在Java中,泛型信息确实会在编译时因为类型擦除而丢失,这意味着泛型参数在运行时被当作它们的边界类型(通常是Object )处理。然而,Java的反射机制仍然能够访问类、方法和字段上声明的泛型类型信息,因为这些信息是作为元数据存储在字节码的Signature属性中的。

简单来说,类型擦除主要影响的是泛型的实例化对象,例如在方法体内部创建的泛型对象。但是,对于类或方法的声明部分,Java编译器会将泛型类型信息保存在类的字节码中,这样即使在运行时,这些信息也可以通过反射获取到。

例如,如果你有一个泛型类Test<T>,并且在这个类中声明了一个返回类型为T的方法,那么这个返回类型的泛型信息就会被保存在字节码中,可以通过反射获取。但是,如果你在方法体内部创建了一个T类型的对象,那么这个对象的具体类型信息在编译后就会丢失,因为它会被擦除为Object

这就是为什么我们可以通过反射获取某些泛型信息,但不是所有的泛型信息。具体到成员变量、方法的返回值和参数的泛型信息可以通过反射获取,而局部变量的泛型信息则不可以。这种设计允许Java在保持向后兼容性的同时,引入泛型特性。

4. 转换器源码

所有注册的convertor都会存储在Converters中,该类结构相对比较复杂:

private  static  class  Converters {

    // 存取通用的转换器,并不限定转换类型,一般用于兜底
    private  final Set<GenericConverter> globalConverters = new  CopyOnWriteArraySet <>();
    // 指定了类型对,对应的转换器们的映射关系。
    // ConvertiblePair:表示一对,包含sourceType和targetType
    // ConvertersForPair:这一对对应的转换器们(因为能处理一对类型转换可能存在多个转换器),内部使用一个双端队列Deque来存储,保证顺序
    private  final Map<ConvertiblePair, ConvertersForPair> converters = new  ConcurrentHashMap <>( 256 );

    public  void  add (GenericConverter converter) {
        // 获得他的类型对儿
        Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
        if (convertibleTypes == null ) {
            // 如果没有限定转换类型,添加到globalConverters
            this .globalConverters.add(converter);
        }
        else {
            // 如果已经存在转换类型,我们写的都在这里
            for (ConvertiblePair convertiblePair : convertibleTypes) {
                // 找到与之匹配的加进去,这里是个链表
                getMatchableConverters(convertiblePair).add(converter);
            }
        }
    }

    @Nullable
    public GenericConverter find (TypeDescriptor sourceType, TypeDescriptor targetType) {
        // 搜索完整的类型层次结构,父类--->
        // 比如想要搜索【虎猫 -> 老虎】,但如过虎猫有父类(猫)
        // 我们还需检索【猫 -> 老虎】
        List<Class<?>> sourceCandidates = getClassHierarchy(sourceType.getType());
        List<Class<?>> targetCandidates = getClassHierarchy(targetType.getType());
        for (Class<?> sourceCandidate : sourceCandidates) {
            for (Class<?> targetCandidate : targetCandidates) {
                // 所有的类型都要匹配
                ConvertiblePair  convertiblePair  =  new  ConvertiblePair (sourceCandidate, targetCandidate);
                // 找到一个就返回
                GenericConverter  converter  = getRegisteredConverter(sourceType, targetType, convertiblePair);
                if (converter != null ) {
                    return converter;
                }
            }
        }
        return  null ;
    }

    @Nullable
    private GenericConverter getRegisteredConverter (TypeDescriptor sourceType,
                                                     TypeDescriptor targetType, ConvertiblePair convertiblePair) {

        // 根据convertiblePair获取ConvertersForPair
        ConvertersForPair  convertersForPair  =  this .converters.get(convertiblePair);
        if (convertersForPair != null ) {
            GenericConverter  converter  = convertersForPair.getConverter(sourceType, targetType);
            if (converter != null ) {
                return converter;
            }
        }
        // 检查是否能匹配兜底的全局转换器
        for (GenericConverter globalConverter : this .globalConverters) {
            if (((ConditionalConverter) globalConverter).matches(sourceType, targetType)) {
                return globalConverter;
            }
        }
        return  null ;
    }

} 

ConvertiblePair:表示一对,包含sourceType和targetType

final  class  ConvertiblePair {
 private  final Class<?> sourceType;
 private  final Class<?> targetType;
} 

ConvertersForPair:这一对对应的转换器们(因为能处理一对类型转换可能存在多个转换器),内部使用一个双端队列Deque来存储,保证顺序。

private  static  class  ConvertersForPair {
    // 内部维护的队列
    private  final Deque<GenericConverter> converters = new  ConcurrentLinkedDeque <>();

    public  void  add (GenericConverter converter) {
        this .converters.addFirst(converter);
    }

    @Nullable
    public GenericConverter getConverter (TypeDescriptor sourceType, TypeDescriptor targetType) {
        for (GenericConverter converter : this .converters) {
            // 此处表明,如果我们有特殊的需求,还可以实现ConditionalGenericConverter,实现特殊的匹配规则,连边中的converter可以有不同的匹配规则,
            // 当然通常情况下会返回第一个
            if (!(converter instanceof ConditionalGenericConverter genericConverter) || genericConverter.matches(sourceType, targetType)) {
                return converter;
            }
        }
        return  null ;
    }
} 

6, reflectionUitls

spring提供的反射工具包。

image.png