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提供的反射工具包。