在Java Spring中将一个bean的值设置为另一个bean

Posted

技术标签:

【中文标题】在Java Spring中将一个bean的值设置为另一个bean【英文标题】:Setting the value of one bean to be another bean in Java Spring 【发布时间】:2017-09-07 21:16:37 【问题描述】:

Spring bean 配置有两个主要问题: 1. 当我从应用程序上下文中检索 bean 时,我通过属性文件读取的属性没有持久化。 2. 我无法弄清楚如何将 bean 的值设置为另一个 bean - 在下面的示例中,我试图将 Plan bean 的 Metric 属性设置为某个 bean。

我有两个小类,一个 Metric 和一个更大的 Plan:

@Component
public class Metric 

   @Value("$nameOfMetric")
   String name;

   @Value("$empID")
   String empID;

   @Value("$value:0.0")
   Double value;
   

还有一个更大的 Plan 对象,它具有三个不同的 Metrics 作为属性:

public class Plan 

public Metric hoursWorked;

public Metric monthlyDeals;

public Metric monthlyGoal;
...

我还有一个正在读取的属性文件:

nameOfMetric = Untitled Metric
empID = Unknown
hoursWorked = 120.0
monthlyDeals = 40.0
monthlyGoal = 100.0

我有没有办法为每个指标(hoursWorked、monthlyDeals、monthlyGoal)创建 bean,然后将这些 bean 中的每一个注入到 Plan bean 中?到目前为止,我已经尝试使用带有 @Configuration 注释的 PropertyConfig 类:

@Inject
private Environment environment;

private Metric injectMetric(String propertyName)

    Metric metric = new Metric();
    metric.setEmpID(environment.getProperty("empID"));
    metric.setName(propertyName);
    metric.setValue(Double.parseDouble(environment.getProperty(propertyName)));
    System.out.println("Metric injected: " + metric.toString());
    return metric;


@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() 
    return new PropertySourcesPlaceholderConfigurer();


@Bean(name = "hoursWorkedBean")
@Scope("prototype")
public Metric hoursWorked() 
    Metric metric = injectMetric("hoursWorked");
    return metric;

但是当我尝试访问这个 bean 时,我在主应用程序中获得了默认值或 null 值,这表明这些属性没有持久化:

    Metric hoursWorked = (Metric) ctx.getBean("hoursWorkedBean");

    System.out.println(hoursWorked.getName()); // prints out "Untitled Metric", but should print out "hoursWorked"

【问题讨论】:

【参考方案1】:

您可以在 Spring Boot 应用程序中使用 @ConfigurationProperties。

您可以在 Spring Boot 应用程序中进行以下配置:

resources/application.yml

metrics:
  hoursWorked:
    name: one
    empID: 1
    value: 1.23
  monthlyDeals:
    name: two
    empID: 2
    value: 2.34
  monthlyGoal:
    name: three
    empID: 3
    value: 3.45

Metric.java(不要忘记 getter/setter)

public class Metric 
    private String name;
    private String empID;
    private Double value;

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public String getEmpID() 
        return empID;
    

    public void setEmpID(String empID) 
        this.empID = empID;
    

    public Double getValue() 
        return value;
    

    public void setValue(Double value) 
        this.value = value;
    

    @Override
    public String toString() 
        return "Metric" +
                "name='" + name + '\'' +
                ", empID='" + empID + '\'' +
                ", value=" + value +
                '';
    

Metrics.java(定义指标名称/限定符/配置)

public class Metrics 

    public static final String HOURS_WORKED = "hoursWorked";
    public static final String MONTHLY_DEALS = "monthlyDeals";
    public static final String MONTHLY_GOAL = "monthlyGoal";

MetricsConfiguration.java(创建 bean 并从配置中初始化它们)

@Configuration
public class MetricsConfiguration 

    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    @Qualifier(Metrics.HOURS_WORKED)
    @ConfigurationProperties(prefix = "metrics." + Metrics.HOURS_WORKED)
    public Metric hoursWorked() 
        return new Metric();
    

    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    @Qualifier(Metrics.MONTHLY_DEALS)
    @ConfigurationProperties("metrics." + Metrics.MONTHLY_DEALS)
    public Metric monthlyDeals() 
        return new Metric();
    

    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    @Qualifier(Metrics.MONTHLY_GOAL)
    @ConfigurationProperties("metrics." + Metrics.MONTHLY_GOAL)
    public Metric monthlyGoal() 
        return new Metric();
    

DemoRunner.java(用于测试代码的命令行运行程序)

@Component
public class DemoRunner implements CommandLineRunner

    @Autowired
    private List<Metric> allMetrics;

    @Autowired
    @Qualifier(Metrics.HOURS_WORKED)
    private Metric hoursWorkedMetric;

    @Autowired
    @Qualifier(Metrics.MONTHLY_DEALS)
    private Metric monthlyDealsMetric;

    @Autowired
    @Qualifier(Metrics.MONTHLY_GOAL)
    private Metric monthlyGoalMetric;

    @Override
    public void run(String... args) throws Exception 
        System.out.println();

        System.out.println("All metrics:");
        allMetrics.stream().forEach(System.out::println);

        System.out.println();
        System.out.println("HOURS_WORKED metric: " + hoursWorkedMetric);
        System.out.println("MONTHLY_DEALS metric: " + monthlyDealsMetric);
        System.out.println("MONTHLY_GOAL metric: " + monthlyGoalMetric);

        System.out.println();
    

DemoApplication.java(SpringBoot 应用程序初始化)

@SpringBootApplication
public class DemoApplication 

    public static void main(String[] args) 
        SpringApplication.run(DemoApplication.class, args);
    

【讨论】:

这太棒了。谢谢你。我最初阅读了您的答案,认为它太复杂了,并决定我自己再搞砸一些,几天后把我的豆子连接起来。我最终完全按照你的描述做了。一个后续问题 - MetricsConfiguration.java 文件似乎很冗长。有什么办法可以减少那里的一些重复注释吗?我还在 Spring 学习曲线上。 @YuChen 您可以将 bean 配置放入任意数量的配置类中,只要您使用 @Configuration 注释它们即可。您还可以将项目作为地图加载到配置类中。因此有一个指标图。如果有兴趣将您的指标加载到地图中,请查看this answer。 @YuChen 如果您将指标作为地图加载,那么您将不需要@Qualifier 用于区分相同类型的bean - 指标。此外,您不需要在配置类中保留多个属性,并且您的代码将可扩展以支持任意数量的指标。【参考方案2】:

你需要自动连接你的指标

public class Plan 

@Autowired   
public Metric hoursWorked;

@Autowired
public Metric monthlyDeals;

@Autowired
public Metric monthlyGoal;
...

【讨论】:

谢谢阿梅尔。您是否可以更详细地了解此实现?例如,我使用@Value("#monthlyGoalBean") 将Metrices 标记为注释,其中monthlyGoalBean 是在我的PropertyConfig 类中实例化的bean。我是否无法像我试图做的那样使用通过 SPEL 引用的 bean 来设置一个组件中的属性值?

以上是关于在Java Spring中将一个bean的值设置为另一个bean的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring 中将原型列表注入 Singleton bean

Spring学习-----Spring bean配置继承

Spring bean配置继承

Spring bean配置继承

在spring中将正则表达式设置为属性值

Spring之设置Bean值