【小家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)

相关文章
|
12天前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
22 0
|
11天前
|
Java 开发者 Spring
Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
【5月更文挑战第1天】Spring AOP的切点是通过使用AspectJ的切点表达式语言来定义的。
23 5
|
12天前
|
XML Java 数据格式
如何在Spring AOP中定义和应用通知?
【4月更文挑战第30天】如何在Spring AOP中定义和应用通知?
17 0
|
12天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
22 0
|
12天前
|
XML Java 程序员
什么是Spring的IoC容器?
【4月更文挑战第30天】什么是Spring的IoC容器?
20 0
|
12天前
|
消息中间件 安全 Java
在Spring Bean中,如何通过Java配置类定义Bean?
【4月更文挑战第30天】在Spring Bean中,如何通过Java配置类定义Bean?
20 1
|
14天前
|
前端开发 Java 数据格式
【Spring系列笔记】定义Bean的方式
在Spring Boot应用程序中,定义Bean是非常常见的操作,它是构建应用程序的基础。Spring Boot提供了多种方式来定义Bean,每种方式都有其适用的场景和优势。
32 2
|
14天前
|
Java Spring 容器
【Spring系列笔记】IOC与DI
IoC 和 DI 是面向对象编程中的两个相关概念,它们主要用于解决程序中的依赖管理和解耦问题。 控制反转是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入和依赖查找。
32 2
|
15天前
|
Java 测试技术 数据库连接
Spring中ioc的优点
总之,Spring中的IoC提供了一种更加灵活、可维护、可测试和可扩展的方式来管理组件之间的依赖关系,从而提高了应用程序的质量和可维护性。这使得开发人员能够更专注于业务逻辑而不是底层的技术细节。
32 1
|
16天前
|
存储 缓存 Java
【spring】06 循环依赖的分析与解决
【spring】06 循环依赖的分析与解决
9 1