java - Spring Bean 构造函数无法设置 POJO 属性

标签 java spring dependency-injection constructor autowired

我正在尝试从我的主要方法创建一个 spring bean。

我已将此 bean 配置为具有特定属性。这些值是在 @Configuration 注释的 SpringConfig 类中指定的。

该 bean 是使用 Spring 应用程序上下文在我的 main() 方法中创建的。该 bean 成功启动,但它没有我在 SpringConfig 类中指定的属性。我不明白为什么?

我可能已经确定了问题的原因:该 bean 的 POJO 类使用 @Autowired 和 @Qualifier 来现场注入(inject)一组不同的属性,并且当我这些值时,这些值仍然存在。创建 bean(使用我的 main 方法中的 Spring Context)。

我不明白为什么我不能通过从 SpringConfig 类调用参数化构造函数来覆盖这些字段注入(inject)。

奇怪的是,我可以从我的主方法中显式地将属性更改为我需要的属性(即myBean.setproperty(NewVal)有效)。为什么它可以工作,但是 new MyBean(OtherVal) 失败?这毫无意义!

这是我的代码
应用程序.java:

package com.qa;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.qa.beans.AutoWiredBean;
import com.qa.beans.Book;
import com.qa.beans.Owner;

public class App {
    private ApplicationContext context;


// Constructor...
public App() {
    context = new AnnotationConfigApplicationContext(SpringConfig.class);

    Owner owner1 = (Owner) context.getBean("ownerBean");
    Book myBook = (Book) context.getBean("bookBean");
    AutoWiredBean myAWBean = (AutoWiredBean) context.getBean("autoWiredBean_Bean");
    AutoWiredBean myAWBean2 = (AutoWiredBean) context.getBean("autoWiredBean_Bean2");

    System.out.println("\n" + owner1);
    System.out.println("\n" + myBook);
    System.out.println("\n" + myAWBean);
    System.out.println("\n" + myAWBean2);  //Observe:  Spring fails to accept property parameters specified in the SpringConfig class.  Yet Spring WILL accept a mutation request (eg: myAWBean2.setName="Tessa") as done so below. 

    myAWBean2.setName("Tessa"); myAWBean2.setId(27);  //This works, but line 25 above does not. Line 25 uses SpringConfig class to set properties via constructor. WEIRD!!!!
    System.out.println("\n" + myAWBean2);
}

public static void main(String[] args) {
    new App();
    }
}


这是 SpringConfig 类:

package com.qa;

import java.util.ArrayList;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import com.qa.beans.AutoWiredBean;
import com.qa.beans.Book;
import com.qa.beans.Owner;
import com.qa.beans.Pet;


@Configuration
@PropertySource("classpath:Bean.properties")  
public class SpringConfig {

@Bean
public AutoWiredBean autoWiredBean_Bean(){
    return new AutoWiredBean();
}

@Bean
public AutoWiredBean autoWiredBean_Bean2(){
    return new AutoWiredBean(nameBean(), idBean());
}

@Bean
public String nameBean(){
    return "Iqbal Hamid";
}

@Bean
public String name2Bean(){
    return "Elouise McDermot";
}

@Bean
public String name3Bean(){
    return "Tony Apsley";
}

@Bean
public String name4Bean(){
    return "Luke Skywalker";
}

@Bean
public int idBean() {
    return 50;
}

@Bean
public int id2Bean() {
    return 37;
}

@Bean
public int id3Bean() {
    return 33;
}

@Bean
public int id4Bean() {
    return 44;
}


@Value("${book.author}") String name;
@Value("${book.title}") String title;
@Bean
public Book bookBean () {
    return new Book (title, name);
}

@Bean
public Pet pet1Bean() {
    Pet pet = new Pet();
    pet.setName("Daisy");
    return pet;
}

@Bean
Pet pet2Bean() {
    return new Pet("Lola");
}

@Bean
Pet pet3Bean() {
    return new Pet("Fido");
}


@Bean
public Owner ownerBean(){
    ArrayList<Pet> petList = new ArrayList<Pet>();

    petList.add(pet1Bean());
    petList.add(pet2Bean());
    petList.add(pet3Bean());

    return new Owner("Lina", petList);
    }
}

这是有问题的 POJO:

package com.qa.beans;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;

@Scope("prototype")
public class AutoWiredBean implements InitializingBean, DisposableBean  {

@Autowired
@Qualifier("name2Bean")
private String name;

@Autowired
@Qualifier ("id2Bean")
private int id;


// No arg constructor...
public AutoWiredBean() {
    super();
}

// Parameterised constructor...
@Autowired
public AutoWiredBean(String name, int id) {
    super();
    this.name = name;
    this.id = id;
}

public String getName() {
    return name;
}

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

public int getId() {
    return id;
}

public void setId(int id) {
    this.id = id;
}

@Override
public String toString() {
    return "AutoWiredBean Details: Name = '" + name + "', ID = " + id;
}

@Override
public void destroy() throws Exception {
    System.out.println("Destroying AWBbean!");
}

@Override
public void afterPropertiesSet() throws Exception {
    System.out.println("Set Property of AWBbean!");     
    }
}

最佳答案

这是因为当实例化 Spring bean 时,它会按照一定的顺序组装:

  1. 首先:构造函数执行;
  2. 第二:注入(inject)依赖项;
  3. 第三:执行初始化 Hook ;

通常,在类中,您会将类初始化代码放在构造函数内。

但是,如果您的类依赖于任何字段注入(inject)的属性,并且您希望使用这些属性来初始化您的类,则无法在构造函数内执行此操作。这是因为这些注入(inject)只有在构造函数执行之后才会发生!

例如,您可能有一些类范围变量,这些变量已被注入(inject)属性文件中的值(通过@Value),或者可能已被注入(inject)bean(通过@Autowired),并且您希望填充数组,您必须在初始化 Hook 内执行此操作,而不是在构造函数内执行此操作。

因此,我认为由于 Spring 容器管理的 bean 生命周期阶段的顺序,上面的构造函数注入(inject)被字段注入(inject)覆盖。

package SpringDemo_Annotations;

import java.util.ArrayList;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Value;


@Component("fortuneService_FileProperties")
public class FortuneService_FileProperties implements IFortuneService {

    @Value("${fortune1}")
    private String fortune1;

    @Value("${fortune2}")
    private String fortune2;

    @Value("${fortune3}")
    private String fortune3;

    @Value("${fortune4}")
    private String fortune4;

    @Value("${fortune5}")
    private String fortune5;

    private ArrayList<String> arrFortunes;


    // Constructor...
    public FortuneService_FileProperties() {
        System.out.println("\nOBSERVE: Order of the lifestages of a bean:\tFIRST we enter the constructor:\t\t INSIDE FortuneService_FileProperties Constructor!");

        //
        // NOTE:  
        // YOU CANNOT PLACE CODE INSIDE THE CONSTRUCTOR WHICH REFERENCES FIELD INJECTED BEANS (via @Autowired) OR FIELD INJECTED VALUES (via @Value)
        // BECAUSE THEY DO NOT GET INJECTED UNTIL AFTER THE CONSTRUCTOR HAS EXECUTED!!!
        // THEREFORE CODE BELOW HAS BEEN SHIFTED OVER TO HOOKOBJECT_INITIALISE...
        //
//      arrFortunes = new ArrayList<String>();
//      
//      arrFortunes.add(this.fortune1);
//      arrFortunes.add(this.fortune2);
//      arrFortunes.add(this.fortune3);
//      arrFortunes.add(this.fortune4);
//      arrFortunes.add(this.fortune5);

        System.out.println("OBSERVE: Order of the lifestages of a bean:\tSECOND Field injections take place:");
    }


    // Getter...
    @Override
    public String getFortune() {
        return arrFortunes.get((int) (Math.random() * arrFortunes.size()));
    }


    @PostConstruct
    public void hookObject_Initialise() {
        System.out.println("OBSERVE: Order of the lifestages of a bean:\tTHIRD we enter the initialisation hook.  Fields have now been injected, so if we need to use field-injected beans or field-injected values, to conduct any initialisation (eg to populate an array), we do this here!:\tINSIDE FortuneService_FileProperties @PostConstruct!");

        //
        // THIS IS THE CORRECT PLACE TO INITIALISE ANY CLASS-SCOPE VARIABLES
        // WHICH ACCESS FIELD INJECTED PROPERTIES...
        //
        arrFortunes = new ArrayList<String>();

        arrFortunes.add(this.fortune1);
        arrFortunes.add(this.fortune2);
        arrFortunes.add(this.fortune3);
        arrFortunes.add(this.fortune4);
        arrFortunes.add(this.fortune5);
    }

    @PreDestroy
    public void hookObject_Destroy() {
        System.out.println("\nOBSERVE: Order of the lifestages of a bean:\tFOURTH at destruction-time, we enter the destruction hook:\tINSIDE FortuneService_FileProperties() @PreDestoy!");
    }
}

关于java - Spring Bean 构造函数无法设置 POJO 属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49556541/

相关文章:

java - 如何在 JPanel 中刷新/重新加载图像

java - 在 PreparedStatement 中为相同的子查询设置参数

java - Spring Servlet 3.0 异步 Controller - 什么线程处理响应?

java - 在 ZooKeeper 中,有没有办法不用自己实现分布式锁,原子地编写层次结构?

spring - Tomcat 在 Spring Boot 应用程序中挂起

java - 当rest模板具有使用Spring boot 1.5.x的拦截器时,我们可以使用@RestClientTest吗?

.NET Core 3.1 中的 Azure 函数 : dependency Injection error

java - 如何在entityManagerFactory中注入(inject)Hibernate Interceptor类

java - NullPointerException:Junit Spring 注解配置

java - 让第二个 okHTTP 请求等待第一个完成 Android