Spring注解(五):容器注册组件的四种方式

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 之前的一篇文章讲解过关于如何使用@Bean给容器中注册组件,可以参考:Spring注解(一):@Configuration、@Bean给容器中注册组件

1、使用@Bean注解:


之前的一篇文章讲解过关于如何使用@Bean给容器中注册组件,可以参考:


Spring注解(一):@Configuration、@Bean给容器中注册组件


2、使用包扫描、组件标注注解的方式


采用@Controller/@Service/@Repository/@Component注解,这种方式只局限于自己写的类,不能用于导入第三方包,可以参考之前的文章:


Spring注解(二):@ComponentScan自动扫描组件


3、使用@import注册组件:


首先这里可以声明一个Role类:


package com.xinyi.bean;
public class Role {
}
复制代码


通过测试类输出容器中注入的bean:


@Test
  public void test4() {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig1.class);
    String[] names = applicationContext.getBeanDefinitionNames();
    for(String name:names) {
      System.out.println(name);
    }
  }
复制代码


386408f51a754fa1aad3dfca1b0d013e~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


这是很可以发现Role并没有被注入进IOC容器。


接着在配置类上添加@Import注解:


@Import(Role.class)
复制代码


具体的代码如下所示:


@Configuration
@Import(Role.class)
public class MyConfig1 {
  @Bean("Lin Sin")
  @Lazy
  public Person person() {
    return new Person("李青",18);
  }
  @Bean("Yasuo")
  public Person person1() {
    return new Person("亚索",23);
  }
  @Bean("Zed")
  public Person person2() {
    return new Person("劫",32);
  }
}
复制代码


此时Role被注入进了IOC容器,id为类的全类名。


4f01033b0d314fcf8cea9ac9374efc6b~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


这里使用的是@Import注解,import注解的源码如下:


public @interface Import {
  /**
   * {@link Configuration @Configuration}, {@link ImportSelector},
   * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
   */
   //数组
  Class<?>[] value();
}
复制代码


根据源码可以发现@import注解后面的参数值可以是数组,所以使用@import注解可以同时注入多个组件,比如


@Import(Role1.class,Role2.class,......)
复制代码


除了直接使用@import注解之外,还可使用ImportSelector(导入选择器)来根据条件进行注册bean,ImportSelector返回需要注册的全类名,这也是SpringBoot开发中bean注册的主要方式。


声明一个MyImportSelector类并实现ImportSelector接口,自定义导入选择器,返回需要注册的组件。


//自定义导入选择器,返回需要注册的组件
public class MyImportSelector implements ImportSelector {
  //返回值就是匹配的要注册的组件(全类名)
  //AnnotationMetadata:当前标注@import注解的类的所有注解信息
  public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    return new String[]{"com.xinyi.bean.Role1","com.xinyi.bean.Role2"};
  }
}
复制代码


ImportSelector 接口的源码如下,返回的是一个string数组,数组中就是匹配的要注册的组件(全类名):


public interface ImportSelector {
  /**
   * Select and return the names of which class(es) should be imported based on
   * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
   */
  String[] selectImports(AnnotationMetadata importingClassMetadata);
}
复制代码


在注册类中@import注解添加MyImportSelector:


@Import(Role.class,MyImportSelector.class)
复制代码


返回结果如下,Role1和Role2被注入IOC容器中,bean的名称则是组件全类名。


720f8d0a74b4404582fb82dbd55325dc~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


另外还可以使用ImportBeanDefinitionRegistrar接口来手动自定义注册的bean名称。


新建一个Role3类和MyImportBeanDefinitionRegistrar 类并实现ImportBeanDefinitionRegistrar接口:


public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{
  /**
   * AnnotationMetadata:当前类的注解信息
   * BeanDefinitionRegistry:bean定义的注册类信息
   * 需要注册到容器中的bean调用BeanDefinitionRegistry.registryBeanDefinition
   */
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //设置注入条件:containsBeanDefinition的参数是组件的全类名
    boolean flag = registry.containsBeanDefinition("com.xinyi.bean.Role");
    //如果容器中包含Role组件,则将Role3注入进IOC容器
    if(flag) {
      RootBeanDefinition beanDefinition = new RootBeanDefinition(Role3.class);
      registry.registerBeanDefinition("自定注入的Bean名称", beanDefinition);
    }
  }
}
复制代码


在注册类文件中的@import注解增加MyImportBeanDefinitionRegistrar.class参数


@Import({Role.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
复制代码


因为包含了com.xinyi.bean.Role,所以Role3组件也被注入进容器,并且组件的名称是自定义的bean的名称(自定义注入的bean的名称)。


0b63ef9535b0423ba80f932ca3929a4a~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


4、使用spring提供的FactoryBean进行组件注册


声明一个RoleBean类并实现FactoryBean接口


//创建spring定义的FactoryBean类
public class RoleBean implements FactoryBean<Role> {
  //返回一个role对象并添加到容器中
  public Role getObject() throws Exception {
    // TODO Auto-generated method stub
    return new Role();
  }
  public Class<?> getObjectType() {
    // TODO Auto-generated method stub
    return Role.class;
  }
}
复制代码


FactoryBean接口的源码如下:


public interface FactoryBean<T> {
  /**
   * Return an instance (possibly shared or independent) of the object
   * managed by this factory.
   * <p>As with a {@link BeanFactory}, this allows support for both the
   * Singleton and Prototype design pattern.
   * <p>If this FactoryBean is not fully initialized yet at the time of
   * the call (for example because it is involved in a circular reference),
   * throw a corresponding {@link FactoryBeanNotInitializedException}.
   * <p>As of Spring 2.0, FactoryBeans are allowed to return {@code null}
   * objects. The factory will consider this as normal value to be used; it
   * will not throw a FactoryBeanNotInitializedException in this case anymore.
   * FactoryBean implementations are encouraged to throw
   * FactoryBeanNotInitializedException themselves now, as appropriate.
   * @return an instance of the bean (can be {@code null})
   * @throws Exception in case of creation errors
   * @see FactoryBeanNotInitializedException
   */
  @Nullable
  T getObject() throws Exception;
  /**
   * Return the type of object that this FactoryBean creates,
   * or {@code null} if not known in advance.
   * <p>This allows one to check for specific types of beans without
   * instantiating objects, for example on autowiring.
   * <p>In the case of implementations that are creating a singleton object,
   * this method should try to avoid singleton creation as far as possible;
   * it should rather estimate the type in advance.
   * For prototypes, returning a meaningful type here is advisable too.
   * <p>This method can be called <i>before</i> this FactoryBean has
   * been fully initialized. It must not rely on state created during
   * initialization; of course, it can still use such state if available.
   * <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
   * {@code null} here. Therefore it is highly recommended to implement
   * this method properly, using the current state of the FactoryBean.
   * @return the type of object that this FactoryBean creates,
   * or {@code null} if not known at the time of the call
   * @see ListableBeanFactory#getBeansOfType
   */
  @Nullable
  Class<?> getObjectType();
  /**
   * Is the object managed by this factory a singleton? That is,
   * will {@link #getObject()} always return the same object
   * (a reference that can be cached)?
   * <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
   * the object returned from {@code getObject()} might get cached
   * by the owning BeanFactory. Hence, do not return {@code true}
   * unless the FactoryBean always exposes the same reference.
   * <p>The singleton status of the FactoryBean itself will generally
   * be provided by the owning BeanFactory; usually, it has to be
   * defined as singleton there.
   * <p><b>NOTE:</b> This method returning {@code false} does not
   * necessarily indicate that returned objects are independent instances.
   * An implementation of the extended {@link SmartFactoryBean} interface
   * may explicitly indicate independent instances through its
   * {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
   * implementations which do not implement this extended interface are
   * simply assumed to always return independent instances if the
   * {@code isSingleton()} implementation returns {@code false}.
   * <p>The default implementation returns {@code true}, since a
   * {@code FactoryBean} typically manages a singleton instance.
   * @return whether the exposed object is a singleton
   * @see #getObject()
   * @see SmartFactoryBean#isPrototype()
   */
   //默认是true:Bean是单例,在容器中保存一份
   //当为false:多实例,每次创建都会获取一个新的bean
  default boolean isSingleton() {
    return true;
  }
复制代码


接着在配置类中注册RoleBean


@Bean
  public RoleBean roleBean() {
    return new RoleBean();
  }
复制代码


在测试方法中添加下述代码返回RoleBean的类型


Object beanObject = applicationContext.getBean("roleBean");
    System.out.println("roleBean的类型是:"+beanObject.getClass());
复制代码


RoleBean的类型是Role,因为RoleBean调用的是getObject方法创建的对象,而getObject方法返回的是new Role()。


微信截图_20220517204729.png


可以通过在RoleBean组件名称前加“&”符号来表明注册的是RoleBean组件本身。


public interface BeanFactory {
  /**
   * Used to dereference a {@link FactoryBean} instance and distinguish it from
   * beans <i>created</i> by the FactoryBean. For example, if the bean named
   * {@code myJndiObject} is a FactoryBean, getting {@code &myJndiObject}
   * will return the factory, not the instance returned by the factory.
   */
   //factorybean前缀符:返回的是工厂类,而不是调用getObject方法产生的对象
  String FACTORY_BEAN_PREFIX = "&";
复制代码


Object beanObject1 = applicationContext.getBean("&roleBean");
System.out.println("获取roleBean本身:"+beanObject1.getClass());
复制代码


85d61fd99bca412d98c510da7adc4015~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


以上就是在IOC容器中进行组件注册的四种方式。

目录
相关文章
|
3月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
15天前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML 缓存 Java
spring源码剖析-spring-beans(内部核心组件,BeanDefinition的注册,BeanWapper创建)
spring源码剖析-spring-beans(内部核心组件,BeanDefinition的注册,BeanWapper创建)
49 10
|
2月前
|
XML 存储 Java
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
|
17天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
46 0
|
2月前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
99 3
|
3月前
|
人工智能 自然语言处理 Java
Spring AI,Spring团队开发的新组件,Java工程师快来一起体验吧
文章介绍了Spring AI,这是Spring团队开发的新组件,旨在为Java开发者提供易于集成的人工智能API,包括机器学习、自然语言处理和图像识别等功能,并通过实际代码示例展示了如何快速集成和使用这些AI技术。
Spring AI,Spring团队开发的新组件,Java工程师快来一起体验吧
|
3月前
|
缓存 开发者 Docker
Dockerfile是Docker容器化过程中的核心组件,它允许开发者以一种可重复、可移植的方式自动化地构建Docker镜像
【8月更文挑战第19天】Dockerfile是构建Docker镜像的脚本文件,含一系列指令定义镜像构建步骤。每条大写指令后跟至少一个参数,按序执行,每执行一条指令即生成新的镜像层。常用指令包括:FROM指定基础镜像;RUN执行构建命令;EXPOSE开放端口;CMD指定容器启动行为等。优化策略涉及减少镜像层数、选择轻量基础镜像、利用缓存及清理冗余文件。示例:基于Python应用的Dockerfile包括设置工作目录、复制文件、安装依赖等步骤。掌握Dockerfile有助于高效自动化构建镜像,加速应用部署。
34 1
|
3月前
|
XML Java 数据格式
Spring5入门到实战------8、IOC容器-Bean管理注解方式
这篇文章详细介绍了Spring5框架中使用注解进行Bean管理的方法,包括创建Bean的注解、自动装配和属性注入的注解,以及如何用配置类替代XML配置文件实现完全注解开发。
Spring5入门到实战------8、IOC容器-Bean管理注解方式
|
3月前
|
前端开发 JavaScript 数据处理
React 中展示组件和容器组件
【8月更文挑战第31天】
61 0