13.Spring IOC容器的加载过程
四个形态
概念态---<Bean信息>---> 定义态----BeanDefinition---->纯净态----DI---->成熟态
对应Bean的生命周期
实例化 --> 属性注入 ---> 实例化
概念态---->定义态
-
实例化一个ApplicationContext的对象
-
调用bean工厂后置处理器完成扫描;
-
循环解析扫描出来的类信息;
-
实例化一个BeanDefinition对象来存储解析出来的信息
-
把实例化好的beanDefinition对象put到beanDefinitionMap当中缓存起来以便后面实例化bean;
-
再次调用其他bean工厂后置处理器
定义态----BeanDefinition---->纯净态
-
当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor等等,如果我们只关心如何实例化一个bean的话那么这一步就是spring调用finishBeanFactoryinitialization方法来实例化单例的bean,实例化之前spring要做验证需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否 abstract等等;
-
如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法;
-
推断完构造方法之后spring调用构造方法反射实例化一个对象;注意我这里说的是对象、对象、对象;这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入,所以不是一个完整的bean;10:spring处理合并后的beanDefinition
纯净态----DI---->成熟态
-
Spring处理合并的BeanDefinition
-
判断是否需要完成属性注入
-
如果需要,则开始注入
初始化
-
判断bean的类型回调Aware接口。
-
调用生命周期回调方法
-
如需要代理完成代理
创建完成
-
放到单例池中去。
14 Spring Ioc的扩展点
挑重点回答:
-
BeanFactoryPostProcessor的postProcessBeanFactory方法
-
BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry
-
BeanPostProcessor实现类:在Bean的生命周期会调用9次Bean的后置处理器
-
初始化阶段回到用很多的Aware接口
-
在初始化和销毁中,也会有很多的回调方法。
15 SpringBean和JavaBean
被Spring IOC容器管理的对象都是Bean。
Java的类就是JavaBean
16 配置Bean的方法
-
XML
-
注解:@Component @Service @Controller @Repository,需要配置扫描包,通过反射获得Bean
-
JavaConfig :@Bean,用在方法上,可以自己控制对象的创建。
-
@Import : @Import(A.class)
17 单例Bean的优势(单例模式的优势)?
单例:对象只会创建一次。
-
减少新的实例创建,减少了服务器性能和内存的消耗
-
对象少了,jvm需要回收的对象也就少了。
-
可以迅速获得到Bean,Bean除了第一次获得是在内存中,别的时候都是在缓存中获取。
18 Spring的Bean是不是线程安全的?
单例Bean会存在线程安全的问题,如果类中有成员变量,并且有读写操作,就会有线程安全的问题,把成员变量放到方法中就是线程安全的了。
-
使用多例解决线程安全问题。
-
把成员变量放的ThreadLocal中,ThreadLocal是每个线程独有的。
-
加同步锁,加入synchronized。
19 Spring实例化Bean有几种方式
-
构造器反射的方式(Spring管理,不能控制)。
-
静态工厂方式。
<xml factory-method="createPersonFactory"/>
然后回调用这个静态方法去实例Bean
-
实例化工厂方式@Bean
<xml factory-bean="" factory-method=""/>
@Bean底层就是通过这两个方式进行的。
-
FactoryBean方式
实现FactoryBean的getObject方法,在这个方法中返回一个对象,在getObjectType()中获得一个class。
20 Bean的装配,Bean的自动装配 (或者说注入)
-
通过XML配置可以装配上
-
@Autowired或者其他注解,可以自动注入。
21 自动装配的条件
-
需要set方法
-
任然可以使用<constructor-arg/> 和 <propety/>进行覆盖
-
不能装配基本的数据类型
-
自动装配不如显示装配精确
23 自动装配的几种方式
-
no:不进行自动装配,通过ref手动注入
-
byName:
-
byType:
-
constructor:
-
autodetect:Spring3.0后弃用.
24 Bean有哪些生命周期的回调方法?有哪几种实现方式?
1.初始化方法
-
注解方式:@PostConstruct
-
实现InitiallizingBean接口
2.销毁方法
1. 注解方式:@PreDestroy 2. 实现DisposableBean接口 3. @Bean(destroyMethod = “destroyMethod”)
25 Spring在加载过程中,有几种形态?
-
概念态
@Bean <Bean/>Bean的配置形态。
-
定义态
BeanDefinition 封装Bean的生产指标。
-
纯静态
二级缓存,早起暴露Bean,循环依赖才能体现纯静态的作用。
-
成熟态
SingletonObjects ,最终在应用使用的Bean
26 Spring是如何帮助我们在并发的情况下避免获取不完整的Bean
什么是不完整的Bean?
实例化之后,没有经过属性赋值。
双重检查锁和二次检查缓存
二个同步锁
再读二级缓存和三级缓存的时候,都有一把锁,如果A线程先进来,会进行Bean的实例化,这其中到放到一级缓存的过程中,都是锁住的,另外一个线程都是堵塞的状态,直到A线程创建完一个对象;但是B线程读的时候,一级缓存是没有加锁的,而且B此时已经读完了一级缓存,结果是null,同时A创建好了一个Bean,二级缓存和三级缓存都清空,B这个时候就得重新创建了吗???不是,在创建Bean之前,B线程也会再次进行getSingleton()的调用,这是就会直接拿到Bean。
为什么一级缓存不加锁呢?
性能问题:如果一个线程创建一个A Bean对象,但是另一线程要拿C Bean对象,这个时候这个线程也是堵塞的。
27 BeanDefinition的加载过程
-
JavaConfig @Bean
-
解析配置类
-
读取配置:BeanDefinitionReader
-
解析:Config : @Bean @Import @Component
-
配置类解析器 ConfigurationClassParser
-
扫描:ClassPathBeanDefinitionSacnner # doScan:根据包路径找到所有的.class文件,判断类是不是标准的@Component注解,排除接口,是不是抽象类
-
注册BeanDefinition
-
-
注册BeanDefinitionMap
-
生产
28 如何在Spring所有Bean创建完后做扩展
哪里才算所有的Bean创建完成:new ApplicationContext() -----> refresh() ------> finishBeanFactoryInitialzation(循环所有的BeanDefinition进行getBeanFactory.getBean()生成所有的Bean) 这个循环之后Bean也就创建完了。
-
扩展方式一:基于SmartInitializingSingleton接口,在finishBeanFactoryInitialization(beanFactory); (源码在DefaultListableBeanFactory.java的preInstantiateSingletons()方法中),实现SmartInitializingSingleton接口,可以在所有单例bean全部实例化以后回调
-
扩展方式二:在AbstractApplicationContext.java中的finishRefresh()方法中,发布了一个最后的事件,publishEvent(new ContextRefreshedEvent(this));监听事件@EventListener(ContextRefreshedEvent.class)