【小家Spring】Spring IoC容器中核心定义之------BeanDefinition深入分析(RootBeanDefinition、ChildBeanDefinition...)(下)

简介: 【小家Spring】Spring IoC容器中核心定义之------BeanDefinition深入分析(RootBeanDefinition、ChildBeanDefinition...)(下)

接下来,再在看看AnnotatedBeanDefinition的三个子类:


ScannedGenericBeanDefinition:存储@Component、@Service、@Controller等注解注释的类


它的源码很简单,就是多了一个属性:private final AnnotationMetadata metadata用来存储扫描进来的Bean的一些注解信息。


// 实现了AnnotatedBeanDefinition 也继承了GenericBeanDefinition
public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
  private final AnnotationMetadata metadata;
  ...
  // 它只有一个构造函数:必须传入MetadataReader
  public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
    Assert.notNull(metadataReader, "MetadataReader must not be null");
    this.metadata = metadataReader.getAnnotationMetadata();
    setBeanClassName(this.metadata.getClassName());
  }
}

AnnotatedGenericBeanDefinition


在基于注解驱动的Spring应用着,它使用得非常的多。因为获取注解信息非常的方便~


AnnotatedGenericBeanDefinition只能用于已经被注册或被扫描到的类(否则你手动new一个,它就不在容器里了,那就脱离管理了)


使用案例:

    public static void main(String[] args) {
        AnnotatedBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(RootConfig.class);
        // 就这么一下子,就把注解们都拿到了,简直不要太方便,简直可以当工具类来用
        Set<String> annotationTypes = beanDefinition.getMetadata().getAnnotationTypes();
        System.out.println(annotationTypes); //[org.springframework.context.annotation.ComponentScan, org.springframework.context.annotation.Configuration]
        System.out.println(beanDefinition.isSingleton()); //true
        System.out.println(beanDefinition.getBeanClassName()); //com.config.RootConfig
    }


源码参考;


public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
  private final AnnotationMetadata metadata;
  @Nullable
  private MethodMetadata factoryMethodMetadata;
  /**
   * Create a new AnnotatedGenericBeanDefinition for the given bean class.
   * @param beanClass the loaded bean class  注意官方这个注释:已经加载进来了的Bean的Class
   */
  public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
    setBeanClass(beanClass);
    this.metadata = new StandardAnnotationMetadata(beanClass, true);
  }
  //@since 3.1.1 此处传入AnnotationMetadata ,也得保证对应的class已经被loaded
  public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata) {
    Assert.notNull(metadata, "AnnotationMetadata must not be null");
    if (metadata instanceof StandardAnnotationMetadata) {
      setBeanClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());
    }
    else {
      setBeanClassName(metadata.getClassName());
    }
    this.metadata = metadata;
  }
   //@since 4.1.1   可以由指定的工厂方法产生这个Bean
  public AnnotatedGenericBeanDefinition(AnnotationMetadata metadata, MethodMetadata factoryMethodMetadata) {
    this(metadata);
    Assert.notNull(factoryMethodMetadata, "MethodMetadata must not be null");
    setFactoryMethodName(factoryMethodMetadata.getMethodName());
    this.factoryMethodMetadata = factoryMethodMetadata;
  }
  @Override
  public final AnnotationMetadata getMetadata() {
     return this.metadata;
  }
  @Override
  @Nullable
  public final MethodMetadata getFactoryMethodMetadata() {
    return this.factoryMethodMetadata;
  }
}


ConfigurationClassBeanDefinition


首先需要注意的是,它是ConfigurationClassBeanDefinitionReader的一个私有的静态内部类:这个类负责将@Bean注解的方法转换为对应的ConfigurationClassBeanDefinition类(非常的重要)

// 它直接继承自RootBeanDefinition 
private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
  ... 源码和之前的差得不是太多,此处就不解释了
}


它有一些默认的设置处理如下:


  • 如果@Bean注解没有指定bean的名字,默认会用方法的名字命名bean
  • @Configuration注解的类会成为一个工厂类,而所有的@Bean注解的方法会成为工厂方法,通过工厂方法实例化Bean,而不是直接通过构造函数初始化(所以我们方法体里面可以很方便的书写逻辑。。。)


Spring初始化时,会用GenericBeanDefinition或是ConfigurationClassBeanDefinition(用@Bean注解注释的类)存储用户自定义的Bean,在初始化Bean时,又会将其转换为RootBeanDefinition。

BeanDefinitionBuilder:快速创建一个Bean定义


使用它的好处是,可以进行方法的连缀。

没有特殊指明,创建的都是GenericBeanDefinition,源码非常的简单,下面只用个Deme看看即可

public class BeanDefinitionBuilder {
  //=================创建一个Builder  没特殊指明,都是GenericBeanDefinition
  public static BeanDefinitionBuilder genericBeanDefinition() {
    return new BeanDefinitionBuilder(new GenericBeanDefinition());
  }
  ....
  public static BeanDefinitionBuilder rootBeanDefinition(String beanClassName) {
    return rootBeanDefinition(beanClassName, null);
  }
  public static BeanDefinitionBuilder childBeanDefinition(String parentName) {
    return new BeanDefinitionBuilder(new ChildBeanDefinition(parentName));
  }
}


demo:

    public static void main(String[] args) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Child.class)
                .setRole(BeanDefinition.ROLE_APPLICATION)
                .setScope(BeanDefinition.SCOPE_SINGLETON)
                .addPropertyValue("name", "fsx")
                .setLazyInit(false)
                //Spring5.0后提供的,可以自己书写函数,在里面做任意事情
                //bdf是个AbstractBeanDefinition
                .applyCustomizers((bdf) -> {
                    AbstractBeanDefinition abdf = (AbstractBeanDefinition) bdf;
                    abdf.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_NO);
                }).getRawBeanDefinition();
        System.out.println(beanDefinition); //Generic bean: class [com.fsx.maintest.Child]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; d...
    }

BeanDefinitionReader:该接口的作用就是加载 Bean


在 Spring 中,Bean 一般来说都在配置文件中定义。而在配置的路径由在 web.xml 中定义(还有全注解的方式)。所以加载 Bean 的步骤大致就是:


1.加载资源,通过配置文件的路径(Location)加载配置文件(Resource)


2.解析资源,通过解析配置文件的内容得到 Bean。

public interface BeanDefinitionReader {
  // 得到Bean定义的register 
  BeanDefinitionRegistry getRegistry();
  // 返回用于加载资源的 ResourceLoader(可以为null)
  @Nullable
  ResourceLoader getResourceLoader();
  // 加载Bean的类加载器
  @Nullable
  ClassLoader getBeanClassLoader();
  // 生成Bean名称的名字生成器(若没有指定名称的话,会调用它生成)
  BeanNameGenerator getBeanNameGenerator();
  // 核心方法,loadbean定义进来,然后注册到上面的register 里面去
  int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
  int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
  int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
  int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}


它的继承结构非常简单,一个抽象实现+3个具体实现


image.png


AbstractBeanDefinitionReader


它实现了一些基本的方法,但是核心方法loadBeanDefinitions肯定是交给子类实现了


public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {
  private final BeanDefinitionRegistry registry;
  @Nullable
  private ResourceLoader resourceLoader;
  @Nullable
  private ClassLoader beanClassLoader;
  // 会有环境变量
  private Environment environment;
  // 默认的名字生成器(类名首字母小写)
  private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
  // 此构造函数,会完成一些参数的初始化
  protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
    // Determine ResourceLoader to use.
    if (this.registry instanceof ResourceLoader) {
      this.resourceLoader = (ResourceLoader) this.registry;
    } else {
      // 注意这个处理~~~~
      this.resourceLoader = new PathMatchingResourcePatternResolver();
    }
    // Inherit Environment if possible
    // 如果注册器里有环境变量,就用它的 否则new一个标准的~~~~  它下面也提供了set方法可以设置
    if (this.registry instanceof EnvironmentCapable) {
      this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
    } else {
      this.environment = new StandardEnvironment();
    }
  }
  public void setEnvironment(Environment environment) {
    Assert.notNull(environment, "Environment must not be null");
    this.environment = environment;
  }
  public void setBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) {
    this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new DefaultBeanNameGenerator());
  }
  ... 
}


XmlBeanDefinitionReader:从xml中加载Bean定义信息


public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
  ...
}


我们注解配置中@Configuration上也可以加上@ImportResource导入外置的xml配置文件。它由此方法ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromImportedResources处理,内部借助的就是XmlBeanDefinitionReader去解析它的


PropertiesBeanDefinitionReader:直接从properties文件或者Map里加载Bean


由于它语法怪异,因此基本不适用了


GroovyBeanDefinitionReader:不在本文讨论中

可能有小伙伴问:那我们注解的@Bean以及@Component的这么些bean定义都是谁去加载的呢? 需要注意的是这个就不属于它了。

@Bean都是@Configuration配置类里,统一由ConfigurationClassParser#parse()里去处理的(直接执行Method就行)

@Component这种组件统一由解析@ComponentScan的处理器的ComponentScanAnnotationParser(借助ClassPathBeanDefinitionScanner) 参考博文:【小家Spring】Spring解析@ComponentScan注解源码分析(ComponentScanAnnotationParser、ClassPathBeanDefinitionScanner)


总结


本编文章旨在讲解贯穿Spring IoC容器上下文的Bean定义接口、实现类等等。从设计中我们能发现,Spring的设计原则还是非常优秀的,单一职责的特性。

宁愿用扩展的方法多写类,也不会在Base里面加内容变得臃肿最终几乎笨重不可维护


有了这些基础,相信你在看Spring源码的时候又能更加顺畅很多了~共勉

(RootBeanDefinition、AnnotatedGenericBeanDefinition)

相关文章
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
NoSQL 算法 Redis
【Docker】(3)学习Docker中 镜像与容器数据卷、映射关系!手把手带你安装 MySql主从同步 和 Redis三主三从集群!并且进行主从切换与扩容操作,还有分析 哈希分区 等知识点!
Union文件系统(UnionFS)是一种**分层、轻量级并且高性能的文件系统**,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem) Union 文件系统是 Docker 镜像的基础。 镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
1130 6
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
959 2
|
XML 人工智能 Java
Spring IOC 到底是什么?
IOC(控制反转)是一种设计思想,主要用于解耦代码,简化依赖管理。其核心是将对象的创建和管理交给容器处理,而非由程序直接硬编码实现。通过IOC,开发者无需手动new对象,而是由框架负责实例化、装配和管理依赖对象。常见应用如Spring框架中的BeanFactory和ApplicationContext,它们实现了依赖注入和动态管理功能,提升了代码的灵活性与可维护性。
371 1
|
XML Java 数据格式
Spring IoC容器的设计与实现
Spring 是一个功能强大且模块化的 Java 开发框架,其核心架构围绕 IoC 容器、AOP、数据访问与集成、Web 层支持等展开。其中,`BeanFactory` 和 `ApplicationContext` 是 Spring 容器的核心组件,分别定位为基础容器和高级容器,前者提供轻量级的 Bean 管理,后者扩展了事件发布、国际化等功能。
506 18
|
XML Java 数据格式
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
|
XML Java 数据格式
Spring IoC容器初始化过程(xml形式)
Spring IoC容器初始化过程(xml形式)
380 0
|
XML Java 数据格式
Spring5源码(15)-IoC容器启动过程简析及XmlBeanFactory初始化
Spring5源码(15)-IoC容器启动过程简析及XmlBeanFactory初始化
395 1
|
XML 设计模式 Java
「推荐收藏!」【Spring源码探究】(一)IOC容器初始化🏅彻底让你明白运行原理和源码流程
「推荐收藏!」【Spring源码探究】(一)IOC容器初始化🏅彻底让你明白运行原理和源码流程
400 0
「推荐收藏!」【Spring源码探究】(一)IOC容器初始化🏅彻底让你明白运行原理和源码流程
|
XML Java 数据格式
Spring源码阅读-IOC容器初始化过程
Spring IOC容器的初始化过程:Resource定位,BeanDefinition载入,向IOC容器注册BeanDefinition。整个过程由refresh()方法触发,三个过程由不同的模块完成,使用户更加灵活的对这三个过程剪裁和扩展。
578 0