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 做缓存?!笑话。。

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