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.

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.

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

The primary goals of Spring Boot are:

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

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

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).

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

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

 

 

 

 

 

 

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
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> HelloProperties helloProperties;

@Bean</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使用java配置方式配置这个类</span>
@ConditionalOnMissingBean(Hello.<span style="color: rgba(0, 0, 255, 1)">class</span>)<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">容器中如果没有Hello这个类,那么自动配置这个Hello</span>
<span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Hello hello() {
    Hello hello </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Hello();
    hello.setMsg(helloProperties.getMsg());
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> 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 支持多种外部配置方式

这些方式优先级如下:

  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.java
main

启动项目 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 {

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">PATH_ROOT</span> <span class="hljs-operator">=</span> <span class="hljs-string">"/"</span>;

<span class="hljs-meta">@RequestMapping(PATH_ROOT)</span>
<span class="hljs-keyword">public</span> String <span class="hljs-title function_">welcome</span><span class="hljs-params">()</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Welcome!"</span>;
}

}

虽然只有几行代码,但是这已经是一个完整的 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) {
    <span class="hljs-built_in">super</span>(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;

<span class="hljs-comment">// 创建并配置Environment</span>
<span class="hljs-type">ConfigurableEnvironment</span> <span class="hljs-variable">environment</span> <span class="hljs-operator">=</span> getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
<span class="hljs-keyword">if</span> (isWebEnvironment(environment) &amp;&amp; !<span class="hljs-built_in">this</span>.webEnvironment) {
    environment = convertToStandardEnvironment(environment);
}

...

<span class="hljs-keyword">return</span> 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()));
}

<span class="hljs-keyword">if</span> (resolver.containsProperty(<span class="hljs-string">"console-available"</span>)) {
    AnsiOutput.setConsoleAvailable(
            resolver.getProperty(<span class="hljs-string">"console-available"</span>, 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)
{

...
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.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);
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.logStartupInfo) {
    logStartupInfo(context.getParent() == <span class="hljs-literal">null</span>);
    logStartupProfileInfo(context);
}

...

<span class="hljs-keyword">return</span> 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 {
<span class="hljs-meta">@Value(<span class="hljs-string">"<span class="hljs-subst">${db.jdbc.driver}</span>"</span>)</span>
<span class="hljs-keyword">private</span> String jdbcDriver;
<span class="hljs-meta">@Value(<span class="hljs-string">"<span class="hljs-subst">${db.jdbc.url}</span>"</span>)</span>
<span class="hljs-keyword">private</span> String jdbcUrl;
<span class="hljs-meta">@Value(<span class="hljs-string">"<span class="hljs-subst">${db.jdbc.username}</span>"</span>)</span>
<span class="hljs-keyword">private</span> String jdbcUsername;
<span class="hljs-meta">@Value(<span class="hljs-string">"<span class="hljs-subst">${db.jdbc.password}</span>"</span>)</span>
<span class="hljs-keyword">private</span> String jdbcPassword;

<span class="hljs-comment">// mysql config class</span>
<span class="hljs-comment">// ..... </span>

}

有时候我们在项目启动的时候,总是需要先启动一些初始化的类,以前比较常见的做法是写再static块中,Spring Boot 提供了一个CommandLineRunner接口,实现这个接口的类总是会被优先启动,并优先执行CommandLineRunner接口中提供的run()方法。

public class ApplicationConfigure implements CommandLineRunner  {
<span class="hljs-meta">@Value("${db.jdbc.driver}")</span>
<span class="hljs-keyword">private</span> String jdbcDriver;
<span class="hljs-meta">@Value("${db.jdbc.url}")</span>
<span class="hljs-keyword">private</span> String jdbcUrl;
<span class="hljs-meta">@Value("${db.jdbc.username}")</span>
<span class="hljs-keyword">private</span> String jdbcUsername;
<span class="hljs-meta">@Value("${db.jdbc.password}")</span>
<span class="hljs-keyword">private</span> String jdbcPassword;

<span class="hljs-comment">// mysql config class</span>
<span class="hljs-comment">// ..... </span>
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">(String... strings)</span> <span class="hljs-keyword">throws</span> Exception {
    <span class="hljs-comment">// 预先加载的一些方法,类,属性。</span>
}

}

如果有多个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