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

时间:2018-03-29 12:49:46

标签: java spring dependency-injection constructor autowired

我正在尝试从main方法创建一个spring bean。

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

使用Spring Application Context在我的main()方法中创建bean。 bean成功启动但它没有我在SpringConfig类中指定的属性。我无法理解为什么?

我可能已经确定了问题的原因:这个bean的POJO类使用@Autowired和@Qualifier来字段注入一组不同的属性,这些这些值在我持续存在时创建bean(使用我的main方法中的Spring Context)。

我无法理解为什么我不能通过从SpringConfig类中调用参数化构造函数来覆盖这些字段注入。

奇怪的是,我可以显式地将属性变为我需要的属性来自我的main方法(即myBean.setproperty(NewVal)工作)。为什么这样做但new MyBean(OtherVal)失败了?这毫无意义!

这是我的代码
App.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!");     
    }
}

1 个答案:

答案 0 :(得分:1)

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

  1. 第一个:构造函数执行;
  2. 第二个:注入依赖关系;
  3. 第三:初始化挂钩已执行;
  4. 通常,在类中,您可以将类初始化代码放在构造函数中。

    但是,如果您的类依赖于任何字段注入的属性,并且您希望使用它们来初始化您的类,则不能在构造函数中执行此操作。这是因为在构造函数执行完 之后才进行那些注入!

    例如,你可能有一些类范围变量,它们已经从属性文件中注入了一个值(通过@Value),或者它可能已经注入了一个bean(通过@Autowired)并且你希望填充一个数组,你必须在初始化钩子里面而不是在构造函数中。

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

    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!");
        }
    }