【小家Spring】Spring注解驱动开发---向Spring Ioc容器中注册Bean的7种方式(中)

简介: 【小家Spring】Spring注解驱动开发---向Spring Ioc容器中注册Bean的7种方式(中)

4、@Conditional按照条件向Spring中期中注册Bean

 /*
 * @author Phillip Webb
 * @author Sam Brannen
 * @since 4.0
 * @see Condition
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
  /**
   * All {@link Condition}s that must {@linkplain Condition#matches match}
   * in order for the component to be registered.
   */
  Class<? extends Condition>[] value();
}


这个接口是Spirng4提供出来的。在SpringBoot底层大量的用到了这个接口来按照条件注册Bean。


从注解的属性value来看,我们可以传入Condition条件,因此我们可以传入系统自带的,也可以我们自己去实现这个接口,按照我们的需求来注册Bean


image.png


从上图可以看出,SpringBoot工程中对此接口有大量的实现。本文通过自己的实现,来看看根据条件注册Bean的强大之处。


比如我们要实现如下功能:

如果系统是windows,给容器中注入"bill",如果系统是linux,给容器中注入"linus"


  public class WindowCondition implements Condition{
      /**
       * @param context 判断条件
       * @param metadata 注释信息
       * @return boolean
       */
      @Override
      public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
          Environment environment = context.getEnvironment();
          String property = environment.getProperty("os.name");
          if (property.contains("Windows")) {
              return true;
          }
          return false;
      }
  }


需要注意的是,context还有以下方法:


  // 能获取ioc使用的beanfactory
  ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  // 能获取到类加载器
  ClassLoader classLoader = context.getClassLoader();
  // 获取到环境变量
  Environment environment = context.getEnvironment();
  // 获取到Bean定义的注册类
  BeanDefinitionRegistry registry = context.getRegistry();


LinuxCondition类的写法略。配置类如下:


@Configuration
public class MainConfig2 {
    @Conditional({WindowCondition.class})
    @Bean("bill")
    public Person person01() {
        return new Person("Bill Gates", 60);
    }
    @Conditional({LinuxCondition.class})
    @Bean("linux")
    public Person person02() {
        return new Person("linus", 45);
    }
}

运行:(测试时候可以设置运行时参数:-Dos.name=linux)


结果我们会发现,注册的Bean已经按照我们的条件去注册了

备注:@Conditonal注解不仅可以标注在方法上,还可以标注在类上。如果标注在配置类上,那么若不生效的话,这个配置类所有就将不会再生效了


5、@Improt快速导入一个组件


@Improt快速导入特别重要,在SpringBoot自动装配的过程中起到了非常关键的作用

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
  /**
   * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
   * or regular component classes to import.
   */
  Class<?>[] value();
}


@Import可以导入第三方包,或则自己写的类,比较方便,Id默认为全类名(这个需要注意)

比如新建一个类Color


public class Color {
}


配置类上:

@Import({Color.class})
@Configuration
public class MainConfig2 {}


6、ImportSelector和ImportBeanDefinitionRegistrar


ImportSelector:


从注解中的注释中可以看出,import除了导入具体的实体类外,还可以导入实现了指定接口的类。现在我们自己来实现一个,编写一个MyImportSelector类实现ImportSelector接口

public class MyImportSelector implements ImportSelector{
    // 返回值就导入容器组件的全类名
    // AnnotationMetadata:当前类标注的@Import注解类的所有注解信息
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[] {"com.cuzz.bean.Car"};
    }
}

在配置类中,通过@Import导入


@Import({Color.class, MyImportSelector.class})
@Configuration
public class MainConfig2 {}

这样子我们发现,Car类已经被导入进去了。


ImportBeanDefinitionRegistrar:


public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * @param importingClassMetadata 当前类的注解信息
     * @param registry 注册类
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 查询容器
        boolean b = registry.containsBeanDefinition("com.cuzz.bean.Car");
        // 如果有car, 注册一个汽油类
        if (b == true) {
            // 需要添加一个bean的定义信息
            RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Petrol.class);
            // 注册一个bean, 指定bean名
            registry.registerBeanDefinition("petrol", rootBeanDefinition);
        }
    }
}

配置类:



@Import({Color.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig2 {}

在SpringBoot中的使用,举个栗子:

注解@ServletComponentScan的解析,从下面代码可以看出:


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServletComponentScanRegistrar.class)
public @interface ServletComponentScan {}


是ServletComponentScanRegistrar注册进去和解析的。在看看这个类:


class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar {}

它就是个标准的ImportBeanDefinitionRegistrar 。然后在方法registerBeanDefinitions这里面做了很多事:比如添加注解的后置处理器等等


相关文章
|
2月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
6月前
|
XML 人工智能 Java
Spring IOC 到底是什么?
IOC(控制反转)是一种设计思想,主要用于解耦代码,简化依赖管理。其核心是将对象的创建和管理交给容器处理,而非由程序直接硬编码实现。通过IOC,开发者无需手动new对象,而是由框架负责实例化、装配和管理依赖对象。常见应用如Spring框架中的BeanFactory和ApplicationContext,它们实现了依赖注入和动态管理功能,提升了代码的灵活性与可维护性。
198 1
|
XML Java 数据格式
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
|
4月前
|
Kubernetes Docker Python
Docker 与 Kubernetes 容器化部署核心技术及企业级应用实践全方案解析
本文详解Docker与Kubernetes容器化技术,涵盖概念原理、环境搭建、镜像构建、应用部署及监控扩展,助你掌握企业级容器化方案,提升应用开发与运维效率。
840 108
|
5月前
|
存储 监控 测试技术
如何将现有的应用程序迁移到Docker容器中?
如何将现有的应用程序迁移到Docker容器中?
458 57
|
2月前
|
监控 Kubernetes 安全
还没搞懂Docker? Docker容器技术实战指南 ! 从入门到企业级应用 !
蒋星熠Jaxonic,技术探索者,以代码为笔,在二进制星河中书写极客诗篇。专注Docker与容器化实践,分享从入门到企业级应用的深度经验,助力开发者乘风破浪,驶向云原生新世界。
还没搞懂Docker? Docker容器技术实战指南 ! 从入门到企业级应用 !
|
2月前
|
NoSQL 算法 Redis
【Docker】(3)学习Docker中 镜像与容器数据卷、映射关系!手把手带你安装 MySql主从同步 和 Redis三主三从集群!并且进行主从切换与扩容操作,还有分析 哈希分区 等知识点!
Union文件系统(UnionFS)是一种**分层、轻量级并且高性能的文件系统**,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem) Union 文件系统是 Docker 镜像的基础。 镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
505 5