from:https://tuhrig.de/why-using-springs-value-annotation-is-bad/
為什么使用Spring的@Value注釋不好
@Value
配置是每個擁有數百行代碼的每個應用程序的重要主題。如果您使用的是Spring,通常會使用Spring的@Value
注釋從Java屬性文件中加載值。可能看起來像這樣:
1
2
3
4
5
6
7
8
|
@Service
public class MyService {
@Value("${my.config.property.key}")
private String someConfigValue;
//...
}
|
如果這樣做,Spring將從my.config.property.key
屬性文件中將鍵的值注入到您的類中,您就完成了。簡單!也許太容易了……
會發生什么…
當您在應用程序中使用此技術時會發生什么?好吧,只要需要一些屬性,就可以注入它。您將其注入服務,控制器和組件中,並將正確的屬性注入不同的類中。這很簡單,而且絕對是該機制的目的。
但是,這僅意味着分散整個應用程序的配置。每個類都可以選擇幾個要使用的屬性。您將不知道或無法控制哪個類正在使用哪些屬性。最后,您將對項目進行全文搜索,以查找使用單個鍵的位置。如果您想重命名這些鍵之一,或者需要為單元測試中的每個類的每個屬性設置每個屬性,您將獲得很多樂趣。
配置即服務
取而代之的是,配置也應該是應用程序的封裝服務,例如任何其他功能。您將持久性封裝在DAO中,將REST服務封裝在控制器中,並將安全性封裝在身份驗證器中。那么為什么不封裝配置呢?
如果這樣做,您將負有單一責任,可以加載並從中獲取配置。與任何其他服務一樣,您可以輕松更改實現。也許您不想從屬性文件中加載屬性,而是從數據庫或Web服務中加載!如果將配置封裝在服務中,那沒什么大不了的。您只需更改實現。您還可以為您的配置編寫一些單元測試(為什么不這樣做?當組合屬性或根據其他幾個屬性確定某個配置時,配置會變得很復雜!),也可以對配置服務中的屬性進行完整性檢查。您可以進行日志記錄,可以進行安全保護(也許密碼文件的路徑在所有地方都不應該看到,對不對?),可以進行緩存,可以進行……好吧,您就明白了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@Service
public class ConfigurationService {
@Value("${my.config.property.key}")
private String someConfigValue;
// getters, setters, sanity checks and so on...
}
@Service
public class MyService {
@Autowire
private ConfigurationService configurationService ;
//...
}
|
甚至更好
如果您使用的是Spring Boot(不僅是Spring),則無需重新發明輪子。Spring Boot提供了一種優雅的方式來將配置與應用程序的其余部分分開:@ConfigurationProperties。通過使用Spring Boot的@ConfigurationProperties,您可以輕松地將配置封裝在一個單獨的類中,並避免在整個代碼中分散對配置文件的直接依賴關系。在這里閱讀更多。
from:https://tuhrig.de/using-configurationproperties-to-separate-service-and-configuration
使用@ConfigurationProperties分隔服務和配置
大約兩年前,我寫了一篇文章“為什么使用Spring的@Value注釋不好”。我描述了為什么@Value
在整個代碼中都應避免使用注釋。通過@Value
在服務,控制器和其他Spring組件中使用批注,您將在整個應用程序中分散配置。在最壞的情況下,您將在多個類中連接相同的值。如果您想在某天更改此值,則需要觸摸所有此類。
取而代之的是,配置應該像數據庫訪問或REST端點一樣是應用程序的封裝方面。我們希望將配置與實際服務,控制器和組件分開。
Spring Boot的方法
Spring Boot提供了一個名為的注釋@ConfigurationProperties
。正如您在這里可以讀到的,它是為分離和外部化配置而設計的。那就是我們想要的!
讓我們舉個例子,並假設我們要配置與外部REST服務(它也可以是數據庫或其他任何東西)的連接。我們需要一個配置,包括用戶名,密碼和URL。配置文件(application.properties
)如下所示:
1
2
3
|
mySuperService.username = admin123
mySuperServcie.password = s3cur3
mySuperService.url = http://some.where.com/api
|
通過使用Spring Boot的@ConfigurationProperties
注釋,我們首先將編寫一個Java類來表示此配置:
1
2
3
4
5
6
7
8
9
10
11
|
@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix="mySuperService")
public class MySuperServiceConfig {
@NotNull private String username;
@NotNull private String password;
@NotNull private String url;
// getter and setter...
}
|
如您所見,屬性文件及其Java表示形式之間有着非常明顯的匹配。讓我們看看如何使用此類:
1
2
3
4
5
6
7
8
9
10
11
12
|
@Service
public class MySuperServiceRestClient {
private final MySuperServiceConfig config;
@Autowired
public MySuperServiceRestClient(MySuperServiceConfig config) {
this.config = config;
}
// actual code...
}
|
如您所見,我們將所有配置封裝在一個類中,該類注入了我們的服務。該服務不再知道配置來自何處。它只知道它可以從MySuperServiceConfig
類中接收配置。分離完成!
我們還能做什么?
我們不僅分離了配置,而且將其轉換為Java類!這意味着我們可以使用它進行編碼!這里有一些例子和想法:
應用默認值
我們僅可以為應該配置但不太可能配置的屬性應用默認值。假設我們有一個端口:
1
|
mySuperService.port = 8080
|
在大多數情況下,端口將是8080。僅在極少數情況下,我們才會在測試系統中使用另一個端口。在這種情況下,我們可以應用默認值:
1
2
3
4
5
6
7
8
9
10
11
12
|
@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix="mySuperService")
public class MySuperServiceConfig {
private String port = "8080"; // default!
@NotNull private String username;
@NotNull private String password;
@NotNull private String url;
// getter and setter...
}
|
工藝配置
讓我們假設以下配置:
1
2
3
|
mySuperService.port = 8080
mySuperService.baseUrl = http://some.where.com
mySuperService.api = api/v1
|
由於某些原因,我們希望能夠自行配置每個屬性。但是,在服務中,我們需要一個URL :http://some.where.com:8080/api/v1
。我們可以在我們的配置類中做到這一點!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix="mySuperService")
public class MySuperServiceConfig {
private String port = "8080"; // default!
@NotNull private String baseUrl;
@NotNull private String api;
public String getUrl() {
return baseUrl + ":" + port + "/" + api;
}
// getter and setter...
}
|
創建不同的實現
我們的配置剛剛成為Spring Bean。這也意味着,我們可以用它來完成所有常見的Spring事情!例如,創建不同的實現。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public interface MySuperServiceConfig {
// getter for the configuration properties!
}
@Profile("PRODUCTION")
@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix="mySuperService")
public class MySuperServiceProductionConfig implements MySuperServiceConfig {
@NotNull private String username;
@NotNull private String password;
@NotNull private String url;
// getter and setter...
}
@Profile("DEVELOPMENT")
@Component
public class MySuperServiceDevelopmentConfig implements MySuperServiceConfig {
// the hard coded test environment for every developer!
private String username = "testUser";
private String password = "123456";
private String url = "http://our.test.server.com";
// getter and setter...
}
@Service
public class MySuperServiceRestClient {
private final MySuperServiceConfig config;
@Autowired
public MySuperServiceRestClient(MySuperServiceConfig config) {
this.config = config;
}
// actual code...
}
|
通過這樣做,我們最終得到了與“為什么使用Spring的@Value注釋不好”中描述的解決方案類似的解決方案。配置成為我們隱藏在界面后面的服務。我們可以更改獲取配置的方式(從屬性文件,硬編碼或從雲中的配置服務器)以及映射和處理方式。我們的實際服務將僅包含業務邏輯,而不必處理配置問題。
最好的問候,
托馬斯
更多
from:https://www.baeldung.com/spring-value-defaults
將Spring @Value與默認值一起使用
1.概述
Spring的@Value注釋提供了一種方便的方法來將屬性值注入到組件中。在屬性可能不存在的情況下提供合理的默認值也很有用。
這就是我們在本文中要重點關注的內容–如何為@Value Spring批注指定默認值。有關@Value的更詳細的快速指南,請參見此處的文章。
2.字符串默認值
讓我們看一下為String屬性設置默認值的基本語法:
private String stringWithDefaultValue; |
如果some.key無法解析,則將stringW ithDefaultValue設置為默認值“ my default value”。
同樣,我們可以將零長度的String設置為默認值:
private String stringWithBlankDefaultValue; |
3.基元
要為基本類型(例如boolean和int)設置默認值,我們使用文字值:
private boolean booleanWithDefaultValue; |
private int intWithDefaultValue; |
如果願意,我們可以使用原始包裝器,方法是將類型更改為Boolean和Integer。
4.數組
我們還可以將逗號分隔的值列表注入數組:
private String[] stringArrayWithDefaults; | |
private int[] intArrayWithDefaults; |
在上面的第一個示例中,值“一個”,“兩個”和“三個”作為默認值注入到stringArrayWithDefaults中。
在第二個示例中,值1、2和3作為默認值注入到intArrayWithDefaults中。
5.使用SpEL
我們還可以使用Spring Expression Language(SpEL)指定表達式和默認值。
在下面的示例中,我們希望將some.system.key設置為系統屬性,如果未設置,則希望使用“我的默認系統屬性值”作為默認值:
private String spelWithDefaultValue; |
六,結論
在這篇快速的文章中,我們研究了如何為希望使用Spring的@Value注釋注入的屬性設置默認值。
像往常一樣,本文中使用的所有代碼示例都可以在GitHub project中找到。
Can I set null as the default value for a @Value in Spring?
You must set the nullValue of the PropertyPlaceholderConfigurer. For the example I'm using the string @null
but you can also use the empty string as nullValue.
Now you can use the string @null
to represent the null
value
@Value("${stuff.value:@null}") private String value;
from:https://farenda.com/spring/spring-value-default-value/
Spring @Value default value
This tutorial presents how to set default values in Spring @Value annotation, using properties syntax and Spring Expression Language (SPEL).
Spring Config class with static PropertySourcesPlaceholderConfigurer bean that is needed to resolve properties syntax inside @Value:
@Configuration
public
class
Config {
@Bean
public
static
PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
// Needed by Spring to resolve ${} inside @Value:
return
new
PropertySourcesPlaceholderConfigurer();
}
}
|
Now a bean with defaults that we can access inside @Value:
@Component
(
"defaults"
)
public
class
Defaults {
public
String getHostname() {
return
"default.host"
;
}
}
|
Examples of setting default values using @Value:
@Component
public
class
SampleSpringComponent {
@Value
(
"${hostname : localhost}"
)
private
String hostName;
@Value
(
"${target.dir : #{systemProperties['java.io.tmpdir']}}"
)
private
String dir;
@Value
(
"${hostconfig.hostname : #{@defaults.hostname}}"
)
private
String spelHostname;
@Value
(
"#{systemProperties['java.user.home'] ?: '/tmp/default'}"
)
private
String spelDir;
}
|
Results:
Host name: localhost
Directory: /tmp
SPEL Host name: default.host
SPEL Directory: /tmp/default
|
Summary of setting defaults:
- Property syntax use: ${expected : default}
- SPEL use: #{expected[‘value’] ?: default} (Elvis operator)
- SPEL expressions can be mixed with property syntax to access beans in context or use other complex expressions.
from:https://stackoverflow.com/questions/31711308/spring-value-escape-colon-in-default-value
Spring @Value轉義冒號(:)為默認值
更新: 對於4.2或更高版本的Spring,不需要單引號。Spring將第一個冒號視為特殊字符,並將其余所有字符用作單個字符串值。
對於4.2或更高版本的春季,
@Value("${prop.url:http://myurl.com}")
對於以前的版本,我相信單引號可以解決問題:
@Value("${prop.url:'http://myurl.com'}")
又見神碼!

toString 做緩存?!笑話。。

貌似找到優雅退出的鼻祖了。