目录
三、值解析器:propertySourcePlaceholderConfigurer
四、合并Bean的定义信息:getMergedLocalBeanDefinition
一、Bean的创建入口
这章主要把以下两点搞清楚就可以了
1、什么是类型转化器,用来干嘛的?
2、什么是属性值解析器,有什么用?
解释:从AbstractApplicationContext下的refresh方法下的finishBeanFactoryInitialization(beanFactory)进入(以下代码混个眼熟)
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 为上下文初始化类型转换器
// 1.判断IOC容器中是否存在了conversionService这个BeanName==>也就是我们在Application中进行注册的那个Bean
// 2.如果存在这个Bean的话,就将转化服务注册到IOC容器中
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// 如果beanFactory之前没有注册嵌入值解析器,则注册默认的嵌入值解析器,主要用于注解属性值的解析
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// 尽早初始化loadTimeWeaverAware bean,以便尽早注册它们的转换器
// 【何时注入】在prepareBeanFactory(beanFactory);时注入了LoadTimeWeaverAware
// AOP时再讲
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 禁止使用临时类加载器进行类型匹配
beanFactory.setTempClassLoader(null);
// 冻结所有的bean定义,说明注册的bean定义将不被修改或任何进一步的处理
beanFactory.freezeConfiguration();
// 实例化剩下的单例对象
beanFactory.preInstantiateSingletons();
}
二、转化服务:ConversionService
1、从源码中可知,如果容器中存在ConversionService这个BeanName的话,就将它注册到IOC容器中
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
2、怎么理解转换服务?
就像之前提到的属性解析器一样,我们可以输入一个字符串"province_city_country",以下划线的方式分隔成独立字段然后注入到Address类的属性中
3、怎么使用转换服务?
①自定义的转化服务可以必须选择继承3个父接口,分别是:Converter,GenericeConverter,ConverterFactory
②在xml配置文件中,将以上三个的实现子类注入到ConversionServiceFactoryBean的converters集合内
③关于三个父接口的差别与xml文件中的配置见下图
4、ConversionService类结构及其作用
① ConversionServiceFactoryBean:转换服务的工厂类,用于创建ConversionService
public class ConversionServiceFactoryBean implements FactoryBean<ConversionService>, InitializingBean {
// 保存自定义的转换器
@Nullable
private Set<?> converters;
// 如果没有自定义conversionService,那么默认使用的是DefaultConversionService
@Nullable
private GenericConversionService conversionService;
public void setConverters(Set<?> converters) {
this.converters = converters;
}
// bean初始化结束之后,注册自定义的转换器进去
@Override
public void afterPropertiesSet() {
this.conversionService = createConversionService(); // 初始化什么,关键 DefaultConversionService();
ConversionServiceFactory.registerConverters(this.converters, this.conversionService);
}
protected GenericConversionService createConversionService() {
return new DefaultConversionService();
}
}
② DefaultConversionService 默认转化类
/**
* 对一系列的converter进行注册
*/
public class DefaultConversionService extends GenericConversionService {
@Nullable
private static volatile DefaultConversionService sharedInstance;
public DefaultConversionService() {
addDefaultConverters(this);
}
// 此处用到了DCL双重检查锁
public static ConversionService getSharedInstance() {
DefaultConversionService cs = sharedInstance;
if (cs == null) {
synchronized (DefaultConversionService.class) {
cs = sharedInstance;
if (cs == null) {
cs = new DefaultConversionService();
sharedInstance = cs;
}
}
}
return cs;
}
// 注册各种默认的转化器
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
converterRegistry.addConverter(new ByteBufferConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new StringToTimeZoneConverter());
converterRegistry.addConverter(new ZoneIdToTimeZoneConverter());
converterRegistry.addConverter(new ZonedDateTimeToCalendarConverter());
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
}
③ GenericConversionService:通用的类型转换实现类,适用于大部分的转换情况
/**
* 通用的类型转换实现类,适用于大部分的转换情况
*
*/
public class GenericConversionService implements ConfigurableConversionService {
/**
* General NO-OP converter used when conversion is not required.
*/
private static final GenericConverter NO_OP_CONVERTER = new NoOpConverter("NO_OP");
/**
* Used as a cache entry when no converter is available.
* This converter is never returned.
*/
private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH");
private final Converters converters = new Converters();
private final Map<ConverterCacheKey, GenericConverter> converterCache = new ConcurrentReferenceHashMap<>(64);
// ConverterRegistry implementation
/**
* 添加converter
* @param converter
*/
@Override
public void addConverter(Converter<?, ?> converter) {
// 获取对应的source、target类型
ResolvableType[] typeInfo = getRequiredTypeInfo(converter.getClass(), Converter.class);
// 如果对应的类型为空并且converter是代理,再次尝试获取
if (typeInfo == null && converter instanceof DecoratingProxy) {
typeInfo = getRequiredTypeInfo(((DecoratingProxy) converter).getDecoratedClass(), Converter.class);
}
// 如果target为null,那么抛出异常
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to determine source type <S> and target type <T> for your " +
"Converter [" + converter.getClass().getName() + "]; does the class parameterize those types?");
}
// 添加到内部类Converters里面进行管理
addConverter(new ConverterAdapter(converter, typeInfo[0], typeInfo[1]));
}
}
5、怎么用?
① 定义一个实体类
public class Student {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
② 实现一对一装换类
public class StudentConverter implements Converter<String,Student> {
@Override
public Student convert(String source) {
System.out.println("-----");
Student s = new Student();
String[] splits = source.split("_");
s.setId(Integer.parseInt(splits[0]));
s.setName(splits[1]);
return s;
}
}
③ 注入转换类到ConversionServiceFactoryBean里面
<bean id="studentConverter" class="com.yang.test.selfConverter.StudentConverter"></bean>
<bean id ="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="studentConverter"></ref>
</set>
</property>
</bean>
④ 测试类
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("selfConverter.xml");
ConversionService bean = context.getBean(ConversionService.class);
Student convert = bean.convert("1_zhangsan", Student.class);
System.out.println(convert);
}
备注:可以读bean.convert()怎么调到自定义的convert,ConversionServeice整体结构是怎样
三、值解析器:propertySourcePlaceholderConfigurer
1、什么作用?,类似于可以对"${","}"进行解析
2、什么时候注入
xml文件中
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
3、类结构图
4、什么时候内置到IOC容器中的,见下图可知
该类是BFPP类型,所以当IOC容器中识别到该类时,会调用一些列的方法,并调用postProcessorBeanFactory方法,之后就进行了一些列的调用
5、代码位置-PlaceholderConfigurerSupport.doProcessProperties
/**
* 使用指定的字符串值解析器处理从起中所有的bean定义属性
*
* @param beanFactoryToProcess 要处理的bean定义所属的容器
* @param valueResolver 属性值解析器
*/
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
// 使用指定的字符串值解析器 valueResolver 定义一个bean定义访问器,
// 该访问器的目的就是每次访问一个bean定义,将其中所有可能包含占位符的属性值,包括bean属性值,
// bean构造函数参数值,双亲bean名称,bean类名,bean工厂bean名称,bean工厂方法名称,作用域
// 等都遍历一遍,进行需要的占位符解析
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
// 获取容器中所有bean的名称
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
// 遍历bean定义进行属性值占位符解析
for (String curName : beanNames) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
// 检查当前bean的名称不等于被处理的bean的名称并且要处理的容器是自己所在的容器
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
// 对bean定义bd进行属性值占位符解析
visitor.visitBeanDefinition(bd);
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
}
}
}
// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
// 处理别名中的占位符
beanFactoryToProcess.resolveAliases(valueResolver);
// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
// 设置占位符处理器为内置的值处理器
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
四、合并Bean的定义信息:getMergedLocalBeanDefinition
1、合并的对象是什么?
是当前对象和其以上父类(父Bean)的信息合并。
2、为什么合并?
① 子类继承父类,也继承了父类的公有属性
② 当我们获取到Bean的初始化BeanDefinition时,并未进行创建,所以父类也没有加载
③ 当我们获取到父类的BeanDefinition并将其丰富自身的BeanDefinition。这样后续在创建Bean的时候,就可以直接那合并后的BeanDefinition直接进行创建。
3、什么时候进行合并的?
->在进行调用BFPP时,我们使用到了getBeanNamesForType(),在该方法的调用链下我们第一次使用到了getMergedLocalBeanDefinition。
->在这里我们只对getMergedLocalBeanDefinition进行阐述,见下图
五、工厂Bean:FactoryBean
1、怎么理解BeanFactory与FactoryBean
2、为什么getBean('&FactoryBean的beanName')和getBean('FactoryBean的beanName')得到的对象不一样?Spring是怎么区分的仅仅是通过一个'&'
3、见下图
&carFactoryBean与carFactoryBean区别
/**
* &carFactoryBean与carFactoryBean区别
* &carFactoryBean:获取carFactoryBean对象,该对象是放到一级缓存里面的
* carFactoryBean:获取到的是Car对象,该对象是在factoryBeanObjectCache
*/
public class FactoryBeanTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("factorybean.xml");
CarFactoryBean carFactoryBean = (CarFactoryBean)context.getBean("&carFactoryBean");
System.out.println(carFactoryBean);
Car car = (Car)context.getBean("carFactoryBean");
System.out.println(car);
// 以下直接获取会报错
// Car car = (Car)context.getBean("car");
// System.out.println(car);
}
}