spring boot提供了sample程序,学习spring boot之前先跑一个最简单的示例:
/* * Copyright 2012-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sample.simple; import sample.simple.ExitException; import sample.simple.service.HelloWorldService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @SpringBootApplication public class SampleSimpleApplication implements CommandLineRunner { // Simple example shows how a command line spring application can execute an // injected bean service. Also demonstrates how you can use @Value to inject // command line args ('--name=whatever') or application properties @Autowired private HelloWorldService helloWorldService; public void run(String... args) { System.out.println(this.helloWorldService.getHelloMessage()); if (args.length > 0 && args[0].equals("exitcode")) { throw new ExitException(); } } public static void main(String[] args) throws Exception { SpringApplication application = new SpringApplication( SampleSimpleApplication.class); application.setApplicationContextClass(AnnotationConfigApplicationContext.class); SpringApplication.run(SampleSimpleApplication.class, args); } }
可以发现在主方法main里启动了一个SpringApplication,启动方法是run方法。
SpringApplication用来从java main方法启动一个spring应用,默认的启动步骤如下:
1)创建一个合适的ApplicationContext实例,这个实例取决于classpath。
2)注册一个CommandLinePropertySource,以spring属性的形式来暴露命令行参数。
3)刷新ApplicationContext,加载所有的单例bean。
4)触发所有的命令行CommanLineRunner来执行bean。
大部分场景下,可以从你的application的main方法中直接调用它的run()静态方法。示例如下:
@Configuration @EnableAutoConfiguration public class MyApplication { // ... Bean definitions public static void main(String[] args) throws Exception { SpringApplication.run(MyApplication.class, args); }
定制则可以这样:
public static void main(String[] args) throws Exception { SpringApplication app = new SpringApplication(MyApplication.class); // ... customize app settings here app.run(args) }
springApplication可以读取不同种类的源文件:
- 类- java类由
AnnotatedBeanDefinitionReader加载。
Resource
- xml资源文件由XmlBeanDefinitionReader读取
, 或者groovy脚本由GroovyBeanDefinitionReader读取
Package
- java包文件由ClassPathBeanDefinitionScanner扫描读取。
CharSequence
- 字符序列可以是类名、资源文件、包名,根据不同方式加载。如果一个字符序列不可以解析程序到类,也不可以解析到资源文件,那么就认为它是一个包。
1.初始化过程
public SpringApplication(Object... sources) { initialize(sources); } private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
2.运行方法run
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.started(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); context = createAndRefreshContext(listeners, applicationArguments); 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, ex); throw new IllegalStateException(ex); } }
2.1 配置属性
private void configureHeadlessProperty() { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty( SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); }
2.2 获取监听器
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); }
2.3 启动监听器
public void started() { for (SpringApplicationRunListener listener : this.listeners) { listener.started(); } }
listener最终会被初始化为ParentContextCloserApplicationListener,FileEncodingApplicationListener,AnsiOutputApplicationListener,ConfigFileApplicationListener,DelegatingApplicationListener,LiquibaseServiceLocatorApplicationListener,ClasspathLoggingApplicationListener,LoggingApplicationListener这几个类的对象组成的list。
下图画出了加载的ApplicationListener,并说明了他们的作用。
2.4 创建并刷新容器(重点)
private ConfigurableApplicationContext createAndRefreshContext( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableApplicationContext context; // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); if (isWebEnvironment(environment) && !this.webEnvironment) { environment = convertToStandardEnvironment(environment); } if (this.bannerMode != Banner.Mode.OFF) { printBanner(environment); } // Create, load, refresh and run the ApplicationContext context = createApplicationContext(); context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); // Load the sources Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[sources.size()])); listeners.contextLoaded(context); // Refresh the context refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } return context; }
2.4.1 获取或者创建环境
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } if (this.webEnvironment) { return new StandardServletEnvironment(); } return new StandardEnvironment(); }
若是有指定环境,则返回指定的ConfigurableEnvironment
public interface ConfigurableEnvironment extends Environment, ConfigurablePropertyResolver
使用示例如下:
以最高搜索级别增加一个属性
ConfigurableEnvironment environment = new StandardEnvironment(); MutablePropertySources propertySources = environment.getPropertySources(); Map myMap = new HashMap(); myMap.put("xyz", "myValue"); propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));
移除默认系统属性
MutablePropertySources propertySources = environment.getPropertySources();
propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)
测试环境mock系统属性
MutablePropertySources propertySources = environment.getPropertySources(); MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue"); propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
若是web环境,则使用StandardServletEnvironment
public class StandardServletEnvironment extends StandardEnvironment implements ConfigurableWebEnvironment
它用于基于server相关的web应用,所有web相关(Servlet相关)Application类默认初始化一个实例。
默认返回StandardEnvironment
public class StandardEnvironment extends AbstractEnvironment
StandardEnvironment例如非web环境等
2.4.2 配置环境
/** * Template method delegating to * {@link #configurePropertySources(ConfigurableEnvironment, String[])} and * {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order. * Override this method for complete control over Environment customization, or one of * the above for fine-grained control over property sources or profiles, respectively. * @param environment this application's environment * @param args arguments passed to the {@code run} method * @see #configureProfiles(ConfigurableEnvironment, String[]) * @see #configurePropertySources(ConfigurableEnvironment, String[]) */ protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { configurePropertySources(environment, args); configureProfiles(environment, args); }
2.4.3 创建ApplicationContext
/** * Strategy method used to create the {@link ApplicationContext}. By default this * method will respect any explicitly set application context or application context * class before falling back to a suitable default. * @return the application context (not yet refreshed) * @see #setApplicationContextClass(Class) */ protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { contextClass = Class.forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); }
2.4.4 加载bean到ApplicationContext
/** * Load beans into the application context. * @param context the context to load beans into * @param sources the sources to load */ protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug( "Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = createBeanDefinitionLoader( getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); }
2.4.5 刷新ApplicationContext
/** * Refresh the underlying {@link ApplicationContext}. * @param applicationContext the application context to refresh */ protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
小结:
上面仅仅是入门,若有谬误,请指正。后面随着学习的深入会修改。
参考文献:
【1】http://www.cnblogs.com/java-zhao/p/5540309.html
【2】http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow
微信公众号: 架构师日常笔记 欢迎关注!