我正在尝试从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!");
}
}
答案 0 :(得分:1)
这是因为当实例化Spring bean时,它按照一定的顺序组装:
通常,在类中,您可以将类初始化代码放在构造函数中。
但是,如果您的类依赖于任何字段注入的属性,并且您希望使用它们来初始化您的类,则不能在构造函数中执行此操作。这是因为在构造函数执行完 之后才进行那些注入!
例如,你可能有一些类范围变量,它们已经从属性文件中注入了一个值(通过@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!");
}
}