本文将继续介绍Spring源码的重要方式AbstractApplicationContext#refresh方式的第三个方式:prepareBeanFactory,准备BeanFactory。
源码如下:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
// 设置类加载器
beanFactory.setBeanClassLoader(getClassLoader());
// 设置Spel 表达式解析器,用于属性填充时对值进行表达式解析
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 扩展点,添加一个属性编辑器的注册器,也可以使用 CustomEditorConfigurer 进行设置
// 后面在进行属性填充的时候会调用这个属性编辑器进行属性的解析
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
// 扩展点,添加一个BeanPostProcessor 这里添加这个进行处理,使用前置处理器执行下面忽略的六个Aware接口
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 由于上面设置了这六个接口,因此需要忽略掉,不让Spring使用自动装配进行Bean的装配,而是使用BeanPostProcessor
// 的后置处理器的前置方法进行调用,因为如果不忽略,那么自定义的Bean中就会使用Setter注入进行装配,
// spring 这样做是为了统一的进行处理在Bean增强的时候
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
// 添加一个事件监听器的装饰器
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// aop织入 编译器织入、运行期织入、类加载织入
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
// 注册环境信息
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
这个方式主要做了以下事情:
这个方式预留了一些扩充点,比如可以添加自定义的属性编辑器,添加自定义的BeanPostProcessor等。
定制Bean的属性解析器
我们晓得在Bean的初始化时是分为两步,一步是属性填充,一步是初始化,在属性填充的时侯,Spring会针对属性进行解析,如果属性值对应的类型和传入的值类型不一致,就会进行值的自定义解析,前提是你自定义了属性解析器el编辑器查物品代码,否则都会报错:报值的类型转换失败。
接下来我们自定义一个属性解析器,比如我如今有个类CustomUser,其中有个属性类型是Address,还有个属性类型是Date,但是我在定义Bean的时侯我把address属性设置为xxx_xxx_xxx,表示xxx省xxx市xxx区(县),date属性设置为yyyy-MM-dd HH:mm:ss格式的日期,我要让Spring帮我解析出正确的值下来,话不多说,上代码。
编写CustomUser类以及Address类
/**
* @author redwinter
* @since 1.0
**/
public class Address {
private String province;
private String city;
private String town;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getTown() {
return town;
}
public void setTown(String town) {
this.town = town;
}
// 为了验证重写toString方法
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", town='" + town + '\'' +
'}';
}
}
/**
* @author redwinter
* @since 1.0
**/
public class CustomUser {
private String name;
private Address address;
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "CustomUser{" +
"name='" + name + '\'' +
", address=" + address +
", date=" + date +
'}';
}
}
编写Address解析器和注册器
/**
* @author redwinter
* @since 1.0
**/
public class AddressPropertyEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
/**
* 自定义属性编辑器,将属性解析成自定义对象,比如传入的是一个字符串,可以解析成另外一个对象
*/
String[] arr = text.split("_");
Address address = new Address();
address.setProvince(arr[0]);
address.setCity(arr[1]);
address.setTown(arr[2]);
// 设置值
setValue(address);
}
}
/**
* @author redwinter
* @since 1.0
**/
public class AddressPropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Address.class,new AddressPropertyEditor());
}
}
编写Date解析器和注册器
/**
* @author redwinter
* @since 1.0
**/
public class DatePropertyEditor extends PropertyEditorSupport {
private final DateFormat dateFormat;
public DatePropertyEditor(DateFormat dateFormat) {
this.dateFormat = dateFormat;
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
if (!StringUtils.hasText(text)) {
System.out.println("日期类型的属性不能为空!");
return;
}
try {
Date date = dateFormat.parse(text);
setValue(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
/**
* @author redwinter
* @since 1.0
**/
public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class,new DatePropertyEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")));
}
}
配置注册器到Spring容器中
配置注册器有两种形式,一种是直接在订制BeanFactory的方式中添加注册器,一种是在Spring配置文件中添加
我在Spring容器中先配置CustomUser的信息以及Date日期类型的注册器:
另外还有个Address的注册器我配置在订制BeanFactory的方式中:
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
// 扩展点 设置不去处理循环依赖或者beanDefinition覆盖
super.setAllowBeanDefinitionOverriding(true);
super.setAllowCircularReferences(true);
super.customizeBeanFactory(beanFactory);
// 添加一个自定义的属性编辑器的注册器
beanFactory.addPropertyEditorRegistrar(new AddressPropertyEditorRegistrar());
}
好了,配置完成,运行试试:
public class BeanCreate {
@Test
public void classPathXml() {
// ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-test.xml");
ClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext("classpath:spring-test.xml");
Redwinter redwinter = (Redwinter) context.getBean("redwinter");
System.out.println(redwinter.getEmail());
Redwinter redwinter123456 = (Redwinter) context.getBean("redwinter123456");
System.out.println(redwinter123456.getEmail());
CustomUser bean = context.getBean(CustomUser.class);
System.out.println(bean);
}
}
输出日志:
abc@qq.com
123456-abc@qq.com
CustomUser{name='冬玲记忆', address=Address{province='四川省', city='成都市', town='郫都区'}, date=Tue Apr 19 19:50:20 CST 2022}
说明配置没有问题,输出了想要的结果。
这里有个疑惑为何你晓得在编撰注册器和解析器的时侯须要实现这种类呢?
其实缘由很简单,根据源码我们晓得Spring默认添加了一个ResourceEditorRegistrar注册器,进去ResourceEditorRegistrar类中发觉,他实现了PropertyEditorRegistrar插口,然后重画了registerCustomEditors方式,在这个方式中他添加了好多编辑器:
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
doRegisterEditor(registry, Resource.class, baseEditor);
doRegisterEditor(registry, ContextResource.class, baseEditor);
doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
ClassLoader classLoader = this.resourceLoader.getClassLoader();
doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
if (this.resourceLoader instanceof ResourcePatternResolver) {
doRegisterEditor(registry, Resource[].class,
new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
}
}
继续点doRegisterEditor方式发觉最终是将这种编辑器加入到了PropertyEditorRegistry插口的默认实现类PropertyEditorRegistrySupport类的customEditors属性中,而且你还在这个类中发觉,有很多的编辑器是默认加载进去的:
private void createDefaultEditors() {
this.defaultEditors = new HashMap<>(64);
// Simple editors, without parameterization capabilities.
// The JDK does not contain a default editor for any of these target types.
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
this.defaultEditors.put(Class[].class, new ClassArrayEditor());
this.defaultEditors.put(Currency.class, new CurrencyEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
this.defaultEditors.put(Path.class, new PathEditor());
this.defaultEditors.put(Pattern.class, new PatternEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Reader.class, new ReaderEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
this.defaultEditors.put(URI.class, new URIEditor());
this.defaultEditors.put(URL.class, new URLEditor());
this.defaultEditors.put(UUID.class, new UUIDEditor());
this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());
// Default instances of collection editors.
// Can be overridden by registering custom instances of those as custom editors.
this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
// Default editors for primitive arrays.
this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
// The JDK does not contain a default editor for char!
this.defaultEditors.put(char.class, new CharacterEditor(false));
this.defaultEditors.put(Character.class, new CharacterEditor(true));
// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
// The JDK does not contain default editors for number wrapper types!
// Override JDK primitive number editors with our own CustomNumberEditor.
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
// Only register config value editors if explicitly requested.
if (this.configValueEditorsActive) {
StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
this.defaultEditors.put(String[].class, sae);
this.defaultEditors.put(short[].class, sae);
this.defaultEditors.put(int[].class, sae);
this.defaultEditors.put(long[].class, sae);
}
}
所以为何我们在定义Bean属性的时侯这种默认的属性会手动帮你转换下来,就是这个缘由。那么注册器的编撰我们也可以直接实现PropertyEditorRegistrar这个插口,然后重画registerCustomEditors方式把自定义的编辑器加入即可。
那编辑器如何实现的呢?
编辑器的话自然也就很简单了,随便点击一个编辑器看下他是如何实现的,你就可以实现下来了,最终发觉这种编辑器都是承继了PropertyEditorSupport这个类,而PropertyEditorSupport这个类实现了PropertyEditor这个插口,那如此方式实现那个呢?
不着急看源码:
private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
try {
editor.setValue(oldValue);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
}
// Swallow and proceed.
}
// 调动转换方法,这里就会调用到自定义的属性编辑器中,执行自定义的逻辑转换
editor.setAsText(newTextValue);
return editor.getValue();
}
源码明晰写了使用编辑器调用setAsText方式进行新值的转换el编辑器查物品代码,然后再去获取getValue得到值,那么说明只须要重画setAsText方式而且将转换的值调用setValue方式即可。
所以我们直接承继PropertyEditorSupport类,然后重画setAsText方式即可实现属性值的解析和转换。
接下来就是剖析AbstractApplicationContext#refresh方式的第四个方式和第五个方式,第四个方式postProcessBeanFactory是一个空方式,留给泛型实现,第五个方式invokeBeanFactoryPostProcessors 是执行BeanFactoryPostProcessor,这篇就到这儿,下一篇文章继续。
如果本文对你有帮助,别忘掉给我个3连 ,点赞,转发,评论,,咱们上期见。