在初始化时加载 Spring Boot 属性并尊重所有属性并根据属性文件中的值控制 @Aspect

Posted

技术标签:

【中文标题】在初始化时加载 Spring Boot 属性并尊重所有属性并根据属性文件中的值控制 @Aspect【英文标题】:Spring boot properties to be loaded at initialization and respect all and control @Aspect based on the value from property file 【发布时间】:2019-06-28 08:50:38 【问题描述】:

我们正在使用@PropertySources 从外部文件加载属性。现在我想基于属性启用/禁用@Aspect。我尝试使用@ConditionalOnExpression,但没有成功。我通过创建propertyplaceholderconfig 的bean 来尝试相同的方法。即使在相同的情况下,它也不起作用。然后我尝试了@profile,它最初也不起作用。

我发现当启动时使用propertysourcepropertyplaceholder bean 时,这些变量在启动时并未初始化。某些变量总是被忽略,例如(logging.file)。但是@Value 工作正常。为了设置这些变量,我必须将它们作为 JVM 参数传递。

所以我的问题是: 1. 如何让 spring 在启动时始终读取指定的属性文件并尊重所有这些文件? 2. 这是启用/禁用@Aspect 的最佳方式。使用@profile@ConditionalOnExpression 或其他什么?

目前,我们在 main 方法中设置 logging.file,因为它的行为方式也相同。但是你们知道这不是正确的方法,因为我最终可能会像这样一个一个地添加属性。我想将所有属性放入外部文件中,以便 spring 读取这些文件并设置其属性。

我们的属性结构:

    common.properties #这有所有的共同属性 service.properties #特定于服务的属性。这还将包含来自 common.properties 的现有属性,这些属性将被覆盖。

我了解我可以使用个人资料。但是,我们希望将属性保留在外部,如果您要更改属性,则需要重新启动服务。我也不想将变量作为 JVM 参数传递,然后我必须以这种方式传递大部分变量。传递 -Dspring.config.location 也很困难,因为使用了 common.propertiesservice.properties,并且每个服务的“service.properties”文件名各不相同。

示例代码:

主类:

@PropertySources(
        @PropertySource(value = "file:$property_path/common.properties", ignoreResourceNotFound = false),
        @PropertySource(value = "file:$property_path/service1.properties", ignoreResourceNotFound = true) )
public class MainClass 
static String logDirectory = ApplicationContext.getGlobalProperty("logging.file");

    public static void main(String[] args) 
        SpringApplication springApplication = new SpringApplication(MainClass.class);

        Properties properties = new Properties();
        properties.put("logging.file", logDirectory);
        springApplication.setDefaultProperties(properties);
        springApplication.run(args);
    

应用程序上下文:

@Configuration
@EnableAutoConfiguration
public class ApplicationContext implements EnvironmentAware 

    private static Environment environment;

    @Override
    public void setEnvironment(Environment environment) 
        ApplicationContext.environment = environment;
    
    public static String getGlobalProperty(String propertyName) 
    return environment.getProperty(propertyName);
    

在这里您可以看到我使用environment 获取property 的任何方式。有没有办法使用环境设置属性,以便在 spring boot 初始化本身时填充属性?

我们还可以实现ApplicationContextInitializer 并重写initialize 方法来读取属性。但是我怎样才能让它读取 2 个属性文件并用最新的值覆盖重复的属性呢? Reference(I'm not sure how to implement my requirements in this way.)。即使在这种情况下,您听起来也不像是想用锤子杀死蚊子吗?

当前工作解决方案:

@Aspect
@Profile("!production")
@Configuration
public class ControllerAspect 
@pointcut(....)
 //Here also I've to pass spring.profiles.active as JVM params.
//setting the value in common.properties or service1.properties is not working. 

我是 Spring Boot 的新手,所以请让我知道以获取更多说明。

【问题讨论】:

【参考方案1】:

似乎 Spring 默认会在初始化时加载一些属性,除非您专门编写逻辑来覆盖它们(就像我在 MainClass.java 中写的那样)没有选项可以覆盖这些。其中一些包括(logging.file,@ConditionalonExpression 中使用的键)。

一些具有自身挑战的技巧:

    在您的类路径中指定application.properties 中的属性。在早期阶段加载的变量总是从此文件中读取。 挑战:我已将所有属性紧密耦合到 jar 中,为了更改值,我必须重新编译并重新启动 Jar。 使用配置文件并将application.properties 定义为application-profile.properties挑战:我要创建这么多个人资料,但之前的挑战仍然存在。 将属性值作为 JVM 参数传递为 -Dproperty.key=value挑战:认真吗?我应该发送多少个属性作为 JVM 参数? 实现 ApplicationContextInitialize 并覆盖 initialize 方法。挑战:不建议覆盖 Spring 的默认行为,仅将其用于读取属性文件是不是有点矫枉过正?

解决办法:

使用-Dspring.config.location 指定属性文件。在这种情况下,spring 总是只从指定的位置读取属性。您也可以提供多个属性文件。 Refer this for much more details. 似乎如果您在目录弹簧以相反的顺序加载它们时提供属性位置。但是如果你指定文件,它会按照指定的顺序。

注意:所有这些都可以组合在一起。 To know about precedence refer this.

【讨论】:

以上是关于在初始化时加载 Spring Boot 属性并尊重所有属性并根据属性文件中的值控制 @Aspect的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 2 - 在初始化 bean 之前做一些事情

Spring Boot启动类的run方法

使用spring boot loader WarLauncher时如何在war文件之外加载属性文件?

Spring Boot - 初始数据的条件加载

如何使用 Spring 重新加载属性?

Spring Boot - 没有写入日志文件(不尊重logging.file)