• 【Spring面试】二、BeanFactory与IoC容器的加载


    Q1、BeanFactory的作用是什么?

    在这里插入图片描述

    答案:

    • BeanFactory是Spring中非常核心的一个顶层接口(顶层即无父接口)
    • 它是Bean的工厂,主要职责就是生产Bean
    • 实现了简单工厂的设计模式,即通过调用getBean传入标识来生产一个Bean
    • 它有非常多的实现类,每个工厂都有不同的职责(单一职责功能),最强大的是DefaultListableBeanFactory,Spring底层就是使用该实现工厂来生产Bean的

    在这里插入图片描述

    Q2、BeanDefinition的作用是什么?

    答案:

    它主要负责存储Bean的定义信息,决定Bean的生产方式。如spring.xml:

    <bean class="com.llg.User " id="user" scope="singleton" lazy="false" abstract="false" autowire="none" >
    	<property name="username" value="9527">
    bean>
    
    • 1
    • 2
    • 3

    把以上Bean的这些定义信息,拿到并赋值给BeanDefinition(的实现类)的属性,然后交给BeanFactory来生产:

    在这里插入图片描述

    以汽车为例来对比:

    在这里插入图片描述

    Q3、BeanFactory和ApplicationContext有什么区别?

    在这里插入图片描述

    答案:

    • ApplicationContext即Spring的上下文,其实现了BeanFactory接口。

    • FactoryBean的getBean方法用于生产Bean,但ApplicationContext不生产Bean,而是通知FactoryBean来干活儿(即调用),它的getBean方法其实是个门面方法,底层在调BeanFactory的getBean方法

    • 它们都可以做为Spring容器,因为它们都可以来管理Bean的声明周期

    • ApplicationContext在开发中打交道最多,是因为它做了更多的事情,如自动把我们配置的Bean(如@Component)注册进来、加载环境变量、实现事件监听等

    • ApplicationContext和BeanFactory,就像4S店和汽车厂的关系,它虽然不造车,从车厂拿车,但它会做销售、保养、上牌等一系列事儿

    Q4、BeanFactory和FactoryBean有什么区别?

    答案:

    • BeanFactory是一个工厂,也是一个容器,用来管理和生产Bean的

    • FactoryBean则只是一个Bean,特殊的Bean,所以也是由BeanFactory来管理的

    • FactoryBean是一个接口,必须被另一个Bean去实现,此时这个被寄生的Bean则不再是是它自身,而是FactoryBean接口中getObject方法返回的那个Bean,getObject方法返回的这个Bean,可以是被寄生的Bean的子类,也可以是和被寄生的Bean的这个类毫无关系的类

    • 如果想获得原来的Bean实例,可加&

    • FactoryBean创建Bean的方式是懒加载的,使用Bean的时候才会调用getObject方法并把对象添加到容器

    代码演示:

    @Component
    public class UserMapper{
    
    	public UserMapper(){
    		System.out.println("UerMapper对象加载");
    	}
    
    	public void query(){
    		System.out.println("query");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    @Configuration
    @ComponentScan  //扫不到时自己调整包路径
    public class MainConfig{
    }
    
    • 1
    • 2
    • 3
    • 4
    public class Run{
    	public static void main(String[] args){
    		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
    		UserMapper userMapper = (USerMapper) context.getBean("userMapper");
    		userMapper.query();
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    此时运行一切正常:

    在这里插入图片描述

    UserMapper再去实现FactoryBean这个接口,此时getBean得到的userMapper对象就是重写的getObject方法返回的对象。

    @Component
    public class UserMapper implements FactoryBean{
    
    	public UserMapper(){
    		System.out.println("UerMapper对象加载");
    	}
    
    	public void query(){
    		System.out.println("query");
    	}
    
    	@Override
    	public Object getObject() throws Exception{
    		return "code9527";   //直接返回一个不相干的String对象
    	}
    
    	@Override
    	public Class<?> getObjectType(){
    		return String.class;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    此时,getBean得到的userMapper就是一个String对象,那自然会类转换异常:

    在这里插入图片描述

    想获得原Bean,则加&:

    UserMapper userMapper = (USerMapper) context.getBean("userMapper");
    
    • 1

    在这里插入图片描述

    且此时创建Bean的方式是懒加载的,使用Bean的时候才会调用getObject方法并把对象添加到容器。修改下getObject:

    在这里插入图片描述
    此时断点,执行完new AnnotationConfigApplication(),即容器创建完成、Bean加载完成,控制台仍未输出"getObject调用===",即并未执行getObject()方法,直到getBean时才输出:

    在这里插入图片描述

    Q5、说下Spring IoC容器的加载过程(※)

    在这里插入图片描述
    在这里插入图片描述
    图片链接:https://www.processon.com/view/link/5f15341b07912906d9ae8642

    答案:

    对照上图:

    • 实例化一个ApplicationContext对象(此时Bean还只是代码里的简单定义,即概念态的Bean)
    • 调用Bean工厂的后置处理器完成扫描
    • 循环解析扫描出来的类信息,看是否有@Component注解
    • 有则实例化一个BeanDefinition对象来存储接续出来的信息
    • 把实例化好的beanDefinition对象put到beanDefinitionMap中缓存起来,以便后面实例化bean(定义态的Bean)
    • 再次调用其他bean工厂的后置处理器
    • 当然spring还会干很多事情,比如国际化,比如注册BeanPostProcessor
    • 实例化Bean则是spring调用finishBeanFactorylnitialization方法来实例化单例的bean,实例化之前spring要做验证,需要遍历所有扫描出来的类,依次判断这个bean是否Lazy,是否prototype,是否abstract等
    • 如果验证完成spring在实例化一个bean之前需要推断构造方法,因为spring实例化对象是通过构造方法反射,故而需要知道用哪个构造方法
    • 推断完构造方法之后spring调用构造射实例代个对象,注意我这里说的是对象、对象、对象,这个时候对象已经实例化出来了,但是并不是一个完整的bean,最简单的体现是这个时候实例化出来的对象属性是没有注入的,所以不是一个完整的bean(纯净态的Bean)
    • Spring处理合并后的beanDefinition
    • 判断是否需要完成属性注入,如果需要,则注入
    • 判断Bean的类型,回调Aware接口
    • 调用生命周期回调方法
    • 如果需要代理则完成代理
    • put到单例池,bean创建完成,存到Spring容器中,IoC容器加载完成。(成熟态的Bean

    Q6、Spring Ioc有哪些扩展点,在什么时候调用?

    问题分析:

    扩展点,即Spring Ioc在加载的过程中,其底层会对外提供很多的扩展接口或者一些钩子方法,当我们自己去实现这些接口时,Spring就会在特定的点,帮我们调用钩子方法,从而实现对Spring底层的扩展。

    在这里插入图片描述

    Spring有非常多的扩展接口,重点有:

    A1: 执行BeanFactoryPostProcessor的postProcessBeanFactory方法

    Spring提供了对BeanFactory进行操作的处理器BeanFactoryProcessor,简单来说就是获取容器BeanFactory,这样就可以在真正初始化bean之前对bean做一些处理操作。bean工厂的bean属性处理容器,说通俗一些就是可以管理我们的bean工厂内所有的beandefinition(未实例化)数据,可以随心所欲的修改属性。

    简单来说就是在工厂里所有的bean被加载进来后但是还没初始化前,对所有bean的属性进行修改也可以add属性值。

    
    /**
     * 作用: 在注册BeanDefinition的可以对beanFactory进行扩展
     * 调用时机: Ioc加载时注册BeanDefinition的时候会调用
     * 看接口的参数,看它把啥给你了,比如这里给了BeanFactory对象
     */
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    	@Override
    	public void postprocessBeanFactory(ConfigurableListableBeanFactory beanFactor) throws BeansException {
    	
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    A2: BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法

    /**
    * 作用:动态注册BeanDefinition
    * 调用时机: Ioc加载时注册BeanDefinition 的时候会调用
    */
    
    @Component
    public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    	@Override
    	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException (
    	RootBeanDefinition beanDefinition = new RootBeanDefinition(Car.class);
    	registry.registerBeanDefinition("car",beanDefinition);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    A3: Bean生命周期的回调方法:初始化和销毁

    InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。

    public interface InitializingBean {
        void afterPropertiesSet() throws Exception;
    }
    
    
    • 1
    • 2
    • 3
    • 4

    实现DisposableBean接口,在这个Bean生命周期结束前调用destory()方法做一些收尾工作

    public interface DisposableBean {
        void destroy() throws Exception;
    }
    
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    swagger---接口文档管理生成管理工具
    【Linux进程间通信】二、pipe管道
    JupyterLab | 这几款插件推荐给天天使用JupyterLab的你!~
    运维工程师面试题及答案(网络运维工程师面试题)
    【无标题】
    Windows右键添加用 IDEA 打开
    【乳腺癌诊断】基于聚类和遗传模糊算法乳腺癌(诊断)分析(Matlab代码实现)
    EasyPoi导入
    ansible自动化管理工具
    Vue3+Ts+Vite项目(第十二篇)——echarts安装与使用,vue3项目echarts组件封装
  • 原文地址:https://blog.csdn.net/llg___/article/details/132745327