【发布时间】:2018-02-20 14:13:49
【问题描述】:
我用 Spring-boot 制作了一个小的 rest api,我正在从配置文件中读取某些内容。由于我无法硬编码配置文件的路径,因为它在生产中改变了位置,我决定从运行时参数中获取它。我在实例化我的 ConfigService 时使用了路径。现在问题是我的所有测试都失败了,因为它需要实例化 ConfigService,但是在运行测试时它还没有到达 main(它获取路径的位置)。 我的主要看起来像这样:
@SpringBootApplication
public class SecurityService {
private static final Logger LOG = LogManager.getLogger(SecurityService.class);
private static String[] savedArgs;
public static String[] getArgs(){
return savedArgs;
}
public static void main(String[] args) throws IOException {
savedArgs = args;
final String configPath = savedArgs[0];
// final String configPath = "src/config.xml";
ConfigService configService = new ConfigService(configPath);
if (configService.getConfigurations().getEnableHttps()) {
LOG.info("Using HTTPS on port {}", configService.getConfigurations().getPort());
configService.setSSL();
}
SpringApplication.run(SecurityService.class, args);
}
}
我在启动 Spring 应用程序之前加载配置,因为我需要在服务器启动之前设置 SSL 设置等。现在,当它运行 SpringApplication 时,它会再次实例化所有类,包括 ConfigService。 ConfigService 如下所示:
@Configuration
@Service
public class ConfigService {
private static final Logger LOG = LogManager.getLogger(ConfigService.class);
private static String[] args = SecurityService.getArgs();
private static final String CONFIG_PATH = args[0];
private Configurations configurations;
public ConfigService() {
this(CONFIG_PATH);
}
public ConfigService(String configPath) {
configurations = setConfig(configPath);
}
// Reads config and assigns values to an object, configurations
private Configurations setConfig(String configPath) {
Configurations configurations = new Configurations();
try {
ApplicationContext appContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
XMLConverter converter = (XMLConverter) appContext.getBean("XMLConverter");
configurations = (Configurations) converter.convertFromXMLToObject(configPath);
} catch (IOException e) {
e.printStackTrace();
}
LOG.info("Loaded settings from config.xml");
return configurations;
}
// Checks if EnableHttps is true in config.xml and then sets profile to secure and sets SSL settings
public void setSSL() {
System.setProperty("spring.profiles.active", "Secure");
System.setProperty("server.ssl.key-password", configurations.getKeyPass());
System.setProperty("server.ssl.key-store", configurations.getKeyStorePath());
}
// Spring profiles
// If EnableHttps is false it uses the Default profile, also sets the port before starting tomcat
@Component
@Profile({"Default"})
public class CustomContainer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory container) {
container.setPort(configurations.getPort());
}
}
// If EnableHttps is True it will use the "Secure" profile, also sets the port before starting tomcat
@Component
@Profile({"Secure"})
public class SecureCustomContainer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory container) {
container.setPort(configurations.getPort());
}
}
public Configurations getConfigurations() {
return configurations;
}
}
当尝试作为 JAR 文件运行时,它会吐出一堆空指针异常,因为 args[0] 为空,因为它实际上还没有得到参数。
我能以某种方式解决这个问题吗?就像先给它路径 src/config.xml,然后在它实际启动时将其覆盖到运行时 args 路径?
我的一个测试类如下所示:
@RunWith(SpringRunner.class)
@SpringBootTest
public class SecurityServiceTests {
@Test
public void contextLoads() {
}
@Test
public void testGetPort() {
String configPath = "src/config.xml";
ConfigService configService = new ConfigService(configPath);
int actualPort = configService.getConfigurations().getPort();
int expectedPort = 8443;
assertEquals(expectedPort, actualPort);
}
@Test
public void testGetTTL(){
String configPath = "src/config.xml";
ConfigService configService = new ConfigService(configPath);
int actualTTL = configService.getConfigurations().getTTL();
int expectedTTL = 15000;
assertEquals(expectedTTL, actualTTL);
}
@Test
public void testSSL(){
String configPath = "src/config.xml";
ConfigService configService = new ConfigService(configPath);
String expectedKeyPass = "changeit";
String expectedKeyStore = "classpath:ssl-server.jks";
configService.setSSL();
assertEquals(expectedKeyPass,System.getProperty("server.ssl.key-password"));
assertEquals(expectedKeyStore,System.getProperty("server.ssl.key-store"));
}
}
配置类:
// Model class that we map config.xml to
@Component
public class Configurations {
private int port;
private boolean enableHttps;
private String keyStorePath;
private String keyPass;
private int TokenTtlMillis;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public boolean getEnableHttps() {
return enableHttps;
}
public void setEnableHttps(boolean enableHttps) {
this.enableHttps = enableHttps;
}
public String getKeyStorePath() {
return keyStorePath;
}
public void setKeyStorePath(String keyStorePath) {
this.keyStorePath = keyStorePath;
}
public String getKeyPass() {
return keyPass;
}
public void setKeyPass(String keyPass) {
this.keyPass = keyPass;
}
public int getTTL() {
return TokenTtlMillis;
}
public void setTTL(int TTL) {
this.TokenTtlMillis = TTL;
}
}
还有我映射到配置类的 config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configurations>
<Port>8443</Port>
<EnableHttps>true</EnableHttps>
<KeyStorePath>classpath:ssl-server.jks</KeyStorePath>
<KeyPass>changeit</KeyPass>
<TokenTtlMillis>15000</TokenTtlMillis>
</Configurations>
【问题讨论】:
-
您应该在测试之前运行的特殊方法之一中实例化 configService。
-
如果您在 bean 中使用 /resources/application.properties 和
Value注释,一切都会变得简单得多。 docs.spring.io/spring/docs/current/javadoc-api/org/… -
你不应该使用
new启动你的服务... -
是的,春天无处可去。不要使用
new,而是context.getBean或类似的,它必须由Spring管理... -
我添加了我的答案,请检查;-)
标签: java spring spring-boot filepath