一. spring boot中传参的方法
1、自动化配置
spring Boot 对于开发人员最大的好处在于可以对 Spring 应用进行自动配置。Spring Boot 会根据应用中声明的第三方依赖来自动配置 Spring 框架,而不需要进行显式的声明。比如
当声明了对 HSQLDB 的依赖时,Spring Boot 会自动配置成使用 HSQLDB 进行数据库操作。
Spring Boot 推荐采用基于 Java 注解的配置方式,而不是传统的 XML。只需要在主配置 Java 类上添加“@EnableAutoConfiguration”注解就可以启用自动配置。Spring Boot 的自动配置
功能是没有侵入性的,只是作为一种基本的默认实现。开发人员可以通过定义其他 bean 来替代自动配置所提供的功能。比如当应用中定义了自己的数据源 bean 时,自动配置所提供的
HSQLDB 就不会生效。这给予了开发人员很大的灵活性。既可以快速的创建一个可以立即运行的原型应用,又可以不断的修改和调整以适应应用开发在不同阶段的需要。可能在应用最开始的时
候,嵌入式的内存数据库(如 HSQLDB)就足够了,在后期则需要换成 MySQL 等数据库。Spring Boot 使得这样的切换变得很简单。
2、外部化的配置
在应用中管理配置并不是一个容易的任务,尤其是在应用需要部署到多个环境中时。通常会需要为每个环境提供一个对应的属性文件,用来配置各自的数据库连接信息、服务器信息和第
三方服务账号等。通常的应用部署会包含开发、测试和生产等若干个环境。不同的环境之间的配置存在覆盖关系。测试环境中的配置会覆盖开发环境,而生产环境中的配置会覆盖测试环境。
Spring 框架本身提供了多种的方式来管理配置属性文件。Spring 3.1 之前可以使用 PropertyPlaceholderConfigurer。Spring 3.1 引入了新的环境(Environment)和概要信息(Profile)
API,是一种更加灵活的处理不同环境和配置文件的方式。不过 Spring 这些配置管理方式的问题在于选择太多,让开发人员无所适从。Spring Boot 提供了一种统一的方式来管理应用的配置
,允许开发人员使用属性文件、YAML 文件、环境变量和命令行参数来定义优先级不同的配置值。
Spring Boot 所提供的配置优先级顺序比较复杂。按照优先级从高到低的顺序,具体的列表如下所示。
(1) 命令行参数
(2) java:comp/env 里的JNDI属性
(3) JVM系统属性
(4) 操作系统环境变量
(5) 随机生成的带 random.* 前缀的属性(在设置其他属性时,可以引用它们,比如 ${random.
long} )
(6) 应用程序以外的application.properties或者appliaction.yml文件
(7) 打包在应用程序内的application.properties或者appliaction.yml文件
(8) 通过 @PropertySource 标注的属性源
(9) 默认属性
这个列表按照优先级排序,也就是说,任何在高优先级属性源里设置的属性都会覆盖低优先级的相同属性。例如,命令行参数会覆盖其他属性源里的属性。
application.properties和application.yml文件能放在以下四个位置。
(1) 外置,在相对于应用程序运行目录的/config子目录里。
(2) 外置,在应用程序运行的目录里。
(3) 内置,在config包内。
(4) 内置,在Classpath根目录。
同样,这个列表按照优先级排序。也就是说,/config子目录里的application.properties会覆盖应用程序Classpath里的application.properties中的相同属性。
此外,如果我们在同一优先级位置同时有application.properties和application.yml,那么application.yml里的属性会覆盖application.properties里的属性。
命令行参数
通过Java -jar app.jar –name=”Spring” –server.port=9090方式来传递参数。
SpringApplication 类默认会把以“–”开头的命令行参数转化成应用中可以使用的配置参数,如 “–name=Alex” 会设置配置参数 “name” 的值为 “Alex”.
可以使用的参数可以是我们自己定义的,也可以是Spring Boot中默认的参数。
注意:命令行参数在app.jar的后面!
可以通过SpringApplication.setAddCommandLineProperties(false)禁用命令行配置。
Java系统属性
注意Java系统属性位置java -Dname=”isea533″ -jar app.jar,可以配置的属性都是一样的,优先级不同。
例如java -Dname=”isea533″ -jar app.jar –name=”Spring!”中name值为Spring.
有些系统,关于一些数据库或其他第三方账户等信息,由于安全问题,其配置并不会提前配置在项目中暴露给开发人员。
对于这种情况,我们在运行程序的时候,可以通过参数指定一个外部配置文件。
以 demo.jar 为例,方法如下: java -jar demo.jar –spring.config.location=/opt/config/application.properties
其中文件名随便定义,无固定要求。
RandomValuePropertySource
RandomValuePropertySource 可以用来生成测试所需要的各种不同类型的随机值,从而免去了在代码中生成的麻烦。RandomValuePropertySource 可以生成数字和字符串。数字的类型
包含 int 和 long,可以限定数字的大小范围。以“random.”作为前缀的配置属性名称由 RandomValuePropertySource 来生成:
系统中用到随机数的地方,例如 使用 RandomValuePropertySource 生成的配置属性:
user.id=${random.value}
user.count=${random.int}
user.max=${random.long}
user.number=${random.int(100)}
user.range=${random.int[100, 1000]
random.int*支持value参数和,max参数,当提供max参数的时候,value就是最小值
3. 读取属性
读取application.yml中的localhost配置的值
package com.gwc.context;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Created by gwcheng on 2017/3/6.
*/
@Component
@ConfigurationProperties
public class SystemProperties
{
private String localhost;
public String getLocalhost() {
return localhost;
}
public void setLocalhost(String localhost) {
this.localhost = localhost;
}
}
还可以在Bean中使用
@Value(“${server.port}”)
private String serverPort;
来读取配置文件中的属性值
当前目录的“/config”子目录。
当前目录。
classpath 中的“/config”包。
classpath
上面的顺序也表示了该位置上包含的属性文件的优先级。优先级按照从高到低的顺序排列。 即:/config优先于classpath根目录
可以通过“spring.config.name”配置属性来指定不同的属性文件名称。也可以通过“spring.config.location”来添加额外的属性文件的搜索路径。如果应用中包含多个 profile,
可以为每个 profile 定义各自的属性文件,按照“application-{profile}”来命名。
@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);
应用程序使用属性@Value(“${xxx}”)
对于配置属性,可以在代码中通过“@Value”来使用,如:
@RestController
@EnableAutoConfiguration
public class Application {
@Value(“${name}”)
private String name;
@RequestMapping(“/”)
String home() {
return String.format(“Hello %s!”, name);
}
}
变量 name 的值来自配置属性中的“name”属性。
比如application.properties有 port=8081
则
@Value(“${port:8082}”)
private String port;
即可获取8081这个值
属性占位符
例如:
app.name=MyApp
app.description=${app.name} is a Spring Boot application
可以在配置文件中引用前面配置过的属性(优先级前面配置过的这里都能用)。
通过如${app.name:默认名称}方法还可以设置默认值,当找不到引用的属性时,会使用默认的属性。
由于${}方式会被Maven处理。如果你pom继承的spring-boot-starter-parent,Spring Boot 已经将maven-resources-plugins默认的${}方式改为了@ @方式,例如@name@。
如果你是引入的Spring Boot,你可以修改使用其他的分隔符
通过属性占位符还能缩短命令参数
例如修改web默认端口需要使用–server.port=9090方式,如果在配置中写上:
server.port=${port:8080}
那么就可以使用更短的–port=9090,当不提供该参数的时候使用默认值8080。
属性名匹配规则
例如有如下配置对象:
@Component
@ConfigurationProperties(prefix=”person”)
public class ConnectionSettings {
private String firstName;
}
firstName可以使用的属性名如下:
person.firstName,标准的驼峰式命名
person.first-name,虚线(-)分割方式,推荐在.properties和.yml配置文件中使用
PERSON_FIRST_NAME,大写下划线形式,建议在系统环境变量中使用
属性验证
可以使用JSR-303注解进行验证,例如:
@Component
@ConfigurationProperties(prefix=”connection”)
public class ConnectionSettings {
@NotNull
private InetAddress remoteAddress;
// … getters and setters
}
这些内容已经足够本文使用来做一个可以配置的连接mongodb的程序了, 网络上还有更多的介绍关于spring boot属性文件的, 大家可以去百度搜搜。
二. 改造 spring boot的mongodb项目
1. 改造中遇到的一个问题
经过上面的关于spring boot的摸索, 编写了如下一个代码
package cn.iigrowing.study.mongodb; ... 代码详情后面会提供 @Component // 这个很重要, 否则类不会被扫描 public class MyMongoClient { @Bean // 生命这个是一个bean @ConditionalOnMissingBean(MongoClient.class) // 说明这个替换那个默认的 public MongoClient getMongodbClients() { ..... MongoCredential credential = MongoCredential.createMongoCRCredential(myUsername, database, myPassword.toCharArray()); MongoClient client = new MongoClient(addresses, Arrays.asList(credential)); return client; } // mongodb://192.168.128.189:27017,192.168.128.132:27017,192.168.128.133:27017/mytest03 @Value("${mongodb.uri}") // 获取uri然后进行解析 private String myUri; @Value("${mongodb.username}") // 获取用户名 private String myUsername; @Value("${mongodb.password}") // 获取密码 private String myPassword; }
然后启动程序, 最后报如下异常:
java.lang.IllegalStateException: Failed to execute CommandLineRunner at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:779) at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:760) at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:747) at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) at cn.iigrowing.study.mongodb.Application.main(Application.java:14) Caused by: org.springframework.data.mongodb.UncategorizedMongoDbException: Command failed with error 13: 'not authorized on test to execute command { insert: "customer", ordered: true, documents: [ { _id: ObjectId('5955fad2bb844c23906eff87'), _class: "cn.iigrowing.study.mongodb.Customer", firstName: "Alice", lastName: "Smith", address: { aa: "100000", ddd: "20000" } } ] }' on server node3.iigrowing.cn:27017. The full response is { "ok" : 0.0, "errmsg" : "not authorized on test to execute command { insert: "customer", ordered: true, documents: [ { _id: ObjectId('5955fad2bb844c23906eff87'), _class: "cn.iigrowing.study.mongodb.Customer", firstName: "Alice", lastName: "Smith", address: { aa: "100000", ddd: "20000" } } ] }", "code" : 13 }; nested exception is com.mongodb.MongoCommandException: Command failed with error 13: 'not authorized on test to execute command { insert: "customer", ordered: true, documents: [ { _id: ObjectId('5955fad2bb844c23906eff87'), _class: "cn.iigrowing.study.mongodb.Customer", firstName: "Alice", lastName: "Smith", address: { aa: "100000", ddd: "20000" } } ] }' on server node3.iigrowing.cn:27017. The full response is { "ok" : 0.0, "errmsg" : "not authorized on test to execute command { insert: "customer", ordered: true, documents: [ { _id: ObjectId('5955fad2bb844c23906eff87'), _class: "cn.iigrowing.study.mongodb.Customer", firstName: "Alice", lastName: "Smith", address: { aa: "100000", ddd: "20000" } } ] }", "code" : 13 } at org.springframework.data.mongodb.core.MongoExceptionTranslator.translateExceptionIfPossible(MongoExceptionTranslator.java:107) at org.springframework.data.mongodb.core.MongoTemplate.potentiallyConvertRuntimeException(MongoTemplate.java:2135) at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:481) at org.springframework.data.mongodb.core.MongoTemplate.insertDBObject(MongoTemplate.java:1046) at org.springframework.data.mongodb.core.MongoTemplate.doInsert(MongoTemplate.java:855) at org.springframework.data.mongodb.core.MongoTemplate.insert(MongoTemplate.java:796) at org.springframework.data.mongodb.repository.support.SimpleMongoRepository.save(SimpleMongoRepository.java:80) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:504) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:489) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy45.save(Unknown Source) at cn.iigrowing.study.mongodb.Application.run(Application.java:28) at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776) ... 6 common frames omitted Caused by: com.mongodb.MongoCommandException: Command failed with error 13: 'not authorized on test to execute command { insert: "customer", ordered: true, documents: [ { _id: ObjectId('5955fad2bb844c23906eff87'), _class: "cn.iigrowing.study.mongodb.Customer", firstName: "Alice", lastName: "Smith", address: { aa: "100000", ddd: "20000" } } ] }' on server node3.iigrowing.cn:27017. The full response is { "ok" : 0.0, "errmsg" : "not authorized on test to execute command { insert: "customer", ordered: true, documents: [ { _id: ObjectId('5955fad2bb844c23906eff87'), _class: "cn.iigrowing.study.mongodb.Customer", firstName: "Alice", lastName: "Smith", address: { aa: "100000", ddd: "20000" } } ] }", "code" : 13 } at com.mongodb.connection.ProtocolHelper.getCommandFailureException(ProtocolHelper.java:115) at com.mongodb.connection.WriteCommandProtocol.receiveMessage(WriteCommandProtocol.java:268)
为了查明原因仔细检查了前文: spring boot访问mongodb副本集方法1
里面的访问方法, 并且运行了那个程序, 程序正确执行, 说明用户名和密码等都正确, 那到底是哪里的问题?
最后发现两个项目的默认配置文件不同, 如下图
原因分析, 在spring boot中 ,程序会自动扫描配置, 根据这些配置属性, 来启动一系列的相关程序, 在刚刚有问题程序中, 由于没有采用默认的spring的mongodb的配置名称, 可能造成spring boot的mongodb环境不完整,因此有问题, 然后修改配置问题后, 启动程序, 程序正确运行了。
另外程序运行过程中有大量日志输出, 不利观察程序运行情况, 对日志进行了设置, 请参考: Spring Boot日志管理
然后在application.properties 文件中, 添加 logging.level.root=INFO
修改了默认的日志级别
2. 修正后的改造
修改应用的 application.properties 配置文件如下
spring.data.mongodb.username=mytest03 spring.data.mongodb.password=password spring.data.mongodb.uri=mongodb://192.168.128.189:27017,192.168.128.132:27017,192.168.128.133:27017/mytest03 logging.level.root=INFO
以及读取的配置文件的部分
@Value("${spring.data.mongodb.uri}") private String myUri; @Value("${spring.data.mongodb.username}") private String myUsername; @Value("${spring.data.mongodb.password}") private String myPassword;
然后从新编译了程序, 启动程序后运行正确