SpringBoot源码分析1——ApplicationContextInitializer接口

前言

当我们创建一个SpringBoot应用,然后在main方法中打开SpringBoot的源码时,如下所示:

SpringApplication.run(Application.class, args);  // Application是自定义的类名

进入SpringBoot中的run方法,可以看到SpringBoot的启动过程其实是包含两个过程,即SpringApplication类的初始化,以及run方法执行,如下所示:

	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

先来看SpringApplication类的构造方法,代码如下:

	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}

		@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

在上述构造函数中,有两个重要的操作,一个是设定初始化器;一个是设定监听器。接下来我们就这是进入ApplicationContextInitializer接口的介绍。

1、ApplicationContextInitializer接口的实现类

先来看一下ApplicationContextInitializer接口中的具体内容:

 /*
 * Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
 * prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
 * Sprng上下文的回调接口,在refresh()之前执行
 */
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
	/**
	 * Initialize the given application context.
	 * @param applicationContext the application to configure
	 */
	void initialize(C applicationContext);
}

在接口中只有一个方法initialize(),主要的作用就是对Spring的上下文进行初始化。

由前言中可以看出,ApplicationContextInitializer接口的实现类主要是在SpringApplication类的构造函数中进行加载和初始化,这里我们先要看一个方法getSpringFactoriesInstances,具体实现如下:

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
        // 根据type来加载spring.factories中配置的类路径,具体实现如下;【这里的type=ApplicationContextInitializer.class】
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 根据类路径来实例化各类,具体实现如下
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		try {
            // 从META-INF/spring.factories中加载类的路径
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

看一下与ApplicationContextInitializer接口相关类路径在Spring.factories文件中的存储方式:

org.springframework.context.ApplicationContextInitializer=[org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer, org.springframework.boot.context.ContextIdApplicationContextInitializer, org.springframework.boot.context.config.DelegatingApplicationContextInitializer, org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer, org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer, org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer, org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener], 

是一个数组,当执行完上述的代码后,这7个子类都会被加载并实例化,另外如果有其他自定义的一些初始化器也会加载并实例化。

在ApplicationContextInitializer接口的子类中重要的方法就是重写接口中的initialize,在SpringBoot启动的时候真正执行是在run()方法内的prepareContext(…)方法内执行的。

2、自定义ApplicationContextInitializer接口子类

自定义一个ApplicationContextInitializer接口的实现类,如下所示:

public class SelfDefinitionInitializer implements ApplicationContextInitializer{

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		System.out.println("容器中Bean的数量为:"+applicationContext.getBeanDefinitionCount());
	}
}

如果要将SelfDefinitionInitializer添加到系统中可以有三种方式:

1、修改系统的启动类,进行如下的修改:

@SpringBootApplication
public class LearnSpringbootBean1Application {

    public static void main(String[] args) {
    	SpringApplication springApplication=new SpringApplication(LearnSpringbootBean1Application.class); 	
    	springApplication.addInitializers(new SelfDefinitionInitializer());
    	springApplication.run(args);
       // SpringApplication.run(LearnSpringbootBean1Application.class, args);
    }

}

2、系统配置文件(application.properties)中进行如下配置:

context.initializer.classes=com.learn.initializer.SelfDefinitionInitializer
# 如果有多个的话,使用,号隔开

3、创建一个META-INF/spring.factories文件,然后添加如下配置:

org.springframework.context.ApplicationContextInitializer=\
com.learn.initializer.SelfDefinitionInitializer

然后启动项目,都可以得到如下执行结果:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值