在这篇文章中我们详细分析了springboot通过main方法启动程序的过程,但是其中涉及到的ApplicationContextInitializer内容并没有详细说明,本文作为补充,对本部分内容进行分析。
ApplicationContextInitializer是spring提供给我们针对ApplicationContext容器的一个扩展点,用于在对ApplicationContext容器刷新前进行一些设置,其源码如下:
package org.springframework.context;
// ConfigurableApplicationContext刷新前的回调接口,一般用在web环境中
// 针对ApplicationContext需要一些程序化定制的场景,比如设置Environment
// 的profile相关环境变量
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
// 初始化给定的applicationContext
void initialize(C applicationContext);
}
从其包名org.springframework.context
可以看出,这是spring提供的原生接口,而非springboot提供,在spring中其实并没有提供任何的实现类,从下图可以看出来这点:
我是在springboot环境中看的,所以只有springboot相关的具体实现类,因为该接口是spring设计用来给第三方框架扩展使用的,所以自己没有任何具体的实现也比较合理。
@SpringBootApplication
public class SpringbootHelloWorldApplication {
public static void main(String[] args) {
// SpringApplication.run(SpringbootHelloWorldApplication.class, args);
SpringApplication springApplication = new SpringApplication(SpringbootHelloWorldApplication.class);
/*springApplication.addInitializers(new ApplicationContextInitializer() {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
}
});*/
springApplication.addInitializers(ac -> System.out.println("initializer added by springboot main"));
springApplication.run(args);
}
}
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.14.RELEASE)
initializer added by springboot main
public class MyContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("initializer added by config file");
}
}
application.properties
文件中配置如下内容:context.initializer.classes=dongshi.daddy.contextinitializer.MyContextInitializer
。 . ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.14.RELEASE)
initializer added by config file
public class MyContextInitializer1 implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("initializer added by springboot spi");
}
}
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.14.RELEASE)
initializer added by springboot spi
结合这篇文章可以很容易知道,调用是在方法org.springframework.boot.SpringApplication#prepareContext
,源码如下:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
...snip...
// <202106071723>
applyInitializers(context);
...snip...
}
<202106071723>
处源码如下:
org.springframework.boot.SpringApplication#applyInitializers
// 在configurable application context刷新之前,应用所有的ApplicationContextInitializer
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
// 获取所有的ApplicationContextInitializer,并循环调用其initialize方法
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
接下来我们看下springboot中提供的具体实现类及其作用。
该类是通过springboot SPI配置的,如下图:
我们在2.2:在配置文件中
中分析了在配置文件中通过属性context.initializer.classes
注册自定义ApplicationContextInitializer,DelegatingApplicationContextInitializer的作用正是读取这些配置信息,并通过调用其initialize方法使其生效,看下源码:
org.springframework.boot.context.config.DelegatingApplicationContextInitializer#initialize
@Override
public void initialize(ConfigurableApplicationContext context) {
// 获取environment,用于获取属性信息
ConfigurableEnvironment environment = context.getEnvironment();
// <202106071755>
List<Class<?>> initializerClasses = getInitializerClasses(environment);
if (!initializerClasses.isEmpty()) {
// <202106071757>
applyInitializerClasses(context, initializerClasses);
}
}
<202106071755>
处是获取所有通过context.initializer.classes
配置的属性值,源码如下:
org.springframework.boot.context.config.DelegatingApplicationContextInitializer#getInitializerClasses
private List<Class<?>> getInitializerClasses(ConfigurableEnvironment env) {
String classNames = env.getProperty(PROPERTY_NAME);
List<Class<?>> classes = new ArrayList<>();
if (StringUtils.hasLength(classNames)) {
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
classes.add(getInitializerClass(className));
}
}
return classes;
}
不是很难,自己看下。
<202106071757>
处源码如下:
private void applyInitializerClasses(ConfigurableApplicationContext context, List<Class<?>> initializerClasses) {
Class<?> contextClass = context.getClass();
// 实例对象集合
List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
// 循环通过class创建实例对象,并添加到集合中
for (Class<?> initializerClass : initializerClasses) {
initializers.add(instantiateInitializer(contextClass, initializerClass));
}
// <202106071758>
applyInitializers(context, initializers);
}
<202106071758>
处是关键代码,应用AppplicationContextInitializer,源码如下:
org.springframework.boot.context.config.DelegatingApplicationContextInitializer#applyInitializers
private void applyInitializers(ConfigurableApplicationContext context,
List<ApplicationContextInitializer<?>> initializers) {
// 排序
initializers.sort(new AnnotationAwareOrderComparator());
// 循环调用initialize方法,进行应用
for (ApplicationContextInitializer initializer : initializers) {
initializer.initialize(context);
}
}
用于生成application context容器的contextId,源码如下:
org.springframework.boot.context.ContextIdApplicationContextInitializer#initialize
@Override
publicvoid initialize(ConfigurableApplicationContext applicationContext) {
// <202106081142>
ContextId contextId = getContextId(applicationContext);
// 设置id到applicationContext
applicationContext.setId(contextId.getId());
// 将contextId作为单例bean存储,以Context.class.getName()作为bean名称
// 这样如果是我们需要获取application context的id信息的话就可以获取使用了
applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
}
<202106081142>
处是生成application context容器的id,ContextId是一个用于表示容器id的内部类,如下:
org.springframework.boot.context.ContextIdApplicationContextInitializer.ContextId
static class ContextId {
private final AtomicLong children = new AtomicLong(0);
private final String id;
ContextId(String id) {
this.id = id;
}
ContextId createChildId() {
return new ContextId(this.id + "-" + this.children.incrementAndGet());
}
String getId() {
return this.id;
}
}
getContextId
源码如下:
org.springframework.boot.context.ContextIdApplicationContextInitializer#getContextId
private ContextId getContextId(ConfigurableApplicationContext applicationContext) {
ApplicationContext parent = applicationContext.getParent();
if (parent != null && parent.containsBean(ContextId.class.getName())) {
return parent.getBean(ContextId.class).createChildId();
}
// 通过getApplicationId方法获取id并封装到ContextId对象中,其中getApplicationid方法源码如下
/*
org.springframework.boot.context.ContextIdApplicationContextInitializer#getApplicationId
private String getApplicationId(ConfigurableEnvironment environment) {
// 如果有属性"spring.application.name"则使用其作为id,否则使用"application"作为id
String name = environment.getProperty("spring.application.name");
return StringUtils.hasText(name) ? name : "application";
}
*/
return new ContextId(getApplicationId(applicationContext.getEnvironment()));
}
该类用于进行一些错误配置的检查等工作,比如我们配置了扫描spring的包org.springframework
,如:
@SpringBootApplication(scanBasePackages = { "org.springframework" })
则会抛出如下异常信息:
图中红框的信息就是ConfigurationWarningsApplicationContextInitializer
生成的,打印日志执行过程如下图:
下面我们来具体看下,initialize
源码如下:
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer#initialize
@Override
public void initialize(ConfigurableApplicationContext context) {
// <202106081408>
context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
}
<202106081408>
处是注册BeanDefnitionRegistryPostProcessor,在bean定义加载完毕后,对BeanDefinitionRegistry执行一些操作,这里是检查相关。ConfigurationWarningsPostProcessor
参考4.3.1:ConfigurationWarningsPostProcessor
,getChecks()
源码如下:
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer#getChecks
// 返回需要被应用的Check
protected Check[] getChecks() {
// 这里的check只有ComponentScanPackageCheck一个类,主要是执行@ComponentScan扫描的包路径
// 是否合法的检查操作,Check是一个接口,如下:
/*
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.Check
// 代表一个可以被应用的检查
@FunctionalInterface
protected interface Check {
// 有警告信息则返回警告,否则返回null
String getWarning(BeanDefinitionRegistry registry);
}
*/
return new Check[] { new ComponentScanPackageCheck() };
}
接下来我们看下ComponentScanPackageCheck的getWarning方法,了解具体是如何执行扫描包检查的:
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.ComponentScanPackageCheck#getWarning
@Override
public String getWarning(BeanDefinitionRegistry registry) {
// <202106081406>
Set<String> scannedPackages = getComponentScanningPackages(registry);
// <202106081436>
List<String> problematicPackages = getProblematicPackages(scannedPackages);
if (problematicPackages.isEmpty()) {
return null;
}
return "Your ApplicationContext is unlikely to " + "start due to a @ComponentScan of "
+ StringUtils.collectionToDelimitedString(problematicPackages, ", ") + ".";
}
<202106081406>
处获取要扫描的包,源码如下:
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.ComponentScanPackageCheck#getComponentScanningPackages
protected Set<String> getComponentScanningPackages(BeanDefinitionRegistry registry) {
// 返回集合
Set<String> packages = new LinkedHashSet<>();
// 获取所有的bean名称
String[] names = registry.getBeanDefinitionNames();
for (String name : names) {
// 根据bean名称获取bean定义
BeanDefinition definition = registry.getBeanDefinition(name);
// 如果是有注解的bean定义
if (definition instanceof AnnotatedBeanDefinition) {
// 强转
AnnotatedBeanDefinition annotatedDefinition = (AnnotatedBeanDefinition) definition;
// 获取@ComponetScan注解配置的扫描包路径的值,源码如下:
/*
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.ComponentScanPackageCheck#addComponentScanningPackages
private void addComponentScanningPackages(Set packages, AnnotationMetadata metadata) {
// 获取注解ComponentScan的属性信息
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(ComponentScan.class.getName(), true));
if (attributes != null) {
// 获取value配置的值,添加到结果中
addPackages(packages, attributes.getStringArray("value"));
// 获取basePackages配置的值,添加到结果中
addPackages(packages, attributes.getStringArray("basePackages"));
// 获取basePackageClasses的值,添加到结果中
addClasses(packages, attributes.getStringArray("basePackageClasses"));
// 如果为空,则使用当前类所在的包为扫描包
if (packages.isEmpty()) {
packages.add(ClassUtils.getPackageName(metadata.getClassName()));
}
}
}
*/
addComponentScanningPackages(packages, annotatedDefinition.getMetadata());
}
}
return packages;
}
<202106081436>
处获取有问题的扫描包路径,源码如下:
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.ComponentScanPackageCheck#getProblematicPackages
private List<String> getProblematicPackages(Set<String> scannedPackages) {
List<String> problematicPackages = new ArrayList<>();
for (String scannedPackage : scannedPackages) {
// <202106081438>
// 若为有问题包路径,则添加到有问题包路径集合中
if (isProblematicPackage(scannedPackage)) {
problematicPackages.add(getDisplayName(scannedPackage));
}
}
return problematicPackages;
}
<202106081438>
处是判断是否为有问题的包路径,源码如下:
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.ComponentScanPackageCheck#isProblematicPackage
private boolean isProblematicPackage(String scannedPackage) {
if (scannedPackage == null || scannedPackage.isEmpty()) {
return true;
}
// 如果在PROBLEM_PACKAGES包含则为true,其信息如下:
/*
private static final Set PROBLEM_PACKAGES;
static {
Set packages = new HashSet<>();
packages.add("org.springframework");
packages.add("org");
PROBLEM_PACKAGES = Collections.unmodifiableSet(packages);
}
*/
// 因此如果是我们扫描org.springframework,或者是扫描org包的话,这里就为true了
return PROBLEM_PACKAGES.contains(scannedPackage);
}
这是ConfigurationWarningsApplicationContextInitializer的一个内部类,实现了BeanDefinitionRegistryPostProcessor接口,关于该接口的调用时机可以参考这篇文章,就是在容器refresh时bean定义加载完毕后,通过bean定义生成spring bean之前的这个时间点,源码如下:
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor
// 用于报告警告信息
protected static final class ConfigurationWarningsPostProcessor
implements PriorityOrdered, BeanDefinitionRegistryPostProcessor {
// 用于执行具体检查的数据,Check是一个接口,如下:
/*
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer.Check
// 代表一个可以被应用的检查
@FunctionalInterface
protected interface Check {
// 有警告信息则返回警告,否则返回null
String getWarning(BeanDefinitionRegistry registry);
}
*/
private Check[] checks;
public ConfigurationWarningsPostProcessor(Check[] checks) {
this.checks = checks;
}
...snip...
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// 调用所有的Check对BeanDefinitionRegistry进行检查
for (Check check : this.checks) {
// 执行检查,获取检查结果
String message = check.getWarning(registry);
// 如果有警告,则调用warn方法打印警告日志
if (StringUtils.hasLength(message)) {
warn(message);
}
}
}
// 打印警告日志
private void warn(String message) {
if (logger.isWarnEnabled()) {
logger.warn(String.format("%n%n** WARNING ** : %s%n%n", message));
}
}
}
用于设置WebServer实际监听的端口号到application context中,源码如下:
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer#initialize
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// <202106081524>
applicationContext.addApplicationListener(this);
}
<202106081524>
处是将自己作为ApplicationListener进行注册,因为实现了ApplicationListener
,因此最终会执行其onApplicationEvent
方法,如下:
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer#onApplicationEvent
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
// getName方法如下:
/*
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer#getName
private String getName(WebServerApplicationContext context) {
String name = context.getServerNamespace();
// 如果是有命名空间serverNamespace则使用,否则使用默认的server
return StringUtils.hasText(name) ? name : "server";
}
*/
// 一般结果是local.server.port
String propertyName = "local." + getName(event.getApplicationContext()) + ".port";
// <202106081538>
setPortProperty(event.getApplicationContext(), propertyName, event.getWebServer().getPort());
}
<202106081538>
处event.getWebServer().getPort()是获取使用的端口号,该处源码如下:
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer#setPortProperty(org.springframework.context.ApplicationContext, java.lang.String, int)
private void setPortProperty(ApplicationContext context, String propertyName, int port) {
// 这里为true
if (context instanceof ConfigurableApplicationContext) {
// <202106081540>
// 强转,调用重载版本
setPortProperty(((ConfigurableApplicationContext) context).getEnvironment(), propertyName, port);
}
if (context.getParent() != null) {
setPortProperty(context.getParent(), propertyName, port);
}
}
<202106081540>
处源码如下:
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer#setPortProperty(org.springframework.core.env.ConfigurableEnvironment, java.lang.String, int)
private void setPortProperty(ConfigurableEnvironment environment, String propertyName, int port) {
// 获取封装各种来源属性信息的PropertySources
MutablePropertySources sources = environment.getPropertySources();
// 获取属性server.ports配置的值
PropertySource<?> source = sources.get("server.ports");
if (source == null) {
source = new MapPropertySource("server.ports", new HashMap<>());
sources.addFirst(source);
}
// 以local.server.port为key,以port为值,存储到server.ports属性源对应的map中
((Map<String, Object>) source.getSource()).put(propertyName, port);
}
https://www.cnblogs.com/hello-shf/p/10987360.html