ApplicationContextInitializer的使用

个人博客导航页(点击右侧链接即可打开个人博客):大牛带你入门技术栈 

1.介绍

ApplicationContextInitializer主要用在容器刷新之前调用改接口实现类的initialize方法,并将ConfigurableApplicationContext类的实例作为参数传入。通常用于根据应用上下文进行处理的编程中。且实现类可以通过Ordered接口或 @Order注解 进行多个Initializer的排序。

2.有三种使用方式

首先定义一个测试initializer

public class TestApplicationContextInitializer
    implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("Initializer调用:" + applicationContext.getApplicationName());
    }

    @Override
    public int getOrder() {
        return 0;
    }

}

第一种:在启动类的main方法中使用

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        // SpringApplication.run(DemoApplication.class, args);
        SpringApplication application = new SpringApplication(DemoApplication.class);
        application.addInitializers(new TestApplicationContextInitializer()); // 直接在SpringApplication中添加
        application.run(args);
        System.out.println("启动完成");
    }

}

输出结果:


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)

Initializer调用:
2020-03-18 19:15:09.448  INFO 87824 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ZZ-WZ112019 with PID 87824 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo)
2020-03-18 19:15:09.454  INFO 87824 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-03-18 19:15:09.950  INFO 87824 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 116.167 seconds (JVM running for 116.788)
启动完成

第二种:通过SPI扩展META-INF/spring.factories使用

spring.factories文件

org.springframework.context.ApplicationContextInitializer=\
com.example.demo.initializer.TestApplicationContextInitializer

启动类:

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
        // SpringApplication application = new SpringApplication(DemoApplication.class);
        // application.addInitializers(new TestApplicationContextInitializer()); // 直接在SpringApplication中添加
        // application.run(args);
        System.out.println("启动完成");
    }

}

输出结果:同上产生效果

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.5.RELEASE)

Initializer调用:
2020-03-18 19:20:23.812  INFO 87920 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on ZZ-WZ112019 with PID 87920 (E:\java_work\demo\target\classes started by jiafeng in E:\java_work\demo)
2020-03-18 19:20:23.814  INFO 87920 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2020-03-18 19:20:24.057  INFO 87920 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 0.443 seconds (JVM running for 0.932)
启动完成

第三种:通过配置文件使用

在配置文件application.properties中添加如下配置:

context.initializer.classes=com.example.demo.initializer.TestApplicationContextInitializer

运行启动类,依然被调用。

3. Ordered排序

通过继承Ordered接口实现int getOrder()方法进行排序,设置高优先级排序则在众多initializer中会被优先调用。

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }

4. 源码分析initializer运行时机

SpringApplication实例化对象时会对所有initializer进行加载。

SpringApplication的run( )方法:

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			//刷新之前准备工作,在该方法进行initialize调用
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//进行刷新
            refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

prepareContext( )方法中

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
        //执行initialize调用
		applyInitializers(context);
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}

在applyInitializers( )方法中对前面加载的initializer进行循环遍历调用initialize方法

    @SuppressWarnings({ "rawtypes", "unchecked" })
	protected void applyInitializers(ConfigurableApplicationContext context) {
        //遍历SpringApplication实例化时加载的所有initializer
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);//调用initialize实现方法
		}
	}

 

附Java/C/C++/机器学习/算法与数据结构/前端/安卓/Python/程序员必读/书籍书单大全:

(点击右侧 即可打开个人博客内有干货):技术干货小栈
=====>>①【Java大牛带你入门到进阶之路】<<====
=====>>②【算法数据结构+acm大牛带你入门到进阶之路】<<===
=====>>③【数据库大牛带你入门到进阶之路】<<=====
=====>>④【Web前端大牛带你入门到进阶之路】<<====
=====>>⑤【机器学习和python大牛带你入门到进阶之路】<<====
=====>>⑥【架构师大牛带你入门到进阶之路】<<=====
=====>>⑦【C++大牛带你入门到进阶之路】<<====
=====>>⑧【ios大牛带你入门到进阶之路】<<====
=====>>⑨【Web安全大牛带你入门到进阶之路】<<=====
=====>>⑩【Linux和操作系统大牛带你入门到进阶之路】<<=====

天下没有不劳而获的果实,望各位年轻的朋友,想学技术的朋友,在决心扎入技术道路的路上披荆斩棘,把书弄懂了,再去敲代码,把原理弄懂了,再去实践,将会带给你的人生,你的工作,你的未来一个美梦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值