Springboot面试篇
目录
面试篇
1.前置知识
1.ApplicationContextInitializer接口
具体实现:
2.ApplicationListener接口
具体实现:
3.BeanFactory接口
4.BeanDefinition接口 编辑
5.BeanFactoryPostProcessor
6.Aware接口
7.InitializingBean/DisposableBean接口
8.BeanPostProcessor
Springboot启动流程:
翻源码:
总结:编辑
ioc容器的初始化流程
编辑
Bean的生命周期
一些疑惑:
1.请问我在ioc容器创建完毕,然后通过ApplicationContextInitializer注入的环境属性有什么用
1. 动态配置
2. 多环境配置
3. 占位符替换
4. 条件注解
5. 集成测试
6. 微服务配置
7. 安全性配置
总结
2.ApplicationListener实现类的作用是啥,说是可以用于资源加载,但是我在初始化完成以后配置好了环境属性中的资源源不应该会自动加载完资源吗,那为啥还需要它来资源加载
ApplicationListener 的作用
资源加载的场景
1. 动态资源加载
2. 资源后处理
3. 资源依赖加载
为什么需要 ApplicationListener 来资源加载
总结
3.debug的一些按钮的作用:
面试篇
1.前置知识
1.ApplicationContextInitializer接口
ApplicationContextInitializer实现类可以在ioc容器对象创建以后执行,常用语给环境属性赋值。
具体实现:
1.在启动类包下创建一个实现类,重写initialize方法。我们要为ioc容器注入环境属性,分为三步:
第一步:准备属性:在这里我们准备的是一个Map属性。
第二部:获取一个属性资源管理器:先获取容器的环境在获取他的资源管理对象。
第三部:注册资源源: 在这里由于我们的资源源有很多,比如yml文件、jvm参数、当前电脑的环境变量等等,我们这个测试的只要添加到最后即可。
总的来看:
我们可以在启动类获取ioc容器,然后把这个环境属性打印出来:
2.ApplicationListener接口
ApplicationListener可以动态的处理资源,ApplicationContextInitializer呢是配置环境变量中的资源源会自动加载资源,前者是在某一阶段处理资源,后者是配置资源。
具体实现:
1.实现ApplicationListener接口,重写onApplicationEvent方法来监听事件。注意要判断传来的ApplicationEvent事件是哪种类型,如初始化成功事件,初始化失败事件等等...
2.在spring.factories里面添加实现类:
3.BeanFactory接口
着重看一下BeanFactory、DefaultListableBeanFactory、AbstractAutowiredCapableBeanFactory、AbstractBeanFactory、DefaultSingletonBeanRegistry。
其实我们的Context对象也继承了根接口BeanFactory,并且DefaultListableBeanFactory是一个集大成者,其他的比如Context是继承自它,也就说,Context的BeanFactory是委托DefaultListableBeanFactory来创建的,其他继承自DefaultListableBeanFactory的接口也是。
这里的意思就是说ApplicationContext实现了getBean方法,但它也是调用DefaultListableBeanFactory对象来调用一些列的Bean方法的,无需记忆。
4.BeanDefinition接口 
注意所有的Bean在创建前,都需要封装成对应的BeanDefinition,根据BeanDefinition进一句创建Bean对象。
具体实现:
由于User是@Component注入,而aa是由@Bean注入,他们包装的BeanDefinition对象不一样,一个是scanned...一个是configuration....
5.BeanFactoryPostProcessor
当BeanFactory准备好后,会回调BeanFactoryPostPorcessor来给BeanFactory注入BeanDefinition,从而初始化各个Bean对象。
ConfigrationClassPostProcessor类用于扫描@Component、@service等注解,把这些需要实例化的对象先转为BeanDefinition对象,添加到BeanFactory中,最后创建Bean实例。
具体实现:
1.定义一个类而不注入
2.通过实现BeanFactoryPostProcessor,往BeanFactory里面注册Definition对象。
6.Aware接口
具体实现:
实现三个不同的接口,感知不同的行为。
7.InitializingBean/DisposableBean接口
具体实现:
注意@@PreDestroy和@PostConstruct也可以实现创建和销毁的时候执行函数。
8.BeanPostProcessor
Springboot启动流程:
翻源码:
这是SpringApplication的构造方法:
1.webApplicationType指明了web应用的类型,99%是servlet类型。
2.加载ApplicationContextInitializer和ApplicationListener。
3.this.mainApplicationClass记录主启动类,将来就可以得到启动类下的包和子包的内容。
run():
1.environment加载系统属性
2.Banner打印Spring图形
3.实例化Context容器
4.为容器添加environment、listener、ApplicationContextPostProcessor等等
5.刷新容器(创建Bean实例)
6.返回容器
banner就是这玩意
总结:
回答:
ioc容器的初始化流程
容器刷新真正依赖的是AbstractApplicationContext类。
Bean的生命周期
通过ioc容器创建Bean对象的方法FinnishBeanFactoryinitializtion方法的深一步调用,可以到达Bean生命周期的核心方法:AbstractAutowirecapableBeanFactory类的doCreateBean方法
翻源码小技巧:
方法的调用过程:
一些疑惑:
1.请问我在ioc容器创建完毕,然后通过ApplicationContextInitializer注入的环境属性有什么用
在 Spring 框架中,ApplicationContextInitializer
是一个接口,用于在 ApplicationContext
初始化阶段注入环境属性(Environment
)。这些环境属性在 Spring 容器启动时被加载,并且可以在整个应用生命周期中被访问和使用。通过 ApplicationContextInitializer
注入的环境属性有多种用途,以下是一些常见的应用场景:
1. 动态配置
环境属性可以用来动态配置 Spring 应用的行为,而无需修改代码。例如,你可以通过环境变量或配置文件来设置数据库连接信息、API 密钥、日志级别等。
示例:
properties复制
# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
在代码中,你可以通过 @Value
注解或 Environment
接口来访问这些属性:
java复制
@Value("${spring.datasource.url}")
private String dbUrl;
2. 多环境配置
你可以根据不同的运行环境(如开发环境、测试环境、生产环境)加载不同的配置。通过环境属性,Spring 可以动态选择适合当前环境的配置。
示例:
properties复制
# application-dev.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydb_dev
# application-prod.properties
spring.datasource.url=jdbc:mysql://prod-server:3306/mydb_prod
通过设置 spring.profiles.active
属性来激活特定的配置文件:
bash复制
java -jar myapp.jar --spring.profiles.active=prod
3. 占位符替换
环境属性可以用于占位符替换,使得配置文件中的值可以在运行时动态替换。这对于配置动态资源路径、外部服务地址等非常有用。
示例:
properties复制
# application.properties
myapp.resource-path=${USER_HOME}/resources
在代码中,USER_HOME
会被替换为当前用户的家目录路径。
4. 条件注解
环境属性可以与 Spring 的条件注解(如 @ConditionalOnProperty
)结合使用,根据属性值动态决定是否加载某个 Bean 或配置类。
示例:
java复制
@Configuration
@ConditionalOnProperty(name = "myapp.feature.enabled", havingValue = "true")
public class MyFeatureConfig {
// 定义相关的 Bean
}
5. 集成测试
在集成测试中,你可以通过 ApplicationContextInitializer
注入测试专用的环境属性,从而隔离测试环境与生产环境的配置。
示例:
java复制
@RunWith(SpringRunner.class)
@ContextConfiguration(initializers = MyTestInitializer.class)
public class MyIntegrationTest {
// 测试代码
}
public class MyTestInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext,
"myapp.test.property=value-for-test"
);
}
}
6. 微服务配置
在微服务架构中,环境属性可以用来动态配置服务的端口、注册中心地址、配置中心地址等,使得微服务可以灵活地在不同的环境中运行。
示例:
properties复制
# application.properties
server.port=${PORT:8080}
spring.cloud.nacos.server-addr=127.0.0.1:8848
7. 安全性配置
环境属性可以用于存储敏感信息,如加密密钥、API 密钥等。这些信息可以通过环境变量或加密的配置文件加载,从而避免将敏感信息硬编码到代码中。
示例:
properties复制
# application.properties
myapp.api.key=${API_KEY}
在代码中,你可以通过 Environment
接口访问这些属性:
java复制
@Autowired
private Environment environment;
public void someMethod() {
String apiKey = environment.getProperty("myapp.api.key");
}
总结
通过 ApplicationContextInitializer
注入的环境属性在 Spring 应用中具有多种用途,包括动态配置、多环境支持、占位符替换、条件注解、集成测试、微服务配置和安全性配置等。这些属性使得 Spring 应用更加灵活和可配置,能够适应不同的运行环境和需求。
2.ApplicationListener实现类的作用是啥,说是可以用于资源加载,但是我在初始化完成以后配置好了环境属性中的资源源不应该会自动加载完资源吗,那为啥还需要它来资源加载
在 Spring 框架中,ApplicationListener
是一个接口,用于监听 Spring 应用上下文(ApplicationContext
)中发布的各种事件。通过实现 ApplicationListener
接口,你可以自定义对这些事件的响应逻辑,从而在特定的生命周期阶段执行特定的操作。
ApplicationListener
的作用
ApplicationListener
的主要作用是允许开发者在 Spring 应用上下文的生命周期中插入自定义逻辑。Spring 提供了多种事件类型,例如:
-
ContextRefreshedEvent
:在上下文刷新时发布,通常用于初始化完成后的操作。 -
ContextStartedEvent
:在上下文启动时发布。 -
ContextStoppedEvent
:在上下文停止时发布。 -
ContextClosedEvent
:在上下文关闭时发布。
通过实现 ApplicationListener
接口,你可以监听这些事件,并在事件发生时执行自定义逻辑。
资源加载的场景
虽然 Spring 在初始化时会自动加载配置文件中的资源,但在某些情况下,你可能需要在特定的生命周期阶段进行额外的资源加载或处理。以下是一些具体的场景:
1. 动态资源加载
在某些情况下,资源的路径或内容可能在运行时动态确定。例如,你可能需要根据环境属性动态加载不同的资源文件。通过实现 ApplicationListener
,你可以在上下文初始化完成后,根据当前的环境属性加载额外的资源。
示例:
java复制
@Component
public class CustomResourceLoader implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private Environment environment;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 获取环境属性
String resourcePath = environment.getProperty("custom.resource.path");
// 动态加载资源
Resource resource = new FileSystemResource(resourcePath);
try (InputStream inputStream = resource.getInputStream()) {
// 处理资源内容
String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
System.out.println("Loaded resource content: " + content);
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 资源后处理
即使资源在初始化时已经加载,你可能需要在上下文初始化完成后对这些资源进行进一步的处理。例如,你可能需要验证资源的内容、解析资源中的特定信息或进行其他后处理操作。
示例:
java复制
@Component
public class ResourcePostProcessor implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private Environment environment;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 获取已加载的资源路径
String resourcePath = environment.getProperty("custom.resource.path");
// 加载并处理资源
Resource resource = new FileSystemResource(resourcePath);
try (InputStream inputStream = resource.getInputStream()) {
// 解析资源内容
String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
// 进行后处理操作
System.out.println("Processed resource content: " + content);
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 资源依赖加载
在某些情况下,资源的加载可能依赖于其他 Bean 的初始化。通过实现 ApplicationListener
,你可以在上下文初始化完成后,确保所有依赖的 Bean 已经初始化,然后加载资源。
示例:
java复制
@Component
public class DependentResourceLoader implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private SomeBean someBean;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 确保依赖的 Bean 已经初始化
someBean.doSomething();
// 加载资源
String resourcePath = someBean.getResourcePath();
Resource resource = new FileSystemResource(resourcePath);
try (InputStream inputStream = resource.getInputStream()) {
// 处理资源内容
String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
System.out.println("Loaded resource content: " + content);
} catch (IOException e) {
e.printStackTrace();
}
}
}
为什么需要 ApplicationListener
来资源加载
虽然 Spring 在初始化时会自动加载配置文件中的资源,但在某些情况下,你可能需要在特定的生命周期阶段进行额外的资源加载或处理。ApplicationListener
提供了一个灵活的机制,允许你在上下文初始化完成后执行自定义逻辑,从而满足这些需求。
总结
ApplicationListener
的作用是允许开发者在 Spring 应用上下文的生命周期中插入自定义逻辑。通过实现 ApplicationListener
,你可以在上下文初始化完成后,根据环境属性动态加载资源、进行资源后处理或处理资源依赖。这使得 Spring 应用更加灵活,能够适应复杂的业务需求。