Spring注解之@Autowired:装配构造函数和属性

       在User类中创建一个构造函数,传入参数student:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.Serializable;

/**
 * @author Wiener
 */
@Component
public class User implements Serializable {
    private static final long serialVersionUID = 6089103683553156328L;
    private Long id;

    private Student student;

    public Long getId() {
        return id;
    }

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

    @Autowired  // 构造函数注入参数的方式
    User(Student student) {
        this.student = student;
        System.out.println("------ 为构造器装配Bean成功 ---------");
    }
    public void isStu() {
        student.studentStudy();
        System.out.println("------ 装配Bean成功 ---------");
    }
}

       其中,Student类如下:

import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Date;

/**
 * @author Wiener
 */
@Getter
@Setter
@Component
public class Student implements Serializable {

    private static final long serialVersionUID = -5246589941647210011L;

    //姓名
    private String name;
  
    public Student() {
        System.out.println("A default student constructor." );
    }
    public void studentStudy() {
        System.out.println("A student is studying." );
    }
}

改造Spring Boot项目启动类:

import com.east7.bean.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

/**
 * @author Wiener
 */
@SpringBootApplication
public class East7Application {

    public static void main(String[] args) {
       ApplicationContext act = SpringApplication.run(East7Application.class, args);
        User user = (User) act.getBean("user");
        user.isStu();
    }

}

执行测试函数后,控制台打印信息如下:

A default student constructor.
------ 为构造器装配Bean成功 ---------
A student is studying.
------ 装配Bean成功 ---------

       说明成员变量已经注入。此处需要注意一点,如果有两个自定义构造方法,而且都没加@Autowired注解,则会报错,因为Spring不知道你要用哪个构造方法初始化;如果只有一个构造方法加了@Autowired注解,那么就会用这个构造方法初始化;同理,如果有多个构造方法都加了@Autowired注解,那么还是会报错提示Only one constructor can have @Autowired annotation。

       可以在属性中使用@Autowired 注解来省略 setter 方法。当使用@Autowired为自动连接属性传递的时候,Spring 会将这些传递过来的值或者引用自动分配给那些属性。像下面这样写在属性上并直接引用会报空指针异常:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
* @author Wiener
*/
@Component
public class User {

    @Autowired //变量注入方式
    private Student student;
    private Long id = student.getId();

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public void isStu() {
        id = student.getId();
        student.studentStudy();
        System.out.println("------ 装配Bean成功 ---------");
    }
}

异常信息如下:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'user' defined in file [xxxxxx\User.class]: 
Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [com.east7.bean.User]:
Constructor threw exception; nested exception is java.lang.NullPointerException

报错信息提示创建Bean时出错,因为实例化bean时构造方法抛出了空指针异常。如果仅仅使用函数isStu()初始化变量id, 并且把【private Long id = student.getId();】改为 【private Long id;】,则属于方法调用的时候初始化,此时,bean已经注入,不会抛异常。 

       其实这两种方式都可以使用,但报错的原因是加载顺序的问题,@autowired写在变量上的注入要等到类完全加载后,才会将相应的bean注入,而变量是在加载类的时候按照相应顺序加载的,所以变量的加载要早于被@autowired注解的变量,那么给变量prefix 赋值的时候所使用的a,其实还没有被注入,所以报空指针,而使用构造器则会在加载类的时候将a加载,这样在内部使用a给prefix 赋值就完全没有问题。 

       如果不使用构造器,那么也可以不给prefix 提前赋值,而是在系统启动后,变量prefix被使用的地方,通过a.getExcelPrefix()进行赋值,此时对a的使用是在类完全加载之后,即a被注入后,所以也是可以的。 

       总之,@Autowired一定要等本类构造完成后,才能从外部引用设置属性,所以@Autowired的注入时间一定会晚于构造函数的执行时间。但在初始化变量的时候就使用了还没注入的bean,所以导致了NPE。若在初始化其它变量时不使用这个要注入的bean,而是在后期调用方法的时候去初始化,是可以使用这个bean的,因为那时构造函数已经执行完毕,即已注入bean了。一言以蔽之,Java变量的初始化顺序为:静态变量或静态语句块–>实例变量或初始化语句块–>构造函数–>@Autowired 注入Bean

 

posted @ 2020-07-11 14:56  楼兰胡杨  阅读(5612)  评论(0编辑  收藏  举报