Spring源码分析-Bean创建流程一

目录

一、Bean的创建入口

二、转化服务:ConversionService

三、值解析器:propertySourcePlaceholderConfigurer

四、合并Bean的定义信息:getMergedLocalBeanDefinition

五、工厂Bean:FactoryBean


一、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);
	}
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值