做为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
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>
标签。 - Properties/yml:通过 @EnableConfigurationProperties 与 @ConfigurationProperties 来定义 bean。这种形式在 Spring boot 自动注入里面大量使用。
2、BeanDefinition
望文生义,很显示这个是 Bean 对象的定义。 Spring 通过不同形式来定义 bean,最终会把这些定义转化成 BeanDefinition 保存在 Spring 容器当中进行依赖注入。下面我们来看一下 BeanDefinition 的接口定义。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
ConstructorArgumentValues getConstructorArgumentValues();
MutablePropertyValues getPropertyValues();
...
}
这个接口的定义很复杂但是,对于初始理解 spring ioc,只需要关心两个方法。
- getConstructorArgumentValues:获取构造器注入定义的参数。
- getPropertyValues:获取 setter 注入定义的参数。
所以 Spring 支持构造器注入与 setter 依赖注入。
1、构造器注入
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
2、setter注入
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
3、BeanWapper
其实什么是依赖注入,简单来说就是 Spring 帮我们创建对象。把创建对象写死在 Java 文件变成了通过不同的 Spring 配置可以注入不同的值。创建对象的职责由 Java 文件变成了 Spring 配置文件。
下面我就问一个简单的问题,如何创建对象。可能大家呵呵一笑,创建对象这还不简单。
1、无参构造器
Object obj = new Object();
obj.setXxx(xxx);
2、有参构造器
Object obj = new Object(xxx, yyyy);
obj.setXxx(xxx);
其实 Spring 也是这样来创建对象的,不信讲看 : (入口方法 BeanFactory#getBean
)
- AbstractAutowireCapableBeanFactory#createBeanInstance() :通过反射 Constructor 调用配置的无参或者有参来创建对象实例。通过 BeanDefinition#getConstructorArgumentValues 获取,并返回 BeanWrapper 对象。
- AbstractAutowireCapableBeanFactory#populateBean():,获取到定义的 bean 生成的所有的定义的setter注入的属性(BeanDefinition#getPropertyValues),然后遍历些这些属性,通过内省获取到对象所有的 属性描述器(PropertyDescriptor),获取到,属性的 PropertyDescriptor#getWriteMethod 方法,也就是 setter 方法,依赖注入值。如果是普通属性或者一些复杂对象,比如普通属性 String, int, long 或者 classpath:*转换为 Resource 复杂对象等等,直接注入即可;对于引用类型对象,继续依赖注入直到所有的属性是普通属性为止。
- AbstractAutowireCapableBeanFactory#initializeBean():调用 Spring 自定义的初始化方法比如:BeanPostProcessor 扩展以及 init-method。
实例化对象返回 BeanWrapper,其实是为了依赖注入服务也就是上面的第二步。 这个接口的功能还是很复杂的,它继承了 4 个接口。
- TypeConverter
- PropertyEditorRegistry
- PropertyAccessor
- ConfigurablePropertyAccessor
下面就来分别介绍一下这些接口的功能。
3.1 TypeConverter
下面就是这个接口的定义。
public interface TypeConverter {
<T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;
<T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException;
<T> T convertIfNecessary(Object value, Class<T> requiredType, Field field)
throws TypeMismatchException;
}
它的作用就是自动类型转换,因为 Spring 作得太无感知了。你也许还没有感觉到它的存在。没关系,我提示一下你应该就会明白。比如,声明一个用户对象这个对象既有 String 类型的名称,又有 Int 类型的年龄。 Spring 怎么知道属性的类型呢?这个就是 Spring 的自动类型转换。关于 Spring 的自动类型转换 我在之前就已经分析过了。
3.2 PropertyEditorRegistry
这个接口主要的作用是注册属性修改器(PropertyEditor),这个是 Java 内省里面的机制。
public interface PropertyEditorRegistry {
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor);
PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath);
}
一般通过继承 java.beans.PropertyEditorSupport 来实现自定义的类型转换。在 Spring 内部有大量的实现,如下图所示:
3.3 PropertyAccessor
public interface PropertyAccessor {
boolean isReadableProperty(String propertyName);
boolean isWritableProperty(String propertyName);
cessor method failed
Class<?> getPropertyType(String propertyName) throws BeansException;
TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
Object getPropertyValue(String propertyName) throws BeansException;
void setPropertyValue(String propertyName, 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;
}
PropertyAccessor 这个接口是判断对象中的某个属性是否可读/可写,并且可以定入或者读取某个属性的值。从这个接口定义我们可以看出,它的使用其实就是真正用来依赖注入的。然后调用属性操作的写入操作,完全依赖注入。
3.4 ConfigurablePropertyAccessor
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
void setConversionService(ConversionService conversionService);
ConversionService getConversionService();
...
}
这个接口的功能和 PropertyEditorRegistry 接口一样,只不过后者是通过 java 内省来进行类型自动转换,而 ConfigurablePropertyAccessor 接口是通过 Spring 自己定义的 org.springframework.core.convert.ConversionService 来作类型转换类型转换。在 Spring 中默认使用的是 DefaultConversionService 来作自动类型转换支持,并且内部还添加了很多默认的类型转换。
public class DefaultConversionService extends GenericConversionService {
/** Java 8's java.util.Optional class available? */
private static final boolean javaUtilOptionalClassAvailable =
ClassUtils.isPresent("java.util.Optional", DefaultConversionService.class.getClassLoader());
/** Java 8's java.time package available? */
private static final boolean jsr310Available =
ClassUtils.isPresent("java.time.ZoneId", DefaultConversionService.class.getClassLoader());
/** Java 8's java.util.stream.Stream class available? */
private static final boolean streamAvailable = ClassUtils.isPresent(
"java.util.stream.Stream", DefaultConversionService.class.getClassLoader());
public DefaultConversionService() {
addDefaultConverters(this);
}
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
if (jsr310Available) {
Jsr310ConverterRegistrar.registerJsr310Converters(converterRegistry);
}
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
if (javaUtilOptionalClassAvailable) {
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
}
public static void addCollectionConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
converterRegistry.addConverter(new MapToMapConverter(conversionService));
converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
converterRegistry.addConverter(new StringToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
if (streamAvailable) {
converterRegistry.addConverter(new StreamConverter(conversionService));
}
}
private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(Enum.class, String.class,
new EnumToStringConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCharsetConverter());
converterRegistry.addConverter(Charset.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToCurrencyConverter());
converterRegistry.addConverter(Currency.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToUUIDConverter());
converterRegistry.addConverter(UUID.class, String.class, new ObjectToStringConverter());
}
private static final class Jsr310ConverterRegistrar {
public static void registerJsr310Converters(ConverterRegistry converterRegistry) {
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
}
}
}
关于 Spring 的自动类型转换 我在之前就已经分析过了。和 java 内省的原理是一样的。
3.5 BeanWrapper
public interface BeanWrapper extends ConfigurablePropertyAccessor {
Object getWrappedInstance();
Class<?> getWrappedClass();
PropertyDescriptor[] getPropertyDescriptors();
PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
...
}
这个接口就挺简单了,通过实现了上面的几个接口具有了依赖注入、类型转换注册(java 内省或者 Spring 自定义的 自动类型转换)。然后这个接口的主要的作用就是通过调用 getWrappedInstance
方法获取到当前实例对象,提供给属性的 writer 方法进行依赖注入。
writeMethod.invoke(getWrappedInstance(), value);
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 IOC 有一个宏观上面的认识。这样就不会在复杂源码的逻辑中迷失。