使用spring boot访问mongodb数据库

一. 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访问mongodb数据库

spring-boot中不同配置名称造成启动问题

原因分析, 在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;

然后从新编译了程序, 启动程序后运行正确

上一篇:javaScript 自定义事件、发布订阅设计模式


下一篇:MySQL☞数值处理函数