Spring Boot 專題

 

Spring is a very popular Java-based framework for building web and enterprise applications. Unlike many other frameworks, which focus on only one area, Spring framework provides a wide verity of features addressing the modern business needs via its portfolio projects.javascript

In relation to Spring, Spring Boot aims to make it easy to create Spring-powered, production-grade applications and services with minimum fuss. It takes an opinionated view of the Spring platform so that new and existing users can quickly get to the bits they need.html

The diagram below shows Spring Boot as a point of focus on the larger Spring ecosystem:
前端

The primary goals of Spring Boot are:vue

To provide a radically faster and widely accessible ‘getting started’ experience for all Spring development.java

To be opinionated out of the box, but get out of the way quickly as requirements start to diverge from the defaults.node

To provide a range of non-functional features that are common to large classes of projects (e.g. embedded servers, security, metrics, health checks, externalized configuration).mysql

Spring Boot does not generate code and there is absolutely no requirement for XML configuration.git

https://github.com/boylegu/SpringBoot-vue

github

 

 

 

 

 

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(HelloProperties.class)//開啓屬性注入,經過@autowired注入
@ConditionalOnClass(Hello.class)//判斷這個類是否在classpath中存在
//當設置hello=enabled的狀況下,若是沒有設置則默認爲true,即條件符合
@ConditionalOnProperty(prefix="hello", value="enabled", matchIfMissing = true)//name屬性和value屬性是互斥的,不能同時使用
public class HelloAutoConfiguration {
    
    @Autowired
    private HelloProperties helloProperties;
    
    @Bean//使用java配置方式配置這個類
    @ConditionalOnMissingBean(Hello.class)//容器中若是沒有Hello這個類,那麼自動配置這個Hello
    public Hello hello() {
        Hello hello = new Hello();
        hello.setMsg(helloProperties.getMsg());
        return hello;
    }
    
}

 

 

 

 

 

@SpringBootApplication至關於@Configuration、@EnableAutoConfiguration和 @ComponentScan,你也能夠同時使用這3個註解。其中@Configuration、@ComponentScan是spring框架的語法,在spring 3.x就有了,用於代碼方式建立配置信息和掃描包。@EnableAutoConfiguration是spring boot語法,表示將使用自動配置。你若是下載了spring boot源碼,就會看到spring boot實現了不少starter應用,這些starter就是一些配置信息(有點相似於docker,一組環境一種應用的概念),spring boot看到引入的starter包,就能夠計算若是自動配置你的應用。

 

http://docs.spring.io/spring-boot/docs/1.2.8.RELEASE/reference/htmlsingle/#boot-features-logging-file-output

Spring Boot 支持多種外部配置方式

這些方式優先級以下:web

  1. 命令行參數
  2. 來自java:comp/env的JNDI屬性
  3. Java系統屬性(System.getProperties()
  4. 操做系統環境變量
  5. RandomValuePropertySource配置的random.*屬性值
  6. jar包外部的application-{profile}.propertiesapplication.yml(帶spring.profile)配置文件
  7. jar包內部的application-{profile}.propertiesapplication.yml(帶spring.profile)配置文件
  8. jar包外部的application.propertiesapplication.yml(不帶spring.profile)配置文件
  9. jar包內部的application.propertiesapplication.yml(不帶spring.profile)配置文件
  10. @Configuration註解類上的@PropertySource
  11. 經過SpringApplication.setDefaultProperties指定的默認屬性

應用配置文件(.properties或.yml)

在配置文件中直接寫:

name=Isea533 server.port=8080

.yml格式的配置文件如:

name: Isea533 server: port: 8080

當有前綴的狀況下,使用.yml格式的配置文件更簡單。關於.yml配置文件用法請看這裏

注意:使用.yml時,屬性名的值和冒號中間必須有空格,如name: Isea533正確,name:Isea533就是錯的。

屬性配置文件的位置

spring會從classpath下的/config目錄或者classpath的根目錄查找application.propertiesapplication.yml

/config優先於classpath根目錄

@PropertySource

這個註解能夠指定具體的屬性配置文件,優先級比較低。

SpringApplication.setDefaultProperties

例如:

SpringApplication application = new SpringApplication(Application.class); Map<String, Object> defaultMap = new HashMap<String, Object>(); defaultMap.put("name", "Isea-Blog"); //還能夠是Properties對象 application.setDefaultProperties(defaultMap); application.run(args);

http://blog.csdn.net/isea533/article/details/50281151

 

 

 

used an application.properties with Spring Boot (1.3 M1) and started to translate it into a yaml file because it grew more and more complex.

But I have problems translating this into yaml:

logging.level.*=WARN
logging.level.com.filenet.wcm=ERROR
logging.level.de.mycompany=DEBUG

The last two lines are easily translated into this:

logging:
    level:
        com.filenet.wcm: ERROR
        de.mycompany: DEBUG

But how to add the values for the root logging level ? These two approaches failed:

Failed approach 1:

logging:
    level: WARN
        com.filenet.wcm: ERROR
        de.mycompany: DEBUG

Failed approach 2:

logging:
    level: 
        star: WARN
        com.filenet.wcm: ERROR
        de.mycompany: DEBUG

I read the docs, searched stackoverflow and googled but did not find an example for a valid syntax.

 

You can use ROOT to configure the root logging level:

logging:
  level:
    ROOT: DEBUG

http://stackoverflow.com/questions/3837801/how-to-change-root-logging-level-programmatically

 

 

logging configuration in Yaml file #1265

https://github.com/spring-projects/spring-boot/issues/1265

 

Spring Boot建議將咱們main方法所在的這個主要的配置類配置在根包名下。

相似以下結構:

com
 +- example
     +- myproject
         +- Application.java
         | +- domain | +- Customer.java | +- CustomerRepository.java | +- service | +- CustomerService.java | +- web +- CustomerController.java
 
中有方法。Application.javamain

啓動項目SpringApplication.run

啓動Spring Boot項目最簡單的方法就是執行下面的方法:

SpringApplication.run(Application.class, args);

該方法返回一個ApplicationContext對象,使用註解的時候返回的具體類型是AnnotationConfigApplicationContextAnnotationConfigEmbeddedWebApplicationContext,當支持web的時候是第二個。

除了上面這種方法外,還能夠用下面的方法:

SpringApplication application = new SpringApplication(Application.class); application.run(args);

SpringApplication包含了一些其餘能夠配置的方法,若是你想作一些配置,能夠用這種方式。

除了上面這種直接的方法外,還可使用SpringApplicationBuilder

new SpringApplicationBuilder() .showBanner(false) .sources(Application.class) .run(args);

當使用SpringMVC的時候因爲須要使用子容器,就須要用到SpringApplicationBuilder,該類有一個child(xxx...)方法能夠添加子容器。

http://blog.csdn.net/isea533/article/details/50278205

 

 

本文記錄Spring Boot application.propertis配置文件的相關通用屬性

# ===================================================================
# COMMON SPRING BOOT PROPERTIES # # This sample file is provided as a guideline. Do NOT copy it in its # entirety to your own application. ^^^ # =================================================================== # ---------------------------------------- # CORE PROPERTIES # ---------------------------------------- # SPRING CONFIG (ConfigFileApplicationListener) spring.config.name= # config file name (default to 'application') spring.config.location= # location of config file # PROFILES spring.profiles= # comma list of active profiles # APPLICATION SETTINGS (SpringApplication) spring.main.sources= spring.main.web-environment= # detect by default spring.main.show-banner=true spring.main....= # see class for all properties # LOGGING logging.path=/var/logs logging.file=myapp.log logging.config= # IDENTITY (ContextIdApplicationContextInitializer) spring.application.name= spring.application.index= # EMBEDDED SERVER CONFIGURATION (ServerProperties) server.port=8080 server.address= # bind to a specific NIC server.session-timeout= # session timeout in seconds server.context-path= # the context path, defaults to '/' server.servlet-path= # the servlet path, defaults to '/' server.tomcat.access-log-pattern= # log pattern of the access log server.tomcat.access-log-enabled=false # is access logging enabled server.tomcat.protocol-header=x-forwarded-proto # ssl forward headers server.tomcat.remote-ip-header=x-forwarded-for server.tomcat.basedir=/tmp # base dir (usually not needed, defaults to tmp) server.tomcat.background-processor-delay=30; # in seconds server.tomcat.max-threads = 0 # number of threads in protocol handler server.tomcat.uri-encoding = UTF-8 # character encoding to use for URL decoding # SPRING MVC (HttpMapperProperties) http.mappers.json-pretty-print=false # pretty print JSON http.mappers.json-sort-keys=false # sort keys spring.mvc.locale= # set fixed locale, e.g. en_UK spring.mvc.date-format= # set fixed date format, e.g. dd/MM/yyyy spring.mvc.message-codes-resolver-format= # PREFIX_ERROR_CODE / POSTFIX_ERROR_CODE spring.view.prefix= # MVC view prefix spring.view.suffix= # ... and suffix spring.resources.cache-period= # cache timeouts in headers sent to browser spring.resources.add-mappings=true # if default mappings should be added # THYMELEAF (ThymeleafAutoConfiguration) spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html spring.thymeleaf.mode=HTML5 spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.content-type=text/html # ;charset=<encoding> is added spring.thymeleaf.cache=true # set to false for hot refresh # FREEMARKER (FreeMarkerAutoConfiguration) spring.freemarker.allowRequestOverride=false spring.freemarker.allowSessionOverride=false spring.freemarker.cache=true spring.freemarker.checkTemplateLocation=true spring.freemarker.contentType=text/html spring.freemarker.exposeRequestAttributes=false spring.freemarker.exposeSessionAttributes=false spring.freemarker.exposeSpringMacroHelpers=false spring.freemarker.prefix= spring.freemarker.requestContextAttribute= spring.freemarker.settings.*= spring.freemarker.suffix=.ftl spring.freemarker.templateEncoding=UTF-8 spring.freemarker.templateLoaderPath=classpath:/templates/ spring.freemarker.viewNames= # whitelist of view names that can be resolved # GROOVY TEMPLATES (GroovyTemplateAutoConfiguration) spring.groovy.template.allowRequestOverride=false spring.groovy.template.allowSessionOverride=false spring.groovy.template.cache=true spring.groovy.template.configuration.*= # See Groovy's TemplateConfiguration spring.groovy.template.contentType=text/html spring.groovy.template.prefix=classpath:/templates/ spring.groovy.template.suffix=.tpl spring.groovy.template.templateEncoding=UTF-8 spring.groovy.template.viewNames= # whitelist of view names that can be resolved # VELOCITY TEMPLATES (VelocityAutoConfiguration) spring.velocity.allowRequestOverride=false spring.velocity.allowSessionOverride=false spring.velocity.cache=true spring.velocity.checkTemplateLocation=true spring.velocity.contentType=text/html spring.velocity.dateToolAttribute= spring.velocity.exposeRequestAttributes=false spring.velocity.exposeSessionAttributes=false spring.velocity.exposeSpringMacroHelpers=false spring.velocity.numberToolAttribute= spring.velocity.prefix= spring.velocity.properties.*= spring.velocity.requestContextAttribute= spring.velocity.resourceLoaderPath=classpath:/templates/ spring.velocity.suffix=.vm spring.velocity.templateEncoding=UTF-8 spring.velocity.viewNames= # whitelist of view names that can be resolved # INTERNATIONALIZATION (MessageSourceAutoConfiguration) spring.messages.basename=messages spring.messages.cacheSeconds=-1 spring.messages.encoding=UTF-8 # SECURITY (SecurityProperties) security.user.name=user # login username security.user.password= # login password security.user.role=USER # role assigned to the user security.require-ssl=false # advanced settings ... security.enable-csrf=false security.basic.enabled=true security.basic.realm=Spring security.basic.path= # /** security.headers.xss=false security.headers.cache=false security.headers.frame=false security.headers.contentType=false security.headers.hsts=all # none / domain / all security.sessions=stateless # always / never / if_required / stateless security.ignored=false # DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties) spring.datasource.name= # name of the data source spring.datasource.initialize=true # populate using data.sql spring.datasource.schema= # a schema (DDL) script resource reference spring.datasource.data= # a data (DML) script resource reference spring.datasource.platform= # the platform to use in the schema resource (schema-${platform}.sql) spring.datasource.continueOnError=false # continue even if can't be initialized spring.datasource.separator=; # statement separator in SQL initialization scripts spring.datasource.driverClassName= # JDBC Settings... spring.datasource.url= spring.datasource.username= spring.datasource.password= spring.datasource.max-active=100 # Advanced configuration... spring.datasource.max-idle=8 spring.datasource.min-idle=8 spring.datasource.initial-size=10 spring.datasource.validation-query= spring.datasource.test-on-borrow=false spring.datasource.test-on-return=false spring.datasource.test-while-idle= spring.datasource.time-between-eviction-runs-millis= spring.datasource.min-evictable-idle-time-millis= spring.datasource.max-wait-millis= # MONGODB (MongoProperties) spring.data.mongodb.host= # the db host spring.data.mongodb.port=27017 # the connection port (defaults to 27107) spring.data.mongodb.uri=mongodb://localhost/test # connection URL spring.data.mongo.repositories.enabled=true # if spring data repository support is enabled # JPA (JpaBaseConfiguration, HibernateJpaAutoConfiguration) spring.jpa.properties.*= # properties to set on the JPA connection spring.jpa.openInView=true spring.jpa.show-sql=true spring.jpa.database-platform= spring.jpa.database= spring.jpa.generate-ddl=false # ignored by Hibernate, might be useful for other vendors spring.jpa.hibernate.naming-strategy= # naming classname spring.jpa.hibernate.ddl-auto= # defaults to create-drop for embedded dbs spring.data.jpa.repositories.enabled=true # if spring data repository support is enabled # SOLR (SolrProperties}) spring.data.solr.host=http://127.0.0.1:8983/solr spring.data.solr.zkHost= spring.data.solr.repositories.enabled=true # if spring data repository support is enabled # ELASTICSEARCH (ElasticsearchProperties}) spring.data.elasticsearch.cluster-name= # The cluster name (defaults to elasticsearch) spring.data.elasticsearch.cluster-nodes= # The address(es) of the server node (comma-separated; if not specified starts a client node) spring.data.elasticsearch.local=true # if local mode should be used with client nodes spring.data.elasticsearch.repositories.enabled=true # if spring data repository support is enabled # FLYWAY (FlywayProperties) flyway.locations=classpath:db/migrations # locations of migrations scripts flyway.schemas= # schemas to update flyway.initVersion= 1 # version to start migration flyway.prefix=V flyway.suffix=.sql flyway.enabled=true flyway.url= # JDBC url if you want Flyway to create its own DataSource flyway.user= # JDBC username if you want Flyway to create its own DataSource flyway.password= # JDBC password if you want Flyway to create its own DataSource # LIQUIBASE (LiquibaseProperties) liquibase.change-log=classpath:/db/changelog/db.changelog-master.yaml liquibase.contexts= # runtime contexts to use liquibase.default-schema= # default database schema to use liquibase.drop-first=false liquibase.enabled=true # JMX spring.jmx.enabled=true # Expose MBeans from Spring # RABBIT (RabbitProperties) spring.rabbitmq.host= # connection host spring.rabbitmq.port= # connection port spring.rabbitmq.addresses= # connection addresses (e.g. myhost:9999,otherhost:1111) spring.rabbitmq.username= # login user spring.rabbitmq.password= # login password spring.rabbitmq.virtualhost= spring.rabbitmq.dynamic= # REDIS (RedisProperties) spring.redis.host=localhost # server host spring.redis.password= # server password spring.redis.port=6379 # connection port spring.redis.pool.max-idle=8 # pool settings ... spring.redis.pool.min-idle=0 spring.redis.pool.max-active=8 spring.redis.pool.max-wait=-1 # ACTIVEMQ (ActiveMQProperties) spring.activemq.broker-url=tcp://localhost:61616 # connection URL spring.activemq.user= spring.activemq.password= spring.activemq.in-memory=true # broker kind to create if no broker-url is specified spring.activemq.pooled=false # HornetQ (HornetQProperties) spring.hornetq.mode= # connection mode (native, embedded) spring.hornetq.host=localhost # hornetQ host (native mode) spring.hornetq.port=5445 # hornetQ port (native mode) spring.hornetq.embedded.enabled=true # if the embedded server is enabled (needs hornetq-jms-server.jar) spring.hornetq.embedded.serverId= # auto-generated id of the embedded server (integer) spring.hornetq.embedded.persistent=false # message persistence spring.hornetq.embedded.data-directory= # location of data content (when persistence is enabled) spring.hornetq.embedded.queues= # comma separate queues to create on startup spring.hornetq.embedded.topics= # comma separate topics to create on startup spring.hornetq.embedded.cluster-password= # customer password (randomly generated by default) # JMS (JmsProperties) spring.jms.pub-sub-domain= # false for queue (default), true for topic # SPRING BATCH (BatchDatabaseInitializer) spring.batch.job.names=job1,job2 spring.batch.job.enabled=true spring.batch.initializer.enabled=true spring.batch.schema= # batch schema to load # AOP spring.aop.auto= spring.aop.proxy-target-class= # FILE ENCODING (FileEncodingApplicationListener) spring.mandatory-file-encoding=false # SPRING SOCIAL (SocialWebAutoConfiguration) spring.social.auto-connection-views=true # Set to true for default connection views or false if you provide your own # SPRING SOCIAL FACEBOOK (FacebookAutoConfiguration) spring.social.facebook.app-id= # your application's Facebook App ID spring.social.facebook.app-secret= # your application's Facebook App Secret # SPRING SOCIAL LINKEDIN (LinkedInAutoConfiguration) spring.social.linkedin.app-id= # your application's LinkedIn App ID spring.social.linkedin.app-secret= # your application's LinkedIn App Secret # SPRING SOCIAL TWITTER (TwitterAutoConfiguration) spring.social.twitter.app-id= # your application's Twitter App ID spring.social.twitter.app-secret= # your application's Twitter App Secret # SPRING MOBILE SITE PREFERENCE (SitePreferenceAutoConfiguration) spring.mobile.sitepreference.enabled=true # enabled by default # SPRING MOBILE DEVICE VIEWS (DeviceDelegatingViewResolverAutoConfiguration) spring.mobile.devicedelegatingviewresolver.enabled=true # disabled by default spring.mobile.devicedelegatingviewresolver.normalPrefix= spring.mobile.devicedelegatingviewresolver.normalSuffix= spring.mobile.devicedelegatingviewresolver.mobilePrefix=mobile/ spring.mobile.devicedelegatingviewresolver.mobileSuffix= spring.mobile.devicedelegatingviewresolver.tabletPrefix=tablet/ spring.mobile.devicedelegatingviewresolver.tabletSuffix= # ---------------------------------------- # ACTUATOR PROPERTIES # ---------------------------------------- # MANAGEMENT HTTP SERVER (ManagementServerProperties) management.port= # defaults to 'server.port' management.address= # bind to a specific NIC management.contextPath= # default to '/' # ENDPOINTS (AbstractEndpoint subclasses) endpoints.autoconfig.id=autoconfig endpoints.autoconfig.sensitive=true endpoints.autoconfig.enabled=true endpoints.beans.id=beans endpoints.beans.sensitive=true endpoints.beans.enabled=true endpoints.configprops.id=configprops endpoints.configprops.sensitive=true endpoints.configprops.enabled=true endpoints.configprops.keys-to-sanitize=password,secret endpoints.dump.id=dump endpoints.dump.sensitive=true endpoints.dump.enabled=true endpoints.env.id=env endpoints.env.sensitive=true endpoints.env.enabled=true endpoints.health.id=health endpoints.health.sensitive=false endpoints.health.enabled=true endpoints.info.id=info endpoints.info.sensitive=false endpoints.info.enabled=true endpoints.metrics.id=metrics endpoints.metrics.sensitive=true endpoints.metrics.enabled=true endpoints.shutdown.id=shutdown endpoints.shutdown.sensitive=true endpoints.shutdown.enabled=false endpoints.trace.id=trace endpoints.trace.sensitive=true endpoints.trace.enabled=true # MVC ONLY ENDPOINTS endpoints.jolokia.path=jolokia endpoints.jolokia.sensitive=true endpoints.jolokia.enabled=true # when using Jolokia endpoints.error.path=/error # JMX ENDPOINT (EndpointMBeanExportProperties) endpoints.jmx.enabled=true endpoints.jmx.domain= # the JMX domain, defaults to 'org.springboot' endpoints.jmx.unique-names=false endpoints.jmx.enabled=true endpoints.jmx.staticNames= # JOLOKIA (JolokiaProperties) jolokia.config.*= # See Jolokia manual # REMOTE SHELL shell.auth=simple # jaas, key, simple, spring shell.command-refresh-interval=-1 shell.command-path-pattern= # classpath*:/commands/**, classpath*:/crash/commands/** shell.config-path-patterns= # classpath*:/crash/* shell.disabled-plugins=false # don't expose plugins shell.ssh.enabled= # ssh settings ... shell.ssh.keyPath= shell.ssh.port= shell.telnet.enabled= # telnet settings ... shell.telnet.port= shell.auth.jaas.domain= # authentication settings ... shell.auth.key.path= shell.auth.simple.user.name= shell.auth.simple.user.password= shell.auth.spring.roles= # GIT INFO spring.git.properties= # resource ref to generated git info properties file

http://www.tuicool.com/articles/veUjQba

 

spring.datasource.schema指定啓動後執行的sql文件位置。

我發現中文亂碼,緣由是沒有指定執行sql script encoding:

spring:
datasource:
url: "jdbc:h2:mem:test"
username: "sa"
password: ""
schema: database/import.sql
sql-script-encoding: utf-8
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.h2.Driver
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat

http://www.cnblogs.com/woshimrf/p/5625474.html

  按照官方的話說:Spring Cloud 爲開發者提供了在分佈式系統(如配置管理、服務發現、斷路器、智能路由、微代理、控制總線、一次性 Token、全局鎖、決策競選、分佈式會話和集羣狀態)操做的開發工具。最關鍵的是它足夠簡單,通常的開發人員只須要幾天時間就能夠學會它的基本用法。

  本Spring Cloud 7天系列教程,包括7個例子和相關短文,都是最簡單的用法,也是默認最基本的用法,在實際生產環境中也能夠用上,固然是初步使用。

  項目開源地址:http://git.oschina.net/zhou666/spring-cloud-7simple

7個例子包括:

1)一個基本的spring boot應用。

2)分佈式配置管理服務端

3)分佈式配置管理客戶端(微服務應用)

4)服務註冊服務端

5)服務註冊發現客戶端(微服務應用)

6)spring boot風格的web前端應用

7)使用docker發佈應用

 

7天學習週期以下:

第1天:查看spring boot官方文檔,實現及實驗spring boot 應用。

第2天:熟讀spring cloud官方文檔配置管理部分並熟悉配置管理相關概念。

第3天:熟悉Git概念,並上傳配置文件到Git服務器,最後實現分佈式配置管理。

第4天:熟讀spring cloud官方文檔服務註冊部分,實現服務註冊及發現。

第5天:熟讀spring cloud官方文檔剩餘部分,並實現斷路器。

第6天:深刻spring boot相關概念,使用angularJS實現web前端應用。

第7天:瞭解docker概念,並結合spring boot搭建一個docker應用。

http://www.cnblogs.com/skyblog/p/5127690.html

環境

本文基於Spring Boot版本1.3.3, 使用了spring-boot-starter-web。

配置完成後,編寫了代碼以下:

@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } @RestController public class RootController { public static final String PATH_ROOT = "/"; @RequestMapping(PATH_ROOT) public String welcome() { return "Welcome!"; } }

雖然只有幾行代碼,可是這已是一個完整的Web程序,當訪問url的path部分爲"/"時,返回字符串"Welcome!"。

首先是一個很是普通的java程序入口,一個符合約定的靜態main方法。在這個main方法中,調用了SpringApplication的靜態run方法,並將Application類對象和main方法的參數args做爲參數傳遞了進去。

而後是一個使用了兩個Spring註解的RootController類,咱們在main方法中,沒有直接使用這個類。

SpringApplication類的靜態run方法

如下代碼摘自:org.springframework.boot.SpringApplication public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); } public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); }

在這個靜態方法中,建立SpringApplication對象,並調用該對象的run方法。

構造SpringApplication對象

如下代碼摘自:org.springframework.boot.SpringApplication public SpringApplication(Object... sources) { initialize(sources); } private void initialize(Object[] sources) { // 爲成員變量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(); }

構造函數中調用initialize方法,初始化SpringApplication對象的成員變量sources,webEnvironment,initializers,listeners,mainApplicationClass。sources的賦值比較簡單,就是咱們傳給SpringApplication.run方法的參數。剩下的幾個,咱們依次來看看是怎麼作的。

首先是webEnvironment:

如下代碼摘自:org.springframework.boot.SpringApplication private boolean webEnvironment; private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private void initialize(Object[] sources) { ... // 爲成員變量webEnvironment賦值 this.webEnvironment = deduceWebEnvironment(); ... } private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }

能夠看到webEnvironment是一個boolean,該成員變量用來表示當前應用程序是否是一個Web應用程序。那麼怎麼決定當前應用程序是否Web應用程序呢,是經過在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES這個數組中所包含的類,若是存在那麼當前程序便是一個Web應用程序,反之則否則。
在本文的例子中webEnvironment的值爲true。

而後是initializers:

initializers成員變量,是一個ApplicationContextInitializer類型對象的集合。 顧名思義,ApplicationContextInitializer是一個能夠用來初始化ApplicationContext的接口。

如下代碼摘自:org.springframework.boot.SpringApplication private List<ApplicationContextInitializer<?>> initializers; private void initialize(Object[] sources) { ... // 爲成員變量initializers賦值 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); ... } public void setInitializers( Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<ApplicationContextInitializer<?>>(); this.initializers.addAll(initializers); }

能夠看到,關鍵是調用getSpringFactoriesInstances(ApplicationContextInitializer.class),來獲取ApplicationContextInitializer類型對象的列表。

如下代碼摘自:org.springframework.boot.SpringApplication private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }

在該方法中,首先經過調用SpringFactoriesLoader.loadFactoryNames(type, classLoader)來獲取全部Spring Factories的名字,而後調用createSpringFactoriesInstances方法根據讀取到的名字建立對象。最後會將建立好的對象列表排序並返回。

如下代碼摘自:org.springframework.core.io.support.SpringFactoriesLoader public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }

能夠看到,是從一個名字叫spring.factories的資源文件中,讀取key爲org.springframework.context.ApplicationContextInitializer的value。而spring.factories的部份內容以下:

如下內容摘自spring-boot-1.3.3.RELEASE.jar中的資源文件META-INF/spring.factories # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer

能夠看到,最近的獲得的,是ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer這四個類的名字。

接下來會調用createSpringFactoriesInstances來建立ApplicationContextInitializer實例。

如下代碼摘自:org.springframework.boot.SpringApplication private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<T>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getConstructor(parameterTypes); T instance = (T) constructor.newInstance(args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } return instances; }

因此在咱們的例子中,SpringApplication對象的成員變量initalizers就被初始化爲,ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer這四個類的對象組成的list。

下圖畫出了加載的ApplicationContextInitializer,並說明了他們的做用。至於什麼時候應用他們,且聽後面慢慢分解。

SpringBootApplicationContextInitializer

接下來是成員變量listeners

如下代碼摘自:org.springframework.boot.SpringApplication private List<ApplicationListener<?>> listeners; private void initialize(Object[] sources) { ... // 爲成員變量listeners賦值 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); ... } public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<ApplicationListener<?>>(); this.listeners.addAll(listeners); }

listeners成員變量,是一個ApplicationListener<?>類型對象的集合。能夠看到獲取該成員變量內容使用的是跟成員變量initializers同樣的方法,只不過傳入的類型從ApplicationContextInitializer.class變成了ApplicationListener.class。

看一下spring.factories中的相關內容:

如下內容摘自spring-boot-1.3.3.RELEASE.jar中的資源文件META-INF/spring.factories # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\ org.springframework.boot.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.logging.LoggingApplicationListener

也就是說,在咱們的例子中,listener最終會被初始化爲ParentContextCloserApplicationListener,FileEncodingApplicationListener,AnsiOutputApplicationListener,ConfigFileApplicationListener,DelegatingApplicationListener,LiquibaseServiceLocatorApplicationListener,ClasspathLoggingApplicationListener,LoggingApplicationListener這幾個類的對象組成的list。

下圖畫出了加載的ApplicationListener,並說明了他們的做用。至於他們什麼時候會被觸發,等事件出現時,咱們再說明。

SpringBootApplicationListener

最後是mainApplicationClass

如下代碼摘自:org.springframework.boot.SpringApplication private Class<?> mainApplicationClass; private void initialize(Object[] sources) { ... // 爲成員變量mainApplicationClass賦值 this.mainApplicationClass = deduceMainApplicationClass(); ... } private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }

在deduceMainApplicationClass方法中,經過獲取當前調用棧,找到入口方法main所在的類,並將其複製給SpringApplication對象的成員變量mainApplicationClass。在咱們的例子中mainApplicationClass便是咱們本身編寫的Application類。

SpringApplication對象的run方法

通過上面的初始化過程,咱們已經有了一個SpringApplication對象,根據SpringApplication類的靜態run方法一節中的分析,接下來會調用SpringApplication對象的run方法。咱們接下來就分析這個對象的run方法。

如下代碼摘自:org.springframework.boot.SpringApplication 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); } }
  • 可變個數參數args便是咱們整個應用程序的入口main方法的參數,在咱們的例子中,參數個數爲零。

  • StopWatch是來自org.springframework.util的工具類,能夠用來方便的記錄程序的運行時間。

SpringApplication對象的run方法建立並刷新ApplicationContext,算是開始進入正題了。下面按照執行順序,介紹該方法所作的工做。

headless模式

如下代碼摘自:org.springframework.boot.SpringApplication private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless"; private boolean headless = true; public ConfigurableApplicationContext run(String... args) { ... //設置headless模式 configureHeadlessProperty(); ... } private void configureHeadlessProperty() { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty( SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); }

其實是就是設置系統屬性java.awt.headless,在咱們的例子中該屬性會被設置爲true,由於咱們開發的是服務器程序,通常運行在沒有顯示器和鍵盤的環境。關於java中的headless模式,更多信息能夠參考這裏

SpringApplicationRunListeners

如下代碼摘自:org.springframework.boot.SpringApplication public ConfigurableApplicationContext run(String... args) { ... SpringApplicationRunListeners listeners = getRunListeners(args); listeners.started(); /**  * 建立並刷新ApplicationContext  * context = createAndRefreshContext(listeners, applicationArguments);   **/ listeners.finished(context, null); ... } private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); }

run方法中,加載了一系列SpringApplicationRunListener對象,在建立和更新ApplicationContext方法先後分別調用了listeners對象的started方法和finished方法, 並在建立和刷新ApplicationContext時,將listeners做爲參數傳遞到了createAndRefreshContext方法中,以便在建立和刷新ApplicationContext的不一樣階段,調用listeners的相應方法以執行操做。因此,所謂的SpringApplicationRunListeners實際上就是在SpringApplication對象的run方法執行的不一樣階段,去執行一些操做,而且這些操做是可配置的。

同時,能夠看到,加載SpringApplicationRunListener時,使用的是跟加載ApplicationContextInitializer和ApplicationListener時同樣的方法。那麼加載了什麼,就能夠從spring.factories文件中看到了:

如下內容摘自spring-boot-1.3.3.RELEASE.jar中的資源文件META-INF/spring.factories # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener

能夠看到,在咱們的例子中加載的是org.springframework.boot.context.event.EventPublishingRunListener。咱們看一看這個SpringApplicationRunListener究竟作了點什麼工做了?

如下代碼摘自:org.springframework.boot.context.event.EventPublishingRunListener public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.multicaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this.multicaster.addApplicationListener(listener); } } @Override public void started() { publishEvent(new ApplicationStartedEvent(this.application, this.args)); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { publishEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); } @Override public void contextPrepared(ConfigurableApplicationContext context) { registerApplicationEventMulticaster(context); } @Override public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } publishEvent(new ApplicationPreparedEvent(this.application, this.args, context)); } @Override public void finished(ConfigurableApplicationContext context, Throwable exception) { publishEvent(getFinishedEvent(context, exception)); }

EventPublishingRunListener在對象初始化時,將SpringApplication對象的成員變量listeners全都保存下來,而後在本身的public方法被調用時,發佈相應的事件,或執行相應的操做。能夠說這個RunListener是在SpringApplication對象的run方法執行到不一樣的階段時,發佈相應的event給SpringApplication對象的成員變量listeners中記錄的事件監聽器。

下圖畫出了SpringApplicationRunListeners相關的類結構,雖然咱們的例子中只有一個SpringApplicationRunListener,但在這樣的設計下,想要擴展是很是容易的!

SpringBootApplicationContextInitializer

接下來,咱們看一下在調用listeners的started方法。在咱們的例子中,也就是發佈了ApplicationStartedEvent時,咱們已經加載的事件監聽器都作了什麼操做。至於其它事件的發佈,咱們按照代碼執行的順序在後面的章節在介紹。

  • ParentContextCloserApplicationListener不監聽ApplicationStartedEvent,沒有操做;
  • FileEncodingApplicationListener不監聽ApplicationStartedEvent,沒有操做;
  • AnsiOutputApplicationListener不監聽ApplicationStartedEvent,沒有操做;
  • ConfigFileApplicationListener不監聽ApplicationStartedEvent,沒有操做;
  • DelegatingApplicationListener不監聽ApplicationStartedEvent,沒有操做;
  • LiquibaseServiceLocatorApplicationListener監聽ApplicationStartedEvent,會檢查classpath中是否有liquibase.servicelocator.ServiceLocator並作相應操做;
如下代碼摘自:org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener @Override public void onApplicationEvent(ApplicationStartedEvent event) { if (ClassUtils.isPresent("liquibase.servicelocator.ServiceLocator", null)) { new LiquibasePresent().replaceServiceLocator(); } }

咱們的例子中,classpath中不存在liquibase,因此不執行任何操做。

  • ClasspathLoggingApplicationListener監聽ApplicationStartedEvent,會打印classpath到debug日誌;
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationStartedEvent) { if (this.logger.isDebugEnabled()) { this.logger.debug("Application started with classpath: " + getClasspath()); } ... } private String getClasspath() { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader instanceof URLClassLoader) { return Arrays.toString(((URLClassLoader) classLoader).getURLs()); } return "unknown"; }

由於是debug級別的日誌,而SpringBoot的默認日誌級別是info級,因此咱們在控制檯不會看到classpath的輸出。

  • LoggingApplicationListener監聽ApplicationStartedEvent,會根據classpath中的類狀況建立相應的日誌系統對象,並執行一些初始化以前的操做;
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationStartedEvent) { onApplicationStartedEvent((ApplicationStartedEvent) event); } ... } private void onApplicationStartedEvent(ApplicationStartedEvent event) { this.loggingSystem = LoggingSystem .get(event.getSpringApplication().getClassLoader()); this.loggingSystem.beforeInitialize(); }

咱們的例子中,建立的是org.springframework.boot.logging.logback.LogbackLoggingSystem類的對象,Logback是SpringBoot默認採用的日誌系統。下圖畫出了SpringBoot中的日誌系統體系:

SpringBootLoggingSystem

好了,ApplicationStartedEvent事件的處理這樣就結束了。之後在介紹事件處理的時候,咱們只介紹監聽該事件的監聽器的操做,而不監聽的,就再也不說明了。

建立並刷新ApplicationContext

如下代碼摘自:org.springframework.boot.SpringApplication public ConfigurableApplicationContext run(String... args) { ... try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); context = createAndRefreshContext(listeners, applicationArguments); afterRefresh(context, applicationArguments); ... } catch (Throwable ex) { handleRunFailure(context, listeners, ex); throw new IllegalStateException(ex); } }

首先是建立一個DefaultApplicationArguments對象,以後調用createAndRefreshContext方法建立並刷新一個ApplicationContext,最後調用afterRefresh方法在刷新以後作一些操做。

先來看看DefaultApplicationArguments吧:

如下代碼摘自:org.springframework.boot.DefaultApplicationArguments DefaultApplicationArguments(String[] args) { Assert.notNull(args, "Args must not be null"); this.source = new Source(args); this.args = args; } private static class Source extends SimpleCommandLinePropertySource { Source(String[] args) { super(args); } ... } 如下代碼摘自:org.springframework.core.env.SimpleCommandLinePropertySource public SimpleCommandLinePropertySource(String... args) { super(new SimpleCommandLineArgsParser().parse(args)); }

能夠看到是把main函數的args參數當作一個PropertySource來解析。咱們的例子中,args的長度爲0,因此這裏建立的DefaultApplicationArguments也沒有實際的內容。

建立並配置ApplicationConext的Environment

如下代碼摘自:org.springframework.boot.SpringApplication private ConfigurableEnvironment environment; private boolean webEnvironment; private ConfigurableApplicationContext createAndRefreshContext( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableApplicationContext context; // 建立並配置Environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); if (isWebEnvironment(environment) && !this.webEnvironment) { environment = convertToStandardEnvironment(environment); } ... return context; } private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } if (this.webEnvironment) { return new StandardServletEnvironment(); } return new StandardEnvironment(); }

Spring Application的Environment表明着程序運行的環境,主要包含了兩種信息,一種是profiles,用來描述哪些bean definitions是可用的;一種是properties,用來描述系統的配置,其來源多是配置文件、JVM屬性文件、操做系統環境變量等等。

首先要調用getOrCreateEnvironment方法獲取一個Environment對象。在咱們的例子中,執行到此處時,environment成員變量爲null,而webEnvironment成員變量的值爲true,因此會建立一個StandardServletEnvironment對象並返回。

以後是調用configureEnvironment方法來配置上一步獲取的Environment對象,代碼以下:

如下代碼摘自:org.springframework.boot.SpringApplication private Map<String, Object> defaultProperties; private boolean addCommandLineProperties = true; private Set<String> additionalProfiles = new HashSet<String>(); protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { configurePropertySources(environment, args); configureProfiles(environment, args); } protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { sources.addLast( new MapPropertySource("defaultProperties", this.defaultProperties)); } if (this.addCommandLineProperties && args.length > 0) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(new SimpleCommandLinePropertySource( name + "-" + args.hashCode(), args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } } } protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { environment.getActiveProfiles(); // ensure they are initialized // But these ones should go first (last wins in a property key clash) Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles); profiles.addAll(Arrays.asList(environment.getActiveProfiles())); environment.setActiveProfiles(profiles.toArray(new String[profiles.size()])); }

configureEnvironment方法先是調用configurePropertySources來配置properties,而後調用configureProfiles來配置profiles。

configurePropertySources首先查看SpringApplication對象的成員變量defaultProperties,若是該變量非null且內容非空,則將其加入到Environment的PropertySource列表的最後。而後查看SpringApplication對象的成員變量addCommandLineProperties和main函數的參數args,若是設置了addCommandLineProperties=true,且args個數大於0,那麼就構造一個由main函數的參數組成的PropertySource放到Environment的PropertySource列表的最前面(這就能保證,咱們經過main函數的參數來作的配置是最優先的,能夠覆蓋其餘配置)。在咱們的例子中,因爲沒有配置defaultProperties且main函數的參數args個數爲0,因此這個函數什麼也不作。

configureProfiles首先會讀取Properties中key爲spring.profiles.active的配置項,配置到Environment,而後再將SpringApplication對象的成員變量additionalProfiles加入到Environment的active profiles配置中。在咱們的例子中,配置文件裏沒有spring.profiles.active的配置項,而SpringApplication對象的成員變量additionalProfiles也是一個空的集合,因此這個函數沒有配置任何active profile。

到如今,Environment就算是配置完成了。接下來調用SpringApplicationRunListeners類的對象listeners發佈ApplicationEnvironmentPreparedEvent事件:

如下代碼摘自:org.springframework.boot.context.event.EventPublishingRunListener @Override public void environmentPrepared(ConfigurableEnvironment environment) { publishEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); }

好,如今來看一看咱們加載的ApplicationListener對象都有哪些響應了這個事件,作了什麼操做:

  • FileEncodingApplicationListener響應該事件,檢查file.encoding配置是否與spring.mandatory_file_encoding一致:
如下代碼摘自:org.springframework.boot.context.FileEncodingApplicationListener @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( event.getEnvironment(), "spring."); if (resolver.containsProperty("mandatoryFileEncoding")) { String encoding = System.getProperty("file.encoding"); String desired = resolver.getProperty("mandatoryFileEncoding"); if (encoding != null && !desired.equalsIgnoreCase(encoding)) { logger.error("System property 'file.encoding' is currently '" + encoding + "'. It should be '" + desired + "' (as defined in 'spring.mandatoryFileEncoding')."); logger.error("Environment variable LANG is '" + System.getenv("LANG") + "'. You could use a locale setting that matches encoding='" + desired + "'."); logger.error("Environment variable LC_ALL is '" + System.getenv("LC_ALL") + "'. You could use a locale setting that matches encoding='" + desired + "'."); throw new IllegalStateException( "The Java Virtual Machine has not been configured to use the " + "desired default character encoding (" + desired + ")."); } } }

在咱們的例子中,由於沒有spring.mandatory_file_encoding的配置,因此這個響應方法什麼都不作。

  • AnsiOutputApplicationListener響應該事件,根據spring.output.ansi.enabled和spring.output.ansi.console-available對AnsiOutput類作相應配置:
如下代碼摘自:org.springframework.boot.context.config.AnsiOutputApplicationListener @Override public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( event.getEnvironment(), "spring.output.ansi."); if (resolver.containsProperty("enabled")) { String enabled = resolver.getProperty("enabled"); AnsiOutput.setEnabled(Enum.valueOf(Enabled.class, enabled.toUpperCase())); } if (resolver.containsProperty("console-available")) { AnsiOutput.setConsoleAvailable( resolver.getProperty("console-available", Boolean.class)); } }

咱們的例子中,這兩項配置都是空的,因此這個響應方法什麼都不作。

  • ConfigFileApplicationListener加載該事件,從一些約定的位置加載一些配置文件,並且這些位置是可配置的。
如下代碼摘自:org.springframework.boot.context.config.ConfigFileApplicationListener @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(event); } } private void onApplicationEnvironmentPreparedEvent( ApplicationEnvironmentPreparedEvent event) { List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); postProcessors.add(this); AnnotationAwareOrderComparator.sort(postProcessors); for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } } List<EnvironmentPostProcessor> loadPostProcessors() { return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader()); } 如下內容摘自spring-boot-1.3.3.RELEASE.jar中的資源文件META-INF/spring.factories # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor

能夠看到,ConfigFileApplicationListener從META-INF/spring.factories文件中讀取EnvironmentPostProcessor配置,加載相應的EnvironmentPostProcessor類的對象,並調用其postProcessEnvironment方法。在咱們的例子中,會加載CloudFoundryVcapEnvironmentPostProcessor和SpringApplicationJsonEnvironmentPostProcessor並執行,因爲咱們的例子中沒有CloudFoundry和Json的配置,因此這個響應,不會加載任何的配置文件到Environment中來。

  • DelegatingApplicationListener響應該事件,將配置文件中key爲context.listener.classes的配置項,加載在成員變量multicaster中:
如下內容摘自:org.springframework.boot.context.config.DelegatingApplicationListener private static final String PROPERTY_NAME = "context.listener.classes"; private SimpleApplicationEventMulticaster multicaster; @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { List<ApplicationListener<ApplicationEvent>> delegates = getListeners( ((ApplicationEnvironmentPreparedEvent) event).getEnvironment()); if (delegates.isEmpty()) { return; } this.multicaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<ApplicationEvent> listener : delegates) { this.multicaster.addApplicationListener(listener); } } if (this.multicaster != null) { this.multicaster.multicastEvent(event); } } @SuppressWarnings("unchecked") private List<ApplicationListener<ApplicationEvent>> getListeners( ConfigurableEnvironment env) { String classNames = env.getProperty(PROPERTY_NAME); List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<ApplicationListener<ApplicationEvent>>(); if (StringUtils.hasLength(classNames)) { for (String className : StringUtils.commaDelimitedListToSet(classNames)) { try { Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader()); Assert.isAssignable(ApplicationListener.class, clazz, "class [" + className + "] must implement ApplicationListener"); listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils .instantiateClass(clazz)); } catch (Exception ex) { throw new ApplicationContextException( "Failed to load context listener class [" + className + "]", ex); } } } AnnotationAwareOrderComparator.sort(listeners); return listeners; }

咱們的例子中,由於沒有key爲context.listener.classes的Property,因此不會加載任何listener到該監聽器中。

  • LoggingApplicationListener響應該事件,並對在ApplicationStarted時加載的LoggingSystem作一些初始化工做:
如下代碼摘自:org.springframework.boot.logging.LoggingApplicationListener @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationStartedEvent) { onApplicationStartedEvent((ApplicationStartedEvent) event); } else if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); } else if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent((ApplicationPreparedEvent) event); } else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event) .getApplicationContext().getParent() == null) { onContextClosedEvent(); } } private void onApplicationEnvironmentPreparedEvent( ApplicationEnvironmentPreparedEvent event) { if (this.loggingSystem == null) { this.loggingSystem = LoggingSystem .get(event.getSpringApplication().getClassLoader()); } initialize(event.getEnvironment(), event.getSpringApplication().getClassLoader()); } protected void initialize(ConfigurableEnvironment environment, ClassLoader classLoader) { LogFile logFile = LogFile.get(environment); setSystemProperties(environment, logFile); initializeEarlyLoggingLevel(environment); initializeSystem(environment, this.loggingSystem, logFile); initializeFinalLoggingLevels(environment, this.loggingSystem); registerShutdownHookIfNecessary(environment, this.loggingSystem); }

在咱們的例子中,是對加載的LogbackLoggingSystem作一些初始化工做。關於日誌系統更詳細的討論,值得再寫一篇文章,就不在這裏展開討論了。

打印banner

如下代碼摘自:org.springframework.boot.SpringApplication private Banner banner; private Banner.Mode bannerMode = Banner.Mode.CONSOLE; public static final String BANNER_LOCATION_PROPERTY = "banner.location"; public static final String BANNER_LOCATION_PROPERTY_VALUE = "banner.txt"; private static final Banner DEFAULT_BANNER = new SpringBootBanner(); private ConfigurableApplicationContext createAndRefreshContext( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ... if (this.bannerMode != Banner.Mode.OFF) { printBanner(environment); } ... } protected void printBanner(Environment environment) { Banner selectedBanner = selectBanner(environment); if (this.bannerMode == Banner.Mode.LOG) { try { logger.info(createStringFromBanner(selectedBanner, environment)); } catch (UnsupportedEncodingException ex) { logger.warn("Failed to create String for banner", ex); } } else { selectedBanner.printBanner(environment, this.mainApplicationClass, System.out); } } private Banner selectBanner(Environment environment) { String location = environment.getProperty(BANNER_LOCATION_PROPERTY, BANNER_LOCATION_PROPERTY_VALUE); ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); Resource resource = resourceLoader.getResource(location); if (resource.exists()) { return new ResourceBanner(resource); } if (this.banner != null) { return this.banner; } return DEFAULT_BANNER; } private String createStringFromBanner(Banner banner, Environment environment) throws UnsupportedEncodingException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); banner.printBanner(environment, this.mainApplicationClass, new PrintStream(baos)); String charset = environment.getProperty("banner.charset", "UTF-8"); return baos.toString(charset); }

printBanner方法中,首先會調用selectBanner方法獲得一個banner對象,而後判斷bannerMode的類型,若是是Banner.Mode.LOG,那麼將banner對象轉換爲字符串,打印一條info日誌,不然的話,調用banner對象的printbanner方法,將banner打印到標準輸出System.out。

在咱們的例子中,bannerMode是Banner.Mode.Console,並且也未曾提供過banner.txt這樣的資源文件。因此selectBanner方法中獲得到即是默認的banner對象,即SpringBootBanner類的對象:

如下代碼摘自:org.springframework.boot.SpringBootBanner private static final String[] BANNER = { "", " . ____ _ __ _ _", " /\\\\ / ___'_ __ _ _(_)_ __ __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\", " \\\\/ ___)| |_)| | | | | || (_| | ) ) ) )", " ' |____| .__|_| |_|_| |_\\__, | / / / /", " =========|_|==============|___/=/_/_/_/" }; private static final String SPRING_BOOT = " :: Spring Boot :: "; private static final int STRAP_LINE_SIZE = 42; @Override public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) { for (String line : BANNER) { printStream.println(line); } String version = SpringBootVersion.getVersion(); version = (version == null ? "" : " (v" + version + ")"); String padding = ""; while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) { padding += " "; } printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding, AnsiStyle.FAINT, version)); printStream.println(); }

先打印個Spring的圖形,而後打印個Spring Boot的文本,再而後打印一下Spring Boot的版本。會在控制檯看到以下輸出:

如下內容是程序啓動後在console的輸出:

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

個人天。分析啓動流程這麼久,終於在屏幕有一行輸出了,不容易。

建立ApplicationContext

private Class<? extends ConfigurableApplicationContext> applicationContextClass; public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." + "annotation.AnnotationConfigApplicationContext"; public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework." + "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext"; private ConfigurableApplicationContext createAndRefreshContext( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableApplicationContext context; ... context = createApplicationContext(); context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } ... return context; } 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); }

createAndRefreshContext中調用createApplicationContext獲取建立ApplicationContext,能夠看到,當檢測到本次程序是一個web應用程序(成員變量webEnvironment爲true)的時候,就加載類DEFAULT_WEB_CONTEXT_CLASS,不然的話加載DEFAULT_CONTEXT_CLASS。咱們的例子是一個web應用程序,因此會加載DEFAULT_WEB_CONTEXT_CLASS,也就是org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext。咱們先來看一看這個AnnotationConfigEmbeddedWebApplicationContext具體有什麼功能。下圖畫出了它的繼承體系。

SpringBootApplicationContext

能夠看到咱們加載的這個AnnotationConfigEmbeddedWebApplicationContext類,從名字就能夠看出來,首先是一個WebApplicationContext實現了WebApplicationContext接口,而後是一個EmbeddedWebApplicationContext,這意味着它會自動建立並初始化一個EmbeddedServletContainer,同時還支持AnnotationConfig,會將使用註解標註的bean註冊到ApplicationContext中。更詳細的過程,後面在例子中再一一剖析。

能夠看到在加載類對象AnnotationConfigEmbeddedWebApplicationContext以後,createApplicationContext方法中緊接着調用BeanUtils的instantiate方法來建立ApplicationContext對象,其代碼以下:

如下代碼摘自:org.springframework.beans.BeanUtils public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { return clazz.newInstance(); } catch (InstantiationException ex) { throw new BeanInstantiationException(clazz, "Is it an abstract class?", ex); } catch (IllegalAccessException ex) { throw new BeanInstantiationException(clazz, "Is the constructor accessible?", ex); } }

經過調用Class對象的newInstance()方法來實例化對象,這等同於直接調用類的空的構造方法,因此咱們來看AnnotationConfigEmbeddedWebApplicationContext類的構造方法:

如下代碼摘自:org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext public AnnotationConfigEmbeddedWebApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } @Override public void setEnvironment(ConfigurableEnvironment environment) { super.setEnvironment(environment); this.reader.setEnvironment(environment); this.scanner.setEnvironment(environment); }

構造方法中初始化了兩個成員變量,類型分別爲AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner用以加載使用註解的bean定義。

這樣ApplicationContext對象就建立出來了,在createAndRefreshContext方法中建立了ApplicationContext對象以後會緊接着調用其setEnvironment將咱們以前準備好的Environment對象賦值進去。以後分別調用postProcessApplicationContext和applyInitializers作一些處理和初始化的操做。

先來看看postProcessApplicationContext:

protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.webEnvironment) { if (context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext configurableContext = (ConfigurableWebApplicationContext) context; if (this.beanNameGenerator != null) { configurableContext.getBeanFactory().registerSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } } } if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context) .setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context) .setClassLoader(this.resourceLoader.getClassLoader()); } } }

若是成員變量beanNameGenerator不爲Null,那麼爲ApplicationContext對象註冊beanNameGenerator bean。若是成員變量resourceLoader不爲null,則爲ApplicationContext對象設置ResourceLoader。咱們的例子中,這兩個成員變量都爲Null,因此什麼都不作。

以後是applyInitializers方法:

protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } } public Set<ApplicationContextInitializer<?>> getInitializers() { return asUnmodifiableOrderedSet(this.initializers); } private static <E> Set<E> asUnmodifiableOrderedSet(Collection<E> elements) { List<E> list = new ArrayList<E>();http://www.cnblogs.com/lic309/p/4079194.html list.addAll(elements); Collections.sort(list, AnnotationAwareOrderComparator.INSTANCE); return new LinkedHashSet<E>(list); }

(寫到這裏,發現篇幅已經不短,就到這裏做爲第一篇吧。下篇繼續。)

http://www.cnblogs.com/xinzhao/p/5551828.html

搭建aop

  原本spring就自帶一套aop實現,咱們直接使用此實現便可,原本使用aop還須要定義一些xml文件,但因爲咱們使用的是spring-boot框架,這一步就省略掉了。也就是說,在spring-boot中,咱們能夠直接使用aop而不須要任何的配置

  具體如何搭建spring-boot請參考:http://www.cnblogs.com/lic309/p/4073307.html

http://www.cnblogs.com/lic309/p/4079194.html

通常在一個項目中,老是會有好多個環境。好比:

開發環境 -> 測試環境 -> 預發佈環境 -> 生產環境

每一個環境上的配置文件老是不同的,甚至開發環境中每一個開發者的環境可能也會有一點不一樣,配置讀取但是一個讓人有點傷腦筋的問題。

Spring Boot提供了一種優先級配置讀取的機制來幫助咱們從這種困境中走出來。

常規狀況下,咱們都知道Spring Boot的配置會從application.properties中讀取。實際上,從resource目錄下的application.properties文件讀取是Spring Boot配置鏈中的一環而已。

根據Spring Boot的文檔,配置使用的優先級從高到低的順序,具體以下所示:

1. 命令行參數。 2. 經過 System.getProperties() 獲取的 Java 系統參數。 3. 操做系統環境變量。 4. 從 java:comp/env 獲得的 JNDI 屬性。 5. 經過 RandomValuePropertySource 生成的「random.*」屬性。 6. 應用 Jar 文件以外的屬性文件(application.properties)。 7. 應用 Jar 文件內部的屬性文件(application.properties)。 8. 在應用配置 Java 類(包含「@Configuration」註解的 Java 類)中經過「@PropertySource」註解聲明的屬性文件。 9. 經過「SpringApplication.setDefaultProperties」聲明的默認屬性。

這意味着,若是Spring Boot在優先級更高的位置找到了配置,那麼它就會無視低級的配置。

好比,我在application.properties目錄中,寫入本地的MySQL的配置:

db.jdbc.driver=com.mysql.jdbc.Driver
db.jdbc.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8 db.jdbc.username=username db.jdbc.password=password

在本身項目調試的階段,項目老是會使用本地的MySQL數據庫。而一旦打包以後,在外部聲明一個test_evn.properties.

啓動Jar包的時候, 指定一個外部配置文件:

java -jar demo.jar --spring.config.location=/path/test_evn.properties

這樣一來,咱們在開發者的機器上老是使用本身的配置,而一到對應的環境,就會使用高級的位置所作的配置。

在代碼中讀取這些配置也是很是方便的,在代碼的邏輯中,實際上是無需去關心這個配置是從什麼地方來的,只用關注能獲取什麼配置就夠了。

public class ApplicationConfigure { @Value("${db.jdbc.driver}") private String jdbcDriver; @Value("${db.jdbc.url}") private String jdbcUrl; @Value("${db.jdbc.username}") private String jdbcUsername; @Value("${db.jdbc.password}") private String jdbcPassword; // mysql config class // ..... }

有時候咱們在項目啓動的時候,老是須要先啓動一些初始化的類,之前比較常見的作法是寫再static塊中,Spring Boot提供了一個CommandLineRunner接口,實現這個接口的類老是會被優先啓動,並優先執行CommandLineRunner接口中提供的run()方法。

public class ApplicationConfigure implements CommandLineRunner { @Value("${db.jdbc.driver}") private String jdbcDriver; @Value("${db.jdbc.url}") private String jdbcUrl; @Value("${db.jdbc.username}") private String jdbcUsername; @Value("${db.jdbc.password}") private String jdbcPassword; // mysql config class // ..... @Override public void run(String... strings) throws Exception { // 預先加載的一些方法,類,屬性。 } }

若是有多個CommandLineRunner接口實現類,那麼能夠經過註解@Order來規定全部實現類的運行順序。

經過這一系列API的幫助,Spring Boot讓環境配置變得輕鬆不少。

http://www.cnblogs.com/whthomas/p/5270917.html

 

Did you put a application.properties or application.yml for your datasource settings? This is an example:

spring.datasource.url=jdbc:mysql://127.0.0.1/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

For an explanation of where you can put your configuration file, and how to configure your application to handle your properties file in any directory, refer to this link for further information.

http://stackoverflow.com/questions/29796818/spring-boot-hibernate

相關文章
相關標籤/搜索