spring 源码阅读(基础)-第二章 基础工具
创始人
2024-06-02 15:37:37

1.内省api

1.什么是内省:

        内省是java语言针对bean属性,事件的一种缺省处理方法,spring源码中也经常会出现相关的api。

        javaBean是一种特殊的类,主要用于信息传递,这种类中的方法主要用于私有字段的访问,且方法名符合某种命名规则,事实上,内省机制也是通过反射来实现的。

        反射的功能是比内省强大的。

在Java内省中,用到的基本上就是上述几个类。

        内省api的一般的做法是通过类 Introspector 的 getBeanInfo方法来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的 取值/赋值 方法,然后我们就可以通过反射机制来调用这些方法,这就是内省机制。


import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;@Test
public void testIntrospect1() throws IntrospectionException {BeanInfo beanInfo = Introspector.getBeanInfo(User.class, Object.class);PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {logger.info("{}",propertyDescriptor.getPropertyType());logger.info("{}",propertyDescriptor.getReadMethod());logger.info("{}",propertyDescriptor.getWriteMethod());}
}// 2.操纵bean的指定属性:age
@Test
public void testIntrospect2() throws Exception {User user = new User();PropertyDescriptor pd = new PropertyDescriptor("age", User.class);// 得到属性的写方法,为属性赋值Method method = pd.getWriteMethod();method.invoke(user, 24);// 获取属性的值method = pd.getReadMethod();System.out.println(method.invoke(user, null));
}
@Test
public void testBeanUtil() throws Exception {User user = new User();// 赋值BeanUtils.setProperty(user,"name","tom");BeanUtils.setProperty(user,"age",10);logger.info("user->{}",user);// 获取值logger.info("the user's name is ->{}.",BeanUtils.getProperty(user,"name"));
}

二、更强的反射工具

在spring中,我们除了能看到内省相关的api,看到的更多的可能是反射api了,当然针对原生api的复杂性,spring同样进行了封装,让其使用起来更简单。

spring给我们提供了强大的反射工具BeanWrapper,下边的例子展示了该类如何配合BeanDefinition对其进行了实例化:

1、bean的创建

@Test
public void testCreate() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {// 1、通过任意形式捕获beanDefinitionGenericBeanDefinition beanDefinition = new GenericBeanDefinition();beanDefinition.setBeanClassName("com.ydlclass.User");MutablePropertyValues propertyValues = new MutablePropertyValues();propertyValues.addPropertyValue("name","lily");propertyValues.addPropertyValue("age",12);beanDefinition.setPropertyValues(propertyValues);// 2、通过权限定名称获得ClassClass aClass = Class.forName(beanDefinition.getBeanClassName());// 3、使用BeanWrapper包裹实例,使其更方便使用反射方法BeanWrapper beanWrapper = new BeanWrapperImpl(aClass);beanWrapper.setPropertyValues(beanDefinition.getPropertyValues());Object bean = beanWrapper.getWrappedInstance();logger.info("The bean is [{}]",bean);
}

我们可以看到BeanWrapperImpl仅仅需要一个Class就能十分友好的结合beanDefinition进行构建和赋值,而不需要通过复杂的反射获取构造器进行实例化,获取字段对象进行赋值,当然这仅仅是api封装的功劳,原理还是那些东西。

2、批量构造

我们可以使用如下的方法进行批量构造:

@Test
public void testBatchCreate() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {// 1、通过任意形式捕获beanDefinitionSimpleBeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(registry);xmlReader.loadBeanDefinitions("classpath:spring.xml");// 2、通过反射实例化String[] definitionNames = registry.getBeanDefinitionNames();for (String definitionName : definitionNames) {BeanDefinition beanDefinition = registry.getBeanDefinition(definitionName);String beanClassName = beanDefinition.getBeanClassName();Class aClass = Class.forName(beanClassName);// 3、使用BeanWrapper包裹实例,使其更方便使用反射方法BeanWrapperImpl beanWrapper = new BeanWrapperImpl(aClass);DefaultConversionService conversionService = new DefaultConversionService();// 转换器在这里呦:使用lamdba表达式写一个转换器conversionService.addConverter((Converter) source -> Integer.valueOf(Objects.requireNonNull(source.getValue())));beanWrapper.setConversionService(conversionService);beanWrapper.setPropertyValues(beanDefinition.getPropertyValues());Object bean = beanWrapper.getWrappedInstance();System.out.println(bean);}
}

3、ResolvableType

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

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

@Test
public void testTypeResolvableType() throws NoSuchFieldException {ResolvableType type = ResolvableType.forField(DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects"));// 获取类型logger.info(type.getType().getTypeName());// 获取泛型logger.info(Arrays.toString(type.getGenerics()));logger.info(Arrays.toString(type.getInterfaces()));logger.info(Arrays.toString(type.resolveGenerics()));// 获取来源Class resolve = type.resolve();logger.info(type.getRawClass().getName());
}

2、类型转化

我们从xml中搜集到的所有数据都是【字符串】,但是实际的类中的成员变量可能是数字,数组,集合,或者是复杂的引用数据类型,所以spring给我们提供了强大的转换服务(conversionService接口)。

public interface ConversionService {boolean canConvert(@Nullable Class sourceType, Class targetType);boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);@Nullable T convert(@Nullable Object source, Class targetType);// 将给定的{@code source}转换为指定的{@code targetType}。Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);}

我们不妨看看DefaultConversionService的源码,更多核心的功能是在器父类中实现的,在构造实例时,他会默认传入大量可用转化器:

public class DefaultConversionService extends GenericConversionService {@Nullableprivate 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());//...还有好多}}

我们可以编写如下的测试用例,可以将字符串转换为数字和列表:

@Test
public void testConvertInteger(){String source = "100";ConversionService conversionService = new DefaultConversionService();if(conversionService.canConvert(String.class,Integer.class)){Integer target = conversionService.convert(source, Integer.class);logger.info("The number is {}.", target);}
}@Test
public void testConvertList(){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);logger.info("The number is {}.", target);}
}

这种类型转换的能力非常有用,我们从【xml到java对象】,从【前端参数到java对象】,都需要这样的强大能力。

2、独立编写转化器

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

我们可以先看看GenericConverter接口:

public interface GenericConverter {// 返回目标类型和源类型的一个setSet 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 {@Overridepublic Set getConvertibleTypes() {// 返回一个Set集合,其中的元素为一个只包含object(obj)的不可变集合return Collections.singleton(new GenericConverter.ConvertiblePair(String.class, User.class));}@Overridepublic Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {// 我们假设字符串的类型如下: name | ageassert 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("转化出现异常.");}}
}

在当前的测试用例中,我们将我们的convert添加到conversionService中:

@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.ydlclass.ToolsTest - The user is User{name='Tom', age=23}.

3.转换器源码:

spring在考虑转换的时候会考虑到 由 a -> b的转换,还会考虑到由a 到 b 的子类的转换。所以在spring中,转换器的结构是如下所示:

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

private static class ConvertersForPair {// 内部维护的队列private final Deque converters = new ConcurrentLinkedDeque<>();public void add(GenericConverter converter) {this.converters.addFirst(converter);}@Nullablepublic 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;}
}
private static class Converters {// 存取通用的转换器,并不限定转换类型,一般用于兜底private final Set globalConverters = new CopyOnWriteArraySet<>();// 指定了类型对,对应的转换器们的映射关系。// ConvertiblePair:表示一对,包含sourceType和targetType// ConvertersForPair:这一对对应的转换器们(因为能处理一对类型转换可能存在多个转换器),内部使用一个双端队列Deque来存储,保证顺序private final Map converters = new ConcurrentHashMap<>(256);public void add(GenericConverter converter) {// 获得他的类型对儿Set convertibleTypes = converter.getConvertibleTypes();if (convertibleTypes == null) {// 如果没有限定转换类型,添加到globalConvertersthis.globalConverters.add(converter);}else {// 如果已经存在转换类型,我们写的都在这里for (ConvertiblePair convertiblePair : convertibleTypes) {// 找到与之匹配的加进去,这里是个链表getMatchableConverters(convertiblePair).add(converter);}}}@Nullablepublic GenericConverter find(TypeDescriptor sourceType, TypeDescriptor targetType) {// 搜索完整的类型层次结构,父类--->// 比如想要搜索【虎猫 -> 老虎】,但如过虎猫有父类(猫)// 我们还需检索【猫 -> 老虎】List> sourceCandidates = getClassHierarchy(sourceType.getType());List> 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;}@Nullableprivate GenericConverter getRegisteredConverter(TypeDescriptor sourceType,
TypeDescriptor targetType, ConvertiblePair convertiblePair) {// 根据convertiblePair获取ConvertersForPairConvertersForPair 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;}}

相关内容

热门资讯

苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...