使用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中不同配置名稱造成啟動問題

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;

然后從新編譯了程序, 啟動程序后運行正確


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM