diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211.md" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211.md" new file mode 100644 index 0000000000000000000000000000000000000000..30040e77f34ec44a580e34e9e36b312c8060c58d --- /dev/null +++ "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211.md" @@ -0,0 +1,890 @@ +Spring官网阅读(十三)ApplicationContext详解(下) + +> 在前面两篇文章中,我们已经对ApplicationContext的大部分内容做了介绍,包括国际化,Spring中的运行环境,Spring中的资源,Spring中的事件监听机制,还剩唯一一个BeanFactory相关的内容没有介绍,这篇文章我们就来介绍BeanFactory,这篇文章结束,关于ApplicationContext相关的内容我们也总算可以告一段落了。本文对应官网中的`1.16`及`1.15`小结 + +前面我们也提到了ApplicationContext继承了BeanFactory接口,其继承关系如下: + +![](./image/2020031801.png) + +下面我们直接进入BeanFactory相关内容的学习 + +# BeanFactory + +## 接口定义 + +```java +public interface BeanFactory { + + // FactroyBean的前缀,如果getBean的时候BeanName有这个前缀,会去获取对应的FactroyBean + // 而不是获取FactroyBean的getObject返回的Bean + String FACTORY_BEAN_PREFIX = "&"; + + // 都是用于获取指定的Bean,根据名称获取指定类型获取 + Object getBean(String name) throws BeansException; + T getBean(String name, Class requiredType) throws BeansException; + Object getBean(String name, Object... args) throws BeansException; + T getBean(Class requiredType) throws BeansException; + T getBean(Class requiredType, Object... args) throws BeansException; + + // 获取指定的Bean的ObjectProvider,这个有个问题,ObjectProvider是什么?请参考我《Spring杂谈》相关文章 + ObjectProvider getBeanProvider(Class requiredType); + ObjectProvider getBeanProvider(ResolvableType requiredType); + + // 检查容器中是否含有这个名称的Bean + boolean containsBean(String name); + + // 判断指定的Bean是否为单例 + boolean isSingleton(String name) throws NoSuchBeanDefinitionException; + + // 判断指定的Bean是否为原型 + boolean isPrototype(String name) throws NoSuchBeanDefinitionException; + + // 判断指定的Bean类型是否匹配,关于ResolvableType我已经专门写文章介绍过了,请参考我《Spring杂谈》相关文章 + boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; + boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException; + + // 返回指定Bean的类型 + Class getType(String name) throws NoSuchBeanDefinitionException; + + // 返回指定Bean的别名 + String[] getAliases(String name); + +} +``` + +> 可以看到`BeanFactory`接口主要提供了查找Bean,创建Bean(在getBean调用的时候也会去创建Bean),以及针对容器中的Bean做一些判断的方法(包括是否是原型,是否是单例,容器是否包含这个名称的Bean,是否类型匹配等等) + +## 继承关系 + + + +## 接口功能 + +作为BeanFactory的直接子接口的有三个,分别是`HierarchicalBeanFactory`,`ListableBeanFactory`,`AutowireCapableBeanFactory`。 + +### 1、HierarchicalBeanFactory + +```java +public interface HierarchicalBeanFactory extends BeanFactory { + // 获取父容器 + @Nullable + BeanFactory getParentBeanFactory(); + // 获取父系容器,只在当前容器中判断是否包含这个名称的Bean + boolean containsLocalBean(String name); +} +``` + +HierarchicalBeanFactory对顶层的BeanFactory做了扩展,让其具有了父子层级关系 + +### 2、ListableBeanFactory + +```java +public interface ListableBeanFactory extends BeanFactory { + + // 1.查找容器中是否包含对应名称的BeanDefinition + // 2.忽略层级关系,只在当前容器中查找 + boolean containsBeanDefinition(String beanName); + + // 1.查找容器中包含的BeanDefinition的数量 + // 2.忽略层级关系,只在当前容器中查找 + int getBeanDefinitionCount(); + + // 1.获取当前容器中所有的BeanDefinition的名称 + // 2.忽略层级关系,只在当前容器中查找 + String[] getBeanDefinitionNames(); + + // 根据指定类型获取容器中的对应的Bean的名称,可能会有多个 + // 既会通过BeanDefinition做判断,也会通过FactoryBean的getObjectType方法判断 + String[] getBeanNamesForType(ResolvableType type); + String[] getBeanNamesForType(@Nullable Class type); + + // 根据指定类型获取容器中的对应的Bean的名称,可能会有多个 + // 既会通过BeanDefinition做判断,也会通过FactoryBean的getObjectType方法判断 + // includeNonSingletons:是否能包含非单例的Bean + // allowEagerInit:是否允许对”懒加载"的Bean进行实例化,这里主要针对FactoryBean,因为FactoryBean + // 默认是懒加载的,为了推断它的类型可能会进行初始化。 + String[] getBeanNamesForType(@Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit); + + // 获取指定类型的Bean,返回一个map,key为bean的名称,value为对应的Bean + Map getBeansOfType(@Nullable Class type) throws BeansException; + + // 获取指定类型的Bean,返回一个map,key为bean的名称,value为对应的Bean + // includeNonSingletons:是否能包含非单例的Bean + // allowEagerInit:是否允许对”懒加载"的Bean进行实例化,这里主要针对FactoryBean,因为FactoryBean + // 默认是懒加载的,为了推断它的类型可能会进行初始化。 + Map getBeansOfType(@Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit) + throws BeansException; + + // 获取添加了指定注解的Bean的名称 + // 为了确定类型,会对FactoryBean所创建的Bean进行实例化 + String[] getBeanNamesForAnnotation(Class annotationType); + + // 获取添加了指定注解的Bean的名称 + // 为了确定类型,会对FactoryBean所创建的Bean进行实例化 + // 返回一个map,key为bean的名称,value为对应的Bean + Map getBeansWithAnnotation(Class annotationType) throws BeansException; + + // 查询指定的Bean上的指定类型的注解,如果没有这个Bean会抛出NoSuchBeanDefinitionException + // 如果指定Bean上不存在这个注解,会从其父类上查找 + @Nullable + A findAnnotationOnBean(String beanName, Class annotationType) + throws NoSuchBeanDefinitionException; + +} +``` + +从上面的方法中可以看出,相对于BeanFactory,ListableBeanFactory提供了批量获取Bean的方法。 + +### 3、AutowireCapableBeanFactory + +```java +public interface AutowireCapableBeanFactory extends BeanFactory { + + // 自动注入下的四种模型,如果有疑问请参考之前的文章《自动注入与精确注入》 + int AUTOWIRE_NO = 0; + int AUTOWIRE_BY_NAME = 1; + int AUTOWIRE_BY_TYPE = 2; + int AUTOWIRE_CONSTRUCTOR = 3; + + // 已经过时了,不考虑 + @Deprecated + int AUTOWIRE_AUTODETECT = 4; + + //该属性是一种约定俗成的用法:以类全限定名+.ORIGINAL 作为Bean Name,用于告诉Spring,在初始化的时候,需要返回原始给定实例,而别返回代理对象 + String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL"; + + + //------------------------------------------------------------------------- + // 下面这三个方法通常用于创建跟填充Bean(对Bean进行属性注入),但是请注意,直接采用下面这些方法创建或者装 // 配的Bean不被Spring容器所管理 + //------------------------------------------------------------------------- + + // 用指定的class创建一个Bean,这个Bean会经过属性注入,并且会执行相关的后置处理器,但是并不会放入 // Spring容器中 + T createBean(Class beanClass) throws BeansException; + + // 为指定的一个对象完成属性注入,这个对象可以不被容器管理,可以是一个Spring容器外部的对象 + // 主要调用populateBean + void autowireBean(Object existingBean) throws BeansException; + + // 配置参数中指定的bean + // beanName表示在Bean定义中的名称。 + // populateBean和initializeBean都会被调用 + // existingBean:需要被配置的Bean + // beanName:对应的Bean的名称 + Object configureBean(Object existingBean, String beanName) throws BeansException; + + + //------------------------------------------------------------------------- + // 下面这一系列方法主要为了更细粒度的操纵Bean的生命周期 + //------------------------------------------------------------------------- + + // 支持以给定的注入模型跟依赖检查级别创建,注入Bean。关于注入模型我这里就不想再说了 + // 依赖检查的级别如下: + // 1.DEPENDENCY_CHECK_NONE = 0,代表不进行依赖检查 + // 2.DEPENDENCY_CHECK_SIMPLE = 2,代表对基本数据类的字段做检查。如果一个int类型的字段没有被赋值,那么会抛出异常 + // 3.DEPENDENCY_CHECK_ALL = 3,对引用类型的字段做检查。如果一个Object类型的字段没有被赋值,那么会抛出异常 + Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException; + Object autowire(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException; + void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) + throws BeansException; + + //就是把Bean定义信息里面的一些东西,赋值到已经存在的Bean里面 + void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException; + + // 初始化Bean,执行初始化回调,及下面两个后置处理器中的方法 + Object initializeBean(Object existingBean, String beanName) throws BeansException; + + // 调用对应的两个后置处理器 + Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) + throws BeansException; + Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) + throws BeansException; + + // 执行销毁相关的回调方法 + void destroyBean(Object existingBean); + + + //------------------------------------------------------------------------- + // 关于注入点的相关方法 + //------------------------------------------------------------------------- + + // 查找唯一符合指定类的实例,如果有,则返回实例的名字和实例本身 + // 底层依赖于:BeanFactory中的getBean(Class)方法 + NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException; + + // DependencyDescriptor:依赖名描述符,描述了依赖的相关情况,比如存在于哪个类,哪个字段,什么类型 + // 查找指定名称,指定类型的Bean + // 底层依赖于:BeanFactory中的getBean(name,Class)方法 + Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException; + + // 解析指定的依赖。就是根据依赖描述符的定义在容器中查找符合要求的Bean + @Nullable + Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException; + + //descriptor 依赖描述 (field/method/constructor) + //requestingBeanName 依赖描述所属的Bean + //autowiredBeanNames 与指定Bean有依赖关系的Bean的名称 + //typeConverter 用以转换数组和连表的转换器 + //备注:结果可能为null,毕竟容器中可能不存在这个依赖嘛~~~~~~~~~~~~~~~~ + @Nullable + Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, + @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException; + +} + +``` + +可以看到这个类中的方法都跟装配Bean,配置Bean相关,另外还有一系列专门处理注入点的方法。可以看到接口有一个很大的作用就是对于一些不受Spring管理的Bean,也能为其提供依赖注入的功能。例如: + +```java +// DmzService没有被放入容器中 +public class DmzService { + @Autowired + IndexService indexService; + + public void test(){ + System.out.println(indexService); + } +} + +// 被容器所管理 +@Component +public class IndexService { +} + +public class Main { + public static void main(String[] args) { + AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); + AutowireCapableBeanFactory beanFactory = ac.getBeanFactory(); + DmzService bean = beanFactory.createBean(DmzService.class); + // 打印:com.dmz.official.beanfactory.IndexService@6ad5c04e + bean.test(); + // 抛出NoSuchBeanDefinitionException + // ac.getBean(DmzService.class); + } +} + +``` + +在上面的例子中,`DmzService`没有被容器管理,所以在调用`ac.getBean(DmzService.class);`会抛出NoSuchBeanDefinitionException,但是我们可以看到,`indexService`被注入到了`DmzService`中。 + +### 4、ConfigurableBeanFactory + +```java +public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry { + // 单例及原型的常量 + String SCOPE_SINGLETON = "singleton"; + String SCOPE_PROTOTYPE = "prototype"; + + // 设置父容器,父容器一旦被设置,不可改变 + void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException; + + // 为Bean设置指定的类加载器 + void setBeanClassLoader(@Nullable ClassLoader beanClassLoader); + + // 获取类型加载器,可能返回null,代表系统类加载器不可访问 + @Nullable + ClassLoader getBeanClassLoader(); + + // 设置临时的类加载器,在进行类加载时期织入时会用到(loadTimeWeaver) + void setTempClassLoader(@Nullable ClassLoader tempClassLoader); + @Nullable + ClassLoader getTempClassLoader(); + + // 是否缓存Bean的元数据,默认是开启的 + void setCacheBeanMetadata(boolean cacheBeanMetadata); + boolean isCacheBeanMetadata(); + + // 定义用于解析bean definition的表达式解析器 + void setBeanExpressionResolver(@Nullable BeanExpressionResolver resolver); + @Nullable + BeanExpressionResolver getBeanExpressionResolver(); + + // 数据类型转换相关 + void setConversionService(@Nullable ConversionService conversionService); + @Nullable + ConversionService getConversionService(); + void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar); + void registerCustomEditor(Class requiredType, Class propertyEditorClass); + void copyRegisteredEditorsTo(PropertyEditorRegistry registry); + void setTypeConverter(TypeConverter typeConverter); + TypeConverter getTypeConverter(); + + // 值解析器,例如可以使用它来处理占位符 + void addEmbeddedValueResolver(StringValueResolver valueResolver); + boolean hasEmbeddedValueResolver(); + @Nullable + String resolveEmbeddedValue(String value); + + // 添加后置处理器 + void addBeanPostProcessor(BeanPostProcessor beanPostProcessor); + int getBeanPostProcessorCount(); + + // 注册指定名称的Scope + void registerScope(String scopeName, Scope scope); + + // 返回所有的注册的scope的名称 + String[] getRegisteredScopeNames(); + + // 返回指定名称的已注册的scope + @Nullable + Scope getRegisteredScope(String scopeName); + + AccessControlContext getAccessControlContext(); + + // 从另外一个容器中拷贝配置,不包含具体的bean的定义 + void copyConfigurationFrom(ConfigurableBeanFactory otherFactory); + + // 为Bean注册别名 + void registerAlias(String beanName, String alias) throws BeanDefinitionStoreException; + // 解析别名 + void resolveAliases(StringValueResolver valueResolver); + + // 合并BeanDefinition,参考我之前的文章,《BeanDefinition下》 + BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; + + // 是否是一个FactoryBean + boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException; + + // 循环依赖相关,标志一个Bean是否在创建中 + void setCurrentlyInCreation(String beanName, boolean inCreation); + boolean isCurrentlyInCreation(String beanName); + + //处理bean依赖问题 + //注册一个依赖于指定bean的Bean + void registerDependentBean(String beanName, String dependentBeanName); + + // 返回所有指定的Bean从属于哪些Bean + String[] getDependentBeans(String beanName); + + // 返回指定名称的bean的所有依赖 + String[] getDependenciesForBean(String beanName); + + // 销毁Bean + void destroyBean(String beanName, Object beanInstance); + + // 先从域中移除,然后再销毁 + void destroyScopedBean(String beanName); + + // 销毁所有单例 + void destroySingletons(); + +} +``` + +可以看到这个接口继承了`HierarchicalBeanFactory`,并基于它扩展了非常多的方法。除了继承了`HierarchicalBeanFactory`,还继承了一个`SingletonBeanRegistry`,其接口定义如下: + +```java +public interface SingletonBeanRegistry { + //以指定的名字将给定Object注册到BeanFactory中。 + //此接口相当于直接把Bean注册,所以都是准备好了的Bean。(动态的向容器里直接放置一个Bean) + //什么BeanPostProcessor、InitializingBean、afterPropertiesSet等都不会被执行的,销毁的时候也不会收到destroy的信息 + void registerSingleton(String beanName, Object singletonObject); + + //以Object的形式返回指定名字的Bean,如果仅仅还是只有Bean定义信息,这里不会反悔 + // 需要注意的是:此方法不能直接通过别名获取Bean。若是别名,请通过BeanFactory的方法先获取到id + @Nullable + Object getSingleton(String beanName); + //是否包含此单例Bean(不支持通过别名查找) + boolean containsSingleton(String beanName); + // 得到容器内所有的单例Bean的名字们 + String[] getSingletonNames(); + int getSingletonCount(); + + // 获取当前这个注册表的互斥量(mutex),使用者通过该互斥量协同访问当前注册表 + Object getSingletonMutex(); +} +``` + +从上面可以看到,`SingletonBeanRegistry`主要是实现了对容器中单例池的管理。 + +### 5、ConfigurableListableBeanFactory + +```java +// 所有接口的集大成者,拥有上面所有接口的功能 +public interface ConfigurableListableBeanFactory + extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory { + // 自动装配的模式下,忽略这个类型的依赖 + void ignoreDependencyType(Class type); + + //自动装配的模式下,忽略这个接口类型的依赖 + void ignoreDependencyInterface(Class ifc); + + // 注入一个指定类型的依赖。这个方法设计的目的主要是为了让容器中的Bean能依赖一个不被容器管理的Bean + void registerResolvableDependency(Class dependencyType, @Nullable Object autowiredValue); + + // 判断指定名称的Bean能否被注入到指定的依赖中 + boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor) + throws NoSuchBeanDefinitionException; + + // 获取指定的BeanDefinition + BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException; + + // 获取包含了所有的Bean的名称的迭代器 + Iterator getBeanNamesIterator(); + + // 清理元数据的缓存 + void clearMetadataCache(); + + // 冻结所有的Bean配置 + void freezeConfiguration(); + boolean isConfigurationFrozen(); + + // 实例化当前所有的剩下的单实例 + void preInstantiateSingletons() throws BeansException; +} +``` + +### 6、AbstractBeanFactory + +```java +public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { + //... 实现了大部分的方法,其中最终的实现为getBean()/doGetBean()方法的实现,提供了模版。其实createBean抽象方法,还是子类去实现的 + //... isSingleton(String name) / isPrototype(String name) / containsBean(String name) 也能实现精准的判断了 + + // ===其中,它自己提供了三个抽象方法,子类必要去实现的=== + + // 效果同:ListableBeanFactory#containsBeanDefinition 实现类:DefaultListableBeanFactory + protected abstract boolean containsBeanDefinition(String beanName); + // 效果同:ConfigurableListableBeanFactory#getBeanDefinition 实现类:DefaultListableBeanFactory + protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException; + // 创建Bean的复杂逻辑,子类去实现。(子类:AbstractAutowireCapableBeanFactory) + protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) + throws BeanCreationException; + +} +``` + +### 7、AbstractAutowireCapableBeanFactory + +```java +public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory + implements AutowireCapableBeanFactory { + ...... + // 1.实现了AbstractBeanFactory中的createBean方法,能够创建一个完全的Bean + // 2.实现了AutowireCapableBeanFactory,能对Bean进行实例化,属性注入,已经细粒度的生命周期管理 +} +``` + +### 8、DefaultListableBeanFactory + +```java +public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory + implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { + ..... + // 没什么好说的了,最牛逼的一个BeanFactory,拥有上面的一切功能,额外的它实现了BeanDefinitionRegistry接口,具备注册管理BeanDefinition的功能 +} +``` + +# ApplicationContext体系汇总 + +ApplicationContext整体可以分为两个体系,一个就是web体系,另外一个就是非web体系。 + +## 非web体系 + +![](./image/2020032301.png) + +### 1、ConfigurableApplicationContext + +ApplicationContext接口中的方法比较简单,之前我们也一一分析它继承的接口以及它所具有的功能。并且ApplicationContext接口的方法都是只读的,不能对当前的容器做任何改变。而ConfigurableApplicationContext接口在ApplicationContext的基础上增加了很多进行配置的方法,比如添加事件监听器,添加后置处理器等等。 + +```java +public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable { + // 配置路径的分隔符 + String CONFIG_LOCATION_DELIMITERS = ",; \t\n"; + String CONVERSION_SERVICE_BEAN_NAME = "conversionService"; + String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver"; + String ENVIRONMENT_BEAN_NAME = "environment"; + String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties"; + String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment"; + + //设置此应用程序上下文的唯一ID。 + void setId(String id); + + //设置父容器,设置后不能再改了 + void setParent(@Nullable ApplicationContext parent); + + //设置environment 此处为ConfigurableEnvironment 也是可以配置的应用上下文 + void setEnvironment(ConfigurableEnvironment environment); + + // 此处修改父类返回值为ConfigurableEnvironment + @Override + ConfigurableEnvironment getEnvironment(); + + //添加一个新的BeanFactoryPostProcessor(refresh()的时候会调用的) + void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor); + + // 添加一个事件监听器 + void addApplicationListener(ApplicationListener listener); + + // 注册协议处理器 允许处理额外的资源协议 + void addProtocolResolver(ProtocolResolver resolver); + + //加载或刷新配置的持久表示 最最最重要的一个方法 + //表示可以是xml、可以是注解、可以是外部资源文件等等。。。。 + // 这个方法执行完成后,所有的单例Bean都已经被实例化,Bean工厂肯定也就被创建好了 + void refresh() throws BeansException, IllegalStateException; + + //JVM运行时注册一个关闭挂钩,在关闭JVM时关闭此上下文,除非此时已经关闭 + void registerShutdownHook(); + + //关闭此应用程序上下文,释放实现可能持有的所有资源和锁 包括一些销毁、释放资源操作 + @Override + void close(); + + //标识上下文是否激活 refresh()后就会激活 + boolean isActive(); + + // 返回此上下文内部的Bean工厂,可以用来访问底层工厂的特定功能。通过此工厂可以设置和验证所需的属性、自定义转换服务 + // 备注:父类方法为获得AutowireCapableBeanFactory接口,而此处的ConfigurableListableBeanFactory可配置、可列出Bean的工厂是它的子类 + ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException; +} +``` + +### 2、AbstractApplicationContext + +```java +public abstract class AbstractApplicationContext extends DefaultResourceLoader + implements ConfigurableApplicationContext { + // 这个类实现了ConfigurableApplicationContext,具备了上面接口大部分功能, + // 但是他没有实现getBeanFactory()方法,这个方法留待子类实现,所以它自己没有实际的管理Bean的能力,只是定义了一系列规范 +} +``` + +### 3、AbstractRefreshableApplicationContext + +```java +public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext { + + // 碰到重复的Bean时,是否允许覆盖原先的BeanDefinition + @Nullable + private Boolean allowBeanDefinitionOverriding; + + // 是否允许循环引用 + @Nullable + private Boolean allowCircularReferences; + + // 默认持有一个DefaultListableBeanFactory + @Nullable + private DefaultListableBeanFactory beanFactory; + + // 对内部工厂进行操作时所采用的锁 + private final Object beanFactoryMonitor = new Object(); + + public AbstractRefreshableApplicationContext() { + } + + public AbstractRefreshableApplicationContext(@Nullable ApplicationContext parent) { + super(parent); + } + + public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) { + this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding; + } + + public void setAllowCircularReferences(boolean allowCircularReferences) { + this.allowCircularReferences = allowCircularReferences; + } + + // 刷新Bean工厂,如果当前上下文中已经存在一个容器的话,会先销毁容器中的所有Bean,然后关闭Bean工厂 + // 之后在重新创建一个DefaultListableBeanFactory + @Override + protected final void refreshBeanFactory() throws BeansException { + if (hasBeanFactory()) { + destroyBeans(); + closeBeanFactory(); + } + try { + DefaultListableBeanFactory beanFactory = createBeanFactory(); + beanFactory.setSerializationId(getId()); + customizeBeanFactory(beanFactory); + loadBeanDefinitions(beanFactory); + synchronized (this.beanFactoryMonitor) { + this.beanFactory = beanFactory; + } + } + catch (IOException ex) { + throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); + } + } + + @Override + protected void cancelRefresh(BeansException ex) { + synchronized (this.beanFactoryMonitor) { + if (this.beanFactory != null) { + this.beanFactory.setSerializationId(null); + } + } + super.cancelRefresh(ex); + } + + @Override + protected final void closeBeanFactory() { + synchronized (this.beanFactoryMonitor) { + if (this.beanFactory != null) { + this.beanFactory.setSerializationId(null); + this.beanFactory = null; + } + } + } + + + protected final boolean hasBeanFactory() { + synchronized (this.beanFactoryMonitor) { + return (this.beanFactory != null); + } + } + + // 复写了getBeanFactory,默认返回的是通过createBeanFactory创建的一个DefaultListableBeanFactory + @Override + public final ConfigurableListableBeanFactory getBeanFactory() { + synchronized (this.beanFactoryMonitor) { + if (this.beanFactory == null) { + throw new IllegalStateException("BeanFactory not initialized or already closed - " + + "call 'refresh' before accessing beans via the ApplicationContext"); + } + return this.beanFactory; + } + } + + protected DefaultListableBeanFactory createBeanFactory() { + return new DefaultListableBeanFactory(getInternalParentBeanFactory()); + } + + ....... + // 提供了一个抽象的加载BeanDefinition的方法,这个方法没有具体实现,不同的配置方式需要进行不同的实现, + // 到这里,配置的方式不能确定,既可能是以XML的方式,也可能是以java config的方式 + // 另外配置文件的加载方式也不能确定 + protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) + throws BeansException, IOException; + +} +``` + +可以看到这个类可以进一步对上下文进行配置,例如进行是否开启循环引用,是否允许进行BeanDefinition的覆盖等等。另外它所提供的一个重要的功能就是使容器具备刷新的功能,换言之凡是需要刷新功能的容器都需要继承这个类。 + +### 4、AbstractRefreshableConfigApplicationContext + +```java +public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext + implements BeanNameAware, InitializingBean { + // 这个变量代表了配置文件的路径,到这里配置的信息相比于其父类AbstractRefreshableApplicationContext做了进一步的明确,但是仍然不能确定是XML还是javaconfig,只能确定配置在configLocations里面 + @Nullable + private String[] configLocations; + ..... +} + +``` + +### 5、AbstractXmlApplicationContext + +```java +public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext { + + + // 是否进行XML类型的校验,默认为true + private boolean validating = true; + + // ..... + + @Override + protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { + + XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); + + beanDefinitionReader.setEnvironment(this.getEnvironment()); + beanDefinitionReader.setResourceLoader(this); + beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); + + initBeanDefinitionReader(beanDefinitionReader); + loadBeanDefinitions(beanDefinitionReader); + } + + protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) { + reader.setValidating(this.validating); + } + + protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { + Resource[] configResources = getConfigResources(); + if (configResources != null) { + reader.loadBeanDefinitions(configResources); + } + String[] configLocations = getConfigLocations(); + if (configLocations != null) { + reader.loadBeanDefinitions(configLocations); + } + } + + @Nullable + protected Resource[] getConfigResources() { + return null; + } + +} +``` + +可以看到这个类进一步对配置的加载做了进一步的明确,首先明确了配置的类型为XML,第二明确了要通过getConfigResources方法来加载需要的配置资源,但是并没有对这个方法做具体实现,因为对于Resource的定义,可能是通过classpath的方式,也可能是通过URL的方式,基于此又多了两个子类 + +1. `ClassPathXmlApplicationContext`,从classPath下加载配置文件 +2. `FileSystemXmlApplicationContext`,基于URL的格式加载配置文件 + +### 6、GenericApplicationContext + +这个类已经不是抽象类了,我们可以直接使用它。但是这个类有一个很大的缺点,它不能读取配置,需要我们手动去指定读取的方式及位置。其实从上文中的分析我们可以看出,从AbstractApplicationContext到AbstractXmlApplicationContext一步步明确了配置的加载方式,Spring通过这种类的继承将配置的加载分了很多层,我们可以从AbstractXmlApplicationContext的子类开始从任意以及进行扩展。 + +而GenericApplicationContext只实现了上下文的基本功能,并没有对配置做任何约束,所以在使用它的我们需要手动往其中注册BeanDefinition。这样虽然很灵活,但是也很麻烦,如果我们使用GenericApplicationContext可能需要进行下面这样的操作 + +```java +GenericApplicationContext ctx = new GenericApplicationContext(); +//使用XmlBeanDefinitionReader,这个地方我们甚至可以自己定义解析器,不使用Spring容器内部的 +XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx); +//加载ClassPathResource +xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml")); +PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx); +propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties")); +//调用Refresh方法 +ctx.refresh(); + +//和其他ApplicationContext方法一样的使用方式 +MyBean myBean = (MyBean) ctx.getBean("myBean"); +``` + +平常开发中我们基本用不到这个东西 + +### 7、AnnotationConfigApplicationContext + +```java +public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry { + + private final AnnotatedBeanDefinitionReader reader; + + private final ClassPathBeanDefinitionScanner scanner; + + ....... +} +``` + +通过`AnnotatedBeanDefinitionReader`注册配置类,用`ClassPathBeanDefinitionScanner`扫描配置类上申明的路径,得到所有的BeanDefinition。然后其余的没啥了。这个我们经常使用,因为不用再需要xml文件了,使用`@Configuration`配置类即可,更加的方便。 + +## web体系 + +![](./image/2020032302.png) + +### 1、WebApplicationContext + +```java +public interface WebApplicationContext extends ApplicationContext { + + String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; + + String SCOPE_REQUEST = "request"; + + String SCOPE_SESSION = "session"; + + String SCOPE_APPLICATION = "application"; + + String SERVLET_CONTEXT_BEAN_NAME = "servletContext"; + + String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters"; + + String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes"; + + @Nullable + ServletContext getServletContext(); + +} +``` + +定义了一堆常量,以及一个方法,约束了所有的web容器必须能返回一个Servlet的上下文(ServletContext) + +### 2、ConfigurableWebApplicationContext + +```java +public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext { + + String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":"; + + String SERVLET_CONFIG_BEAN_NAME = "servletConfig"; + + void setServletContext(@Nullable ServletContext servletContext); + + void setServletConfig(@Nullable ServletConfig servletConfig); + + @Nullable + ServletConfig getServletConfig(); + + // 设置及获取当前上下文的命名空间,命名空间用于区分不同的web容器的配置,在查找配置时会根据命名空间查找 + // 默认不进行命名空间配置,配置会在/WEB-INF/applicationContext.xml下查找 + // 如果配置了,会在/WEB-INF+"namespace"+/applicationContext.xml下查找 + // 根容器没有Namespace + void setNamespace(@Nullable String namespace); + @Nullable + String getNamespace(); + + void setConfigLocation(String configLocation); + + void setConfigLocations(String... configLocations); + + @Nullable + String[] getConfigLocations(); + +} +``` + +可以看到使用这个类能指定上下文配置加载的位置 + +### 3、AbstractRefreshableWebApplicationContext + +```java +public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext + implements ConfigurableWebApplicationContext, ThemeSource { + ....... +} +``` + +首先可以看到这个类继承了`AbstractRefreshableConfigApplicationContext`,代表它需要从指定的位置加载配置,其次它首先了ConfigurableWebApplicationContext,所以它具有web容器的属性。 + +### 4、XmlWebApplicationContext + +```java +public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { + + public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"; + + public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/"; + + + public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml"; + + // ....... + @Override + protected String[] getDefaultConfigLocations() { + if (getNamespace() != null) { + return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX}; + } + else { + return new String[] {DEFAULT_CONFIG_LOCATION}; + } + } + +} +``` + +进一步指定了配置文件的加载形式 + +1. 需要加载XML类型配置 +2. 对于根容器,加载路径为`/WEB-INF/applicationContext.xml` +3. 对于子容器,加载路径为`/WEB-INF/+'namespace'+.xml`,比如常用的dispatchServlet.xml + +### 5、AnnotationConfigWebApplicationContext + +指定了以注解的方式配置web容器 + +### 6、GenericWebApplicationContext + +类比`GenericApplicationContext`,没有指定配置相关的任何东西,全手动 + +# 总结 + +从上面我们可以看到,整个一套体系下来不可谓不庞大,Spring在单一职责可以说做到了极致。不论是按功能分,比如`HierarchicalBeanFactory`,`ListableBeanFactory`,`AutowireCapableBeanFactory`就是按照不同功能拆分,或者是按照功能实现的层级划分,比如上面说到的配置文件的加载机制。对类之间的关系进行明确的分层,代表了整个体系会具备非常强大的扩展性,我们可以在每一步进行自己的扩展。这是让Spring能组件化开发,可插拔,变得如此优秀、普适的重要原因 + +到此,关于ApplicationContext相关的内容终于也可以告一段落了,代表着IOC已经结束了,粗略看了下官网,接下来还剩数据绑定,数据校验,类型转换以及AOP,任重而道远,加油吧!~ \ No newline at end of file diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020031801.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020031801.png" new file mode 100644 index 0000000000000000000000000000000000000000..00de4cf98a1b58bcc280eba79181415ef6bcb655 Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020031801.png" differ diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020031901.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020031901.png" new file mode 100644 index 0000000000000000000000000000000000000000..a1121861c650dca202ecb86faa470e1effef3bb2 Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020031901.png" differ diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020032301.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020032301.png" new file mode 100644 index 0000000000000000000000000000000000000000..2b011a4fa76413b5985398532fd785a1bfb08b58 Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020032301.png" differ diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020032302.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020032302.png" new file mode 100644 index 0000000000000000000000000000000000000000..be3e4ecc72c31b2fa8476c0fb973f0f9c072d8ec Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\344\270\211\357\274\211ApplicationContext\350\257\246\350\247\243\357\274\210\344\270\213\357\274\211/image/2020032302.png" differ diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper\345\217\212\347\261\273\345\236\213\350\275\254\346\215\242.md" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper\345\217\212\347\261\273\345\236\213\350\275\254\346\215\242.md" new file mode 100644 index 0000000000000000000000000000000000000000..543a86af29c3c22a39f4faf04b98d709e8428a3e --- /dev/null +++ "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper\345\217\212\347\261\273\345\236\213\350\275\254\346\215\242.md" @@ -0,0 +1,747 @@ +Spring官网阅读(十四)Spring中的BeanWrapper及类型转换 + +> BeanWrapper是Spring中一个很重要的接口,Spring在通过配置信息创建对象时,第一步首先就是创建一个BeanWrapper。这篇文章我们就分析下这个接口,本文内容主要对应官网中的`3.3`及`3.4`小结 + +# 接口定义 + +```java +// Spring低级JavaBeans基础设施的中央接口。通常来说并不直接使用BeanWrapper,而是借助BeanFactory或者DataBinder来一起使用,BeanWrapper对Spring中的Bean做了包装,为的是更加方便的操作Bean中的属性 +public interface BeanWrapper extends ConfigurablePropertyAccessor { + + void setAutoGrowCollectionLimit(int autoGrowCollectionLimit); + int getAutoGrowCollectionLimit(); + + // 获取包装的Bean + Object getWrappedInstance(); + + // 获取包装的Bean的class + Class getWrappedClass(); + + // 获取所有属性的属性描述符 + PropertyDescriptor[] getPropertyDescriptors(); + + // 获取指定属性的属性描述符 + PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException; + +} +``` + +这里需要解释一个概念,什么是属性描述符? + +> PropertyDescriptor:属性描述符,能够描述javaBean中的属性,通过属性描述符我们能知道这个属性的类型,获取到操纵属性的方法(getter/setter) + +# 继承关系 + +![](./image/2020032401.png) + + + +BeanWrapper的子类只有一个:`BeanWrapperImpl`,它继承了`ConfigurablePropertyAccessor`,这个接口的主要功能是进行属性访问,同时它又有三个父接口,接下来我们一一分析他们的功能。 + +# 接口功能 + +## 1、PropertyEditorRegistry(属性编辑器注册器) + +### 接口定义 + +```java +// 这个接口的功能很简单,就是用来注入属性编辑器(PropertyEditor),那么什么是PropertyEditor呢? +public interface PropertyEditorRegistry { + + void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor); + + void registerCustomEditor(@Nullable Class requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor); + + @Nullable + PropertyEditor findCustomEditor(@Nullable Class requiredType, @Nullable String propertyPath); + +} +``` + +### PropertyEditor + +#### 概念 + +> **PropertyEditor是JavaBean规范定义的接口**,这是`java.beans`中一个接口,**其设计的意图是图形化编程上**,方便对象与String之间的转换工作,而Spring将其扩展,方便各种对象与String之间的转换工作。 + +#### Spring中对PropertyEditor使用的实例 + +1. 我们在通过XML的方式对Spring中的Bean进行配置时,不管Bean中的属性是何种类型,都是直接通过字面值来设置Bean中的属性。那么是什么在这其中做转换呢?这里用到的就是PropertyEditor +2. SpringMVC在解析请求参数时,也是使用的PropertyEditor + +#### Spring内置的PropertyEditor + +![](./image/2020032402.png) + +## 2、PropertyAccessor(属性访问器) + +### 接口定义 + +```java +public interface PropertyAccessor { + + // 嵌套属性的分隔符,比如"foo.bar"将会调用getFoo().getBar()两个方法 + String NESTED_PROPERTY_SEPARATOR = "."; + char NESTED_PROPERTY_SEPARATOR_CHAR = '.'; + + // 代表角标index的符号 如person.addresses[0] 这样就可以把值放进集合/数组/Map里了 + String PROPERTY_KEY_PREFIX = "["; + char PROPERTY_KEY_PREFIX_CHAR = '['; + String PROPERTY_KEY_SUFFIX = "]"; + char PROPERTY_KEY_SUFFIX_CHAR = ']'; + + // 该属性是否可读/可写,不存在则返回false + boolean isReadableProperty(String propertyName); + boolean isWritableProperty(String propertyName); + + // 获取/设置属性的方法,基本见名知意 + @Nullable + Class getPropertyType(String propertyName) throws BeansException; + @Nullable + TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException; + @Nullable + Object getPropertyValue(String propertyName) throws BeansException; + void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException; + void setPropertyValue(PropertyValue pv) throws BeansException; + void setPropertyValues(Map map) throws BeansException; + void setPropertyValues(PropertyValues pvs) throws BeansException; + void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) + throws BeansException; + void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) + throws BeansException; + +} +``` + +这里需要解释一个概念,什么是PropertyValue? + +> 当设置属性值时,少不了两样东西: +> +> 1. 属性访问表达式:如`listMap[0][0]` +> 2. 属性值: +> +> `ProperyValue`对象就是用来封装这些信息的。如果某个值要给赋值给bean属性,Spring都会把这个值包装成`ProperyValue`对象。 + +## 3、TypeConverter(类型转换器) + +### 接口定义 + +```java +// 定义了进行类型转换时的一些规范,就像名字定义的那样,主要用来做类型转换 +public interface TypeConverter { + + // 将指定的值转换成指定的类型 + @Nullable + T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType) throws TypeMismatchException; + + // 相对于上面这个方法下面这个三种方法能处理转换过程中的泛型 + @Nullable + T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, + @Nullable MethodParameter methodParam) throws TypeMismatchException; + @Nullable + T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, @Nullable Field field) + throws TypeMismatchException; + default T convertIfNecessary(@Nullable Object value, @Nullable Class requiredType, + @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException { + + throw new UnsupportedOperationException("TypeDescriptor resolution not supported"); + } + +} +``` + +## 4、ConfigurablePropertyAccessor + +```java +public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter { + // ConversionService:进行转换的业务类,转换系统的入口 + void setConversionService(@Nullable ConversionService conversionService); + @Nullable + ConversionService getConversionService(); + + // 进行属性编辑是是否返回旧的值 + void setExtractOldValueForEditor(boolean extractOldValueForEditor); + boolean isExtractOldValueForEditor(); + + // 当设置(dog.name)这种嵌套属性的情况下,如果dog属性为null是否会报错 + // 为true的话不会,为false会抛出NullValueInNestedPathException + void setAutoGrowNestedPaths(boolean autoGrowNestedPaths); + boolean isAutoGrowNestedPaths(); + +} +``` + +**从上面可以看到,BeanWrapper接口自身对Bean进行了一层包装**。**另外它的几个通过间接继承了几个接口,所以它还能对Bean中的属性进行操作。PropertyAccessor赋予了BeanWrapper对属性进行访问及设置的能力,在对Bean中属性进行设置时,不可避免的需要对类型进行转换,而恰好PropertyEditorRegistry,TypeConverter就提供了类型转换的统一约束。** + +在了解了接口之后,我们接下来看看它的唯一实现类`BeanWrapperImpl` + +# 唯一子类(BeanWrapperImpl) + +## 继承关系 + +![](./image/2020032403.png) + +结合我们之前对接口的分析以及上面这张UML图,我们可以知道BeanWrapperImpl主要实现了一下几个功能 + +1. 对Bean进行包装 +2. 对Bean的属性进行访问以及设置 +3. 在操作属性的过程中,必然涉及到类型转换,所以还有类型转换的功能 + +## Java中的内置机制 + +> 在详细了解BeanWrapperImpl前,必须要了解java中的一个机制:**内省** + +### 核心概念 + +​ 首先可以先了解下JavaBean的概念:一种特殊的类,主要用于传递数据信息。这种类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。如果在两个模块之间传递信息,可以将信息封装进JavaBean中,这种对象称为“值对象”(Value Object),或“VO”。 + +因此JavaBean都有如下几个特征: + +1. 属性都是私有的; +2. 有无参的public构造方法; +3. 对私有属性根据需要提供公有的getXxx方法以及setXxx方法; +4. getters必须有返回值没有方法参数;setter值没有返回值,有方法参数; + +符合这些特征的类,被称为JavaBean;JDK中提供了一套API用来访问某个属性的getter/setter方法,这些API存放在java.beans中,这就是内省(Introspector)。 + +**内省和反射的区别:** + +> 反射:Java反射机制是在运行中,对任意一个类,能够获取得到这个类的所有属性和方法;它针对的是任意类 +> 内省(Introspector):是Java语言对JavaBean类属性、事件的处理方法 + +1. 反射可以操作各种类的属性,而内省只是通过反射来操作JavaBean的属性 + +2. 内省设置属性值肯定会调用setter方法,反射可以不用(反射可直接操作属性Field) + +3. 反射就像照镜子,然后能看到.class的所有,是客观的事实。内省更像主观的判断:比如**看到getName(),内省就会认为这个类中有name字段,但事实上并不一定会有name**;通过内省可以获取bean的getter/setter + +### 使用示例 + +```java +public class Main { + public static void main(String[] args) throws Exception{ + BeanInfo beanInfo = Introspector.getBeanInfo(People.class); + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { + System.out.print(propertyDescriptor.getName()+" "); + } + } + // 程序输出:age class name + // 为什么会输出class呢?前文中有提到,“看到getName(),内省就会认为这个类中有name字段,但事实上并不一定会有name”,我们知道每个对象都会有getClass方法,所以使用内省时,默认就认为它具有class这个字段 +} + +class People{ + String name; + + int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} +``` + +## 源码分析 + +```java +// 这个类我只保留一些关键的代码,其余的琐碎代码都不看了 +public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper { + // 缓存内省的结果,BeanWrapperImpl就是通过这个对象来完成对包装的Bean的属性的控制 + @Nullable + private CachedIntrospectionResults cachedIntrospectionResults; + ...... + public void setBeanInstance(Object object) { + this.wrappedObject = object; + this.rootObject = object; + // 实际进行类型转换的对象:typeConverterDelegate + this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); + setIntrospectionClass(object.getClass()); + } + ...... + // 最终调用的就是CachedIntrospectionResults的forClass方法进行内省并缓存,底层调用的就是java的内省机制 + private CachedIntrospectionResults getCachedIntrospectionResults() { + if (this.cachedIntrospectionResults == null) { + this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass()); + } + return this.cachedIntrospectionResults; + } + ....... + // 最终进行类型转换的方法 + private Object convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, + @Nullable Object newValue, @Nullable Class requiredType, @Nullable TypeDescriptor td) + throws TypeMismatchException { + + Assert.state(this.typeConverterDelegate != null, "No TypeConverterDelegate"); + try { + // 可以看到,最后就是调用typeConverterDelegate来进行类型转换 + return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td); + } + ...... + } +} +``` + +## 父类作用分析 + +对于接口,我们已经分析过了,这里就不再赘述了,我们重点看下BeanWrapperImpl继承的几个父类 + +### PropertyEditorRegistrySupport + +这个类最大的作用在于管理`PropertyEditor`,添加了很多的默认的`PropertyEditor`。在`PropertyEditorRegistry`的基础上做了进一步的扩展,提供的还是属性编辑器注册的功能。 + +### TypeConverterSupport + +```java +public abstract class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter { + @Nullable + TypeConverterDelegate typeConverterDelegate; +...... +} +``` + +这个接口实现了TypeConverter,所以它具有类型转换的能力,而它这种能力的实现,依赖于它所持有的一个TypeConverterDelegate。 + +### AbstractPropertyAccessor + +```java +public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor { + // 省略部分代码...... + @Override + public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid) + throws BeansException { + + List propertyAccessExceptions = null; + List propertyValues = (pvs instanceof MutablePropertyValues ? + ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues())); + for (PropertyValue pv : propertyValues) { + try { + setPropertyValue(pv); + } + // .... + } + } + + @Override + @Nullable + public abstract Object getPropertyValue(String propertyName) throws BeansException; + + @Override + public abstract void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException; + +} + +``` + +核心的代码其实就是这些,这个类继承了`TypeConverterSupport`,所以它具备了类型转换的能力。同时它也是一个属性访问器,但是它只是实现了批量设置属性的方法,真正的`setPropertyValue`还是留待子类实现。可以看到,到这个类为止,还没有将属性的设置跟类型转换的能力结合起来。 + +### AbstractNestablePropertyAccessor + +这个类开始真正的将属性访问跟类型转换结合到一起,它真正的实现了`setPropertyValue`,并在设置属性的时候会进行类型的转换,具体代码就不看了,非常繁杂,但是整体不难。 + +上面我们多次提到了类型转换,但是还没有真正看到类型转换的逻辑,因为上面类最终将类型转换的逻辑委托给了`TypeConverterDelegate`。接下来我们看看,类型转换到底是怎么完成。 + +# 类型转换 + +## TypeConverterDelegate + +这个类我们只看一个核心方法,如下: + +```java +class TypeConverterDelegate { + + private final PropertyEditorRegistrySupport propertyEditorRegistry; + + @Nullable + private final Object targetObject; + + public T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue, + @Nullable Class requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException { + + // 查看是否为当前这个类型配置了定制的PropertyEditor + PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName); + + ConversionFailedException conversionAttemptEx = null; + + // 获取当前容器中的类型转换业务类 + ConversionService conversionService = this.propertyEditorRegistry.getConversionService(); + + // 在这里可以看出,Spring底层在进行类型转换时有两套机制 + // 1.首选的是采用PropertyEditor + // 2.在没有配置PropertyEditor的情况下,会采用conversionService + if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) { + TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue); + if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) { + try { + // 通过conversionService进行类型转换 + return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor); + } + catch (ConversionFailedException ex) { + // fallback to default conversion logic below + conversionAttemptEx = ex; + } + } + } + + Object convertedValue = newValue; + + // 配置了定制的属性编辑器,采用PropertyEditor进行属性转换 + if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { + if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && + convertedValue instanceof String) { + TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor(); + if (elementTypeDesc != null) { + Class elementType = elementTypeDesc.getType(); + if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) { + convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); + } + } + } + if (editor == null) { + // 没有配置定制的属性编辑器,采用默认的属性编辑器 + editor = findDefaultEditor(requiredType); + } + // 采用属性编辑器进行转换,需要注意的是,默认情况下PropertyEditor只会对String类型的值进行类型转换 + convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor); + } + // ..... + return (T) convertedValue; + } + +} +``` + +从上面的代码中我们可以知道,Spring在实现类型转换时,有两套机制,第一套机制依赖于PropertyEditor,第二套机制依赖于ConversionService。关于属性编辑器PropertyEditor我们之前已经介绍过了,主要进行的是String到Object的转换,正因为如此,属性编辑器进行类型转换有很大的局限性,所以Spring又推出了一套ConversionService的体系。 + +## ConversionService体系 + +### 1、Converter + +##### 接口定义 + +```java +package org.springframework.core.convert.converter; + +// 将一个S类型的数据转换成T类型 +public interface Converter { + + T convert(S source); +} +``` + +这个接口只能进行一对一的转换,S->T + +### 2、ConverterFactory + +##### 接口定义 + +```java +public interface ConverterFactory { + + Converter getConverter(Class targetType); +} +``` + +利用这个转换工厂,我们可以进行一对多的转换,以Spring内置的一个转换器为例: + +```java +final class StringToEnumConverterFactory implements ConverterFactory { + + @Override + public Converter getConverter(Class targetType) { + return new StringToEnum(ConversionUtils.getEnumType(targetType)); + } + + + private class StringToEnum implements Converter { + + private final Class enumType; + + public StringToEnum(Class enumType) { + this.enumType = enumType; + } + + @Override + public T convert(String source) { + if (source.isEmpty()) { + // It's an empty enum identifier: reset the enum value to null. + return null; + } + return (T) Enum.valueOf(this.enumType, source.trim()); + } + } + +} +``` + +通过传入不同的枚举类型,我们可以从这个工厂中获取到不同的转换器,并把对应的String类型的参数转换成对应的枚举类型数据。 + +可以看到,通过ConverterFactory,我们能实现一对多的类型转换S->(T extends R) + +### 3、GenericConverter + +#### 接口定义 + +```java +public interface GenericConverter { + + // 获取能够转换的ConvertiblePair的集合,这个对象就是一组可以进行转换的类型 + @Nullable + Set getConvertibleTypes(); + + // 根据源数据类型转换成目标类型数据 + @Nullable + Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType); + + final class ConvertiblePair { + // 源数据类型 + private final Class sourceType; + // 目标数据类型 + private final Class targetType; + + // .....省略部分代码 + } + +} +``` + +相比于前面的Converter以及ConverterFactory,这个接口就更加牛逼了,使用它能完成多对多的转换。因为它内部保存了一个能够进行转换的ConvertiblePair的集合,每个ConvertiblePair代表一组能进行转换的数据类型。同时,这个接口相比我们前面介绍的两个接口,更加的复杂,所以一般情况也不推荐使用这个接口,没有非常必要的话,最好是使用上面两种 + +一般GenericConverter会与ConditionalGenericConverter配合使用,其接口定义如下: + +```java +public interface ConditionalConverter { + // 判断是否需要对目标类型转换到原类型,返回true的话代表要执行转换,否则不执行转换 + boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType); +} + +// 结合了上面两个接口 +public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter { +} +``` + +我们来看下Spring内部的一个实际使用的例子: + +```java +final class StringToCollectionConverter implements ConditionalGenericConverter { + + private final ConversionService conversionService; + + @Override + public Set getConvertibleTypes() { + return Collections.singleton(new ConvertiblePair(String.class, Collection.class)); + } + + @Override + public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { + return (targetType.getElementTypeDescriptor() == null || + // 根据conversionService来判断是否需要执行转换 + this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor())); + } + + @Override + @Nullable + public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + // 这里会借助conversionService来执行转换 + } + +} +``` + +可以看到,最终的实现还是借助了ConversionService,那么ConversionService到底是啥呢? + +### 4、ConversionService + +##### 接口定义 + +```java +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); + @Nullable + Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType); + +} +``` + +#### UML类图 + +![](./image/2020032501.png) + +一般来说,实现了`ConversionService`跟`ConverterRegistry`会结合使用,对于这种`xxxRegistry`我相信大家猜都能猜出来它是干什么的了,代码如下: + +##### ConverterRegistry + +```java + +// 就是在添加Converter或者ConverterFactory +public interface ConverterRegistry { + + void addConverter(Converter converter); + + void addConverter(Class sourceType, Class targetType, Converter converter); + + void addConverter(GenericConverter converter); + + void addConverterFactory(ConverterFactory factory); + + void removeConvertible(Class sourceType, Class targetType); + +} +``` + +##### ConfigurableConversionService + +```java +// 单纯的整合了ConversionService以及ConverterRegistry的功能 +public interface ConfigurableConversionService extends ConversionService, ConverterRegistry { + +} +``` + +##### GenericConversionService + +这个类已经是一个具体的实现类,可以直接使用,但是我们一般不会直接使用它,而是使用它的子类`DefaultConversionService`,因为子类提供了很多默认的转换器。 + +##### DefaultConversionService + +```java +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)); + ...... + } + + public static void addCollectionConverters(ConverterRegistry converterRegistry) { + ...... + } + + private static void addScalarConverters(ConverterRegistry converterRegistry) { + converterRegistry.addConverterFactory(new NumberToNumberConverterFactory()); + ...... + } + +} + +``` + +相比其父类`GenericConversionService`,这个子类默认添加了很多的转换器,这样可以极大的方便我们进行开发,所以一般情况下我们都会使用这个类。 + +### 如何配置ConversionService + +讲了这么多,那么如何往容器中配置一个ConversionService呢?我们需要借助Spring提供的一个`ConversionServiceFactoryBean`。其代码如下: + +```java +public class ConversionServiceFactoryBean implements FactoryBean, InitializingBean { + + @Nullable + private Set converters; + + @Nullable + private GenericConversionService conversionService; + + public void setConverters(Set converters) { + this.converters = converters; + } + + @Override + public void afterPropertiesSet() { + this.conversionService = createConversionService(); + ConversionServiceFactory.registerConverters(this.converters, this.conversionService); + } + + protected GenericConversionService createConversionService() { + return new DefaultConversionService(); + } + + @Override + @Nullable + public ConversionService getObject() { + return this.conversionService; + } + + @Override + public Class getObjectType() { + return GenericConversionService.class; + } + + @Override + public boolean isSingleton() { + return true; + } + +} + +``` + +这个类的实现逻辑很简单,`ConversionServiceFactoryBean`创建完成后,在进行初始化时调用`afterPropertiesSet`方法,创建一个`DefaultConversionService`,然后将提供的`converters`全部注册到这个`DefaultConversionService`中。所以我们进行如下的配置就行了 + +```xml + + + + # 提供自己的converter,可以覆盖默认的配置 + + + + +``` + +# 总结 + +这篇文章中,我们学习了BeanWrapper,知道一个BeanWrapper其实就是一个Bean的包装器,它对Bean包装的目的是为了能操纵Bean中的属性,所以它同时需要具备获取以及设置Bean中的属性能力,所以它也必须是一个属性访问器(PropertyAccessor),另外为了将各种不同类型的配置数据绑定到Bean的属性上,那么它还得具备属性转换的能力,因为它还得是一个类型转换器(TypeConverter)。 + +通过上面的分析,我们知道Spring中将类型转换的功能都委托给了一个`TypeConverterDelegate`,这个委托类在进行类型转换时会有两套方案: + +1. PropertyEditor,这是Spring最初提供的方案,扩展了java中的PropertyEditor(java原先提供这个接口的目的更多是为了进行图形化编程) +2. ConversionService,Spring后来提供的一个进行类型转换的体系,用来取代PropertyEditor,因为PropertyEditor有很大的局限性,只能进行String->Object的转换。 + +画图如下: + +![](./image/2020032502.png) + + + diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032401.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032401.png" new file mode 100644 index 0000000000000000000000000000000000000000..c11350e0796e98f2c38d01d7488c85848b2ed5cd Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032401.png" differ diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032402.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032402.png" new file mode 100644 index 0000000000000000000000000000000000000000..987c71c6d514f3b68c0cfac175259a7ab162bae3 Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032402.png" differ diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032403.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032403.png" new file mode 100644 index 0000000000000000000000000000000000000000..f61efca30b18c95c82a8a420994242bbf4a9a23c Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032403.png" differ diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032501.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032501.png" new file mode 100644 index 0000000000000000000000000000000000000000..742dd1c8de5953fe4a21744da94fcb6666662aab Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032501.png" differ diff --git "a/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032502.png" "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032502.png" new file mode 100644 index 0000000000000000000000000000000000000000..99ffeaa83e99481469d09eeeb7894d175efe886e Binary files /dev/null and "b/docs/Spring/Spring\345\256\230\347\275\221\345\255\246\344\271\240\347\254\224\350\256\260/Spring\345\256\230\347\275\221\351\230\205\350\257\273\357\274\210\345\215\201\345\233\233\357\274\211Spring\344\270\255\347\232\204BeanWrapper/image/2020032502.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\200\344\271\210\346\230\257ObjectFactory\357\274\237\344\273\200\344\271\210\346\230\257ObjectProvider\357\274\237/image/2020031901.png" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\200\344\271\210\346\230\257ObjectFactory\357\274\237\344\273\200\344\271\210\346\230\257ObjectProvider\357\274\237/image/2020031901.png" new file mode 100644 index 0000000000000000000000000000000000000000..48d4a1db72056436af7ee686fb09ebf1b762f491 Binary files /dev/null and "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\200\344\271\210\346\230\257ObjectFactory\357\274\237\344\273\200\344\271\210\346\230\257ObjectProvider\357\274\237/image/2020031901.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\200\344\271\210\346\230\257ObjectFactory\357\274\237\344\273\200\344\271\210\346\230\257ObjectProvider\357\274\237/\344\273\200\344\271\210\346\230\257ObjectFactory\357\274\237\344\273\200\344\271\210\346\230\257ObjectProvider\357\274\237.md" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\200\344\271\210\346\230\257ObjectFactory\357\274\237\344\273\200\344\271\210\346\230\257ObjectProvider\357\274\237/\344\273\200\344\271\210\346\230\257ObjectFactory\357\274\237\344\273\200\344\271\210\346\230\257ObjectProvider\357\274\237.md" new file mode 100644 index 0000000000000000000000000000000000000000..aa479dee7ed8a438e08e1e9a336f28b7cf8214a5 --- /dev/null +++ "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\200\344\271\210\346\230\257ObjectFactory\357\274\237\344\273\200\344\271\210\346\230\257ObjectProvider\357\274\237/\344\273\200\344\271\210\346\230\257ObjectFactory\357\274\237\344\273\200\344\271\210\346\230\257ObjectProvider\357\274\237.md" @@ -0,0 +1,306 @@ +什么是ObjectFactory?什么是ObjectProvider? + +> 在Spring的学习过程中,总是学的越多,不懂的越多。本来只是想将ApplicationContext的相关内容全部梳理一遍,结果发现涉及的东西越来越多,比如上篇文章中的ResolvableType,到这篇文章介绍的ObjectFactory跟ObjectProvider。不过想想也没办法,一步一步往前走呗,在这个过程中也确实学到了很多东西。废话不多说,直接进入正文。 + +# ObjectFactory + +## 接口定义 + +```java +// 一个对象工厂 +public interface ObjectFactory { + + // 返回一个对象 + T getObject() throws BeansException; + +} +``` + +这个接口的定义非常简单,就是一个对象工厂,定义了一个返回对象的工厂方法。回顾我们直接介绍过的一个内容`FactroyBean`,其接口定义如下: + +```java +public interface FactoryBean { + + @Nullable + T getObject() throws Exception; + + @Nullable + Class getObjectType(); + + default boolean isSingleton() { + return true; + } + +} +``` + +可以看到两者都有一个`getObject`方法,那么它们有什么区别或者联系呢? + +## 跟FactoryBean的区别及联系 + +### 联系 + +二者在功能设计上是没有什么联系的,他们最大的共同点就是都采用了工厂模式,通过工厂模式来返回一个对象 + +### 区别 + +1. `FactoryBean`在`BeanFacotry`的实现中有着特殊的处理,如果一个对象实现了`FactoryBean` 那么通过它get出来的对象实际是`factoryBean.getObject() `得到的对象,如果想得到`FactoryBean`必须通过在 `'&' + beanName `的方式获取 + +2. `FactoryBean`的设计主要是为了进行扩展容器中Bean的创建方式,所以FactoryBean着重于自定义创建对象过程,同时`FactoryBean`都会放到容器中,`FactoryBean`所创建的Bean也会放入容器中 + +3. `ObjectFactory`则只是一个普通的对象工厂接口。在Spring中主要两处用了它 + + - Scope接口中的get方法,需要传入一个`ObjectFactory`,如下: + + ```java + Object get(String name, ObjectFactory objectFactory); + ``` + + 这个方法的目的就是从对于的域中获取到指定名称的对象。为什么要传入一个objectFactory呢?主要是为了方便我们扩展自定义的域,而不是仅仅使用request,session等域。 + + - ConfigurableListableBeanFactory类中的registerResolvableDependency方法,其定义如下, + + ```java + void registerResolvableDependency(Class dependencyType, @Nullable Object autowiredValue); + ``` + + 粗看起来,好像这个方法跟`ObjectFactory`没有什么关联,但是我们留意这个方法上面的JavaDoc,其中有一段关于参数autowiredValue的介绍,如下 + + > ```java + > @param autowiredValue the corresponding autowired value. This may also be an* implementation of the {@link org.springframework.beans.factory.ObjectFactory}* interface, which allows for lazy resolution of the actual target value. + > ``` + + 从这段内容中我们能知道,`autowiredValue`这个参数可能就是一个`ObjectFactory`,主要是为了让注入点能够被延迟注入。Spring通过这种方式注入了request,response等对象 + + ```java + beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); + beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory()); + beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); + beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); + ``` + + 我们看看RequestObjectFactory的定义: + + ```java + private static class RequestObjectFactory implements ObjectFactory, Serializable { + + @Override + // 是从当前线程中获取的 + public ServletRequest getObject() { + return currentRequestAttributes().getRequest(); + } + + @Override + public String toString() { + return "Current HttpServletRequest"; + } + } + ``` + + 当我们在某一个类中如果注入了ServletRequest对象,并不会直接创建一个ServletRequest然后注入进去,而是注入一个代理类,代理类中的方法是通过`ObjectFactoryDelegatingInvocationHandler`实现的,而这个对象中会持有一个RequestObjectFactory对象。基于此,我们可以通过下面这种方式直接注入request对象,并且保证线程安全 + + ```java + + @RestController + public class AutowiredRequestController { + + @Autowired + private HttpServletRequest request; + } + ``` + +# ObjectProvider + +## 接口定义 + +```java +// 1.可以看到ObjectProvider本身继承了ObjectFactory接口,所以它本身就是一个ObjectFactory +// 2.从5.1之后,这个接口还多继承了一个Iterable接口,意味着能对它进行迭代以及流式操作 +public interface ObjectProvider extends ObjectFactory, Iterable { + + // 返回用指定参数创建的bean, 如果容器中不存在, 抛出异常 + T getObject(Object... args) throws BeansException; + + // 如果指定类型的bean注册到容器中, 返回 bean 实例, 否则返回 null + @Nullable + T getIfAvailable() throws BeansException; + + // 如果返回对象不存在,则用传入的Supplier获取一个Bean并返回,否则直接返回存在的对象 + default T getIfAvailable(Supplier defaultSupplier) throws BeansException { + T dependency = getIfAvailable(); + return (dependency != null ? dependency : defaultSupplier.get()); + } + + // 消费对象的一个实例(可能是共享的或独立的),如果存在通过Consumer回调消耗目标对象。 + // 如果不存在则直接返回 + default void ifAvailable(Consumer dependencyConsumer) throws BeansException { + T dependency = getIfAvailable(); + if (dependency != null) { + dependencyConsumer.accept(dependency); + } + } + + // 如果不可用或不唯一(没有指定primary)则返回null。否则,返回对象。 + @Nullable + T getIfUnique() throws BeansException; + + // 如果不存在唯一对象,则调用Supplier的回调函数 + default T getIfUnique(Supplier defaultSupplier) throws BeansException { + T dependency = getIfUnique(); + return (dependency != null ? dependency : defaultSupplier.get()); + } + + // 如果存在唯一对象,则消耗掉该对象 + default void ifUnique(Consumer dependencyConsumer) throws BeansException { + T dependency = getIfUnique(); + if (dependency != null) { + dependencyConsumer.accept(dependency); + } + } + + // 返回符合条件的对象的Iterator,没有特殊顺序保证(一般为注册顺序) + @Override + default Iterator iterator() { + return stream().iterator(); + } + + // 返回符合条件对象的连续的Stream,没有特殊顺序保证(一般为注册顺序) + default Stream stream() { + throw new UnsupportedOperationException("Multi element access not supported"); + } + + // 返回符合条件对象的连续的Stream。在标注Spring应用上下文中采用@Order注解或实现Order接口的顺序 + default Stream orderedStream() { + throw new UnsupportedOperationException("Ordered element access not supported"); + } +} +``` + +## 接口分析 + +在Spring4.3之前,如果你*构造函数*中要依赖另外一个bean,你必须显示依赖`@Autowired`(*这里不考虑使用了自动注入的方式,关于自动注入跟精确注入请参我之前的文章,《Spring官网阅读》系列第二,三篇*) ,像这样子 + +```java +@Service +public class FooService { + private final FooRepository repository; + @Autowired + public FooService(FooRepository repository) { + this.repository = repository + } +} +``` + +而在4.3版本之后,已经不需要这么做了,只要我们只提供了一个构造函数,并且构造函数所需要的参数都在Spring容器中(*实际上官网中也指出,如果依赖关系是强制的,那么最好使用构造函数进行注入*),那么不需要进行精确的指定使用`@Autowired`。相比于4.3版本这无疑简化了我们的开发,但是这种隐式的注入仍然存在一些不足。例如,就上面的例子而言,如果容器中存在了一个以上的FooRepository甚至一个都没有的情况下,抛出异常 + +>Parameter 0 of constructor in com.example.demo.FooServicerequired a bean of type 'com.example.demo.FooRepository' that could not be found. +> +>或者是 +> +>No qualifying bean of type 'com.example.demo.FooRepository'' available: expected single matching bean but found 2: + +那么我们有什么办法解决它呢?基于这个原因,`ObjectProvider`就出场了。如果注入实例为空时,使用ObjectProvider则避免了强依赖导致的依赖对象不存在异常;如果有多个实例,ObjectProvider的方法可以根据Bean实现的Ordered接口或@Order注解指定的先后顺序获取一个Bean。从而了提供了一个更加宽松的依赖注入方式。Spring主要在`org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency`方法中使用了它,具体代码如下: + +```java +@Override +public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { + // descriptor代表当前需要注入的那个字段,或者方法的参数,也就是注入点 + // ParameterNameDiscovery用于解析方法参数名称 + descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); + // 1. Optional + if (Optional.class == descriptor.getDependencyType()) { + return createOptionalDependency(descriptor, requestingBeanName); + // 2. ObjectFactory、ObjectProvider + } else if (ObjectFactory.class == descriptor.getDependencyType() || + ObjectProvider.class == descriptor.getDependencyType()) { + return new DependencyObjectProvider(descriptor, requestingBeanName); + // 3. javax.inject.Provider + } else if (javaxInjectProviderClass == descriptor.getDependencyType()) { + return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName); + } else { + // 4. @Lazy + Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( + descriptor, requestingBeanName); + // 5. 正常情况 + if (result == null) { + result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); + } + return result; + } +} +``` + +其实不管是上面的哪个情况,最终都会调用到正常情况下的`doResolveDependency`方法中。我们着重关注上面的第二种情况,可以看到当注入点为`ObjectFactory`或者`ObjectProvider`时,会new一个`DependencyObjectProvider`返回出去,那么返回的这个`DependencyObjectProvider`是什么呢? + +其继承关系如下: + +![官网实例化](image/2020031901.png) + +这个`DependencyObjectProvider`对象,其实就是一个`ObjectProvider`,我们看看它是如何实现`ObjectProvider`中的方法的(方法的实现逻辑都差不多,这里就看一个方法): + +```java +public Object getIfAvailable() throws BeansException { + // 用于解决嵌套的情况,像这种:ObjectProvider> + if (this.optional) { + return createOptionalDependency(this.descriptor, this.beanName); + } + else { + DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) { + @Override + public boolean isRequired() { + return false; + } + }; + // 最终还是会调用这个方法解决依赖 + return doResolveDependency(descriptorToUse, this.beanName, null, null); + } +} +``` + +从上面的过程中我们可以看出,但Spring中某个Bean的依赖类型为ObjectProvider时,我们不需要提供一个ObjectProvider类型的Bean到容器中,只需要提供一个T类型的Bean到容器中,容器会自动将其包装成一个ObjectProvider,然后注入到依赖中。 + +而基于ObjectProvider的一系列方法,我们就能解决之前提到的问题。 + +## ObjectProvider解决的问题 + +### 问题一 + +容器中没有Bean时,抛出`Parameter 0 of constructor in com.example.demo.FooServicerequired a bean of type 'com.example.demo.FooRepository' that could not be found.`。 + +解决方式: + +```java +@Component +public class IndexService { + + B b; + + public IndexService(ObjectProvider b) { + this.b = b.getIfAvailable(); + } +} +``` + +**但是上面这种解决方式的弊病也很明显,就是b可能为空,则可能将异常从启动阶段转移到业务运行阶段。** + +### 问题二 + +容器中存在多个Bean时,抛出`No qualifying bean of type 'com.example.demo.FooRepository'' available: expected single matching bean but found 2` + +```java +@Component +public class IndexService { + + B b; + + public IndexService(ObjectProvider b) { + this.b = b.orderedStream().findFirst().orElse(null); + } +} +``` + +当容器存在多个Bean,我们可以调用它的流式方法获取一个自己想要的依赖。 + +# 总结 + +本文介绍了`ObjectFactory`跟`ObjectProvider`,对于`ObjectFactory`主要介绍了它的应用并且将其与我们之前学习过的`FactoryBean`做了比较。关于`ObjectProvider`也对其中定义的方法,以及它解决的问题做了分析。可能有些读者觉得这些东西也不是很重要,并不影响核心代码的阅读。不过笔者在阅读源码过程中,看到了不去弄明白实在是难受,本人也是秉着愚公移山的精神慢慢啃,一个知识点一个知识点慢慢摸索,虽然这样慢了点,但是能学到东西也是有很大满足感的。不管怎么样,我还是写了这篇笔记,也会继续写下去。加油,为自己! \ No newline at end of file diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031301.png" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031301.png" new file mode 100644 index 0000000000000000000000000000000000000000..01074ac8bfe07853fea542b4b7391bc42074aaef Binary files /dev/null and "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031301.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031501.png" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031501.png" new file mode 100644 index 0000000000000000000000000000000000000000..08d0190410343c204437de3eeac11b6dc69058e9 Binary files /dev/null and "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031501.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031601.png" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031601.png" new file mode 100644 index 0000000000000000000000000000000000000000..f0146a5ea908dcba618deaa608ac5aa2b6f8e7ca Binary files /dev/null and "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031601.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031602.png" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031602.png" new file mode 100644 index 0000000000000000000000000000000000000000..16eec044b277cf7b18b55b924c3cf82752469314 Binary files /dev/null and "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031602.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031701.png" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031701.png" new file mode 100644 index 0000000000000000000000000000000000000000..04c6daac1dfe74e5857c28517770f7db04b0e385 Binary files /dev/null and "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031701.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031702.png" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031702.png" new file mode 100644 index 0000000000000000000000000000000000000000..91c94084283a31ab383806db32c9c798e8dc61eb Binary files /dev/null and "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031702.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031703.png" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031703.png" new file mode 100644 index 0000000000000000000000000000000000000000..a9d15257906b006daf24213c07c65a79d7049f19 Binary files /dev/null and "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031703.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031704.png" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031704.png" new file mode 100644 index 0000000000000000000000000000000000000000..c15757c04c61dfed609b637739882e4b61e6fb62 Binary files /dev/null and "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/image/2020031704.png" differ diff --git "a/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType.md" "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType.md" new file mode 100644 index 0000000000000000000000000000000000000000..b53e49467750215bb7c3616eb8cee70947843bca --- /dev/null +++ "b/docs/Spring/Spring\346\235\202\350\260\210/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType/\344\273\216java\347\232\204Type\345\210\260Spring\347\232\204ResolvableType.md" @@ -0,0 +1,928 @@ +你真的了解泛型吗?从java的Type到Spring的ResolvableType + +>关于泛型的基本知识在本文中不会过多提及,本文主要解决的是如何处理泛型,以及java中Type接口下对泛型的一套处理机制,进而分析Spring中的ResolvableType。 + +# Type + +## 简介 + +Type是Java 编程语言中所有类型的公共高级接口(官方解释),也就是Java中所有类型的“爹”;其中,“所有类型”的描述尤为值得关注。它并不是我们平常工作中经常使用的 int、String、List、Map等数据类型,而是从Java语言角度来说,对基本类型、引用类型向上的抽象; + +Type体系中类型的包括:原始类型(Class)、参数化类型(ParameterizedType)、数组类型(GenericArrayType)、类型变量(TypeVariable)、基本类型(Class); + +原始类型,不仅仅包含我们平常所指的类,还包括枚举、数组、注解等; + +参数化类型,就是我们平常所用到的泛型List、Map这种; + +数组类型,并不是我们工作中所使用的数组String[] 、byte[],而是带有泛型的数组,即T[] ; + +基本类型,也就是我们所说的java的基本类型,即int,float,double等 + +**Type体系的出现主要是为了解决泛型的一系列问题。** + +## 接口定义 + +```java +public interface Type { + // 返回这个类型的名称 + default String getTypeName() { + return toString(); + } +} +``` + +可以看到`Type`接口内只定义了一个方法,这个方法会返回该类型的名称 + +## UML类图 + +![官网实例化](image/2020031301.png) + +在上面的图中对于Class我相信大家都已经很了解了。我们主要对其余四个子接口进行测试分析 + +#### ParameterizedType + +##### 简介 + +参数化类型,也就是我们所说的泛型。像List就是一个参数化类型,但是List并不是,因为没有使用泛型。 + +##### 接口定义 + +```java +public interface ParameterizedType extends Type { + // 对于一个参数化类型而言,必定是带有泛型的,所有这里是为了获取到其中的泛型的具体类型,也就是<>中的内容 + // 返回一个数组是因为,有时候会定义多个泛型,比如Map + Type[] getActualTypeArguments(); + + // 获取原始类型,这里不带泛型,就是class + Type getRawType(); + + // 获取这个类所在类的类型,这里可能比较拗口,举个例子,假如当前这个ParameterizedType的类型为 + // O.I,那么调用这个方法所返回的就是一个O类型 + Type getOwnerType(); +} +``` + +##### 使用示例 + +```java +public class Main extends OwnerTypeDemo { + + private List stringList; + + private Map stringStringMap; + + private Map.Entry entry; + + private OwnerTypeDemo.Test testOwnerType; + + private List list; + + private Map map; + + public void test(List stringList, List list) { + + } + + public static void main(String[] args) { + Class
mainClass = Main.class; + Field[] fields = mainClass.getDeclaredFields(); + for (Field field : fields) { + Type genericType = field.getGenericType(); + String typeName = genericType.getTypeName(); + String name = field.getName(); + if (genericType instanceof ParameterizedType) { + System.out.println(name + "是一个参数化类型,类型名称为:" + typeName); + ParameterizedType parameterizedType = (ParameterizedType) genericType; + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + System.out.println(name + "的actualTypeArguments:" + Arrays.toString(actualTypeArguments)); + Type ownerType = parameterizedType.getOwnerType(); + System.out.println(name + "的ownerType:" + ownerType); + Type rawType = parameterizedType.getRawType(); + System.out.println(name + "的rawType:" + rawType); + } else { + System.out.println(name + "不是一个参数化类型,类型名称为:" + typeName); + } + } + System.out.println("===================开始测试方法中的参数========================="); + Method[] declaredMethods = mainClass.getDeclaredMethods(); + for (Method declaredMethod : declaredMethods) { + String methodName = declaredMethod.getName(); + Type[] genericParameterTypes = declaredMethod.getGenericParameterTypes(); + for (int i = 0; i < genericParameterTypes.length; i++) { + Type parameterType = genericParameterTypes[i]; + String typeName = parameterType.getTypeName(); + System.out.println("打印" + methodName + "方法的参数," + "第" + (i + 1) + "个参数为:" + parameterType); + if (parameterType instanceof ParameterizedType) { + System.out.println("第" + (i + 1) + "个参数是一个参数化类型, 类型名称为 : " + typeName); + } else { + System.out.println("第" + (i + 1) + "个参数不是一个参数化类型, 类型名称为 : " + typeName); + } + } + } + System.out.println("===================开始测试父类中的泛型========================="); + // 获取带有泛型的父类 + Type genericSuperclass = mainClass.getGenericSuperclass(); + if (genericSuperclass instanceof ParameterizedType) { + System.out.println("父类是一个参数化类型,类型名称为:" + genericSuperclass.getTypeName()); + } + + } +} + +class OwnerTypeDemo { + class Test { + + } +} + +``` + +程序会做如下输出: + +```java +stringList是一个参数化类型,类型名称为:java.util.List +stringList的actualTypeArguments:[class java.lang.String] +stringList的ownerType:null +stringList的rawType:interface java.util.List +stringStringMap是一个参数化类型,类型名称为:java.util.Map +stringStringMap的actualTypeArguments:[class java.lang.String, class java.lang.String] +stringStringMap的ownerType:null +stringStringMap的rawType:interface java.util.Map +entry是一个参数化类型,类型名称为:java.util.Map$Entry +entry的actualTypeArguments:[class java.lang.String, ?] +entry的ownerType:interface java.util.Map +entry的rawType:interface java.util.Map$Entry +testOwnerType是一个参数化类型,类型名称为:main.java.OwnerTypeDemo$Test +testOwnerType的actualTypeArguments:[class java.lang.String] +testOwnerType的ownerType:main.java.OwnerTypeDemo +testOwnerType的rawType:class main.java.OwnerTypeDemo$Test +list不是一个参数化类型,类型名称为:java.util.List +map不是一个参数化类型,类型名称为:java.util.Map +===================开始测试方法中的参数========================= +打印main方法的参数,第1个参数为:class [Ljava.lang.String; +第1个参数不是一个参数化类型, 类型名称为 : java.lang.String[] +打印test方法的参数,第1个参数为:java.util.List +第1个参数是一个参数化类型, 类型名称为 : java.util.List +打印test方法的参数,第2个参数为:interface java.util.List +第2个参数不是一个参数化类型, 类型名称为 : java.util.List +===================开始测试父类中的泛型========================= +父类是一个参数化类型,类型名称为:main.java.OwnerTypeDemo +``` + +------ + +通过上面的例子可以看出,`ParameterizedType`可以让我们明确字段或者方法参数上是否使用了泛型,并获取到泛型的具体类型。那是不是依赖`ParameterizedType`就能解决所有的泛型问题了呢?答案显然是不是的,我们看一个特殊的例子: + +```java +public class SpecialDemo { + + T t; + + public static void main(String[] args) { + Class specialDemoClass = SpecialDemo.class; + Field[] declaredFields = specialDemoClass.getDeclaredFields(); + for (Field declaredField : declaredFields) { + Type genericType = declaredField.getGenericType(); + if (genericType instanceof ParameterizedType) { + System.out.println("t是一个参数化类型"); + } else { + System.out.println("t不是一个参数化类型"); + } + } + } + // 程序输出:t不是一个参数化类型 +} +``` + +运行上面的程序,会发现字段t不是一个参数化类型,这就意味着没办法通过`ParameterizedType`来解决这一类泛型问题。我们分析``,会发现其实T类似于一个变量,我们可以在使用时可以传入具体的类,比如我们可以这样: + +```java +SpecialDemo specialDemo = new SpecialDemo<>(); +``` + +同时这个基于这个``表达式,我们知道这个变量是具有属性的,最直观的就是T是有上界的,所有的T都继承了`Type`。基于这种情况,Java对其进行了抽象,得到了一个新的类型`TypeVariable`。 + +#### TypeVariable + +##### 简介 + +类型变量,或者也可以叫泛型变量。具体就是指我们在申明泛型时定义的`T,K,U`这种变量。在之前的例子中,`SpecialDemo`,T就是一个类型变量。 + +##### 接口定义 + +```java +public interface TypeVariable extends Type, AnnotatedElement { + // 获取泛型的边界 + Type[] getBounds(); + // 获取申明所在的具体对象 + D getGenericDeclaration(); + // 获取具体类型变量的名称 + String getName(); + // 获取类型变量边界上添加的注解 + AnnotatedType[] getAnnotatedBounds(); +} +``` + +可以看到,`TypeVariable`本身也使用了泛型,并且泛型的上界为`GenericDeclaration`。在了解`TypeVariable`之前,有必要先对`GenericDeclaration`做一个简单的说明。`GenericDeclaration`这个接口主要限定了哪些地方可以定义`TypeVariable`,换言之,也就是定义了哪些地方可以申明泛型。这个接口只有3个实现类(忽略`Executable`抽象类)。如下: + +![官网实例化](image/2020031501.png) + +从这里我们也能看到,我们只能在方法(包括普通方法跟构造方法)以及类上申明泛型。 + +这里需要对接口定义的方法做进一步的说明: + +1. getBounds()会返回泛型的边界,但是这里的边界跟我们在参数化类型中定义的边界不同,这里的边界只有上界。即我们不通通过super关键字来申明一个泛型,例如下面这种: + +```java +class A{} +``` + +在申明泛型时,我们要明确一点,***申明是为了使用***,而在上面的例子中,我们不能使用T来干任何事情,因为我们不能确定T中的任何方法(只能确定它是一个Object,但是这没有任何意义)。所以对于泛型变量来说,只存在上界,也就是只能使用extends关键字进行申明 + +2. getGenericDeclaration(),返回泛型申明时所在的类或者方法 +3. 返回泛型变量的名称,也就是我们定义泛型时采用的T,K,U这一类的名称 +4. getAnnotatedBounds(),此方法返回一个`AnnotatedType`类型的数组,获取的是我们在类型变量的上界。不同于getBounds()方法的是,这个方法可以获取到边界上添加的注解 + +##### 使用示例 + +```java +public class TypeVariableMain { + + public void testTypeVariable(Map map) { + + } + + public static void main(String[] args) { + Class typeVariableMainClass = TypeVariableMain.class; + TypeVariable>[] typeParameters = typeVariableMainClass.getTypeParameters(); + for (int i = 0; i < typeParameters.length; i++) { + TypeVariable> typeParameter = typeParameters[i]; + Type[] bounds = typeParameter.getBounds(); + String name = typeParameter.getName(); + AnnotatedType[] annotatedBounds = typeParameter.getAnnotatedBounds(); + Class genericDeclaration = typeParameter.getGenericDeclaration(); + System.out.println("第" + (i + 1) + "个类型变量的名称为:" + name); + System.out.println("通过getBounds方法获取到,第" + (i + 1) + "个类型变量的边界为:" + Arrays.toString(bounds)); + System.out.println("第" + (i + 1) + "个类型变量的申明的位置为:" + genericDeclaration); + System.out.println("通过getAnnotatedBounds方法获取到,第" + (i + 1) + "个类型变量的边界为:" + + Arrays.stream(annotatedBounds).map(AnnotatedType::getType).collect(Collectors.toList())); + for (AnnotatedType annotatedType : annotatedBounds) { + Annotation[] annotations = annotatedType.getAnnotations(); + if (annotations.length > 0) { + System.out.println("第" + (i + 1) + "个类型变量的上界上添加了注解,注解为" + annotations[0]); + } + } + } + System.out.println("===================基于方法获取类型变量===================="); + Method[] declaredMethods = typeVariableMainClass.getDeclaredMethods(); + for (Method declaredMethod : declaredMethods) { + String methodName = declaredMethod.getName(); + if (methodName.equals("main")) { + // 为了方便,直接排除main函数了 + continue; + } + TypeVariable[] typeVariables = declaredMethod.getTypeParameters(); + int i = 1; + for (TypeVariable typeVariable : typeVariables) { + System.out.println("方法:\"" + methodName + "\"的第" + (i++) + "个类型变量为" + typeVariable.getName()); + } + } + } +} +``` + +程序打印如下: + +```java +第1个类型变量的名称为:T +通过getBounds方法获取到,第1个类型变量的边界为:[class java.lang.Object] +第1个类型变量的申明的位置为:class main.java.TypeVariableMain +通过getAnnotatedBounds方法获取到,第1个类型变量的边界为:[class java.lang.Object] +第2个类型变量的名称为:K +通过getBounds方法获取到,第2个类型变量的边界为:[class java.lang.Integer, interface java.lang.reflect.Type] +第2个类型变量的申明的位置为:class main.java.TypeVariableMain +通过getAnnotatedBounds方法获取到,第2个类型变量的边界为:[class java.lang.Integer, interface java.lang.reflect.Type] +第2个类型变量的上界上添加了注解,注解为@main.java.TypeAnnotation() +===================基于方法获取类型变量==================== +方法:"testTypeVariable"的第1个类型变量为U +方法:"testTypeVariable"的第2个类型变量为V +``` + +------- + +为了让大家加深对`ParameterizedType`以及`TypeVariable`理解,这里我额外添加一个Demo + +```java +public class TypeVariableMain02 { + + private K k; + + private List list; + + public static void main(String[] args) { + Class typeVariableMain02Class = TypeVariableMain02.class; + + Field[] declaredFields = typeVariableMain02Class.getDeclaredFields(); + for (Field field : declaredFields) { + Type genericType = field.getGenericType(); + String typeName = genericType.getTypeName(); + String name = field.getName(); + if (genericType instanceof ParameterizedType) { + System.out.println(name + "是一个参数化类型,类型名称为:" + typeName); + ParameterizedType parameterizedType = (ParameterizedType) genericType; + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + System.out.println(name + "的actualTypeArguments:" + Arrays.toString(actualTypeArguments)); + Type ownerType = parameterizedType.getOwnerType(); + System.out.println(name + "的ownerType:" + ownerType); + Type rawType = parameterizedType.getRawType(); + System.out.println(name + "的rawType:" + rawType); + for (Type actualTypeArgument : actualTypeArguments) { + if (actualTypeArgument instanceof TypeVariable) { + System.out.println("字段:" + name + "中包含一个类型变量"); + String name1 = ((TypeVariable) actualTypeArgument).getName(); + AnnotatedType[] annotatedBounds = ((TypeVariable) actualTypeArgument).getAnnotatedBounds(); + Type[] bounds = ((TypeVariable) actualTypeArgument).getBounds(); + GenericDeclaration genericDeclaration = ((TypeVariable) actualTypeArgument).getGenericDeclaration(); + System.out.println("类型变量的名称为:" + name1); + System.out.println("个类型变量的边界为:" + Arrays.toString(bounds)); + System.out.println("类型变量的申明的位置为:" + genericDeclaration); + System.out.println("通过getAnnotatedBounds方法获取到,类型变量的边界为:" + annotatedBounds[0].getType()); + } + } + } else if (genericType instanceof TypeVariable) { + System.out.println(name + "是一个类型变量,类型名称为:" + typeName); + TypeVariable typeVariable = (TypeVariable) genericType; + Type[] bounds = typeVariable.getBounds(); + String name1 = typeVariable.getName(); + AnnotatedType[] annotatedBounds = typeVariable.getAnnotatedBounds(); + GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); + System.out.println("类型变量的名称为:" + name1); + System.out.println("个类型变量的边界为:" + Arrays.toString(bounds)); + System.out.println("类型变量的申明的位置为:" + genericDeclaration); + System.out.println("通过getAnnotatedBounds方法获取到,类型变量的边界为:" + annotatedBounds[0].getType() + " " + annotatedBounds[1].getType()); + } + } + } +} +``` + +程序输出: + +```java +k是一个类型变量,类型名称为:K +类型变量的名称为:K +个类型变量的边界为:[class java.lang.Integer, interface java.lang.reflect.Type] +类型变量的申明的位置为:class main.java.TypeVariableMain02 +通过getAnnotatedBounds方法获取到,类型变量的边界为:class java.lang.Integer interface java.lang.reflect.Type +list是一个参数化类型,类型名称为:java.util.List +list的actualTypeArguments:[T] +list的ownerType:null +list的rawType:interface java.util.List +字段:list 中包含一个类型变量 +类型变量的名称为:T +个类型变量的边界为:[class java.lang.Object] +类型变量的申明的位置为:class main.java.TypeVariableMain02 +通过getAnnotatedBounds方法获取到,类型变量的边界为:class java.lang.Object +``` + +#### GenericArrayType + +##### 简介 + +GenericArrayType是Type的子接口,用于表示“泛型数组”,描述的是形如:A[]或T[]的类型。其实也就是描述ParameterizedType类型以及TypeVariable类型的数组,即形如:classA[][]、T[]等 + +##### 接口定义 + +```java +public interface GenericArrayType extends Type { + // 返回数组中元素的类型,TypeVariable或者ParameterizedType + Type getGenericComponentType(); +} +``` + +##### 使用示例 + +```java +public class GenericArrayTypeMain { + T[] t1; + + T[][] t2; + + List list; + + List[] stringListArray; + + String[][] stringArray; + + public static void main(String[] args) { + Class genericArrayTypeMainClass = GenericArrayTypeMain.class; + Field[] declaredFields = genericArrayTypeMainClass.getDeclaredFields(); + for (Field declaredField : declaredFields) { + String name = declaredField.getName(); + Type genericType = declaredField.getGenericType(); + if (genericType instanceof GenericArrayType) { + System.out.println(name + "是一个泛型数组"); + Type genericComponentType = ((GenericArrayType) genericType).getGenericComponentType(); + System.out.println("数组的元素类型为:" + genericComponentType); + } else { + System.out.println(name + "不是一个泛型数组"); + } + } + } +} +``` + +程序输出: + +```java +t1是一个泛型数组 +数组的元素类型为:T +t2是一个泛型数组 +数组的元素类型为:T[] +list不是一个泛型数组 +stringListArray是一个泛型数组 +数组的元素类型为:java.util.List +stringArray不是一个泛型数组 +``` + +通过上面的Demo我们会发现,无论从左向右有几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。 + +另外,在上面的例子中,大家可以思考以下几个问题: + +1. t1是一个泛型数组,数组的元素类型为:T,那么T是一个什么类型呢? +2. t2是一个泛型数组,数组的元素类型为:T[],那么T[]又是什么类型? + +上述问题留给大家自行思考 + +------ + +了解了ParameterizedType跟TypeVariable以及这两种类型的数组类型GenericArrayType之后,接着我们思考一个问题,我们在定义泛型时,经常会使用来通配符,形如下面这种形式`{? extends Number}`,这个时候即使我们获取到`? extends Number`也没有办法做进一步的处理。这个时候就要用到我们接下来要介绍的这个接口了,请往下看 + +#### WildcardType + +##### 简介 + +专门用来处理泛型中的通配符,需要注意的是,`WildcardType`并不是JAVA所有类型中的一种,表示的仅仅是类似 `{? extends T}`、`{? super K}`这样的通配符表达式。 + +##### 接口定义 + +```java +public interface WildcardType extends Type { + + // 获取通配符表达式的上界 + Type[] getUpperBounds(); + + // 获取通配符表达式的下界 + Type[] getLowerBounds(); + +} +``` + +上面这两个方法之所以会返回数组是为了保持扩展性,实际上现在返回的数组的大小就是1,JDK8中至少是这样的吗,更高版本的没有去尝试。 + +##### 使用示例 + +```java +public class WildcardTypeDemo { + + Map> map; + + public static void main(String[] args) { + Class wildcardTypeDemoClass = WildcardTypeDemo.class; + Field[] declaredFields = wildcardTypeDemoClass.getDeclaredFields(); + for (Field field : declaredFields) { + Type genericType = field.getGenericType(); + if (genericType instanceof ParameterizedType) { + // 使用了通配符表达泛型的,必定是一个参数化类型 + // 获取泛型的实际类型,就是获取<>中的内容,这里获取到的是> + Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments(); + for (Type actualTypeArgument : actualTypeArguments) { + // 两个泛型都使用了通配符,都会进入这个判断 + if (actualTypeArgument instanceof WildcardType) { + // 分別获取上界跟下界 + // ? super String,这个表达式的下界为String,上界为Object + // ? extends List,这个表达式的下界为Object,上界为List, + // 同时List又是一个参数化类型,而T又是一个类型变量 + Type[] lowerBounds = ((WildcardType) actualTypeArgument).getLowerBounds(); + Type[] upperBounds = ((WildcardType) actualTypeArgument).getUpperBounds(); + // 这里我主要处理? extends List + for (Type upperBound : upperBounds) { + if (upperBound instanceof ParameterizedType) { + System.out.println("参数化类型的名称为:" + upperBound.getTypeName()); + Type[] actualTypeArguments1 = ((ParameterizedType) upperBound).getActualTypeArguments(); + for (Type type : actualTypeArguments1) { + if (type instanceof TypeVariable) { + String name = ((TypeVariable) type).getName(); + System.out.println("类型变量名称为:" + name); + } + } + } + } + } + } + } + } + } + + // 程序输出: + // 参数化类型的名称为:java.util.List + // 类型变量名称为:T +``` + +我相信如果你对Java中的类型已经完全理解了,上面的代码配合注释应该不难看懂 + +# ResolvableType + +在学习了Java的Type体系后,我们会发现,依赖于整个Type体系去处理泛型代码非常的繁琐,并且不易于理解。基于这种情况,Spring开发了一个`ResolvableType`类,这个类对整个Type体系做了系统的封装。 + +> 实际上关于`ResolvableType`的学习大家可以参数Spring中的`org.springframework.core.ResolvableTypeTests`类,这是作者写好的单元测试类,覆盖了`ResolvableType`的所有方法。 + +这个类的代码量很大,不过我们也没有必要去详细地看每一行代码,粗略阅读源码后会发现这个类有以下几个特点 + +## 概览 + +1. 所有的构造函数都是私有的 + +![官网实例化](image/2020031601.png) + +在上图中那把小锁代表权限为private,就是私有的意思 + +2. 构造函数都是在为相同的成员变量赋值,这里我随便放一个构造函数如下 + +```java +private ResolvableType(Type type, @Nullable TypeProvider typeProvider, + @Nullable VariableResolver variableResolver, @Nullable ResolvableType componentType) { + + this.type = type; + this.typeProvider = typeProvider; + this.variableResolver = variableResolver; + this.componentType = componentType; + this.hash = null; + this.resolved = resolveClass(); +} +``` + +3. 因为构造函数是私有的,所有它提供了一系列的方法来创建一个ResolvableType,如下: + +![官网实例化](image/2020031602.png) + +所有for开头的方法都是静态方法,同时都能获取一个ResolvableType,现在对常见的几个方法进行分析: + +## 方法分析 + +### forClass系列方法 + +Spring中经常会用到一个方法,`ResolvableType.forRawClass(type)`,我们就先看下这一系列的三个方法 + +1. ResolvableType.forRawClass(type) +2. ResolvableType forClass(@Nullable Class clazz) +3. ResolvableType forClass(Class baseType, Class implementationClass) +4. ResolvableType forClassWithGenerics(Class clazz, Class... generics) +5. ResolvableType forClassWithGenerics(Class clazz, ResolvableType... generics) + +#### forRawClass(Class clazz) + +```java +public static ResolvableType forRawClass(@Nullable Class clazz) { + return new ResolvableType(clazz) { + @Override + public ResolvableType[] getGenerics() { + return EMPTY_TYPES_ARRAY; + } + @Override + public boolean isAssignableFrom(Class other) { + return (clazz == null || ClassUtils.isAssignable(clazz, other)); + } + @Override + public boolean isAssignableFrom(ResolvableType other) { + Class otherClass = other.getRawClass(); + return (otherClass != null && (clazz == null || ClassUtils.isAssignable(clazz, otherClass))); + } + }; +} +``` + +这个方法实际上做了两件事 + +1. 调用了构造函数,`private ResolvableType(@Nullable Class clazz)` +2. 复写了三个方法 + +对比另外一个方法 + +#### forClass(Class clazz) + +```java +public static ResolvableType forClass(@Nullable Class clazz) { + return new ResolvableType(clazz); +} +``` + +对比后可以发现,这两个方法唯一的区别就是没有复写其中的三个方法。大家可以思考下,这是为什么呢? + +> 其实区别在于,对于第一个`forRawClass`方法,入参传入的一定是一个原始数据类型,也就是一个不带泛型的类的Class对象,比如传入的可能是一个Person.class,Dog.class。对于这种原始数据类型,其`getGenerics`,`isAssignableFrom`方法的实现逻辑是固定的,所以`forRawClass`方法直接对这三个方法进行了复写。 + +#### forClass(Class baseType, Class implementationClass) + +```java +public static ResolvableType forClass(Class baseType, Class implementationClass) { + Assert.notNull(baseType, "Base type must not be null"); + // as方法在之后分析,就是根据继承链找打对应的父类 + ResolvableType asType = forType(implementationClass).as(baseType); + return (asType == NONE ? forType(baseType) : asType); +} +``` + +implementationClass是baseType的子类,这个方法主要获取baseType上定义的泛型,例如: + +```java +public class ResolvableTypeDemo { + public static void main(String[] args) { + // 获取到C继承的HashMap所构建的一个ResolvableType,会带用泛型 + ResolvableType resolvableType = ResolvableType.forClass(HashMap.class, C.class); + ResolvableType[] generics = resolvableType.getGenerics(); + for (ResolvableType generic : generics) { + // 程序打印: + // class java.lang.String + // class java.lang.Integer + System.out.println(generic.getType()); + } + } +} + +class C extends HashMap { + +} +``` + +### forConstructor系列方法 + +```java +public static ResolvableType forConstructorParameter(Constructor constructor, int parameterIndex, + Class implementationClass) { + + Assert.notNull(constructor, "Constructor must not be null"); + MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex); + methodParameter.setContainingClass(implementationClass); + return forMethodParameter(methodParameter); +} + +public static ResolvableType forConstructorParameter(Constructor constructor, int parameterIndex) { + Assert.notNull(constructor, "Constructor must not be null"); + return forMethodParameter(new MethodParameter(constructor, parameterIndex)); +} +``` + +可以看到,forConstructor系列方法最后都调用了forMethod系列方法,我们直接分析forMethod系列的方法 + +### forMethod系列方法 + +![官网实例化](image/2020031701.png) + +主要分为两类方法 + +1. forMethodParameter,解决方法参数上的类型问题 +2. forMethodReturnType,解决方法返回值的类型问题 + +#### forMethodParameter + +```java +public class ResolvableTypeDemo { + + public void test(List list, Map> map) { + + } + + public static void main(String[] args) throws Exception { + Class resolvableTypeDemoClass = ResolvableTypeDemo.class; + Method[] declaredMethods = resolvableTypeDemoClass.getDeclaredMethods(); + Method test = declaredMethods[1]; + // 获取方法的第一个参数对应的ResolvableType,参数为-1代表返回值,0为第一个,1为第二个,一次增加 + ResolvableType resolvableType0 = ResolvableType.forMethodParameter(test, 0); + System.out.println(resolvableType0.resolve()); + System.out.println(resolvableType0.getType()); + // 获取方法的第二个参数对应的ResolvableType + ResolvableType resolvableType1 = ResolvableType.forMethodParameter(test, 1); + System.out.println(resolvableType1.resolve()); + System.out.println(resolvableType1.getType()); + } +} +``` + +#### forMethodReturnType + +```java +public static ResolvableType forMethodReturnType(Method method) { + Assert.notNull(method, "Method must not be null"); + return forMethodParameter(new MethodParameter(method, -1)); +} +``` + +调用逻辑很简单,调用forMethodParameter,并将方法的参数索引替换为-1,代表返回值 + +### forConstructor系列方法 + +构造函数就是一个特殊的方法,所以都是直接调用的forMethod系列方法,这里就不多介绍了 + +### forField系列方法 + +专门用于处理字段的类型,如下: + +![官网实例化](image/2020031702.png) + +测试方法Demo + +```java +public class ResolvableTypeDemo { + + List stringList; + + List> lists; + + public static void main(String[] args) throws Exception { + Class resolvableTypeDemoClass = ResolvableTypeDemo.class; + Field[] declaredFields = resolvableTypeDemoClass.getDeclaredFields(); + for (Field declaredField : declaredFields) { + System.out.println("=======字段名称"+declaredField.getName()+"========="); + System.out.println("nestingLevel为1"); + ResolvableType resolvableType1 = ResolvableType.forField(declaredField,1); + System.out.println(resolvableType1.getType()); + System.out.println(resolvableType1.resolve()); + System.out.println("nestingLevel为2"); + ResolvableType resolvableType2 = ResolvableType.forField(declaredField,2); + System.out.println(resolvableType2.getType()); + System.out.println(resolvableType2.resolve()); + System.out.println("nestingLevel为3"); + ResolvableType resolvableType3 = ResolvableType.forField(declaredField,3); + System.out.println(resolvableType3.getType()); + System.out.println(resolvableType3.resolve()); + } + } +} +``` + +程序打印: + +```java +=======字段名称stringList========= +nestingLevel为1 +java.util.List +interface java.util.List +nestingLevel为2 +class java.lang.String +class java.lang.String +nestingLevel为3 +org.springframework.core.ResolvableType$EmptyType@723279cf +null +=======字段名称lists========= +nestingLevel为1 +java.util.List> +interface java.util.List +nestingLevel为2 +java.util.List +interface java.util.List +nestingLevel为3 +class java.lang.String +class java.lang.String +``` + +在上面的所有方法,最后都会调用一个forType方法,所以我们着重也就分析这个系列的方法 + +### forType系列(源码分析) + +![官网实例化](image/2020031703.png) + +最终都会调用到这个方法中,源码如下: + +```java +static ResolvableType forType( + @Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { + + // 这里可以看出,即使我们提供了一个typeProvider,也不会直接调用它的getType返回,而是会进行一层包装,这个是为什么呢?我们稍后分析 + if (type == null && typeProvider != null) { + type = SerializableTypeWrapper.forTypeProvider(typeProvider); + } + + if (type == null) { + // 自身定义的一个常量,ResolvableType NONE = new ResolvableType(EmptyType.INSTANCE, null, null, 0); + return NONE; + } + + // 如果是原始的数据类型(一个简单的Class引用),那么直接封装后返回,这里不做缓存,因为没有上面昂贵的开销 + if (type instanceof Class) { + return new ResolvableType(type, typeProvider, variableResolver, (ResolvableType) null); + } + + // 省略缓存相关的代码。。。 + return resultType; +} +``` + +上面这段代码比较核心的就是`SerializableTypeWrapper.forTypeProvider(typeProvider)`,我之前也提到了一个问题,为什么要多包装一层呢?这么做的目的主要就是为了得到一个可以进行序列化的Type。 + +它的核心代码如下: + +```java +static Type forTypeProvider(TypeProvider provider) { + // 直接从provider获取到具体的类型 + Type providedType = provider.getType(); + if (providedType == null || providedType instanceof Serializable) { + // 如果本身可以序列化的直接返回,例如Java.lang.Class。 + // 如果不能进行序列化,多进行一层包装 + return providedType; + } + // 不用管这段代码,我们开发过程中必定不成立 + if (GraalDetector.inImageCode() || !Serializable.class.isAssignableFrom(Class.class)) { + return providedType; + } + + // 从缓存中获取 + Type cached = cache.get(providedType); + if (cached != null) { + return cached; + } + // 遍历支持的集合,就是GenericArrayType.class, ParameterizedType.class, TypeVariable.class, WildcardType.class,处理这个四种类型 + for (Class type : SUPPORTED_SERIALIZABLE_TYPES) { + if (type.isInstance(providedType)) { + ClassLoader classLoader = provider.getClass().getClassLoader(); + // 创建的代理类实现的接口,type就不用说了代理类跟目标类必须是同一个类型 + // SerializableTypeProxy:标记接口,标志是一个代理类 + // Serializable:代表可以被序列化 + Class[] interfaces = new Class[] {type, SerializableTypeProxy.class, Serializable.class}; + // 核心代码:TypeProxyInvocationHandler是什么? + InvocationHandler handler = new TypeProxyInvocationHandler(provider); + // 依赖于先前的InvocationHandler,以当前的type为目标对象创建了一个代理对象 + // + cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler); + cache.put(providedType, cached); + return cached; + } + } + throw new IllegalArgumentException("Unsupported Type class: " + providedType.getClass().getName()); +} +``` + +解析来我们分下下`TypeProxyInvocationHandler`这个类 + +```java +private static class TypeProxyInvocationHandler implements InvocationHandler, Serializable { + + private final TypeProvider provider; + + public TypeProxyInvocationHandler(TypeProvider provider) { + this.provider = provider; + } + + @Override + @Nullable + public Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { + // 复写目标类的equals方法 + if (method.getName().equals("equals") && args != null) { + Object other = args[0]; + // Unwrap proxies for speed + if (other instanceof Type) { + other = unwrap((Type) other); + } + return ObjectUtils.nullSafeEquals(this.provider.getType(), other); + } + // 复写目标类的hashCode方法 + else if (method.getName().equals("hashCode")) { + return ObjectUtils.nullSafeHashCode(this.provider.getType()); + } + + // 复写目标类的getTypeProvider方法 + else if (method.getName().equals("getTypeProvider")) { + return this.provider; + } + + // 之所以不直接返回method.invoke(this.provider.getType(), args);也是为了缓存 + // 空参的时候才能缓存,带参数的话不能缓存,因为每次调用传入的参数可能不一样 + if (Type.class == method.getReturnType() && args == null) { + return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1)); + } + else if (Type[].class == method.getReturnType() && args == null) { + Type[] result = new Type[((Type[]) method.invoke(this.provider.getType())).length]; + for (int i = 0; i < result.length; i++) { + result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i)); + } + return result; + } + + try { + return method.invoke(this.provider.getType(), args); + } + catch (InvocationTargetException ex) { + throw ex.getTargetException(); + } + } +} +``` + +------ + +# 总结 + +在这篇文章中我们主要学习了java的Type机制,如下: + +![官网实例化](image/2020031704.png) + +Type主要是用来处理泛型的,但是通过Java原始的这一套,处理起来及其的繁琐,所以Spring自行封装了一个ResolvableType,我们在处理类,方法,构造函数,字段时,只需要调用对应的方法就能返回一个对应的ResolvableType,一个ResolvableType就封装了对应的这个对象的原始类型,泛型等等,封装了Java中的所有类型。从这里也能看出Spring的牛逼之处,处理提供了IOC,AOP这两个强大的功能,还封装了一系列的简单易用的工具类。 \ No newline at end of file