【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的顺序更靠后


相关推荐
杨DaB7 分钟前
【SpringBoot】Swagger 接口工具
java·spring boot·后端·restful·swagger
YA3338 分钟前
java基础(九)sql基础及索引
java·开发语言·sql
恋喵大鲤鱼15 分钟前
Golang 后台技术面试套题 1
面试·golang
why技术20 分钟前
也是震惊到我了!家里有密码锁的注意了,这真不是 BUG,是 feature。
后端·面试
桦说编程28 分钟前
方法一定要有返回值 \ o /
java·后端·函数式编程
小李是个程序1 小时前
登录与登录校验:Web安全核心解析
java·spring·web安全·jwt·cookie
David爱编程1 小时前
Java 创建线程的4种姿势,哪种才是企业级项目的最佳实践?
java·后端
ciku1 小时前
Spring AI 集成阿里云百炼平台
人工智能·spring·阿里云
hrrrrb2 小时前
【Java Web 快速入门】十一、Spring Boot 原理
java·前端·spring boot
Java微观世界2 小时前
Object核心类深度剖析
java·后端