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

本文涉及的产品
容器镜像服务 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容器中进行组件注册的四种方式。

目录
相关文章
|
4天前
|
Java 开发者 Spring
深入理解Spring Boot的@ComponentScan注解
【4月更文挑战第22天】在构建 Spring Boot 应用时,@ComponentScan 是一个不可或缺的工具,它使得组件发现变得自动化和高效。这篇博客将详细介绍 @ComponentScan 的基本概念、关键属性及其在实际开发中的应用。
21 4
|
6天前
|
Java 开发者 Spring
Spring Framework 中的 @Autowired 注解:概念与使用方法
【4月更文挑战第20天】在Spring Framework中,@Autowired 注解是实现依赖注入(Dependency Injection, DI)的一种非常强大的工具。通过使用 @Autowired,开发者可以减少代码中的引用绑定,提高模块间的解耦能力
29 6
|
1月前
|
XML Java 数据库连接
spring boot 参数的过滤注解与实战
在Spring Boot应用中,对于入参的过滤,通常会涉及到对Web层的数据验证和处理。Spring Boot借助Spring框架提供了强大的验证框架支持,主要基于JSR-303/JSR-380(Bean Validation API)规范,以及Spring自身的@Valid或@Validated注解来实现请求参数的验证。以下是一些常见的使用案例来展示如何对参数进行过滤和验证。
29 1
|
1月前
|
Java Spring 容器
【Java】Spring如何扫描自定义的注解?
【Java】Spring如何扫描自定义的注解?
35 0
|
1月前
|
Java 数据库 Spring
【spring(四)】Spring事务管理和@Transactional注解
【spring(四)】Spring事务管理和@Transactional注解
|
14天前
|
XML Java 数据格式
进阶注解探秘:深入Spring高级注解的精髓与实际运用
进阶注解探秘:深入Spring高级注解的精髓与实际运用
26 2
|
14天前
|
XML Java 数据格式
从入门到精通:Spring基础注解的全面解析
从入门到精通:Spring基础注解的全面解析
30 2
从入门到精通:Spring基础注解的全面解析
|
18天前
|
Java 容器
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
SpringBoot使用配置注解开启自动配置功能&整合spring-boot-configuration-processor
14 0
|
1月前
|
SpringCloudAlibaba Java 持续交付
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(一)基础知识+各个组件介绍+聚合父工程创建
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(一)基础知识+各个组件介绍+聚合父工程创建
95 1
|
1月前
|
Java 容器 Spring
【spring(一)】核心容器总结
【spring(一)】核心容器总结