SpringBoot this调用@Bean效果详解

 更新时间:2023年02月03日 10:57:40   作者:愿做无知一猿  
这篇文章主要介绍了在一个@Bean方法内,this调用同一个类的@Bean方法会有什么效果,我们可以通过bean的名称、bean的类型或者bean的名称+类型来获取容器中的bean

在一个@Bean方法内,this调用同一个类的@Bean方法会有什么效果呢?

思考的起源

首先上代码:

public class BeanOne { 
}
public class BeanTwo {
    public BeanTwo(BeanOne beanOne){
    }
}
@Configuration
public class BeanConfigTest {
    @Bean
    @ConditionalOnMissingBean
    public BeanOne beanOne() {
        System.err.println("带有@ConditionalOnMissingBean的默认 BeanOne 产生------");
        return new BeanOne();
    }
    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo(this.beanOne());
    }
}

​ 可以看到上述三个类,其中BeanOne就是一个默认的Bean实现,标注了@ConditionalOnMissingBean代表它可以被覆盖;BeanTwo是一个使用BeanOne的类,类似于注入;BeanConfigTest就是用来注册这俩Bean的,可以看到在BeanTwo这个里面,直接使用了this.beanOne(),我一开始的想法就是,this调用,那始终调用的都是beanOne()这个方法呀,那不就代表着BeanOne不能被覆盖了。

​ 但是,当我将BeanOne加上@Component注解之后,运行程序,会发现,控制台根本没有输出我打印的那句话,那就可以猜测 this.beanOne()其实并不是简单的方法调用方法。

查找信息

阅读@Bean上的注释:

@Bean Methods in @Configuration Classes
Typically, @Bean methods are declared within @Configuration classes. In this case, bean methods may reference other @Bean methods in the same class by calling them directly. This ensures that references between beans are strongly typed and navigable. Such so-called 'inter-bean references' are guaranteed to respect scoping and AOP semantics, just like getBean() lookups would. These are the semantics known from the original 'Spring JavaConfig' project which require CGLIB subclassing of each such configuration class at runtime. As a consequence, @Configuration classes and their factory methods must not be marked as final or private in this mode. For example:
   @Configuration
   public class AppConfig {
       @Bean
       public FooService fooService() {
           return new FooService(fooRepository());
       }
       @Bean
       public FooRepository fooRepository() {
           return new JdbcFooRepository(dataSource());
       }
       // ...
   }

​ 简要概述就是:

Spring会对每个@Configuration标注的类进行CGLIB子类化,在一个Bean内使用方法调用另一个Bean,就像是getBean()查找一样。

​ 从它注释上的描述可以总结出,像this.beanOne()这类方法调用,其实就类似于getBean()去获取一个名叫beanOneBean。那么上面的输出结果就能解释了。

​ 上面这段注释下面,紧跟着一段注释,一起看一下:

@Bean Lite Mode
@Bean methods may also be declared within classes that are not annotated with @Configuration. For example, bean methods may be declared in a @Component class or even in a plain old class. In such cases, a @Bean method will get processed in a so-called 'lite' mode.
Bean methods in lite mode will be treated as plain factory methods by the container (similar to factory-method declarations in XML), with scoping and lifecycle callbacks properly applied. The containing class remains unmodified in this case, and there are no unusual constraints for the containing class or the factory methods.
In contrast to the semantics for bean methods in @Configuration classes, 'inter-bean references' are not supported in lite mode. Instead, when one @Bean-method invokes another @Bean-method in lite mode, the invocation is a standard Java method invocation; Spring does not intercept the invocation via a CGLIB proxy. This is analogous to inter-@Transactional method calls where in proxy mode, Spring does not intercept the invocation — Spring does so only in AspectJ mode.
For example:
   @Component
   public class Calculator {
       public int sum(int a, int b) {
           return a+b;
       }
       @Bean
       public MyBean myBean() {
           return new MyBean();
       }
   }

​ 简要概述就是:

@Component标注的类中,你使用@Bean标注的方法处于一种叫做lite模式下,lite模式中的Bean方法将被容器视为普通工厂方法,lite模式中的Bean,不支持Bean间的相互调用,如果相互调用,那么将会被视为标准的Java方法调用,Spring不会通过CGLIB为当前类生成子类。最后他说,这类似于内部 @Transactional方法调用,在代理模式下,Spring不会拦截调用;但是仅在AspectJ模式下,Spring会拦截调用。好像也是,标注@Transcational的方法是不能直接相互调用的。

​ 那用上面的例子试一下看看,是不是变成了普通Java方法调用了:

@Component
public class BeanOne {
}
public class BeanTwo {
    public BeanTwo(BeanOne beanOne){
    }
}
@Component
//@Configuration
public class BeanConfigTest {
    /**
     * @Bean 创建的默认是单例Bean
     */
    @Bean
    @ConditionalOnMissingBean
    public BeanOne beanOne() {
        System.err.println("带有@ConditionalOnMissingBean的默认 BeanOne 产生------");
        return new BeanOne();
    }
    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo(this.beanOne());
    }
}

​ 此时BeanOne上标注了@Component,但是打印了输出语句,可见其变成了普通方法调用。

更远一步

Bean的Full和Lite模式

​ 当@Bean方法在没有标注@Configuration注释的类中声明时,它们被称为Lite模式的Bean。例如:在@Component中声明的@Bean方法,甚至只是在一个非常普通的类中声明的Bean方法,都被认为是Lite版的配置类。和Full模式的@Configuration不同,Lite模式的@Bean方法不能声明Bean之间的依赖关系。因此,这样的@Bean方法不应该调用其他@Bean方法。每个这样的方法实际上只是一个特定Bean引用的工厂方法(factory-method),没有任何特殊的运行时语义。

​ 怎么确定一个Bean是不是Lite模式呢?

只要不标识@Configuration(proxyBeanMethods=true)其他都是lite模式。(当然,标注了的就是Full模式啦)

上述例子的spring-context版本是6.0.2,其中@Configuration的属性proxyBeanMethods的默认值是true。

​ 那么proxyBeanMethods为true和false有什么使用上的区别呢?

设置为false此时bean是lite模式:

此时运行时不再需要给对应类生成CGLIB子类,提高了运行性能,降低了启动时间,但是不能声明@Bean之间的依赖,也就是说不能通过方法调用来依赖其它Bean。

设置为true时为Full模式

此时配置类会被CGLIB增强(生成代理对象),放进IoC容器内的是代理,方法相互调用能够保证是同一个实例,都指向IoC内的那个单例,可以支持通过常规Java调用相同类的@Bean方法而保证是容器内的Bean,但是运行时会给该类生成一个CGLIB子类放进容器,有一定的性能、时间开销。

到此这篇关于SpringBoot this调用@Bean效果详解的文章就介绍到这了,更多相关SpringBoot调用@Bean内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springmvc html资源请求404的问题解决并分析

    springmvc html资源请求404的问题解决并分析

    这篇文章主要介绍了springmvc html资源请求404的问题解决并分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Java 泛型超详细入门讲解

    Java 泛型超详细入门讲解

    这篇文章主要介绍了Java基础泛型详情,泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型,下面文章的详细介绍,需要的朋友可以参考一下
    2022-04-04
  • java异常与错误处理基本知识

    java异常与错误处理基本知识

    本文内容是java的异常与错误处理基本知识
    2013-11-11
  • Java 精炼解读方法的定义与使用

    Java 精炼解读方法的定义与使用

    Java语言中的“方法”(Method)在其他语言当中也可能被称为“函数”(Function)。对于一些复杂的代码逻辑,如果希望重复使用这些代码,并且做到“随时任意使用”,那么就可以将这些代码放在一个大括号“{}”当中,并且起一个名字。使用的时候,直接找到名字调用即可
    2022-03-03
  • JavaSwing基础之Layout布局相关知识详解

    JavaSwing基础之Layout布局相关知识详解

    上次我们说到View的Mearsure流程,今天接着说说layout. 关于layout,很多朋友知道它是负责布局的,那么具体是怎么布局的?viewGroup和view的layout方法又有什么不同?一起来看看吧,需要的朋友可以参考下
    2021-05-05
  • Spring @Async 的使用与实现的示例代码

    Spring @Async 的使用与实现的示例代码

    本篇文章主要介绍了Spring @Async 的使用与实现的示例代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • Java实现用位运算维护状态码

    Java实现用位运算维护状态码

    位运算是一种非常高效的运算方式,在算法考察中比较常见,那么业务代码中我们如何使用位运算呢,感兴趣的小伙伴快跟随小编一起学习一下吧
    2024-03-03
  • 如何使用CountDownLatch同步java多线程

    如何使用CountDownLatch同步java多线程

    这篇文章主要介绍了如何使用CountDownLatch同步java多线程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Idea 搭建Spring源码环境的超详细教程

    Idea 搭建Spring源码环境的超详细教程

    这篇文章主要介绍了Idea 搭建Spring源码环境,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • Java数组中的元素删除并实现向前移的代码

    Java数组中的元素删除并实现向前移的代码

    这篇文章主要介绍了Java数组中的元素删除并实现向前移的代码的相关资料,需要的朋友可以参考下
    2016-05-05

最新评论