@ConfigurationproxyBeanMethods 属性与 @Bean 方法

@Configuration 注解所标注的配置类中,我们常常使用 @Bean 标注来方法向 IoC 容器中注册组件。

@ConfigurationproxyBeanMethods 属性用来控制是否对 @Bean 方法进行代理。

  • 如果 proxyBeanMethods = true,那么 Spring 会将 @Configuration 所标注的配置类作为目标类来生成相应的代理类,最后将代理类注册到容器中。

    类中的 @Bean 方法均被加上一层代理,此时向 IoC 容器中注册组件的具体逻辑,是在 @Bean 方法的代理的后置增强中完成。

    无论是 Spring 为了注册组件去调用 @Bean 方法,还是用户代码中去主动调用 @Bean 方法,所调用的都是代理方法,需要经过增强代码处理。

    • 前置增强:尝试从 IoC 容器中获取类型为 @Bean 方法返回值类型的对象,如果能够获取到,则直接返回 IoC 容器中的相应对象,如果获取不到执行目标方法。

    • 目标方法:就是我们自己编写的 @Bean 方法中的内容,它负责创建一个新对象然后返回

    • 后置增强:获取到目标方法的返回值以后,将该新对象注册到 IoC 容器中。

  • 如果 proxyBeanMethods = false,那么 Spring 会将 @Configuration 所标注的配置类直接注册到容器中。

    类中的 @Bean 方法此时无代理,此时向 IoC 容器中注册组件的具体逻辑,是在 Spring 中另外一块代码来完成。

    无论是 Spring 为了注册组件去调用 @Bean 方法,还是用户代码中去主动调用 @Bean 方法,都是直接创建新对象返回。

    只不过 Spring 为了注册去调用的话,得到返回值以后还需要将该返回值注册到 IoC 容器中。

proxyBeanMethods = true + 组件依赖的情况下,Spring 调用 @Bean 方法进行组件注册的流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration(proxyBeanMethods = true)
public class MyConfig {

@Bean
public User user01() {
User zhangsan = new User("zhangsan", 18);
zhangsan.setPet(tomcatPet());
return zhangsan;
}

@Bean("tom")
public Pet tomcatPet() {
return new Pet("tomcat");
}
}

Spring 会顺序扫描 @Configuration 所标注的 MyConfig 类中的 @Bean 方法并进行调用,所以 Spring 会先调用 user01(),然后再调用 tomcatPet()

  1. Spring 调用 user01() 来进行组件注册,该方法已被代理,代理发现 IoC 容器中目前还没有 User 对象,所以接下来会执行目标 user01() 方法
  2. 执行目标 user01() 方法,在内部新创建了一个 User 对象命名为 zhangsan,然后在执行 zhangsan.setPet(tomcatPet()) 时,转而去调用 tomcatPet() 方法。
  3. tomcatPet() 方法也是 @Bean 方法,因此先经过代理,代理发现,IoC 容器中目前还没有 Pet 对象,所以接下来会执行目标 tomcatPet() 方法
  4. 执行目标 tomcatPet() 方法,创建了一个 Pet 对象并返回,代理的后置增强将该新 Pet 对象注册到 IoC 容器中,然后继续返回。
  5. zhangsan.setPet(tomcatPet()) 将返回的 Pet 对象设置到 zhangsan 的属性中,然后 user01() 目标方法执行结束并返回一个 User 对象,代理的后置增强,将该新 User 对象注册到 IoC 容器中。
  6. Spring 调用 tomcatPet() 来进行组件注册,该方法已被代理,代理发现 IoC 容器中已存在 Pet 对象,因此直接返回。

proxyBeanMethods = false + 组件依赖的情况下,Spring 调用 @Bean 方法进行组件注册的流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration(proxyBeanMethods = false)
public class MyConfig {

@Bean
public User user01() {
User zhangsan = new User("zhangsan", 18);
zhangsan.setPet(tomcatPet());
return zhangsan;
}

@Bean("tom")
public Pet tomcatPet() {
return new Pet("tomcat");
}
}

Spring 会顺序扫描 @Configuration 所标注的 MyConfig 类中的 @Bean 方法并进行调用,所以 Spring 会先调用 user01(),然后再调用 tomcatPet()

  1. Spring 调用 user01() 来进行组件注册,方法无代理可直接执行,方法内部创建了一个 User 对象并命名为 zhangsan,然后在执行 zhangsan.setPet(tomcatPet()) 时,转而去调用 tomcatPet() 方法。
  2. tomcatPet() 方法无代理可直接执行,创建一个 Pet 对象并返回。
  3. zhangsan.setPet(tomcatPet()) 将返回的 Pet 对象设置到 zhangsan 的属性中,然后 user01() 方法执行结束并返回一个 User 对象。
  4. Spring 将返回的 User 对象注册到 IoC 容器中。
  5. Spring 调用 tomcatPet() 来进行组件注册,方法无代理可直接执行,方法内部创建了一个 Pet 对象并返回。
  6. Spring 将返回的 Pet 对象注册到 IoC 容器中。