【README】
本文总结自《spring揭秘》,作者王福强,非常棒的一本书,墙裂推荐;
spring容器根据配置元素组装可用系统分2个阶段,包括spring容器启动, springbean实例化阶段; 本文详细分析spring容器启动阶段;
【1】Spring容器根据配置元素组装可用系统的过程
Spring容器根据配置元素组装可用系统的过程,有2个阶段:
- spring容器启动;
- springbean实例化;
容器启动阶段: 加载配置元数据(xml文件),然后使用工具类如 BeanDefinitionReader对加载的配置元数据进行解析,并将结果编组为 BeanDefinition ,注册到相应的 BeanDefinitionRegistry,这样容器启动工作就完成了;
Bean实例化阶段: 容器会首先检查所请求的对象之前是否已经初始化,若没有,则根据注册的BeanDefinition实例化bean,并为其注入依赖。 如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它; 当该对象被装配完成后 ,容器会立即将其返回给请求方使用;
【2】BeanFactoryPostProcessor-Bean工厂后置处理器
BeanFactoryPostProcessor: Spring提供了叫做 BeanFactoryPostProcessor 容器扩展机制; 该机制允许我们在容器启动阶段完成后新增逻辑;
常用BeanFactoryPostProcessor:
- PropertySourcePlaceHolderConfigurer:属性占位符配置器, 如jdbc连接串属性通过properties文件的属性值 替换 占位符;
- PropertyOverrideConfigurer : 属性覆盖替换配置器;如修改属性值;
- CustomEditorConfigurer:自定义编辑器配置器,用于不同数据格式间的转换;
【补充】
(1)上述2个BeanFactoryPostProcessor 都是通过修改BeanDefinition 来对属性进行替换或修改的;
(2) CustomerEditorConfigurer:没有修改BeanDefinition,而是把后期要用到的信息注册到容器;
【2.1】属性占位符配置器使用场景代码
PropertySourcesPlaceholderConfigurer-属性占位符配置器使用场景:初始化数据库连接池属性, 并利用 PropertyOverrideConfigurer 重写属性值;
【PropertySourcesPlaceholderConfigurerMain】入口
public class PropertySourcesPlaceholderConfigurerMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans0404.xml");
BasicDataSource dataSource = context.getBean("dataSource", BasicDataSource.class);
System.out.println(dataSource.getUrl());
System.out.println(dataSource.getUserName());
System.out.println(dataSource.getDriverClassName());
}
}
【beans0404.xml】
<!-- 为 PropertySourcesPlaceholderConfigurer 这个BeanFactoryPostProcessor 配置属性文件地址 -->
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<value>jdbc.properties</value>
</property>
</bean>
<!-- 为 PropertyOverrideConfigurer 这个BeanFactoryPostProcessor 配置重写属性文件的地址 -->
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations" value="ds-pool-override.properties" />
</bean>
<!-- 数据源bean,其中属性值通过变量指定,变量通过 BeanFactoryPostProcessor 设置值-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="driverClassName">
<value>${jdbc.driverClassName}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
</bean>
【jdbc.properties】
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=root
【ds-pool-override.properties】重写属性值的属性文件
dataSource.username=rootOverride
【打印日志】
jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai
rootOverride
com.mysql.cj.jdbc.Driver
【2.2】CustomerEditorConfigurer-自定义编辑器配置器
应用场景: xml配置的bean信息都是字符串,但最终都要要转换为bean对象的,从字符串类型到对象类型的转换,就是由CustomerEditorConfigurer(EditorConfigurer)来完成的;
- 只要为每种对象类型提供一个 PropertyEditor,就可以做类型转换
Spring 提供的PropertyEditor列表:
- (1) StringArrayPropertyEditor:把符合csv格式的字符串转换为String[] 数组的形式;类似的还有 ByteArrayPropertyEditor, CharArrayPropertyEditor;
- (2) ClassEditor: 根据class名称转为 Class对象;
- (3) FileEditor: file类型的PropertyEditor;类似的还有InputStreamEditor, URLEditor;
- (4) LocaleEditor: 本地化转换;
- (5) PatternEditor: 正则表达式转换
自定义属性编辑器PropertyEditor: 继承自 PropertyEditorSupport ; PropertyEditorSupport 实现了 PropertyEditor 接口; PropertyEditor 接口定义如下:
public interface PropertyEditor {
void setValue(Object value);
Object getValue();
boolean isPaintable();
void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box);
String getJavaInitializationString();
String getAsText();
void setAsText(String text) throws java.lang.IllegalArgumentException;
String[] getTags();
java.awt.Component getCustomEditor();
boolean supportsCustomEditor();
void addPropertyChangeListener(PropertyChangeListener listener);
void removePropertyChangeListener(PropertyChangeListener listener);
}
【2.3】自定义编属性编辑器案例代码
自定义日期i字符串转日期类型的编辑器;
【CustomPropertyEditorMain】入口
public class CustomPropertyEditorMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans0404.xml");
System.out.println(context.getBean("customDateDto", CustomDateDto.class));
}
}
【beans0404.xml】
<!-- 自定义属性编辑器 -->
<bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<ref bean="customDatePropertEditroResigtrar" />
</list>
</property>
</bean>
<bean id="customDatePropertEditroResigtrar" class="com.tom.springnote.chapter04.t0404.CustomDatePropertEditroResigtrar">
<property name="customDatePropertyEditor">
<ref bean="customDatePropertyEditor"/>
</property>
</bean>
<bean id="customDatePropertyEditor" class="com.tom.springnote.chapter04.t0404.CustomDatePropertyEditor">
<property name="datePattern" value="yyyy-MM-dd" />
</bean>
<bean id="customDateDto" class="com.tom.springnote.chapter04.t0404.CustomDateDto">
<property name="date" value="2024-08-04" />
</bean>
【CustomDatePropertEditroResigtrar】自定义日期属性编辑器注册器
public class CustomDatePropertEditroResigtrar implements PropertyEditorRegistrar {
private PropertyEditor customDatePropertyEditor;
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, getCustomDatePropertyEditor());
}
public PropertyEditor getCustomDatePropertyEditor() {
return customDatePropertyEditor;
}
public void setCustomDatePropertyEditor(PropertyEditor customDatePropertyEditor) {
this.customDatePropertyEditor = customDatePropertyEditor;
}
}
【CustomDatePropertyEditor】自定义日期属性编辑器
public class CustomDatePropertyEditor extends PropertyEditorSupport {
private String datePattern;
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
setValue(new SimpleDateFormat(getDatePattern()).parse(text));
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
public String getDatePattern() {
return datePattern;
}
public void setDatePattern(String datePattern) {
this.datePattern = datePattern;
}
}
【CustomDateDto】自定义dto (带date类型属性)
public class CustomDateDto {
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String toString() {
return "CustomDateDto{" +
"date=" + date +
'}';
}
}
【打印日志】 (把字符串2024-08-04转换为 date类型属性)
CustomDateDto{date=Sun Aug 04 00:00:00 CST 2024}