Spring中注入bean的几种方式

2022-06-30 09:35:40

前言

  Spring是一个开源的框架,为开发者提供了便利,也为企业级开发产生的复杂问题提供了多种解决方法。当我们想要将组件注入到Spring的IOC容器中,除了@Controller、@Service、@Repository、@Component,还有一些常用的方法,下面我就这些方法简单的介绍下。
ps:下面所有的输出都是基于以下代码:

publicvoidtestBean(){AnnotationConfigApplicationContext applicationContext=newAnnotationConfigApplicationContext(MainConfig.class);String[] beanDefinitionNames= applicationContext.getBeanDefinitionNames();for(String name: beanDefinitionNames){// 循环遍历容器中定义的bean的nameSystem.out.println(name);}}

@Bean

我们自己写的组件,可以加上那四个注解,将组件注入到IOC容器当中,但是如果我们导入的是第三方组件呢?我们没法在类上标注注解,所以可以使用@Bean 注解来实现。@Bean 注解既可以注入自己写的组件,也可以注入第三方组件。

@Configuration@ComponentScan(value={"com.zxb"})publicclassMainConfig{@BeanpublicCatcat(){returnnewCat();}@Beanpubliccom.mysql.cj.jdbc.DriverjdbcDriver()throwsSQLException{returnnewDriver();}}

Spring提供了强大的@Bean 注解,使得我们可以方便的注入组件。标注了@Bean 注解的方法,该方法的返回值就是要注入到IOC容器中的组件,而方法名就是该组件在IOC容器中的名称。 当然,我们也可以使用@Bean注解的value属性来给组件改名儿,如@Bean(value = "driver")
在这里插入图片描述

@Import

  除了使用@Bean注解来导入第三方组件,还可以使用@Import 注解来实现,在Spring的底层中,大量用到了@Import注解。此注解的value值是一个数组,可以写多个Class。

@Configuration@ComponentScan(value={"com.zxb"})@Import(value={com.mysql.cj.jdbc.Driver.class})// 导入第三方组件publicclassMainConfig{}

可以看到,除了Spring内置的一些组件以及配置类本身,还加入了第三方组件com.zxb.cj.jdbc.Driver类。
在这里插入图片描述

@Import之实现 ImportSelector 接口

  上面提到了使用@Import 注解来实现bean的注入,但是细想一下,一个一个注入十分麻烦,这样@Import太长也不美观。那么我们就可以自己写一个 selector ,让它继承 ImportSelector 接口,来实现批量注入。

自定义selector:
实现ImportSelector 而重写的selectImports()方法,该方法的返回值数组就是要导注册的组件的全类名数组

importorg.springframework.context.annotation.ImportSelector;importorg.springframework.core.type.AnnotationMetadata;publicclassMyImportSelectorimplementsImportSelector{@Override// AnnotationMetadata:当前标注 @Import 注解的类的所有注解信息,这个类的所有注解信息都能获取到publicString[]selectImports(AnnotationMetadata annotationMetadata){returnnewString[]{"com.zxb.bean.Cat","com.zxb.bean.Dog","com.zxb.Turtle"};}}

配置类:

@Configuration@ComponentScan(value={"com.zxb"})@Import(value={Fish.class,MyImportSelector.class})publicclassMainConfig{@Beanpubliccom.mysql.cj.jdbc.DriverjdbcDriver()throwsSQLException{returnnewDriver();}}

可以看到,@Import 注解中value里面只有两个Class类型,一个是Fish,另一个是MyImportSelector,但是却将Cat、Dog和Turtle都注册到IOC容器中去了。
在这里插入图片描述

@Import之实现 ImportBeanDefinitionRegistrar 接口

@Import还有一种用法, 就是使用ImportBeanDefinitionRegistrar来注入bean。这个ImportBeanDefinitionRegistrar是一个接口,其中有一个方法。通过这个方法,我们可以给容器中,自己添加一些组件。

自定义一个 MyImportBeanDefinitionRegistrar:

importcom.zxb.bean.Pet;importorg.springframework.beans.factory.support.BeanDefinitionRegistry;importorg.springframework.beans.factory.support.RootBeanDefinition;importorg.springframework.context.annotation.ImportBeanDefinitionRegistrar;importorg.springframework.core.type.AnnotationMetadata;publicclassMyImportBeanDefinitionRegistrarimplementsImportBeanDefinitionRegistrar{/**
     * @param importingClassMetadata 当前类的注册信息
     * @param registry               BeanDefinition 注册类
     *                               把所有需要添加到IOC容器中的bean,通过BeanDefinitionRegistry中的一个专门方法注册进来
     *                               方法名为 registerBeanDefinition,调用该方法来进行手工注册
     */@OverridepublicvoidregisterBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitionRegistry registry){boolean definitionCat= registry.containsBeanDefinition("com.zxb.bean.Cat");boolean definitionDog= registry.containsBeanDefinition("com.zxb.bean.Dog");// 写一个简单的逻辑判断// 如果 IOC 容器中有 Cat 和 Dog 这两个组件,就注册 Pet 这个组件if(definitionCat&& definitionDog){// 这个 beanDefinition 是一个接口,需要new它的子类,子类就是RootBeanDefinition,传入我们要注册的class信息RootBeanDefinition petDefinition=newRootBeanDefinition(Pet.class);// 注册 Bean,并指定bean的名称和类型
            registry.registerBeanDefinition("pet", petDefinition);}}}

配置类:

@Configuration@ComponentScan(value={"com.zxb"})@Import(value={Fish.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})publicclassMainConfig{@Beanpubliccom.mysql.cj.jdbc.DriverjdbcDriver()throwsSQLException{returnnewDriver();}}

可以看到,Fish、以及自定义的选择器Selector,和自定义的 BeanDefinitionRegistrar都生效了。
在这里插入图片描述

使用FactoryBean进行bean注入

  不能搞混两个概念。一个是BeanFactory ,另一个是FactoryBeanBeanFactory 是Spring的顶级IOC接口,定义的是最基本的编码规范。而FactoryBean则是一个bean,但是又不是简单的一个bean,它可以生产或者修饰产生的bean,它和设计模式中的工厂模式和修饰器模式相近。
  首先自定义一个FactoryBean,并且要实现Factory接口。在该接口中,一共有三个方法,一个是getObject()方法,该方法的返回类型就是要注入到容器当中的bean,第二个是获取到对象的类型,第三个是判断单例与多实例。

packagecom.zxb.filter;importcom.zxb.bean.Animal;importcom.zxb.bean.Pet;importorg.springframework.beans.factory.FactoryBean;publicclassAnimalFactoryBeanimplementsFactoryBean<Animal>{// 返回一个 Animal对象,注册到 IOC 容器中@OverridepublicAnimalgetObject()throwsException{returnnewAnimal();}// 注册的bean的类型@OverridepublicClass<?>getObjectType(){returnPet.class;}// 该方法表示该bean是否为单实例bean@OverridepublicbooleanisSingleton(){returnfalse;}}

输入结果如下:
在这里插入图片描述
可以看到,AnimalFactoryBean也被注册到 IOC 容器中,名字默认是类名首字母小写。此时们输出下这个名为 animalFactoryBean的class信息,发现它是 com.zxb.bean.Animal 类型的。 但是如果我就要AnimalFacotry类型,而不要Animal类型的呢? 那么就在获取 bean 的时候在bean名称前加上一个& 符号即可。

publicvoidtestBean(){AnnotationConfigApplicationContext applicationContext=newAnnotationConfigApplicationContext(MainConfig.class);Object animal= applicationContext.getBean("animalFactoryBean");System.out.println(animal);Object animalFactory= applicationContext.getBean("&animalFactoryBean");System.out.println(animalFactory);}

在这里插入图片描述

@ImportResource

使用写xml配置的方式来注入bean,我们会在spring的配置文件中写 bean 标签,通过id,class来注册一个组件。我们可以通过 new 一个 ClassPathXmlApplicationContext 对象来读取该配置文件,将配置文件中的组件全部注册到IOC容器当中。那么如何使用注解实现相同效果呢? 就要使用@ImportResource 注解。@ImportResource 一定要标注在某个配置类上,可以是启动类,也可以使我们自己写的配置类。

我创建一个 spring.xml 配置文件,里面注册上一个 Dog 对象。

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><beanid="dog"class="com.zxb.demo.bean.Dog"></bean></beans>

随后创建一个配置类,并通过 @ImportResource 注解导入该配置文件,这样我们不需要去new IOC容器,也能加载配置文件了。

@Configuration@ImportResource(value={"classpath:/spring.xml"})publicclassMainConfig{}
@DatapublicclassDog{@Value("#{5-4}")privateInteger age;@Value("doggy")privateString name;}

测试 Spring的IOC容器中是否注入了 dog 这个组件。

@SpringBootTestclassDemoApplicationTests{@AutowiredprivateApplicationContext ioc;@TestvoidcontextLoads(){Dog dog= ioc.getBean(Dog.class);System.out.println(dog);boolean isExist= ioc.containsBean("dog");System.out.println(isExist);}}

在这里插入图片描述
可以看到,成功注入。

  • 作者:血莲丹
  • 原文链接:https://blog.csdn.net/weixin_44061521/article/details/120601845
    更新时间:2022-06-30 09:35:40