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容器中进行组件注册的四种方式。

目录
相关文章
|
7天前
|
运维 Java 程序员
Spring5深入浅出篇:基于注解实现的AOP
# Spring5 AOP 深入理解:注解实现 本文介绍了基于注解的AOP编程步骤,包括原始对象、额外功能、切点和组装切面。步骤1-3旨在构建切面,与传统AOP相似。示例代码展示了如何使用`@Around`定义切面和执行逻辑。配置中,通过`@Aspect`和`@Around`注解定义切点,并在Spring配置中启用AOP自动代理。 进一步讨论了切点复用,避免重复代码以提高代码维护性。通过`@Pointcut`定义通用切点表达式,然后在多个通知中引用。此外,解释了AOP底层实现的两种动态代理方式:JDK动态代理和Cglib字节码增强,默认使用JDK,可通过配置切换到Cglib
|
7天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
22 0
|
6天前
|
Java
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
|
7天前
|
存储 缓存 Java
【JavaEE】Spring中注解的方式去获取Bean对象
【JavaEE】Spring中注解的方式去获取Bean对象
3 0
|
7天前
|
存储 Java 对象存储
【JavaEE】Spring中注解的方式去存储Bean对象
【JavaEE】Spring中注解的方式去存储Bean对象
10 0
|
7天前
|
JSON 前端开发 Java
【JAVA进阶篇教学】第七篇:Spring中常用注解
【JAVA进阶篇教学】第七篇:Spring中常用注解
|
7天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
24 2
|
7天前
|
前端开发 Java
SpringBoot之自定义注解参数校验
SpringBoot之自定义注解参数校验
21 2
|
7天前
|
XML Java 程序员
什么是Spring的IoC容器?
【4月更文挑战第30天】什么是Spring的IoC容器?
20 0
|
7天前
|
Java Spring
springboot自带的@Scheduled注解开启定时任务
springboot自带的@Scheduled注解开启定时任务