spring boot提供了sample程序,學習spring boot以前先跑一個最簡單的示例:html
/* * 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方法。java
SpringApplication用來從java main方法啓動一個spring應用,默認的啓動步驟以下:git
1)建立一個合適的ApplicationContext實例,這個實例取決於classpath。github
2)註冊一個CommandLinePropertySource,以spring屬性的形式來暴露命令行參數。web
3)刷新ApplicationContext,加載全部的單例bean。spring
4)觸發全部的命令行CommanLineRunner來執行bean。express
大部分場景下,能夠從你的application的main方法中直接調用它的run()靜態方法。示例以下:apache
@Configuration @EnableAutoConfiguration public class MyApplication { // ... Bean definitions public static void main(String[] args) throws Exception { SpringApplication.run(MyApplication.class, args); }
定製則能夠這樣:app
public static void main(String[] args) throws Exception { SpringApplication app = new SpringApplication(MyApplication.class); // ... customize app settings here app.run(args) }
springApplication能夠讀取不一樣種類的源文件:less
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