DecadeAnkle / JavaStudyRecord

Java学习内容汇总
0 stars 0 forks source link

SpringBoot成员初始化顺序 #2

Open DecadeAnkle opened 1 year ago

DecadeAnkle commented 1 year ago

结论 “static 成员变量 ”--> “非static成员变量” --> “被@Autowired修饰的构造函数” --> “被@Autowired修饰的成员变量b” --> “被@PostConstruct修饰的init()函数”

@Component
public class A {

  @Autowired
  public B b;  // B is a bean

  public static C c;  // C is also a bean

  public static int count;

  public float version;

  public A() {
    System.out.println("This is A constructor.");
  }

  @Autowired
  public A(C c) {
    A.c = c;
    System.out.println("This is A constructor with c argument.");
  }

  @PostConstruct
  public void init() {

    count = 5;
    System.out.println("This is A post construct.");
  }
}

其他注意事项

  1. 静态成员依赖注入 有时我们想要对静态成员进行依赖注入(通常是Field dependency injection,即直接在成员上加@Autowired,此种做法不推荐),直接在静态成员上加@Autowired是无效的(其值总为null),这是因为静态成员变量是类的属性,不属于任何对象,而Spring实现Field dependency injection 是要依靠基于实例的reflection(反射)进行的。在这个例子中,Spring通过反射生成bean a, 并且发现a使用了bean b,然后去生成bean b,再次利用反射生成setter方法将b注入进a,这样就实现了Field dependency injection。通过上述过程我们可以知道static成员由于不属于任何实例,所以无法实现这样的依赖注入,但是我们可以通过Constructor dependency injection(构造函数依赖注入)来实现。以上面的例子为例,Spring在生成bean a(调用A的构造函数)时,由于A的构造函数带有参数c,Spring将在容器里寻找是否有符合c类型的bean,找到后将bean c赋值给构造函数的参数c,然后当执行到A.c = c时成员变量c就被“注入”成功了。

  2. Bean的延迟生成与注入 如果我们希望某个Bean不要在Spring容器启动时初始化(这样可以加快应用的启动速度),而是在用到时才实例化,可以用@Lazy这个注解。将这个注解加在@Bean、@Component、@Service、@Configuration等注解上时,这些注解所修饰的Bean将在第一次引用时才实例化。我们举个简单的例子:

存在一个普通Bean

@Component
public class CommonBean {
    public CommonBean () {
        System.out.println("This is CommonBean constructor.");
    }
}

以及加了@Lazy的“LazyBean”

@Lazy
@Component
public class LazyBean {
    public LazyBean() {
        System.out.println("This is LazyBean constructor.");
    }
}

假设由于某种原因,Spring扫描路径的顺序是LazyBean先于CommonBean,如果LazyBean不加@Lazy注解,则Bean的生成顺序永远是LazyBean,CommonBean;若是加上@Lazy,则CommonBean先生成,并且如果没有其他地方引用LazyBean(例如没有其他Bean @Autowired LazyBean),那么LazyBean将一直不进行初始化(直观表现为它的构造函数一直未被调用)。在实际应用中,如果Bean的数目比较多,无法立即理清依赖关系,但确定自己的Bean需要在其他Bean生成之后才生成时,可以通过此方法简单的控制Bean的生成顺序。

注意,若是在CommonBean里引用了LazyBean ,则LazyBean有没有使用@Lazy,它都会先完成实例化,即Bean之间引用的作用会大于@Lazy的作用。

@Lazy同样可以加在@Autowired注解上,比如

@Component
public class CommonBean {
    @Lazy
    @Autowired
    private LazyBean lazyBean;
}

在CommonBean生成过程中的DI阶段,它不关心LazyBean是否存在于Spring容器中(如果没有添加@Lazy注解并且没有类型为LazyBean的Bean在容器中的情况下, 会抛找不到Bean的异常),它会生成一个有同样接口的代理(由于是代理,没有真正的功能,但是LazyBean的构造函数及@PostConstruct所修饰的方法都会被调用),只有真正要调用LazyBean的方法时,该代理才会在自身内部生成LazyBean实例(此时不会再次调用构造函数及@PostConstruct所修饰的方法,调用代理的各种方法将会转发到生成的真正的LazyBean中),这样也相当于实现了延迟加载Bean的功能。

搬运