spring面试题5( 2021-12-17)

2021-12-17积累

27、描述BeanDefinition的加载过程

这个问的不多,但是属于Spring源码里面比较难的知识点。调用链很深很复杂。

BeanDefinition用来描述Bean的生产信息,决定Bean以什么方式去生产。定义态的bean。

以javaConfig+@Bean的方式配置bean的形式为例:

1、new AnntationConfigApplicationContext 了一个Spring容器

2、解析配置类

3、注册成BeanDefinition,统一放到一个map里面

4、根据BeanDefinition信息,通过BeanFactory的getBean方法去生产bean。

【解析配置类的步骤】:

1、读取配置:

使用的API:BeanDefinitionReader

2、解析:这些加了注解@Bean,@Import,@ComponentScan的类

使用的API:ConfigurationClassParser配置类的解析器。会调用doProcess方法进行真正的解析。

以解析@ComponentScan为例讲解是怎么扫描的?

扫描API: ClassPathBeanDefinitonScanner里面的方法doScan()帮我们进行扫描。

扫描方法:根据包路径找到所有的.class文件,判断这个类是否加了标准的@Component注解。判断是不是接口,抽象类。如果都符合的话,就会注册成BeanDefinition。

28、如何在Spring所有Bean创建完之后做扩展?

哪里才算所有的bean创建完了。

首先是所有的配置bean会注册成BeanDefinition

然后根据BeanDefinition进行循环调用一个一个的getBean进行生产。

循环完所有的BeanDefiniton,通过BeanFactory的getBean方法生成所有的Bean

这个循环结束之后,所有的Bean就创建完了。

源码:

new ApplicationContext()里面有refresh()体现IOC容器加载。

refresh方法里面会调用finishBeanFactoryInitialization方法。

在finishBeanFactoryInitialization方法里面循环所有的BeanDefinition,一个一个getBean创建 。这个循环结束之后,那么所有的bean就创建完了。

【注意】:

扩展点1:

等所有的bean创建完之后,会再次循环所有的bean,看有哪些bean是否实现了SmartInitializingSingleton接口,如果实现了这个接口,就会继续调用afterSingletonsInstantiated方法。

扩展点2:

所有的bean创建完之后都会调用的扩展接口。

refresh方法里面会调用finishBeanFactoryInitialization方法的后面就是调用finishRefresh方法。

在finishRefresh方法里面会调用ContextRefreshedEvent事件,发布一个ContextRefreshed事件。

创建一个监听器来监听这个事件,就可以完成这个扩展。

比如:创建监听器RefreshedEventListener,使用注解@EventListener来监听这个事件。同样会在所有bean创建完之后调用。

class RefreshedEventListener{
    @EventListener(ContextRefreshedEvent.class)
    public void onApplicationEvent(ContextRefreshedEvent event){
        
    }
}

IOC的容器加载体现在refresh方法里面。

refreshed说明IOC容器已经加载完了。

这两个扩展接口就可以实现所有的bean创建完之后的扩展。

29、如何在所有BeanDefinition注册完之后做扩展?

考察你是是否知道BeanDefinition。

在第11道面试题里面就讲到了这个。

BeanDefinition是在IOC容器加载的时候,会将配置的Bean,先注册成定义的临时形态。临时形态就叫做BeanDefinition,也叫做定义态。BeanDefinition主要是去读取我们配置的定义信息。比如:class,lazy,scope,autowired,id这些配置会被读取到对象BeanDefinition里面。

会在IOC容器加载的时候调用invokeBeanFactoryPostProcessor方法进行注册。

refresh方法是体现IOC容器的关键方法,里面会调用invokeBeanFactoryPostProcessor方法-bean工厂的后置处理器帮我们注册所有的Bean Definition。注册完之后调用BeanFactoryPostProcessor接口,帮我们进行扩展。

我们只要实现这样的接口BeanFactoryPostProcessor,那么Spring‘就会在注册完之后帮我们进行扩展

@Component
class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
    //下面这个方法是在所有的BeanDefinition注册完之后才会调用的。
    @override 
    public void postProcessBeanFactory(ConfigurationListableBeanFactory beanFactory){
        
        
        
    }
}

总结:Bean工厂的后置处理器可以在所有的BeanDefinition注册完成之后进行扩展。

扩展什么呢?

比如拿到Person这个bean的定义信息之后,本来它写在xml的时候是默认单例的,现在拿到它的定义信息之后,可以在postProcessBeanFactory方法里面修改为多例。

@Component
class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
    //下面这个方法是在所有的BeanDefinition注册完之后才会调用的。
    @override 
    public void postProcessBeanFactory(ConfigurationListableBeanFactory beanFactory){
        
        BeanDefinition personDefinition = beanFactory.getBeanDefinition("person");
        
        personDefinition.setScope("prototype");
        
    }
}

30、Bean的生产顺序是由什么决定的?

BeanDefinition的注册顺序是有什么决定的?

Bean在生产之前有个临时状态:BeanDefinition;存储着bean的描述信息。由BeanDefinition决定着Bean的生产顺序。会按照BeanDefinition的注册顺序来决定Bean的生产顺序。因为所有的BeanDefinition存储在List集合里面,list是有序的。

【Bean的创建顺序是由Bean Definition的注册顺序来决定的,当然依赖关系也会影响Bean的创建顺序】。比如B依赖A,那么,A先生产之后再生产B。

【问:BeanDefinition的注册顺序是由谁来决定的呢?】

BeanDefinition的注册顺序主要是由注解或者配置的解析顺序来决定的。

注册BeanDefinition就是在invokeBeanFactoryPostProcessor这个方法里面执行的,这个方法调用链非常深。

注解的解析顺序:(从上到下)

@Configuration

@Component

@Import直接导入类

@Bean

@Import导入一个实现接口:ImportBeanDefinitionRegistrar

BeanDefinitionRegistryPostProcessor是最后解析的(毕竟叫:后置处理器)

测试这几个顺序:@Configuration @Component @Import直接导入类 @Bean

@Componenet
class User{
    
}

@Component
class UserImport{
    
}

@Configuration
@ComponentScan
@Import({UserImport.class})
class MainConfig{
    @Bean
    public User userBean(){
        return new User();
    }
}

测试类:

psvm{
 context = new ApplicationContext();
    for(String beanDefinitionName : context.getBeanDefinitionNames){
        sout(beanDefinitionName);
    }
}

输出结果:

mainConfig:这个是第一位,通过@Configuration注解的
user:这个是第2位,通过@Component注解的
userImport:这个是第3位,通过@Import直接导入一个类
UserBean:这个是第4位,通过Bean直接导入一个类

31、Spring有哪几种配置方式?

三种配置方式

1、基于XML的;有Spring的配置文件,xxx.xml配置文件里面有bean标签

2、基于注解的配置(Spring2.5之后);在xxx.xml里面有component-scan注解 指定base-package属性,然后在这个扫描包下使用注解@Component

@Autowired进行bean注入

3、基于java的配置(Spring3.0之后);

基于JavaConfig 的配置方式。

使用@Configuration来代替Spring的配置文件,使用@Bean代替配置文件里面的bean标签。

32、JavaConfig是如何代替Spring.xml的?

应用层面:

1、new ApplicationContext的区别:

以前XML的方式:Spring是去new了一个ClassPathXmlApplicationContext,参数传入的是xml文件

而avaConfig的方式:new了一个AnnotationConfigApplicationContext。参数传入的是java的配置类。

2、配置文件和配置类以及属性的区别

使用@Configuration标注在javaConfig上面。这个配置类相当于是Spring.xml文件

使用@Bean配置某个节点,相当于xml文件里面的bean标签。其中标签bean里面的很多属性,比如:

scope,lazy都可以使用对应的注解@Scope和@Lazy

3、扫描包的区别:

使用xml,在xml里面写component-scan base-package

使用javaConfig,用@ComponentScan(base-packages="…")注解

4、引入外部属性配置文件:

xml:place-holder resource="…",拿到外部属性配置文件的值,是通过property标签,name=…,value="${}"

javaConfig:使用注解@PropertySource(classpath:…)

拿到外部属性配置文件,通过@Value("${}")

5、需要引入另外一个Spring的配置文件

xml:使用import标签 resource="…"

javaConfig:使用@Import({xx.class})注解,这个注解很灵活

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值