spring源码解析-(3)spring基于反射的api

内省是什么?内省是java中提供个一个特性,允许我们在运行时检查java对象的类类型和属性,它主要是通过反射来实现的。

内省能操作什么类型的类?内省可以操作特殊的java类,这些类有统一的规则,比如拥有一个无参构造器以及getter和setter方法。

能做什么?主要用于在不清楚类的具体情况下,发现并访问java的属性。Spring框架中就使用了内省来实现依赖注入和属性绑定。

通过class获取对象的信息

可以观察一下,发现内省的api统一存放在java.beans包中。

测试方法如下:

// 获取Person类的BeanInfo对象
BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
// 从BeanInfo对象中获取BeanDescriptor对象
BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
// 打印出BeanDescriptor的名称
System.out.println(beanDescriptor.getName());
// 获取所有属性
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
    // 属性名
    System.out.println("name " + propertyDescriptor.getName());
    // 读方法
    System.out.println("getReadMethod " + propertyDescriptor.getReadMethod());
    // 写方法
    System.out.println("getWriteMethod " + propertyDescriptor.getWriteMethod());
    // 字段类型
    System.out.println("getPropertyType " + propertyDescriptor.getPropertyType().getName());
    System.out.println();
}

输出结果为:

Person
name class
getReadMethod public final native java.lang.Class java.lang.Object.getClass()
getWriteMethod null
getPropertyType java.lang.Class

name id
getReadMethod public java.lang.Integer com.test.entity.Person.getId()
getWriteMethod public void com.test.entity.Person.setId(java.lang.Integer)
getPropertyType java.lang.Integer

name name
getReadMethod public java.lang.String com.test.entity.Person.getName()
getWriteMethod public void com.test.entity.Person.setName(java.lang.String)
getPropertyType java.lang.String

通过读写方法进行赋值操作和读取操作

测试方法:

Person person = new Person();
PropertyDescriptor nameProperty = new PropertyDescriptor("name", Person.class);
Method writeMethod = nameProperty.getWriteMethod();
writeMethod.invoke(person, "test");
Method readMethod = nameProperty.getReadMethod();
Object invoke = readMethod.invoke(person);
System.out.println(invoke);
System.out.println(person);

输出结果

test
Person{id=null, name='test'}

Spring中提供的反射工具BeanWrapper

测试方法:

BeanWrapper beanWrapper = new BeanWrapperImpl(new Person());
beanWrapper.setPropertyValue("name", "test");
System.out.println(beanWrapper.getWrappedInstance());

// 通过map批量设置
Map<String, Object> propertyNameAndValue = new HashMap<>(4);
propertyNameAndValue.put("name", "1");
propertyNameAndValue.put("id", 12);
beanWrapper.setPropertyValues(propertyNameAndValue);
System.out.println(beanWrapper.getWrappedInstance());

// 通过MutablePropertyValues 批量设置
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.addPropertyValue("name", "123");
propertyValues.addPropertyValue("id", 21);
beanWrapper.setPropertyValues(propertyValues);
System.out.println(beanWrapper.getWrappedInstance());

批量构造

测试代码如下:但该代码会报错

// 创建一个简单的BeanDefinitionRegistry
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
// 创建一个XmlBeanDefinitionReader对象,用于读取xml文件
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
// 使用XmlBeanDefinitionReader读取test.xml文件
reader.loadBeanDefinitions("test.xml");
// 获取BeanDefinitionRegistry中所有注册的BeanDefinition名称
String[] beanDefinitionNames = registry.getBeanDefinitionNames();
// 遍历所有注册的BeanDefinition
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
    // 获取每个BeanDefinition
    BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionName);
    // 根据BeanDefinition的类名,获取对应的Class对象
    Class<?> aClass = Class.forName(beanDefinition.getBeanClassName());
    // 创建一个BeanWrapper对象,用于包装Class对象
    BeanWrapper beanWrapper = new BeanWrapperImpl(aClass);
    // 将BeanDefinition的属性值设置到BeanWrapper中
    beanWrapper.setPropertyValues(beanDefinition.getPropertyValues());
    System.out.println(beanWrapper);
}

报错如下:截取部分内容,可以通过日志发现,该错误是因为无法将TypedStringValue转换为Integer

Failed properties: Failed to convert property value of type 'org.springframework.beans.factory.config.TypedStringValue' to required type 'java.lang.Integer' for property 'id';

ResolvableType

该类用于封装java类型,提供对【超类类型、接口和泛型参数】的访问。

// 获取DefaultSingletonBeanRegistry类的declaredField属性singletonObjects
ResolvableType type = 		
    ResolvableType.forField(DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects"));
// 打印出type的类型
System.out.println(type.getType().getTypeName());
// 打印出type的泛型
System.out.println(type.getGenerics());
// 打印出type的接口
System.out.println(type.getInterfaces());
// 打印出type resolveGenerics
System.out.println(type.resolveGenerics());
// 打印出type resolve的类名
Class<?> resolve = type.resolve();
System.out.println(resolve.getName());
// 打印出type的原始类名
System.out.println(type.getRawClass().getName());

类型转换器

我们从xml中读取到的内容均为字符串内容,但实际变量的类型,可能是数字,数组或更复杂的引用数据类型,所以我们需要一个类型转换器,将我们获取到的字符串类型转换为对应的类型。

类型转换

  1. 类型转换,其本身可以判断两个类型是否可以转换,具体的转换工作交给converter进行。
  2. spring包含了大量默认的类型转换器,同时我们可以自由的添加和删除。
  3. 给定两个类型,converter可以判断是否转换,如果可以则进行转换,匹配不成功则继续匹配converter,直到匹配到合适的converter或结束。

类型转换的接口

@Nullable: 表明该参数可以为null,提高代码的可读性、强制类检查和错误处理,从而避免空指针的发生

具体来说,@Nullable注解可以标注在以下位置:

  1. 方法参数:表示该参数可能为null。
  2. 方法返回值:表示该方法可能返回null。
  3. 字段:表示该字段可能为null
public interface ConversionService {
    // 转换 从source的class类型转换为target类型
    // 这个方法返回一个布尔值,表示是否能够将sourceType类型的对象转换为targetType类型的对象。如果返回true,表示convert方法可以执行这种转换。这个方法不会抛出任何异常
	boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
    // 这个方法与canConvert方法类似,但它提供了更多的上下文信息,包括源类型和目标类型的详细信息。这个方法不会抛出任何异常。
	boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
    // 这个方法将source对象转换为targetType类型的对象。如果source为null,则返回null。如果转换失败,将抛出一个ConversionException异常。
	@Nullable
	<T> T convert(@Nullable Object source, Class<T> targetType);
    // 这个方法与convert方法类似,但它提供了更多的上下文信息,包括源类型和目标类型的详细信息。如果source为null,则返回null。如果转换失败,将抛出一个ConversionException异常。
	@Nullable
	Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);

}

该接口的默认实现,大多数方法都是继承自父类,代码较多,父类只截取部分方法说明

@Override
public boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType) {
    Assert.notNull(targetType, "Target type to convert to cannot be null");
    return canConvert((sourceType != null ? TypeDescriptor.valueOf(sourceType) : null),
            TypeDescriptor.valueOf(targetType));
}

实现类

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());

		converterRegistry.addConverter(new ObjectToObjectConverter());
		converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
		converterRegistry.addConverter(new FallbackObjectToStringConverter());
		converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
	}

	public static void addCollectionConverters(ConverterRegistry converterRegistry) {
		ConversionService conversionService = (ConversionService) converterRegistry;

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

		converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
		converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
		converterRegistry.addConverter(new MapToMapConverter(conversionService));

		converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToArrayConverter(conversionService));

		converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
		converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));

		converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
		converterRegistry.addConverter(new StringToCollectionConverter(conversionService));

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

		converterRegistry.addConverter(new StreamConverter(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 StringToCharacterConverter());
		converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new NumberToCharacterConverter());
		converterRegistry.addConverterFactory(new CharacterToNumberFactory());

		converterRegistry.addConverter(new StringToBooleanConverter());
		converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToStringConverter((ConversionService) converterRegistry));

		converterRegistry.addConverterFactory(new IntegerToEnumConverterFactory());
		converterRegistry.addConverter(new EnumToIntegerConverter((ConversionService) converterRegistry));

		converterRegistry.addConverter(new StringToLocaleConverter());
		converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCharsetConverter());
		converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToCurrencyConverter());
		converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());

		converterRegistry.addConverter(new StringToPropertiesConverter());
		converterRegistry.addConverter(new PropertiesToStringConverter());

		converterRegistry.addConverter(new StringToUUIDConverter());
		converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
	}

}

可以看到,大部分的方法都通过父类来实现了,其中子类中大多数为初始化配置,即添加更多的转换器,和单例获取。(转换器有且仅有一个就足够了,没必要有多个实例)

测试用例

思考:我们在编写接口的时候,前端传给我们的实际上也是一个字符串,是不是也是这样进行转换的呢?

@Test
public void testConversionService() {
    String str = "123";
    ConversionService converter = new DefaultConversionService();
    boolean b = converter.canConvert(String.class, Integer.class);
    if (b) {
        Integer result = converter.convert(str,Integer.class);
        System.out.println(result);
    }
}

自定义转换器

假如,我现在有一个需求,将一个给定格式的字符串转换为对应的引用类型,我们该如何实现呢?

public class Dog {
	private String color;
	private String name;
	// 省略get、set、构造器和toString方法
}

实现一个转换器

/**
 * 转换器
 */
public class DogConverter implements GenericConverter {

	/**
	 * 获取到可以转换的列表
	 * 比如将String转成Dog
	 *
	 * @return
	 */
	@Override
	public Set<ConvertiblePair> getConvertibleTypes() {
		return Collections.singleton(new ConvertiblePair(String.class, Dog.class));
	}

	/**
	 * 假定,我们现在有如下固定格式的字符串:red,coco  它代表的含义即color为red name为coco的Dog对象
	 *
	 * @param source     the source object to convert (may be {@code null})
	 * @param sourceType the type descriptor of the field we are converting from
	 * @param targetType the type descriptor of the field we are converting to
	 * @return
	 */
	@Override
	public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
		assert source != null;
		String[] split = source.toString().split(",");
		if (split.length >= 2) {
			return new Dog(split[0], split[1]);
		}
		// 如果转换不成功,这里就不抛出异常了
		return null;
	}
}

测试

@Test
public void testSelfConverter() {
    String str = "yellow,coco";
    DogConverter dogConverter = new DogConverter();
    DefaultConversionService conver = new DefaultConversionService();
    conver.addConverter(dogConverter);
    if (conver.canConvert(String.class, Dog.class)) {
        Dog convert = conver.convert(str, Dog.class);
        System.out.println(convert);
    }
}

输出结果:Dog{color=‘yellow’, name=‘coco’}

有关类型转换的源码这里就不深究了,涉及的内容比较多,可自行查看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值