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对其进行了实例化:
@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封装的功劳,原理还是那些东西。
我们可以使用如下的方法进行批量构造:
@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);}
}
该类可以封装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());
}
我们从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对象】,都需要这样的强大能力。
我们也可以实现自己的转化器,我们不妨写一个【从字符串到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}.
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;}}