栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring - @Configuration 注解中 proxyBeanMethod 属性的作用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring - @Configuration 注解中 proxyBeanMethod 属性的作用

概述

@Configuration 注解是 Spring 提供的以 Java 代码方式 配置 IoC 容器 的一种方式。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

	
	@AliasFor(annotation = Component.class)
	String value() default "";

	
	boolean proxyBeanMethods() default true;

}

在 Spring 5.2 版本之后,提供了一个属性 proxyBeanMethods,从源码注释中我们知道:

  1. 该属性的作用是指定 标注了 @Bean 的方法 在执行生命周期的时候是否应该被代理。比如在代码中直接调用 @Bean 标注的方法 时要返回共享的单例 Bean 实例(关闭代理之后不会返回共享的 Bean 实例)。
  2. 该特性是通过 运行时 CGLIB 子类 实现的方法拦截。该子类有一些限制,比如配置类和它的方法不允许声明为 final 类型。
  3. 默认为 true,即允许 “inter-bean references” 通过配置类内部的直接方法调用,也可以通过外部调用该配置类的 @Bean 标注的方法。如果该配置类是一个特殊的配置类:每一个 @Bean 标注的方法 都是自包含的,并设计为供容器使用的普通工厂方法,可以设置该属性为 false,以避免 CGLIB 子类处理。
  4. 关闭 Bean 方法拦截可以高效的单独处理 @Bean 方法,就像声明在一个 non-@Configuration 类上一样,参考 @Bean Lite Mode。这种行为类似于删除 @Configuration 原型。
分析

针对以上几点,我们做一下测试,以便加深理解:

首先定义两个类 Person、Pet:

@Data
@AllArgsConstructor
public class Person {

    private String name;

    private Integer age;
}
@Data
@AllArgsConstructor
public class Pet {

    private String name;
}

定义配置类,定义 Bean,并加入到容器中:

@Configuration("appConfig")
public class MainConfig3 {

    @Bean
    public Person person() {
        return new Person("Nikang", 46);
    }

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

获取 Bean:

@Test
void testProxyBeanMethod() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
    printBeans(applicationContext);

	// 注册到容器中的 Bean 默认都是单例的
    Person person = applicationContext.getBean(Person.class);
    Person person1 = applicationContext.getBean(Person.class);
    System.out.println(person == person1);	// true
}
针对第一点和第三点
// proxyBeanMethods 属性默认为 true
boolean proxyBeanMethods() default true;

当使用代码调用 @Bean 注解的方法获取 Bean 时:

MainConfig3 config = applicationContext.getBean(MainConfig3.class);
Person person = config.person();
Person person1 = config.person();
System.out.println(person == person1);	// true

结果可知:外部获取 Bean,无论调用多少次获取的都是注册到容器中的单例对象。因为默认 proxyBeanMethods 为 true,所以我们是使用代理对象调用的 @Bean 方法,首先会去 IoC 容器 中查询是否有这个组件,有则返回,无则创建。

当设置 proxyBeanMethods 值为 false 时:

@Configuration(proxyBeanMethods = false)

再判断调用 @Bean 方法 获取的 Bean 是否相等:

但是通过 applicationContext 获取的 Bean 仍然是单例且相等的。并且获取到的配置类对象也不是经 CGLIB 代理过的对象了,而是对象本身

MainConfig3 config = applicationContext.getBean(MainConfig3.class);
System.out.println(config);	// com.keke.config.MainConfig3@43599640

// 外部调用,如果 proxyBeanMethods 设置为 false,则不会去 IoC 容器中找,即是否要代理 Bean 方法
Person person = config.person();
Person person1 = config.person();
System.out.println(person == person1);	// false

// 直接从容器中获取
Person person2 = applicationContext.getBean(Person.class);
Person person3 = applicationContext.getBean(Person.class);
System.out.println(person2 == person3);	// true
针对第二点
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
MainConfig3 config = applicationContext.getBean(MainConfig3.class);
System.out.println(config);

# 输出
com.keke.config.MainConfig3$$EnhancerBySpringCGLIB$$9f4a4070@598bd2ba

可以看到,默认设置 proxyBeanMethods 属性为 true 的情况下,配置类是一个由 CGLIB 代理的子类

针对第四点

如果组件间有依赖的话:

@Configuration(value = "appConfig", proxyBeanMethods = false)
public class MainConfig3 {

    @Bean
    public Person person() {
        Person nikang = new Person("Nikang", 46);
        // 依赖 pet()
        nikang.setPet(pet());
        return nikang;
    }

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

当我想要使用上述代码时,IDEA 提示如下(但仍然可以编译、运行通过):

Method annotated with @Bean is called directly in a @Configuration where proxyBeanMethods set to false. 
Set proxyBeanMethods to true or use dependency injection. 

需要我们设置 proxyBeanMethods 为 true,或者使用 依赖注入,只有这样,才能保证我们 set 的 pet() 方法和 @Bean pet() 方法是同一个 IoC 容器中的组件:

// 获取容器中的 Person 组件
Person person4 = applicationContext.getBean(Person.class);
// 获取容器中的 Pet 组件
Pet pet = applicationContext.getBean(Pet.class);
// 比较 Person 组件中 set 的 pet() 方法与 pet 组件
System.out.println(person4.getPet() == pet);	// true

显然,当设置 proxyBeanMethods 为 true 时,当使用到组件时,始终都会去 IoC 容器中获取组件,以保证单例

总结
  • @Bean Full Mode:proxyBeanMethods = true
  • @Bean Lite Mode:proxyBeanMethods = false,不需要去 IoC 容器中查找

  • 当配置类组件之间无依赖关系时,用 Lite 模式 可以加速容器启动过程,减少判断
  • 当配置类组件之间有依赖关系时,@Bean 方法 会被调用得到之前单实例组件,使用 Full 模式

TODO:补充 SpringBoot 源码中应用的例子

暑期编程PK赛 得CSDN机械键盘等精美礼品!
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1017336.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号