Spring底层实现之源码解读

做为Java开源世界的第一框架,Spring已经成为事实上的Java EE开发标准Spring框架最根本的使命是简化Java开发,因此学习、研究、掌握Spring框架成为每一位Java开发人员的必修课。而阅读源码则是学习Spring的最好方式之一。

Spring里面最重要的特性就是Ioc,可能你还会说aop。其实aop的实现也是基于ioc。Ioc(Inversion of Control),即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

关于Spring IOC源码分析的文章网上很多,现在我就来另辟蹊径。Spring Ioc的对象扭转以及涉及到的核心接口来分析一下它的源码实现。

我把Spring Ioc的对象转换分为以下4个步骤:

Resource -> BeanDefinition -> BeanWrapper -> Object

1 Resource

Spring底层实现之源码解读

Resouce 其实是一个接口,代表的是资源,在计算机里面从一个地方移动到另外一个地方所需要的东西就是数据流,所以 Resource 实现了 InputStreamSource 接口,通过 InputStreamSource 接口可以获取到 Inputstream,这样就可以读取不同的 Bean 定义了。

public interface InputStreamSource {

InputStream getInputStream() throws IOException;

}

Spring可以定义不同类型的bean,最后都可以封装成Resource通过IO流进行读取。 Spring可以定义类型的bean对象:

  • XML:这是Spring最开始定义bean的形式
  • Annotation :由于通过XML定义bean的繁琐,Spring进行了改进可以通过@Component以及基于它的注解来定义bean。例如:@Service,@Controller等等,它们都可以定义bean ,只不过语义更加明确。
  • Class:通过@Configuration与@Bean注解定义,@Configuration代理xml资源文件,而@Bean代替<bean>标签。/<bean>
  • Properties/yml:通过 @EnableConfigurationProperties 与 @ConfigurationProperties 来定义bean。这种形式在Spring boot自动注入里面大量使用。

2、BeanDefinition

望文生义,很显示这个是 Bean 对象的定义。 Spring通过不同形式来定义bean,最终会把这些定义转化成BeanDefinition 保存在Spring容器当中进行依赖注入。下面我们来看一下BeanDefinition的接口定义。

Spring底层实现之源码解读

这个接口的定义很复杂但是,对于初始理解spring ioc,只需要关心两个方法。

  • getConstructorArgumentValues:获取构造器注入定义的参数。
  • getPropertyValues:获取setter注入定义的参数。

所以Spring支持构造器注入与setter依赖注入。

Spring底层实现之源码解读

3、BeanWapper

其实什么是依赖注入,简单来说就是Spring帮我们创建对象。把创建对象写死在Java文件变成了通过不同的Spring配置可以注入不同的值。创建对象的职责由Java文件变成了Spring配置文件。

下面我就问一个简单的问题,如何创建对象。可能大家呵呵一笑,创建对象这还不简单。

Spring底层实现之源码解读

其实 Spring 也是这样来创建对象的,不信讲看 : (入口方法 BeanFactory#getBean)

  1. AbstractAutowireCapableBeanFactory#createBeanInstance() :通过反射Constructor调用配置的无参或者有参来创建对象实例。通过BeanDefinition#getConstructorArgumentValues 获取,并返回 BeanWrapper 对象。
  2. AbstractAutowireCapableBeanFactory#populateBean():,获取到定义的bean生成的所有的定义的setter注入的属性(BeanDefinition#getPropertyValues),然后遍历些这些属性,通过内省获取到对象所有的 属性描述器(PropertyDescriptor),获取到,属性的PropertyDescriptor#getWriteMethod方法,也就是setter方法,依赖注入值。如果是普通属性或者一些复杂对象,比如普通属性String, int, long或者classpath:*转换为Resource复杂对象等等,直接注入即可;对于引用类型对象,继续依赖注入直到所有的属性是普通属性为止。
  3. AbstractAutowireCapableBeanFactory#initializeBean():调用 Spring 自定义的初始化方法比如:BeanPostProcessor 扩展以及 init-method。

实例化对象返回BeanWrapper,其实是为了依赖注入服务也就是上面的第二步。 这个接口的功能还是很复杂的,它继承了 4 个接口。

  • TypeConverter
  • PropertyEditorRegistry
  • PropertyAccessor
  • ConfigurablePropertyAccessor

下面就来分别介绍一下这些接口的功能。

3.1 TypeConverter

下面就是这个接口的定义。

Spring底层实现之源码解读

它的作用就是自动类型转换,因为Spring作得太无感知了。你也许还没有感觉到它的存在。没关系,我提示一下你应该就会明白。比如,声明一个用户对象这个对象既有String类型的名称,又有Int类型的年龄。Spring怎么知道属性的类型呢?这个就是 Spring的自动类型转换。关于Spring的自动类型转换 我在之前就已经分析过了。

3.2 PropertyEditorRegistry

这个接口主要的作用是注册属性修改器(PropertyEditor),这个是 Java 内省里面的机制。

Spring底层实现之源码解读

一般通过继承 java.beans.PropertyEditorSupport 来实现自定义的类型转换。在 Spring 内部有大量的实现,如下图所示

Spring底层实现之源码解读

3.3 PropertyAccessor

Spring底层实现之源码解读

PropertyAccessor这个接口是判断对象中的某个属性是否可读/可写,并且可以定入或者读取某个属性的值。从这个接口定义我们可以看出,它的使用其实就是真正用来依赖注入的。然后调用属性操作的写入操作,完全依赖注入。

3.4 ConfigurablePropertyAccessor

Spring底层实现之源码解读

这个接口的功能和PropertyEditorRegistry接口一样,只不过后者是通过java内省来进行类型自动转换,而ConfigurablePropertyAccessor接口是通过Spring自己定义的org.springframework.core.convert.ConversionService来作类型转换类型转换。在Spring中默认使用的DefaultConversionService来作自动类型转换支持,并且内部还添加了很多默认的类型转换。

Spring底层实现之源码解读

Spring底层实现之源码解读

Spring底层实现之源码解读

Spring底层实现之源码解读

关于Spring的自动类型转换 我在之前就已经分析过了。和java内省的原理是一样的。

3.5 BeanWrapper

Spring底层实现之源码解读

这个接口就挺简单了,通过实现了上面的几个接口具有了依赖注入、类型转换注册(java 内省或者Spring自定义的自动类型转换)。然后这个接口的主要的作用就是通过调用getWrappedInstance方法获取到当前实例对象,提供给属性的writer方法进行依赖注入。

writeMethod.invoke(getWrappedInstance(), value);

  • 1

4、总结

让我们再来看一下Spring的对象扭转过过程:

Resource -> BeanDefinition -> BeanWrapper -> Object

相信基于以上的讲解,大家对于上面的过程能够理解Spring IOC的项目过程。在Spring 进行 依赖注入的时候,首先把这种资源转化成Resource抽象,通过里面的IO流读取定义的bean。然后再转化成BeanDefinitioin,里面定义了包括构造器注入,以及setter注入的定义。最后通过BeanWrapper这个接口,首先获取定义的构造器注入属性,通过反射中的Constructor来创建对象。基于这个对象,通过java里面的内省机制获取到定义属性的属性描述器(PropertyDescriptor),调用属性的写入方法完成依赖注入,最后再调用Spring的自定义初始化逻辑,主要包括以下三个扩展点:

  • BeanPostProcess,Spring aop就是基于此扩展。
  • Init-method,可以在 bean 标签通过 init-method定义,也可以实现InitializingBean
  • XXXAware,Spring容器感知类,可以在bean里面获取到 Spring容器的内部属性。
Spring底层实现之源码解读


分享到:


相關文章: