@PropertySource(factory=…) breaks @SpringBootTest when loading META-INF/build-info.properties

huangapple go评论78阅读模式
英文:

@PropertySource(factory=...) breaks @SpringBootTest when loading META-INF/build-info.properties

问题

我正在尝试设置一个从 HOCON 语法的 .conf 文件加载的自定义 @ConfigurationProperties 类。

我有一个使用 @PropertySource(factory=TypesafePropertySourceFactory.class, value = "classpath:app.conf") 注解的类:

@Configuration
@ConfigurationProperties(value = "app.server")
@PropertySource(factory = TypesafePropertySourceFactory.class, value = "classpath:app.conf")
public class ServerProperties {
    public int port;
}

还有一个简单的测试类:

@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeTest {
    @Test
    public void someCoolTest() {/* ... */}
    // ...
}

当我运行 JUnit 测试运行器时,我得到以下错误:

Caused by: com.typesafe.config.ConfigException$BadPath: path parameter: Invalid path 'spring.info.build.location:classpath:META-INF/build-info.properties': Token not allowed in path expression: ':' (you can double-quote this token if you really want it here)
    at com.typesafe.config.impl.PathParser.parsePathExpression(PathParser.java:155) ~[config-1.4.0.jar:1.4.0]
    at com.typesafe.config.impl.PathParser.parsePathExpression(PathParser.java:74) ~[config-1.4.0.jar:1.4.0]
    at com.typesafe.config.impl.PathParser.parsePath(PathParser.java:61) ~[config-1.4.0.jar:1.4.0]
...

如果我取消注释 ServerProperties 类中的 @PropertySource 行,测试会正常进行。对我来说,我的自定义 PropertySourceFactory 会干扰默认的 .properties 文件解析过程,这看起来很奇怪。

PropertySource 和 Factory 类

// TypesafeConfigPropertySource.java
import com.typesafe.config.Config;
import org.springframework.core.env.PropertySource;

public class TypesafeConfigPropertySource extends PropertySource<Config> {
    public TypesafeConfigPropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String path) {
        if (source.hasPath(path)) {
            return source.getAnyRef(path);
        }
        return null;
    }
}

// TypesafePropertySourceFactory.java
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;

import java.io.IOException;
import java.util.Objects;

public class TypesafePropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
        Config config = ConfigFactory.load(Objects.requireNonNull(resource.getResource().getFilename())).resolve();

        String safeName = name == null ? "typeSafe" : name;
        return new TypesafeConfigPropertySource(safeName, config);
    }
}

我是否遗漏了关于配置自定义属性资源工厂的基本知识,还是这是一个 bug?

版本信息

  • Spring Boot 2.3.4
  • JUnit Jupiter 5.6.2
英文:

I am trying to setup a custom @ConfigurationProperties class loaded from a HOCON syntax .conf file.

I have a Class annotated with @PropertySource(factory=TypesafePropertySourceFactory.class, value = &quot;classpath:app.conf&quot;)

@Configuration
@ConfigurationProperties(value = &quot;app.server&quot;)
@PropertySource(factory = TypesafePropertySourceFactory.class, value = &quot;classpath:app.conf&quot;)
public class ServerProperties {
    public int port;
}

and a simple test class:

@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SomeTest {
    @Test
    public void someCoolTest() {/* ... */}
    // ...
}

When i run my junit test runner, i get the following error:

Caused by: com.typesafe.config.ConfigException$BadPath: path parameter: Invalid path &#39;spring.info.build.location:classpath:META-INF/build-info.properties&#39;: Token not allowed in path expression: &#39;:&#39; (you can double-quote this token if you really want it here)
    at com.typesafe.config.impl.PathParser.parsePathExpression(PathParser.java:155) ~[config-1.4.0.jar:1.4.0]
    at com.typesafe.config.impl.PathParser.parsePathExpression(PathParser.java:74) ~[config-1.4.0.jar:1.4.0]
    at com.typesafe.config.impl.PathParser.parsePath(PathParser.java:61) ~[config-1.4.0.jar:1.4.0]
...

If i uncomment the @PropertySource line on the ServerProperties class, the tests proceed normally. It seems strange to me that my custom PropertySourceFactory gets in the way of the default .properties file resolution process.

PropertySource and Factory classes

//     TypesafeConfigPropertySource.java
import com.typesafe.config.Config;
import org.springframework.core.env.PropertySource;

public class TypesafeConfigPropertySource extends PropertySource&lt;Config&gt; {
    public TypesafeConfigPropertySource(String name, Config source) {
        super(name, source);
    }

    @Override
    public Object getProperty(String path) {
        if (source.hasPath(path)) {
            return source.getAnyRef(path);
        }
        return null;
    }
}

//     TypesafePropertySourceFactory.java
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;

import java.io.IOException;
import java.util.Objects;

public class TypesafePropertySourceFactory implements PropertySourceFactory {

    @Override
    public PropertySource&lt;?&gt; createPropertySource(String name, EncodedResource resource) throws IOException {
        Config config = ConfigFactory.load(Objects.requireNonNull(resource.getResource().getFilename())).resolve();

        String safeName = name == null ? &quot;typeSafe&quot; : name;
        return new TypesafeConfigPropertySource(safeName, config);
    }

}

Am I missing something fundamental about configuring custom property resource factories, or is this a bug?

Versions

  • Spring boot 2.3.4
  • Junit Jupiter 5.6.2

答案1

得分: 0

也许您也可以像这里的答案建议的那样,使用ContextInitializer来解决这个问题:

链接:https://stackoverflow.com/questions/38803381/spring-environment-backed-by-typesafe-config/58789778#58789778

英文:

Maybe you can also solve it with the use of a ContextInitializer as suggested in the answer here:

https://stackoverflow.com/questions/38803381/spring-environment-backed-by-typesafe-config/58789778#58789778

答案2

得分: 0

TL;DR

如果您的自定义实现无法处理 path,则返回 null

public class TypesafeConfigPropertySource extends PropertySource<Config> {
    // ...    
    @Override
    public Object getProperty(String path) {
        try {
            if (source.hasPath(path)) {
                return source.getAnyRef(path);
            }
        } catch(ConfigException.BadPath ignore) {
        }
        return null;
    }
    // ...
}

解释

> 我正在做一些有根据的猜测,但从功能上看,代码的行为支持这一点

在这里,最可能的情况是解析顺序会考虑我们的自定义实现,而不是任何默认实现。我们实现中使用的方法将在任何包含 ":" 和 "[" 的路径上报错,因为错误发生在对路径存在性的检查中。

我只是简单地捕获了 BadPath 异常,以便捕捉任何问题,然后返回 null 来表示没有匹配。

英文:

TL;DR

Return null if you cannot process the path in your custom impl

public class TypesafeConfigPropertySource extends PropertySource&lt;Config&gt; {
    // ...    
    @Override
    public Object getProperty(String path) {
        try {
            if (source.hasPath(path)) {
                return source.getAnyRef(path);
            }
        } catch(ConfigException.BadPath ignore) {
        }
        return null;
    }
    // ...
}

Explanation

> I am making educated guesses, but functionally this appears supported by the way the code behaves

the most likely scenario here is the resolution order will consider our custom implementation before any default implementation. The method used in our implementation will error out with any path containing a ":" and "[" as the error occurs in the check for the path's existence.

I'm simply wrapping the BadPath exception in order to catch any problem and then returning null to signify no match.

huangapple
  • 本文由 发表于 2020年9月25日 15:25:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/64059677.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定