当前位置:   article > 正文

【面试题】Spring中bean的生命周期,Spring的DI与IoC的区别_bean 创建实例 初始化 属性填充区别

bean 创建实例 初始化 属性填充区别

一、Spring中bean的生命周期

class --> BeanDefinition --> new 对象 (原始对象)--> 属性填充 --> 初始化 --> bean 加入单例池

bean生命周期主要:构造(实例化)-->属性注入(set)-->初始化前-->初始化-->初始化后-->加入单例池待使用-->销毁

1、四个主要阶段

Spring bean的生命周期只有四个主要阶段,其他都是在这四个主要阶段前后的扩展点,这四个阶段是:

  1. 实例化 Instantiation ---> 构造方法
  2. 属性赋值 Populate  ---> setter() 注入
  3. 初始化 Initialization
  4. 销毁 Destruction

其中实例化和属性赋值分别对应构造方法setter方法的注入;初始化和销毁是用户能自定义扩展的两个阶段属性赋值 Populate,主要是多bean间的依赖属性进行填充

可通过查源码的方式发现,他们都在doCreateBean()方法中,

  1. // 忽略了无关代码
  2. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
  3. throws BeanCreationException {
  4. // Instantiate the bean.
  5. BeanWrapper instanceWrapper = null;
  6. if (instanceWrapper == null) {
  7. // 实例化阶段!
  8. instanceWrapper = createBeanInstance(beanName, mbd, args);
  9. }
  10. // Initialize the bean instance.
  11. Object exposedObject = bean;
  12. try {
  13. // 属性赋值阶段!
  14. populateBean(beanName, mbd, instanceWrapper);
  15. // 初始化阶段!
  16. exposedObject = initializeBean(beanName, exposedObject, mbd);
  17. }
  18. }

可以发现,分别调用三种方法:

  1. createBeanInstance() -> 实例化
  2. populateBean() -> 属性赋值
  3. initializeBean() -> 初始化

销毁阶段是在容器关闭时调用的,在ConfigurableApplicationContext类的close()中

2、常用的扩展点

2.1 影响多个bean的接口

两个最重要的接口:

  • InstantiationAwareBeanPostProcessor作用于实例化阶段的前后
  • BeanPostProcessor:作用于初始化阶段的前后

实现这两个接口的bean,会自动切入到相应的生命周期中

具体代码如下:

  1. package cn.xmx.ioc.lifecycle;
  2. import org.springframework.beans.BeansException;
  3. import org.springframework.beans.PropertyValues;
  4. import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
  5. import java.beans.PropertyDescriptor;
  6. public class MyInstantiationAwareBeanPostProcessorAdapter extends InstantiationAwareBeanPostProcessorAdapter {
  7. @Override
  8. public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
  9. if(beanName.equals("car")){
  10. System.out.println(beanName+"在实例化之前");
  11. }
  12. return super.postProcessBeforeInstantiation(beanClass, beanName);
  13. }
  14. @Override
  15. public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
  16. if(beanName.equals("car")){
  17. System.out.println(beanName+"在实例化之后");
  18. }
  19. return super.postProcessAfterInstantiation(bean, beanName);
  20. }
  21. }
  1. package cn.xmx.ioc.lifecycle;
  2. import java.lang.reflect.Proxy;
  3. import org.springframework.beans.BeansException;
  4. import org.springframework.beans.factory.config.BeanPostProcessor;
  5. //后置处理器
  6. public class NdBeanPostProcessor implements BeanPostProcessor {
  7. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  8. System.out.println("NdBeanPostProcessor 在"+beanName+"对象初始化之【前】调用......");
  9. if(beanName.equals("car")) {
  10. return new CglibInterceptor().getIntance(bean.getClass());
  11. }
  12. return bean;
  13. }
  14. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  15. System.out.println("NdBeanPostProcessor 在"+beanName+"对象初始化之【后】调用......");
  16. return bean;
  17. }
  18. }
  1. package cn.xmx.ioc.lifecycle;
  2. import org.springframework.beans.factory.config.BeanPostProcessor;
  3. import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. @Configuration
  7. public class MainConfig {
  8. @Bean(name="car",initMethod="init",destroyMethod="destroy")
  9. public Car getCar() {
  10. return new Car("大黄蜂");
  11. }
  12. @Bean
  13. public BeanPostProcessor getBeanPostProcessor() {
  14. return new NdBeanPostProcessor();
  15. }
  16. @Bean
  17. public InstantiationAwareBeanPostProcessorAdapter getIns(){
  18. return new MyInstantiationAwareBeanPostProcessorAdapter();
  19. }
  20. }
  1. package cn.xmx.ioc.lifecycle;
  2. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  3. /*
  4. 四个主生命周期
  5. (1)实例化之前干预 InstantiationAwareBeanPostProcessorAdapter.postProcessBeforeInstantiation()
  6. 1.实例化
  7. (2)实例化之后干预 InstantiationAwareBeanPostProcessorAdapter.postProcessAfterInstantiation()
  8. 2.填充属性(给属性赋值)
  9. (1)初始化之前干预 BeanPostProcessor.postProcessBeforeInitialization()
  10. 3.初始化化(比如准备资源文件)
  11. 3) 属性进行干预 ----修改属性或属性值
  12. (2)初始化之后干预 BeanPostProcessor.postProcessAfterInitialization()
  13. 4.销毁(释放资源---对象从内存销毁)
  14. N个接口
  15. 1、干预多次
  16. 1)BeanPostProcessor
  17. 2、干预一次
  18. 1)Aware
  19. */
  20. public class TestLifeCycle {
  21. public static void main(String[] args) {
  22. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
  23. Car car = (Car) context.getBean("car");
  24. System.out.println(car);//toString
  25. System.out.println("beanname:"+car.getBeanName());
  26. System.out.println("beanfactory:"+car.getBeanFactory());
  27. System.out.println("applicationContext:"+car.getApplicationContext());
  28. }
  29. }

2.2 只调用一次的接口

2.2.1 与aware有关的接口:

  • BeanNameAware
  • BeanFactoryAware
  • ApplicationContextAware

需要实体类实现这些接口,分别调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值,调用 setBeanFactory() 方法传入当前工厂实例的引用,调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
具体代码如下图所示:

  1. package cn.xmx.ioc.lifecycle;
  2. import org.springframework.beans.BeansException;
  3. import org.springframework.beans.factory.BeanFactory;
  4. import org.springframework.beans.factory.BeanFactoryAware;
  5. import org.springframework.beans.factory.BeanNameAware;
  6. import org.springframework.context.ApplicationContext;
  7. import org.springframework.context.ApplicationContextAware;
  8. public class Car implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {
  9. private String name;
  10. private String beanName;
  11. private BeanFactory beanFactory;
  12. private ApplicationContext applicationContext;
  13. public void init() {
  14. System.out.println("car 在初始化---加载资源");
  15. }
  16. public void destroy() {
  17. System.out.println("car 在销毁---释放资源");
  18. }
  19. public Car() {
  20. super();
  21. }
  22. public String getName() {
  23. return name;
  24. }
  25. public void setName(String name) {
  26. this.name = name;
  27. }
  28. public Car(String name) {
  29. super();
  30. this.name = name;
  31. System.out.println("car实例化了");
  32. }
  33. public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
  34. System.out.println("beanFactory:"+beanFactory);
  35. this.beanFactory = beanFactory;
  36. }
  37. public BeanFactory getBeanFactory(){
  38. return this.beanFactory;
  39. }
  40. public void setBeanName(String s) {
  41. System.out.println("beanName:"+s);
  42. this.beanName = s ;
  43. }
  44. public String getBeanName() {
  45. return this.beanName;
  46. }
  47. public ApplicationContext getApplicationContext() {
  48. return this.applicationContext;
  49. }
  50. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  51. System.out.println("applicationContext:"+applicationContext);
  52. this.applicationContext = applicationContext;
  53. }
  54. }

2.2.2 两个生命周期接口

实例化和属性赋值都是Spring帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段。
1.InitializingBean 对应生命周期的初始化阶段,在源码的invokeInitMethods(beanName, wrappedBean, mbd);方法中调用。
2.DisposableBean 类似于InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()方法作为入口,实现是通过循环取所有实现了DisposableBean接口的Bean然后调用其destroy()方法 。

3、总结

Bean 生命周期的整个执行过程描述如下:

  1. 如果创建了一个类继承了InstantiationAwareBeanPostProcessorAdapter接口,并在配置文件中配置了该类的注入,即InstantiationAwareBeanPostProcessorAdapter和bean关联,则Spring将调用该接口的postProcessBeforeInstantiation()方法。
  2. 根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。
  3. 如果InstantiationAwareBeanPostProcessorAdapter和bean关联,则Spring将调用该接口的postProcessAfterInstantiation()方法。
  4. 利用依赖注入完成 Bean 中所有属性值的配置注入。
  5. 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
  6. 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
  7. 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
  8. 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
  9. 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。
  10. 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
  11. 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。
    注意:以上工作完后才能以后就可以应用这个bean了,那这个bean是一个singleton的,所以一般这种情况下我们调用同一个id的bean会是在内容地址相同的实例,当然在spring配置文件中也可以配置非Singleton。
  12. 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

原文

二、面试题:Spring 的DI 与 IOC的区别

1、浅谈Spring IOC和DI,以及它们的区别

IOC(控制反转):

控制反转就是本身不负责对象的创建和维护,将管理对象的创建的权利反转给Spring 容器。由Spring 容器进行创建和维护。

DI(依赖注入):

依赖注入由两个或两个以上类体现,会将对象依赖关系自动交给目标对象(DI)管理,无需对象自己获取依赖。

区别:

  • IOC是将对象的创建权交给Spring 容器(管理bean的生命周期)
  • DI依赖于IOC容器,负责实现对象依赖关系和创建,不需要通过new来实现

2、面试:什么是IoC,IoC和DI的区别是什么?

(1)IoC也称为控制反转,是一种思考方式,其主要关注点在于Java对象的创建与管理的问题。和传统的方式相比,当我们需要一个对象的时候,不需要直接new一个,而是去Spring容器中拿一个即可,此时我们失去了对对象的控制权,仅保有使用权。但这样也可以无需关注对象的管理。

(2)所谓控制,指的是管理对象的权利。

(3)所谓反转,指的是由Spring管理而不是开发者管理。

(4)IoC的其中一个目的是为了解耦合,当将一个对象交给第三方容器管理后,那么对象之间的耦合相较于传统new方式会降低。同时Spring IoC也可以降低对象的管理成本,比如实现单例模式(默认即是单例)等等。

(5)要注意的是,IoC和DI的关系并不是一个,类似于接口和实现类的区别,IoC是一种设计思想,DI是IoC的一种实现,DI也称为依赖注入,在Spring中可以通过@Autowired注解将Spring容器中的对象注入到指定的位置。

【注意】这里罗列了许多知识点总结,并不是要我们去死记硬背,而是在真正理解Spring的底层如何实现后,做一个归纳总结。

史上最强Tomcat8性能优化

阿里巴巴为什么能抗住90秒100亿?--服务端高并发分布式架构演进之路

B2B电商平台--ChinaPay银联电子支付功能

学会Zookeeper分布式锁,让面试官对你刮目相看

SpringCloud电商秒杀微服务-Redisson分布式锁方案

查看更多好文,进入公众号--撩我--往期精彩

一只 有深度 有灵魂 的公众号0.0

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/740644
推荐阅读
相关标签
  

闽ICP备14008679号