然后Spring在实例化A时选择的构造函数的原则是:如果有构造函数被@Autowired所修饰,则采用该构造函数(注意,@Autowired(required = true)只能修饰一个构造函数),否则采用默认的无参构造函数。(当设置为 true 时(默认值),如果 Spring 找不到匹配的 bean,会抛出异常,表示注入失败。如果设置为 false,则允许注入失败,不会抛出异常。)
注意执行完该构造函数后,此时的成员变量B并没有被注入,值还是null。
Spring容器选择合适的Bean注入b 。Dependency Injection阶段
执行被@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.");
}
}
首先初始化的是static成员变量, 此处的count采用默认值0。
然后初始化的是非static成员变量,此处的version采用默认值0.0。
然后Spring在实例化A时选择的构造函数的原则是:如果有构造函数被@Autowired所修饰,则采用该构造函数(注意,@Autowired(required = true)只能修饰一个构造函数),否则采用默认的无参构造函数。(当设置为 true 时(默认值),如果 Spring 找不到匹配的 bean,会抛出异常,表示注入失败。如果设置为 false,则允许注入失败,不会抛出异常。)
注意执行完该构造函数后,此时的成员变量B并没有被注入,值还是null。
Spring容器选择合适的Bean注入b 。Dependency Injection阶段
执行被@PostConstruct修饰的init()函数
静态成员依赖注入 有时我们想要对静态成员进行依赖注入(通常是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就被“注入”成功了。
Bean的延迟生成与注入 如果我们希望某个Bean不要在Spring容器启动时初始化(这样可以加快应用的启动速度),而是在用到时才实例化,可以用@Lazy这个注解。将这个注解加在@Bean、@Component、@Service、@Configuration等注解上时,这些注解所修饰的Bean将在第一次引用时才实例化。我们举个简单的例子:
存在一个普通Bean
以及加了@Lazy的“LazyBean”
假设由于某种原因,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注解上,比如
在CommonBean生成过程中的DI阶段,它不关心LazyBean是否存在于Spring容器中(如果没有添加@Lazy注解并且没有类型为LazyBean的Bean在容器中的情况下, 会抛找不到Bean的异常),它会生成一个有同样接口的代理(由于是代理,没有真正的功能,但是LazyBean的构造函数及@PostConstruct所修饰的方法都会被调用),只有真正要调用LazyBean的方法时,该代理才会在自身内部生成LazyBean实例(此时不会再次调用构造函数及@PostConstruct所修饰的方法,调用代理的各种方法将会转发到生成的真正的LazyBean中),这样也相当于实现了延迟加载Bean的功能。
搬运