您当前的位置: 首页 > 

恐龙弟旺仔

暂无认证

  • 0浏览

    0关注

    282博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

CommandLineRunner与ApplicationRunner的使用及源码解析

恐龙弟旺仔 发布时间:2018-06-29 15:05:02 ,浏览量:0

前言:

    我们在使用SpringBoot搭建项目的时候,如果希望在项目启动完成之前,能够初始化一些操作,针对这种需求,可以考虑实现如下两个接口(任一个都可以)

org.springframework.boot.CommandLineRunner
org.springframework.boot.ApplicationRunner

1.CommandLineRunner接口的使用

@Component
public class TestCommandLineRunner implements CommandLineRunner {

	@Override
    // 实现该接口之后,实现其run方法,可以在run方法中自定义实现任务
	public void run(String... args) throws Exception {
		System.out.println("服务启动,TestCommandLineRunner执行启动加载任务...");
		if(null != args){
			for (String s : args) {
				System.out.println(s);
			}
		}
	}
}

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application.class);
        ConfigurableApplicationContext context = application.run(args);
        context.close();
    }
}

    启动Application类,可以看到TestCommandLineRunner.run()方法被执行。

    至于String... args参数,是用户在启动Application的时候,通过Run Configurations中的Arguments添加的参数(参数格式为KV格式,类似于--foo=bar),如下图所示

2.ApplicationRunner接口的使用

@Component
public class TestApplicationRunner implements ApplicationRunner {

	@Override
	public void run(ApplicationArguments args) throws Exception {
		System.out.println("服务启动,TestApplicationRunner执行启动加载任务...");
		String[] sourceArgs = args.getSourceArgs();
		if(null != sourceArgs){
			for (String s : sourceArgs) {
				System.out.println(s);
			}
		}
	}
}
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(Application.class);
        ConfigurableApplicationContext context = application.run(args);
        context.close();
    }
}

    ApplicationRunner接口的使用方式与CommandLineRunner接口基本相似,不同的只是run方法的参数类型,CommandLineRunner是基本类型,而ApplicationRunner是ApplicationArguments对象类型,经过一次简单封装,用户可对参数进行更多操作,具体可见ApplicationArguments的方法

    总结:用户使用CommandLineRunner或者ApplicationRunner接口均可实现应用启动初始化某些功能的需求,如果希望对参数有更多的操作,则可以选择实现ApplicationRunner接口。

3.CommandLineRunner、ApplicationRunner执行流程源码分析

    用户只要实现这两个接口,其中的run方法就会在项目启动时候被自动调用,那么究竟是在什么时候调用的呢?下面可以随笔者看一下Application的启动流程

    1)SpringApplication.run(args)

	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.started();
		try {
            // 对用户在Arguments输入的参数进行封装
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
            
            // 在更新完ApplicationContext之后,进行操作,run方法的调用就在这里被执行
			afterRefresh(context, applicationArguments);
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			return context;
		}
		catch (Throwable ex) {
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		}
	}

    2)跟踪afterRefresh(ConfigurableApplicationContext context,ApplicationArguments args)方法,代码如下:

	protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
		callRunners(context, args);
	}

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List runners = new ArrayList();
        // 1.获取所有实现ApplicationRunner接口的类
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        // 2.获取所有实现CommandLineRunner的类
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        // 3.根据其@Order进行排序
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet(runners)) {
            // 4.调用ApplicationRunner其run方法
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
            // 5.调用CommandLineRunner其run方法
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

    3)跟踪context.getBeansOfType()方法,具体实现如下(在类DefaultListableBeanFactory中):

	public  Map getBeansOfType(Class type, boolean includeNonSingletons, boolean allowEagerInit)
			throws BeansException {
        // 根据类型获取beanName
		String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
		Map result = new LinkedHashMap(beanNames.length);
		for (String beanName : beanNames) {
			try {
				result.put(beanName, getBean(beanName, type));
			}
			...
		}
		return result;
	}

	private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
		List result = new ArrayList();

		// 检查容器中所有的bean
		for (String beanName : this.beanDefinitionNames) {
			// Only consider bean as eligible if the bean name
			// is not defined as alias for some other bean.
			if (!isAlias(beanName)) {
				try {
					RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
					// Only check bean definition if it is complete.
					if (!mbd.isAbstract() && (allowEagerInit ||
							((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
									!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
						// In case of FactoryBean, match object created by FactoryBean.
						boolean isFactoryBean = isFactoryBean(beanName, mbd);
						BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
						boolean matchFound =
								(allowEagerInit || !isFactoryBean ||
										(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
								(includeNonSingletons ||
										(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
								isTypeMatch(beanName, type);
						if (!matchFound && isFactoryBean) {
							// In case of FactoryBean, try to match FactoryBean instance itself next.
							beanName = FACTORY_BEAN_PREFIX + beanName;
							matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
						}
                        // 对于符合类型的,添加到result中
						if (matchFound) {
							result.add(beanName);
						}
					}
				}
				...
			}
            return StringUtils.toStringArray(result);
		}

    总结:通过以上分析可知,实现这两个接口的类,在ApplicationContext.run()方法里被执行  

关注
打赏
1655041699
查看更多评论
立即登录/注册

微信扫码登录

0.3422s