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

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【小家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)

相关文章
|
21天前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
108 69
|
20天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
49 21
|
26天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
25天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
63 2
|
3月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
292 2
|
4天前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
34 11
|
6天前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
115 12
|
26天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
12天前
|
Java 测试技术 应用服务中间件
Spring Boot 如何测试打包部署
本文介绍了 Spring Boot 项目的开发、调试、打包及投产上线的全流程。主要内容包括: 1. **单元测试**:通过添加 `spring-boot-starter-test` 包,使用 `@RunWith(SpringRunner.class)` 和 `@SpringBootTest` 注解进行测试类开发。 2. **集成测试**:支持热部署,通过添加 `spring-boot-devtools` 实现代码修改后自动重启。 3. **投产上线**:提供两种部署方案,一是打包成 jar 包直接运行,二是打包成 war 包部署到 Tomcat 服务器。
39 10

热门文章

最新文章