不要滥用反射获取/设置私有字段
在这里使用反射是可以避免的。它虽然带来一定的价值,但也有多个缺点:
- 只能在运行时检测到反射问题(例如:字段不再存在)
- 我们需要封装,但不需要一个关于依赖性的不透明类,应该让依赖性可见,并且使得该类更加透明和易于测试。
- 它鼓励不良设计。今天你声明一个
@Value String field
,明天你可能会在该类中声明5
或10
个这样的字段,而你甚至可能不知道这会降低该类的设计质量。通过更明显的方式来设置这些字段(例如构造函数),你将在添加所有这些字段之前三思而后行,并且可能将其封装成另一个类并使用@ConfigurationProperties
。
使您的类既可以进行单元测试又可以进行集成测试
为了能够编写纯粹的单元测试(即在没有运行Spring容器的情况下),以及针对您的Spring组件类的集成测试,您必须使此类可与或无需Spring一起使用。
在不需要的情况下在单元测试中运行容器是一种不良实践,会使本地构建变慢:您不希望这样。
因此,我认为您应该将此属性定义为类的内部属性:
@Component
public class Foo{
@Value("${property.value}") private String property;
}
将其作为构造函数参数传递,由Spring进行注入:
@Component
public class Foo{
private String property;
public Foo(@Value("${property.value}") String property){
this.property = property;
}
}
单元测试示例
您可以在不使用Spring的情况下实例化Foo
,并且由于构造函数,可以为property
注入任何值:
public class FooTest{
Foo foo = new Foo("dummyValue");
@Test
public void doThat(){
...
}
}
集成测试示例
借助@SpringBootTest
的properties
属性,您可以以这种简单的方式在Spring Boot中将属性注入上下文:
@SpringBootTest(properties="property.value=dummyValue")
public class FooTest{
@Autowired
Foo foo;
@Test
public void doThat(){
...
}
}
你可以使用 @TestPropertySource
作为替代方案,但它会增加一个额外的注释:
@SpringBootTest
@TestPropertySource(properties="property.value=dummyValue")
public class FooTest{ ...}
使用 Spring(不使用 Spring Boot)可能会更加复杂,但是由于我很久没有使用过没有 Spring Boot 的 Spring,所以我不想说一些愚蠢的话。
顺便说一句:如果您有许多需要设置的 @Value
字段,则将它们提取到一个带有 @ConfigurationProperties
注释的类中更为相关,因为我们不希望构造函数有太多参数。