spring 源码阅读(基础)-第二章 基础工具

文章介绍了Java中的内省机制,它是通过BeanInfo和PropertyDescriptor等类来访问和操作JavaBean的属性。同时,对比了反射的强大功能。在Spring中,内省API被广泛使用,同时Spring提供了BeanWrapper工具类简化了反射操作。文章还提到了Spring的ConversionService,用于处理类型转换,以及ResolvableType类用于处理复杂的类型信息。此外,文章展示了如何自定义转换器并集成到Spring的转换服务中。
摘要由CSDN通过智能技术生成

 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、通过任意形式捕获beanDefinition
    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    beanDefinition.setBeanClassName("com.ydlclass.User");
    MutablePropertyValues propertyValues = new MutablePropertyValues();
    propertyValues.addPropertyValue("name","lily");
    propertyValues.addPropertyValue("age",12);
    beanDefinition.setPropertyValues(propertyValues);

    // 2、通过权限定名称获得Class
    Class<?> 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、通过任意形式捕获beanDefinition
    SimpleBeanDefinitionRegistry 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<TypedStringValue, Integer>) 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> T convert(@Nullable Object source, Class<T> targetType);

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

}

我们不妨看看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());
        
        //...还有好多
	}

}

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

@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 {

   // 返回目标类型和源类型的一个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("转化出现异常.");
        }
    }
}

在当前的测试用例中,我们将我们的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<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;
    }
}
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;
    }
   
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值