Java 使用 spring 个 bean 设置静态字段

Java Setting Static field with spring beans

我有一个 spring 引导项目,我正在尝试从 spring bean 中设置一些静态常量变量。这是相关文件

application.properties

app.constants.name=FooTester
app.constants.version=v1
app.constants.port=123

AppConfig.java

@Configuration
public class AppConfig {
    @Value("${app.constants.name}")
    private String appName;

    @Bean
    public MethodInvokingBean initAppConstants() {
        MethodInvokingBean miBean = new MethodInvokingBean();

        miBean.setStaticMethod("app.constants.AppConstants.setAppName");
        miBean.setArguments(new String[]{appName});
        try {
            miBean.prepare();
            miBean.invoke();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return miBean;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }
}

AppConstants.java

public final class AppConstants {
    public static String APP_NAME;
    public static String APP_VERSION;
    public static String APP_PORT;

    private AppConstants(){}

    public static void setAppName(Properties p) {
        APP_NAME = p.toString();
    }
}

这可以很好地获取名称的值,但是当它到达 setAppName 方法时,属性值是一个 hashmap,其键为 FooTester,值为 ""。如果我像这样使用 setArguments 方法添加多个值:

miBean.setArguments(new String[]{"test", "test2", "test3"});

属性对象在哈希图中只有 1 个条目,键只是 test, test2, test3,值是 ""

此外,我希望常数值为 final 而不仅仅是 public static。所以我不确定这是不是正确的方法。我如何完成我想要的?

  1. AppConstants中更改setAppName()以接收String(为什么Properties?)

    public final class AppConstants {
        // ...
    
        public static void setAppName(String appName) {
            APP_NAME = appName;
        }
    }
    
  2. 为什么要用反射?您可以简单地调用静态方法。
    你的整个AppConfigclass可以这么简单:

    @Configuration
    public class AppConfig {
        @Value("${app.constants.name}")
        public void setAppName(String appName) {
            AppConstants.setAppName(appName);
        }
    }
    

P.S。 Spring 中的设置 static 字段不被视为最佳做法。


编辑: 回答"I would like to know what would be best practice"

答:首先,为什么不推荐注入 static 字段:

  1. 这让测试变得更难了:

    • 使用可配置的 static 字段时,很容易忘记设置并搞砸测试。
    • 这使得 运行 并行测试(使用不同的配置)变得更加困难。
  2. 理论上,您可以加载 2 个 Spring 上下文,每个都有不同的配置,然后当第二个上下文加载时,它会覆盖第一个上下文设置的值(因此您不能在那种情况下使用静态字段,您需要替换所有用法)。

    在实践中,这可能会发生,例如,当您有两个 Web 应用程序(具有不同的配置)共享一个包含 AppConstants.

  3. 的 jar 时
  4. (这并不常见,但是 - 在某处您可能想要更改您的配置范围(例如,从 Singleton 到 Prototype 或 Session 范围)。[=19 这会更难=] 字段。)

最佳做法是什么?

最佳实践很简单(但可能很难转移到)- 一直使用注入:

AppConstants.APP_NAME替换为:

@Inject AppConstants appConstants;
// ...
appConstants.getAppName()

但是 只有当您觉得我说的有道理并且值得为您的情况付出努力时才更改它。


如果您确实考虑更改它,我建议如下:

  • 考虑将 AppConstants 重命名为 AppProperties。按照惯例,此类包含注入属性的 bean 通常命名为 XxxProperties。

  • 考虑像这样使用 @ConfigurationProperties
    (删除了一些样板文件)。

    @EnableConfigurationProperties
    public class AppConfig {
    }
    
    @ConfigurationProperties("app.constants")
    public class AppProperties {
        private String name;        // = ${app.constants.name}
        private String version;     // = ${app.constants.version}
        private int port;           // = ${app.constants.port}
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getVersion() {
            return version;
        }
    
        public void setVersion(String version) {
            this.version = version;
        }
    
        public int getPort() {
            return port;
        }
    
        public void setPort(int port) {
            this.port = port;
        }
    }