1、容器技术内幕-AbstractApplicationContext.Refresh()
AbstractApplicationContext : 是ApplicationContext的抽象类,里面的refresh()方法是容器加载的入口。
Refresh()方法的主要流程如下:
第1步:初始化BeanFactory。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()
第2步:调用工厂后置处理器,之前生命周期中的BeanFactoryPostProcessor。
invokeBeanFactoryPostProcessors()
第3步:注册BeanPostProcessor,之前生命周期中的BeanPostProcessor在ApplicationContext加载的时候,在此处统一注册到 BeanFactory中。
registerBeanPostProcessors();
第4步:初始化消息源。
initMessageSource();
第5步:初始化应用上下文事件广播器。
initApplicationEventMulticaster();
第6步:初始化其他特殊的bean,由具体的子类实现。
onRefresh();
第7步:注册事件监听器。
registerListeners();
第8步:初始化所有单实例bean,延迟加载的除外,所有的bean都在此步骤实例化、属性设置包含依赖注入、初始化。
finishBeanFactoryInitialzation(beanFactory);
第9步:完成容器刷新,并广播容器刷新事件。
finishRefresh();
2主要组件
2.1、BeanDefinition:用于描述一个java类
org.springframework.beans.factory.config.BeanDefinition, 主要就是定义bean的一些属性,此类结构图如下:
Spring 通过BeanDefinitionReader读取配置信息的Resource,将其转化为容器的内部表示BeanDefinition,并且将这些 BeanDefinition注册到BeanDefinitionRegistry中,BeanDefinitionRegistry就像是spring配置信息的内存数据库,后续操 作将直接从BeanDefinitionRegistry中读取配置信息。
2.2、BeanDefinition加载流程
分为以下两步:
1、通过BeanDefinitionReader读取配置信息的Resource,通过解析,生成BeanDefinition,此处的BeanDefinition 可能是个半成品,因为在bean的定义中,可能存在占位符变量应用外部属性文件中配置的属性,如${jdbc.url}等 变量引用,这些变量在此处还没有被解析出来,因此此时的BeanDefinition可能是一个半成品。
2、利用BeanFactoryPostProcessor对半成品的BeanDefinition进行加工处理,将使用占位符应用的配置解析为最 终的实际值,这样这些半成品的BeanDefinition就被加工成为成品了。
2.3、PropertyPlaceholderConfigurer项目配置载入
PropertyPlaceholderConfigurer, 此类能够将bean 配置的引用外部属性文件中的参数解析为实际值, PropertyPlaceholderConfigurer此类也是实现了BeanFactoryPostProcessor接口,因而也是一个工厂后置处理器。像 ${jdbc.url}就是使用此工厂后置处理器解析为外部属性文件中的实际值的。
使用PropertyPlaceholderConfigurer:
1、直接定义<bean>:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:locations="classpath:application.properties,classpath:tapp.properties"
p:fileEncoding="utf-8"
p:placeholderPrefix="${"
p:placeholderSuffix="}"
p:order="1"/>
2、使用context命名空间简化上述配置如下:默认使用${变量名}
<context:property-placeholder ignore-unresolvable="true"
location="classpath:application.properties"
file-encoding="utf-8"
order="1" />
属性引用方式:
Xml 中获取配置方式: <property name="maxIdle" value="${redis.maxIdle}"/>
注解中获取配置方式:
@Component()
public class Car {
@Value("${jdbcRrl}")
private String jdbcurl;
}
2.4、<util:properties/> util命名空间项目配置载入
配置方式:
<util:properties id="meta" location="classpath:config/metainfo.properties" />
属性引用方式:
Xml 中获取配置方式: <property name="maxIdle" value="#{meta['redis.maxIdle']}"/>
注解中获取配置方式:
@Component()
public class Car {
@Value("#{meta['redis.maxIdle']}")
private String jdbcurl;
}
2.5、属性文件自身引用
如下:比如在application.properties文件中
dbName=aaa
dbUrl=jdbc:mysql://localhost:3306/${dbname} 此处就是属性文件中自身引用,参数名称字符中不能引用!!!
使用小技巧:如果一个属性值太长,可以使用“\”将属性划分为多行如:
desc : aaa\
bbb\
ccc
就等价于desc=aaabbbccc
引用bean的属性值:
xml 中引用格式: 使用#{beanName.属性名称} 来取值。
<bean id="boss" class="com.wzy.springstudy.propertyEditor.Boss" p:name="马云" />
<bean id="boss1" class="com.wzy.springstudy.propertyEditor.Boss" p:name="#{boss.name}"/>
注解中引用格式:@Value("#{beanName.name}")。
@Component
public class Car {
@Value("#{boss.name}")
private String name;
}
2.6、InstantiationStrategy: bean的实例化策略
org.springframework.beans.factory.support.InstantiationStrategy 主要负责根据BeanDefinition对象创建一个Bean实 例,此接口类图如下:
SimpleInstantiationStrategy 是最常用的实例化策略,该策略利用bean实现类的默认构造函数、有参数构造函数、或工 厂方法创建bena实例。
CglibSubclassingInstantiationStrategy 扩展了SimpleInstantiationStrategy ,为需要进行方法注入(如前面提到的 lookup、replace方法注入)的bean提供支持,它是利用CGLIB类库为bean动态生成子类,在子类中生成方法注入的逻 辑,然后使用这个动态生成的子类创建bean的实例。
总结:InstantiationStrategy仅负责bean的实例化工作,相当于执行Java中的new 对象的功能,它并不会参与bean属性 的设置工作。所以InstantiationStrategy返回的beanshi里实际上只是一个半成品的bean实例,属性填充的工作由 BeanWrapper来完成。
2.7、BeanWrapper:bean包装器
org.springframework.beans.BeanWrapper ,相当于一个代理器,spring 委托BeanWrapper完成bean的属性填充工作, 在bean被InstantiationStrategy实例化之后,容器主程序将bean实例通过BeanWrapper包装起来,这个包装是通过 BeanWrapper实现类BeanWrapperImpl的setWrappedInstance方法来完成的。
BeanWrapper 类图:
实现类只有一个就是BeanWrapperImpl, 从类图来看BeanWrapper的实现类
BeanWrapperImpl有三重身份:
1、Bean包装器。
2、属性访问器。
3、属性编辑器注册表。
属性填充流程:
从BeanDefinitionRegistry中获取BeanDefinition,然后获取到bean的propertyValue,然后使用PropertyEditor进 行转换得到bean的属性值。
2.8、属性编辑器
spring的属性编辑器也是使用Java的属性编辑器,我们只需要配置
org.springframework.beans.factory.config.CustomEditorConfigurer此配置即可。
1、自定义属性编辑器方式一
步骤1:定义自己的属性编辑器,直接继承PropertyEditorSupport 重写setAsText(String text)方法。
步骤2:将自定义的属性编辑器配置到spring的CustomEditorConfigurer即可。
案例如下:
public class Boss {
private String name;
private Car car;
}
public class Car {
private String name;
private String price;
}
public class CustomCarEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] strings = text.split(",");
Car car = new Car();
car.setName(strings[0] );
car.setPrice(strings[1]);
//调用父类的setValue()方法设置转换后的属性值。
setValue(car);
}
}
<bean id="boss" class="com.wzy.springstudy.propertyEditor.Boss" p:name="马云" p:car="迈巴赫,1100w"/>
<!--用户属性编辑器配置-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
//Key 表示需要属性编辑的类型,value 表示使用的编辑器。
<entry key="com.wzy.springstudy.propertyEditor.Car"
value="com.wzy.springstudy.propertyEditor.CustomCarEditor"/>
</map>
</property>
</bean>
2、自定义属性编辑器方式二
使用JavaBean规范,就是在相同的包路径下面找ClassName+Edior的类,如果找到了就直接使用此属性编辑器, 无需配置org.springframework.beans.factory.config.CustomEditorConfigurer
如:com.wzy.springstudy.propertyEditor.Car
com.wzy.springstudy.propertyEditor.CarEditor 就是JavaBean的规范,因此CarEditor 就是Car类型的属性编辑器。
在spring中配置依旧如下:
<bean id="boss" class="com.wzy.springstudy.propertyEditor.Boss" p:name="马云" p:car="迈巴 赫,1100w"/>