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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用Spring Boot+MyBatis框架做查询操作的示例代码

    使用Spring Boot+MyBatis框架做查询操作的示例代码

    这篇文章主要介绍了使用Spring Boot+MyBatis框架做查询操作的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • Java 数据库连接池Druid 的介绍

    Java 数据库连接池Druid 的介绍

    这篇文章主要给大家分享的是 Java 数据库连接池Druid 的介绍,Druid是一个JDBC组件,它包括三部分: DruidDriver 代理Driver,能够提供基于Filter-Chain模式的插件体系。 DruidDataSource 高效可管理的数据库连接池,下面来看看文中的详细内容,需要的朋友也可以参考一下
    2021-11-11
  • java实现简单的给sql语句赋值的示例

    java实现简单的给sql语句赋值的示例

    这篇文章主要介绍了java实现简单的给sql语句赋值的示例,需要的朋友可以参考下
    2014-05-05
  • JAVA多线程处理for循环数据详细讲解

    JAVA多线程处理for循环数据详细讲解

    这篇文章主要给大家介绍了关于JAVA多线程处理for循环数据的相关资料,我们在代码中经常需要使用for循环这个操作来达到目的,而当for循环的次数过多时我们会发现执行效率会变的很低,整体耗时非常多,需要的朋友可以参考下
    2023-07-07
  • SpringBoot3安全管理操作方法

    SpringBoot3安全管理操作方法

    这篇文章主要介绍了SpringBoot3安全管理,在实际开发中,最常用的是登录验证和权限体系两大功能,在登录时完成身份的验证,加载相关信息和角色权限,在访问其他系统资源时,进行权限的验证,保护系统的安全,文中有详细的操作步骤,需要的朋友可以参考下
    2023-08-08
  • JPA原生SQL实现增删改查的示例详解

    JPA原生SQL实现增删改查的示例详解

    JPA除了对JPQL提供支持外,还对原生SQL语句也提供了支持。本文将利用生SQL实现增删改查功能,文中的示例代码讲解详细,需要的可以参考一下
    2022-09-09
  • Java之Mybatis的二级缓存

    Java之Mybatis的二级缓存

    本文主要介绍Java中Mybatis的二级缓存,缓存就是一块内存空间,保存临时数据,它是SqlSessionFactory的缓存,对Mybaits感兴趣的小伙伴可以参考阅读
    2023-03-03
  • SpringTask-Timer实现定时任务的详细代码

    SpringTask-Timer实现定时任务的详细代码

    在项目中开发定时任务应该一种比较常见的需求,今天通过示例代码给大家讲解SpringTask-Timer实现定时任务的相关知识,感兴趣的朋友一起看看吧
    2024-06-06
  • SpringBoot登录用户权限拦截器

    SpringBoot登录用户权限拦截器

    这篇文章主要介绍了SpringBoot登录用户权限拦截器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java简单实现session保存到redis的方法示例

    Java简单实现session保存到redis的方法示例

    这篇文章主要介绍了Java简单实现session保存到redis的方法,结合实例形式分析了Java将session存入redis缓存服务器的相关设置、实现技巧与操作注意事项,需要的朋友可以参考下
    2018-05-05

最新评论