【Spring面试】五、Bean扩展、JavaConfig、@Import

文章目录

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

问题涉及的源码分析:

点下Download Sources,把源码注释下载进来。new Spring容器时,调用了refresh方法:

refresh方法:(IoC容器的加载,就是在refresh方法里实现的)

new ApplicationContext() --> refresh() --> finishBeanFactorylnitialization (循环所有的BeanDefinition ,通过BeanFactory.getBean()生成所有的Bean), 这个循环结束之后所有的bean也就创建完了。而在循环结束后,还有一段代码,就是扩展的关键:

即这个实例实现了SmartInitializingSingleton接口,这就是实现的扩展点之一。再看refresh源码,finishBeanFactorylnitialization下面还有个finishRefresh方法:

这里发布了一个ContextRefreshedEvent的事件,我们只要创建个监听器,监听这个事件,也可以完成扩展。

答案:

  • 方式一:实现SmartInitializingSingleton接口
  • 方式二:要创建个监听器,监听ContextRefreshedEvent事件

看下效果,以上即Bean加载完成后输出一句测试的话:

Q2、Spring容器启动时,为什么先加载BeanFactoryPostProcess?

答案:

  • 因为BeanDefinition会在IoC容器加载的时候先注册,而BeanFactoryPostProcess是所有BeanDefinition注册完后做扩展用的,所以要先加载
  • 其次,Ioc容器的加载,即创建Bean,这就需要解析配置类(xml的bean标签、@Component...),负责解析的类也实现了BeanFactoryPostProcessor

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

BeanDefinition里装着Bean的一些信息,看创建IoC容器的源码中的refresh方法的源码:

答案:

所以Bean的生产顺序是由BeanDefinition的注册顺序决定的,当然依赖关系也会影响Bean的创建顺序。

java 复制代码
那BeanDefinition的注册顺序由什么决定?

主要由注解(或配置)的解析顺序来决定,由源码看到解析顺序为:

  • @Configuration
  • @Component(当然它还有@Order来细分,这里不细分)
  • @Import的一类
  • @Bean
  • 使用BeanDefinitionRegistryPostProcessor注册的

从上下文拿一下BeanDefinitionNames,是个String[],有序,输出下:

按照上面这个注册顺序,以及Spring中后面的Bean覆盖前面的Bean,还可以这么应用:有个用@Component注册的Bean,我想让他失效并替换成另一个,则可以用@Bean再写一个

Q4、Spring有哪几种配置方式

答案:

有三种方式给Spring容器提供配置的元数据:

  • XML配置文件 :从Spring诞生开始用,spring.xml中用<bean>标签
  • 基于注解 :从Spring2.5+开始,spring.xml <component-scan base-package=""/> + @Component
  • 基于JavaConfig:从Spring3.0开始,使用@Configuration代替xml,使用@Bean代替Bean标签

Q5、JavaConfig是如何替代spring.xml的?

答案:

对于XML

  • 创建Spring容器是new ClassPathXmlApplicationContext("a.xml")
  • 有一个spring.xml文件
  • 配置一个Bean是用bean标签,scope、lazy属性
  • 扫描包用<component-scan base-package=""/>
  • 引入外部的属性配置文件用<property-placeHolder resource="/xx.properties">
  • 引入配置文件后,使用其属性给Bean属性赋值为${}
java 复制代码
<property name="password" value="${mysql.password}"></property>
  • 指定其他配置文件用<import resource="">

对于JavaConfig

  • 创建Spring容器是new AnnotationConfigApplicationContext(javaConfig.class)
  • 有一个配置类,被@Configuration注解修饰
  • 配置一个Bean是用@Bean注解,属性也用注解@Scope、@Lazy
  • 扫描包用@ComponentScan
  • 引入外部的属性配置文件用@PropertySource("classpath:db.properties")
  • 使用配置文件中的值为@Value("${}")
  • 指定其他用@Import(配置类)

以上是应用层的区别,最后,从源码和流程上来说:大方向都是创建Spring容器(同一个接口,不同的实现类),然后加载、解析配置,再注册为BeanDefinition后交给Bean工厂生产。

Q6、@Component、@Controller、@Service、@Repository有何区别?

答案:

这四个注解其实是一个注解,@Controller、@Service、@Repository的源注解都是@Component

之所以分为三种,是因为开发一个项目,常使用三层架构,即控制层、业务层、数据返回层,提供这三个注解用于不同层,提高代码的阅读性。

Q7、@Import可以有几种用法?

答案:

四种:

  • 第一种:直接指定类(如果是配置类,就会按照配置类来解析,如果是个普通类,则直接解析成Bean)
java 复制代码
@Import(UserMapper.class)
@Import(JavaConfig.class)   //此时,JavaConfig这个配置类中的Bean也会生效
  • 第二种:导入一个实现了ImportSelector这个接口的类,这种方式可以一次性注册多个,返回一个String[ ],每一个值是完整类路径
java 复制代码
public class MyImportSelector implements ImportSelector{
	
	@Override
	public String[] selectImports(AnnotationMetadata importingClassMetadata){
		//可以以字符串数组的形式注册多个Bean 
		//字符串必须是类的完整类名
		return new String[]{"com.llg.UserMapper","com.llg.Role"};
	}
}
java 复制代码
@Configuration
@Import(MyImportSelector.class)
public class MainConfig{

}

但注意,这个时候的Bean创建不是正常的IoC加载流程,使用@Import,之前的一个postProcessBeanFactory在所有Beanfinition注册完之后修改这个Import的Bean的信息就会报错

  • 第三种:导入一个实现ImportBeanDefinitionRegistrar的接口的类,这种也可以一次导入多个,是通过BeanDefinitionRegistry来动态注册BeanDefinition,因此这种方式是没有上面在所有Beanfinition注册完之后修改这个Import的Bean的信息就会报错的这个情况的。


  • 第四种:这种其实是第二种的衍生,第二种的接口有个子接口,叫延迟导入选择器,也可以一次导入多个Bean,和ImportSelector的区别是DeferredImportSelector的顺序更靠后


相关推荐
952365 小时前
MyBatis
后端·spring·mybatis
FQNmxDG4S7 小时前
Java多线程编程:Thread与Runnable的并发控制
java·开发语言
虹科网络安全7 小时前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
axng pmje8 小时前
Java语法进阶
java·开发语言·jvm
rKWP8gKv78 小时前
Java微服务性能监控:Prometheus与Grafana集成方案
java·微服务·prometheus
老前端的功夫8 小时前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_435287928 小时前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
小江的记录本8 小时前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka
yaoxin5211239 小时前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python
极客先躯11 小时前
高级java每日一道面试题-2025年11月24日-容器与虚拟化题[Dockerj]-runc 的作用是什么?
java·oci 的命令行工具·最小可用·无守护进程·完全标准·创建容器的核心流程·runc 核心职责思维导图