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 支持多种外部配置方式
这些方式优先级如下:
- 命令行参数
- 来自
java:comp/env
的 JNDI 属性 - Java 系统属性(
System.getProperties()
) - 操作系统环境变量
RandomValuePropertySource
配置的random.*
属性值jar
包外部的application-{profile}.properties
或application.yml
( 带spring.profile
) 配置文件jar
包内部的application-{profile}.properties
或application.yml
( 带spring.profile
) 配置文件jar
包外部的application.properties
或application.yml
( 不带spring.profile
) 配置文件jar
包内部的application.properties
或application.yml
( 不带spring.profile
) 配置文件@Configuration
注解类上的@PropertySource
- 通过
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.properties
或application.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
对象,使用注解的时候返回的具体类型是AnnotationConfigApplicationContext
或AnnotationConfigEmbeddedWebApplicationContext
,当支持 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,并说明了他们的作用。至于何时应用他们,且听后面慢慢分解。
接下来是成员变量 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,并说明了他们的作用。至于他们何时会被触发,等事件出现时,我们再说明。
最后是 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,但在这样的设计下,想要扩展是非常容易的!
接下来,我们看一下在调用 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 中的日志系统体系:
好了,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) && !<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 具体有什么功能。下图画出了它的继承体系。
可以看到我们加载的这个 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