扒一扒Bean注入到Spring的那些姿势

大家好,我是三友~~,这篇文章我准备来扒一扒Bean注入到Spring的那些姿势。,其实关于Bean注入Spring容器的方式网上也有很多相关文章,但是很多文章可能会存在以下常见的问题,所以本文就带着解决上述的问题的目的来重新梳理一下Bean注入到Spring的那些姿势。,配置文件的方式就是以外部化的配置方式来声明Spring Bean,在Spring容器启动时指定配置文件。配置文件方式现在用的不多了,但是为了文章的完整性和连续性,这里我还是列出来了,知道的小伙伴可以自行跳过这节。,配置文件的类型Spring主要支持xml和properties两种类型。,在XmlBeanInjectionDemo.xml文件中声明一个class为类型为User的Bean,User,测试:,结果:,可以看出成功将User注入到Spring中,由于没有设置username属性值,所以是null。,除了xml,spring还支持properties配置文件声明Bean的方式。,如下,在PropertiesBeanInjectionDemo.properties文件中声明了class类型为User的Bean,并且设置User的username属性为sanyou。,测试:,结果:,成功获取到User对象,并且username的属性为properties设置的sanyou。,除了可以配置属性之外还支持其它的配置,如何配置可以查看PropertiesBeanDefinitionReader类上的注释。,图片,上一节介绍了通过配置文件的方式来声明Bean,但是配置文件这种方式最大的缺点就是不方便,因为随着项目的不断扩大,可能会产生大量的配置文件。为了解决这个问题,Spring在2.x的版本中开始支持注解的方式来声明Bean。,这种方式其实就不用多说,在项目中自定义的业务类就是通过@Component及其派生注解(@Service、@Controller等)来注入到Spring容器中的。,在SpringBoot环境底下,一般情况下不需要我们主动调用@ComponentScan注解,因为@SpringBootApplication会调用@ComponentScan注解,扫描启动引导类(加了@SpringBootApplication注解的类)所在的包及其子包下所有加了@Component注解及其派生注解的类,注入到Spring容器中。,图片,虽然上面@Component + @ComponentScan的这种方式可以将Bean注入到Spring中,但是有个问题那就是对于第三方jar包来说,如果这个类没加@Component注解,那么@ComponentScan就扫不到,这样就无法注入到Spring容器中,所以Spring提供了一种@Bean的方式来声明Bean。,比如,在使用MybatisPlus的分页插件的时候,就可以按如下方式这么来声明。,此时就能将MybatisPlusInterceptor这个Bean注入到Spring容器中。,@Import注解也可以用来将Bean注入到Spring容器中,@Import注解导入的类可以分为三种情况:,普通类其实就很简单,就是将@Import导入的类注入到Spring容器中,这没什么好说的。,当@Import导入的类实现了ImportSelector接口的时候,Spring就会调用selectImports方法的实现,获取一批类的全限定名,最终这些类就会被注册到Spring容器中。,比如如下代码中,UserImportSelector实现了ImportSelector,selectImports方法返回User的全限定名,当使用@Import注解导入UserImportSelector这个类的时候,其实最终就会把User注入到Spring容器中,如下测试,运行结果,对于类实现了ImportBeanDefinitionRegistrar接口的情况,这个后面说。,一般来说,@Import都是配合@EnableXX这类注解来使用的,比如常见的@EnableScheduling、@EnableAsync注解等,其实最终都是靠@Import来实现的。,图片,@EnableScheduling,图片,@EnableAsync,讲完通过注解的方式来声明Bean之后,可以来思考一个问题,那就是既然注解方式这么简单,为什么Spring还写一堆代码来支持配置文件这种声明的方式?,其实答案很简单,跟Spring的发展历程有关。Spring在创建之初Java还不支持注解,所以只能通过配置文件的方式来声明Bean,在Java1.5版本开始支持注解之后,Spring才开始支持通过注解的方式来声明Bean。,在说注册BeanDefinition之前,先来聊聊什么是BeanDefinition?,BeanDefinition是Spring Bean创建环节中很重要的一个东西,它封装了Bean创建过程中所需要的元信息。,如上代码是BeanDefinition接口的部分方法,从这方法的定义名称可以看出,一个Bean所创建过程中所需要的一些信息都可以从BeanDefinition中获取,比如这个Bean的class类型,这个Bean是否是懒加载,这个Bean是否是单例的等等,因为有了这些信息,Spring才知道要创建一个什么样的Bean。,有了BeanDefinition这个概念之后,再来看一下配置文件和注解声明这些方式往Spring容器注入Bean的原理。,图片,Bean注入到Spring原理,如图为Bean注入到Spring大致原理图,整个过程大致分为以下几个步骤,好了,通过以上分析我们知道,配置文件和注解声明的方式其实都是声明Bean的一种方式,最终都会转换成BeanDefinition,Spring是基于BeanDefinition的信息来创建Bean。,既然Spring最终是基于BeanDefinition的信息来创建Bean,那么我们是不是可以跳过配置文件和注解声明的方式,直接通过手动创建和注册BeanDefinition的方式实现往Spring容器中注入呢?,答案是可以的。,前面说过,BeanDefinition最终会被注册到BeanDefinitionRegistry中,那么如何拿到BeanDefinitionRegistry呢?主要有以下两种方式:,上面在说@Import的时候,关于导入的类实现了ImportBeanDefinitionRegistrar接口的情况没有说,主要是因为在这里说比较合适,ImportBeanDefinitionRegistrar中有两个方法,方法的参数就是BeanDefinitionRegistry。当@Import导入的类实现了ImportBeanDefinitionRegistrar接口之后,Spring就会调用registerBeanDefinitions方法,传入BeanDefinitionRegistry。,UserImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar,从结果可以看出,成功将User注入到了Spring容器中。,上面的例子中有行代码,这行代码的意思就是把UserImportBeanDefinitionRegistrarDemo这个Bean注册到Spring容器中,所以这里其实也算一种将Bean注入到Spring的方式,原理也跟上面一样,会为UserImportBeanDefinitionRegistrarDemo生成一个BeanDefinition注册到Spring容器中。,除了ImportBeanDefinitionRegistrar可以拿到BeanDefinitionRegistry之外,还可以通过BeanDefinitionRegistryPostProcessor拿到BeanDefinitionRegistry,图片,BeanDefinitionRegistryPostProcessor,这种方式就不演示了。,手动注册BeanDefinition这种方式还是比较常见的。就比如说OpenFeign在启用过程中,会为每个标注了@FeignClient注解的接口创建一个BeanDefinition,然后再往Spring中的注册的,如下是OpenFeign注册FeignClient的部分代码,上一节说可以跳过配置文件或者是注解,直接通过注册BeanDefinition以达到将Bean注入到Spring中的目的。,既然已经可以跳过配置文件或者是注解,那么我们可不可以更激进一步,跳过注册BeanDefinition这一步,直接往Spring中注册一个已经创建好的Bean呢?,答案依然是可以的。,因为上面在提到当创建的Bean是单例的时候,会将这个创建完成的Bean保存到SingletonBeanRegistry中,需要用到直接从SingletonBeanRegistry中查找。既然最终是从SingletonBeanRegistry中查找的Bean,那么直接注入一个创建好的Bean有什么不可以呢?,既然可以,那么如何拿到SingletonBeanRegistry呢?,其实拿到SingletonBeanRegistry的方法其实很多,因为ConfigurableListableBeanFactory就继承了SingletonBeanRegistry接口,所以只要能拿到ConfigurableListableBeanFactory就相当于拿到了SingletonBeanRegistry。,图片,ConfigurableListableBeanFactory类图,而ConfigurableListableBeanFactory可以通过BeanFactoryPostProcessor来获取,图片BeanFactoryPostProcessor,来个Demo,RegisterUserBeanFactoryPostProcessor实现BeanFactoryPostProcessor, 往Spring容器中添加一个手动创建的User对象,测试,结果,从结果还是可以看出,成功从Spring容器中获取到了User对象。,这种直接将创建好的Bean注入到Spring容器中在Spring框架内部使用的还是比较多的,Spring的一些内建的Bean就是通过这个方式注入到Spring中的。,图片,如上图,在SpringBoot项目启动的过程中会往Spring容器中添加两个创建好的Bean,如果你的程序需要使用到这些Bean,就可以通过依赖注入的方式获取到。,虽然基于这种方式可以将Bean注入到Spring容器,但是这种方式注入的Bean是不经过Bean的生命周期的,也就是说这个Bean中诸如@Autowired等注解和Bean生命周期相关的回调都不会生效的,注入到Spring时Bean是什么样就是什么样,Spring不做处理,仅仅只是做一个保存作用。,FactoryBean是一种特殊的Bean的类型,通过FactoryBean也可以将Bean注入到Spring容器中。,图片,FactoryBean,当我们通过配置文件、注解声明或者是注册BeanDenifition的方式,往Spring容器中注入了一个class类型为FactoryBean类型的Bean时候,其实真正注入的Bean类型为getObjectType方法返回的类型,并且Bean的对象是通过getObject方法返回的。,UserFactoryBean实现了FactoryBean,getObjectType返回了User类型,所以这个UserFactoryBean会往Spring容器中注入User这个Bean,并且User对象是通过getObject()方法的实现返回的。,测试,结果,成功通过UserFactoryBean将User这个Bean注入到Spring容器中了。,FactoryBean这中注入的方式使用也是非常多的,就拿上面举例的OpenFeign来说,OpenFeign为每个FeignClient的接口创建的BeanDefinition的Bean的class类型FeignClientFactoryBean就是FactoryBean的实现。,getObject()方法就会返回接口的动态代理的对象,并且这个代理对象是由Feign创建的,这也就实现了Feign和Spring的整合。,通过以上分析可以看出,将Bean注入到Spring容器中大致可以分为5类:,以上几种注入的方式,在日常业务开发中,基本上都是使用注解声明的方式注入Spring中的;在第三方框架在和Spring整合时,注册BeanDefinition和FactoryBean这些注入方式也会使用的比较多;至于配置文件和注册创建完成的Bean的方式,有但是不多。,最后,本文所有的示例代码地址:

https://github.com/sanyou3/spring-bean-injection.git

文章版权声明

 1 原创文章作者:cmcc,如若转载,请注明出处: https://www.52hwl.com/19266.html

 2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈

 3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)

 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023年3月5日 上午12:00
下一篇 2023年3月7日 下午10:34