首页 > 其他分享 >【Spring】BeanDefinition 深入了解

【Spring】BeanDefinition 深入了解

时间:2024-03-11 10:15:17浏览次数:19  
标签:String Nullable Spring void Bean 深入 type BeanDefinition

1  前言

今天想细细研究下 BeanDefinition,至于为什么,主要是看了很多遍,没太关注 mergedBeanDefinition 所以比较好奇,它是干啥的呢?所以得先搞清 BeanDefinition,开整。

2  源码分析

我们得先知道 BeanDefinition 在 Spring 这个大框架下是个什么位置。我的理解它就类似于 Java 中的类(Class),在 Java 中,Class 是创建对象的模板,那 BeanDefinition 就是创建生成 Bean的模板。

Spring 中我大概理解为三层或者四层吧:BeanDefinition 生成 Bean 实例,Bean实例交给 BeanFactory 管理,BeanFactory 交给 ApplicationContext 提供基础服务。

知道它的位置后,我们接下来先看看它内部有哪些属性和方法,然后看它的上下类图大概划分为哪些类型的 BeanDefinition,然后看看常规下 SpringBoot 启动下不同的 Bean 方式都转换为了哪些 BeanDefinition。

2.1  BeanDefinition 接口

我们首先来看看该接口都有哪些东西:

/**
 * BeanDefinition 描述了一个 Bean 实例(包含属性、构造器、关系)
 * A BeanDefinition describes a bean instance, which has property values,
 * constructor argument values, and further information supplied by
 * concrete implementations.
 * 可以通过 BeanFactoryPostProcessor 改变属性以及其他元信息
 * <p>This is just a minimal interface: The main intention is to allow a
 * {@link BeanFactoryPostProcessor} to introspect and modify property values
 * and other bean metadata.
 * @see ConfigurableListableBeanFactory#getBeanDefinition
 * @see org.springframework.beans.factory.support.RootBeanDefinition
 * @see org.springframework.beans.factory.support.ChildBeanDefinition
 */
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    /**
     * 作用范围:单例、多例
     * isSingleton 判断是不是单例
     * isPrototype 判断是不是多例
     * 通过 setScope/getScope 获取修改作用范围
     */
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    boolean isSingleton();
    boolean isPrototype();
    void setScope(@Nullable String scope);
    @Nullable
    String getScope();

    /**
     * 三个角色类型
     * ROLE_APPLICATION 表示这个 Bean 是用户自己定义的 Bean
     * ROLE_SUPPORT 表示这个 Bean 是某些复杂配置的支撑部分
     * ROLE_INFRASTRUCTURE 表示这是一个 Spring 内部的 Bean
     * 通过 setRole/getRole 可以修改
     */
    int ROLE_APPLICATION = 0;
    int ROLE_SUPPORT = 1;
    int ROLE_INFRASTRUCTURE = 2;
    void setRole(int role);
    int getRole();

    /**
     * parentName 说明 BeanDefinition 存在父子继承的关系 即类似类里的继承关系 extends
     * 通过 setParentName/getParentName 可以修改
     */
    void setParentName(@Nullable String parentName);
    @Nullable
    String getParentName();

    /**
     * 表示当前 BeanDefinition 的类型,全类名
     */
    void setBeanClassName(@Nullable String beanClassName);
    @Nullable
    String getBeanClassName();

    /**
     * 延迟初始化 <bean lazy-init="">
     * lazyInit = true 表示用到的时候才创建
     */
    void setLazyInit(boolean lazyInit);
    boolean isLazyInit();

    /**
     * 依赖列表 <bean depends-on=""> @DependsOn
     */
    void setDependsOn(@Nullable String... dependsOn);
    @Nullable
    String[] getDependsOn();

    /**
     * 是否自动装配
     */
    void setAutowireCandidate(boolean autowireCandidate);
    boolean isAutowireCandidate();

    /**
     * 是否抽象的
     */
    boolean isAbstract();
    /**
     * 是否主要的首选的 也就是当存在多个的时候,优先取的
     */
    void setPrimary(boolean primary);
    boolean isPrimary();

    /**
     * factoryBean 相关的
     */
    void setFactoryBeanName(@Nullable String factoryBeanName);
    @Nullable
    String getFactoryBeanName();
    void setFactoryMethodName(@Nullable String factoryMethodName);
    @Nullable
    String getFactoryMethodName();

    /**
     * Bean 构造方法的参数,用于实例化 Bean 判断
     */
    ConstructorArgumentValues getConstructorArgumentValues();
    default boolean hasConstructorArgumentValues() {
        return !getConstructorArgumentValues().isEmpty();
    }

    /**
     * Bean 属性列表
     */
    MutablePropertyValues getPropertyValues();
    default boolean hasPropertyValues() {
        return !getPropertyValues().isEmpty();
    }

    /**
     * Bean 初始化方法 @Bean(initMethod="init")来指定初始化方法 类似 @PostConstruct
     */
    void setInitMethodName(@Nullable String initMethodName);
    @Nullable
    String getInitMethodName();

    /**
     * Bean 销毁方法  @Bean(destroyMethod = "") 类似 @PreDestory
     */
    void setDestroyMethodName(@Nullable String destroyMethodName);
    @Nullable
    String getDestroyMethodName();

    /**
     * 描述信息 类似订单的备注
     */
    void setDescription(@Nullable String description);
    @Nullable
    String getDescription();

    /**
     * 资源描述 还是描述信息 类似订单的商家备注
     */
    @Nullable
    String getResourceDescription();

    /**
     * Return a resolvable type for this bean definition,
     * based on the bean class or other specific metadata.
     * <p>This is typically fully resolved on a runtime-merged bean definition
     * but not necessarily on a configuration-time definition instance.
     * @return the resolvable type (potentially {@link ResolvableType#NONE})
     * @since 5.2
     * @see ConfigurableBeanFactory#getMergedBeanDefinition
     */
    ResolvableType getResolvableType();

    /**
     * Return the originating BeanDefinition, or {@code null} if none.
     * <p>Allows for retrieving the decorated bean definition, if any.
     * <p>Note that this method returns the immediate originator. Iterate through the
     * originator chain to find the original BeanDefinition as defined by the user.
     */
    @Nullable
    BeanDefinition getOriginatingBeanDefinition();
}

上边的注释,我是把一些属性和方法归了下类,大概有这么几块是我们需要关注的:

(1)单例还是多例的:scope、isSingleton、isPrototype

(2)BeanDefinition 类型划分:role 主要区分是框架内部的还是用户自定义的

(3)父子关系:parentName,说明 BeanDefinition 存在父子关系,也就说明某些属性可以继承类似 extends

(4)class 类型:beanClassName,Bean 表现的 class

(5)属性:ConstructorArgumentValues 构造器属性(用于创建实例)、MutablePropertyValues 属性字段(用于类型注入等)

(6)钩子函数:initMethodName (初始化方法)、destoryMethodName (销毁方法)

(7)实例化时机:lazyInit 比如 spring的 refresh 当 = false 的时候会提前创建出来

2.2  实现类以及SpringBoot 启动下的 BeanDefinition

看完它有的内部结构后,我们看看它的实现类:

可以看到 AbstractBeanDefinition 作为底座的,大概分三个派系:

(1)常规派:以 GenericBeanDefinition 为首的

(2)注解派:以 AnnotatedBeanDefinition 为首的

(3)根派的:RootBeanDefinition (这个不常用了)

(4)父子派的:ChildBeanDefinition(这个也不常用了)

我们先不解释每个类型的 BeanDefinition,太枯燥了,我们直接先看下 SpringBoot 启动完后,BeanFactory 里都有哪些类型的 BeanDefinition,在哪看呢?其实它存储在 BeanFactory 的实现里:

可以看到有179个 BeanDefinition,在看看有哪些类型的:

(1)org.springframework.beans.factory.support.RootBeanDefinition 比如internalCommonAnnotationProcessor、internalEventListenerProcessor、internalAutoProxyCreator都是些 spring 内部的高级类

(2)org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$ConfigurationClassBeanDefinition @Configuration里的Bean 比如 SqlSessionTemplate、SqlSessionFactory

(3)org.springframework.context.annotation.ScannedGenericBeanDefinition 扫描型的注解比如我们写的 Controller、Service、@Component的工具类等、还有 Mapper 接口

(4)org.springframework.beans.factory.support.ChildBeanDefinition 就一个我自己的类= = 看来这个不常用了

(5)org.springframework.beans.factory.support.GenericBeanDefinition 比如 MybatisPlusProperties、WebMvcProperties、ServerProperties、JdbcProperties 等属性配置类还有一些 spring 内部类

(6)org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition 比如 MybatisPlusAutoConfiguration、RestTemplateAutoConfiguration 基本的自动装配类、还有带@Configuration 的

除了 Root 和 Child 的这两个比较少,其他四个都比较多。

从上边的规律大概能猜出我们平时自定义的一些比如 @Component、@Controller、@Service、@Mapper 基本都转为了 ScannedGenericBeanDefinition , @Configuration 配置类基本转为 AnnotatedGenericBeanDefinition,@Configuration 里的@Bean 基本转为了 ConfigurationClassBeanDefinition ,我们的属性配置类基本转为了普通的 GenericBeanDefinition 。

2.3  AbstractBeanDefinition

我们就来看看底座 AbstractBeanDefinition,东西还挺多,这个类就一千多行的代码,从属性上下手,粗略看了一下以及我之前看源码的过程,大概多了一个重要的属性就是装配模式:autowireMode,这个就跟我们看 Mybatis 代理创建过程里的 SqlSessionTemplate 里的属性 SqlSessionFactory 能自动赋值,就呼应上了。

autowireMode:默认的都是 no,也就是不装配,1是根据名称 2 是根据类型 3是构造器,mybatis那里就是设置的 2,类型装配根据 set方法设置进去的。

其他方法的话,就不看了哈,没什么大特殊的,先不追那么细了。

2.4  GenericBeanDefinition

GenericBeanDefinition 是从 Spring2.5 以后新加入的 BeanDefinition 实现类。GenericBeanDefinition 可以动态设置父 Bean,同时兼具 RootBeanDefinition 和 ChildBeanDefinition 的功能,因此,自从有了 GenericBeanDefinition 之后,RootBeanDefinition 和 ChildBeanDefinition 现在相对就用的少了。我们看上边,我们大多的属性配置都会包装成 GenericBeanDefinition ,我这里就从 MybatisPlus 的配置看看源码:

那我们继续看下注解 @EnableConfigurationProperties:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EnableConfigurationPropertiesRegistrar.class})
public @interface EnableConfigurationProperties {
    String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";

    Class<?>[] value() default {};
}

可以看到引入了 EnableConfigurationPropertiesRegistrar 属性配置注册器:

class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
    EnableConfigurationPropertiesRegistrar() {
    }
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        registerInfrastructureBeans(registry);
        // 交给 ConfigurationPropertiesBeanRegistrar 注册
        ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
        this.getTypes(metadata).forEach(beanRegistrar::register);
    }
    ...
}

继续跟进 ConfigurationPropertiesBeanRegistrar :

final class ConfigurationPropertiesBeanRegistrar {
    private final BeanDefinitionRegistry registry;
    private final BeanFactory beanFactory;

    ConfigurationPropertiesBeanRegistrar(BeanDefinitionRegistry registry) {
        this.registry = registry;
        this.beanFactory = (BeanFactory)this.registry;
    }

    void register(Class<?> type) {
        MergedAnnotation<ConfigurationProperties> annotation = MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY).get(ConfigurationProperties.class);
        this.register(type, annotation);
    }

    void register(Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
        String name = this.getName(type, annotation);
        if (!this.containsBeanDefinition(name)) {
            // 注册 BeanDefinition
            this.registerBeanDefinition(name, type, annotation);
        }

    }
    ...

    private void registerBeanDefinition(String beanName, Class<?> type, MergedAnnotation<ConfigurationProperties> annotation) {
        Assert.state(annotation.isPresent(), () -> {
            return "No " + ConfigurationProperties.class.getSimpleName() + " annotation found on  '" + type.getName() + "'.";
        });
        // 创建 BeanDefinition 并注册
        this.registry.registerBeanDefinition(beanName, this.createBeanDefinition(beanName, type));
    }
   
    private BeanDefinition createBeanDefinition(String beanName, Class<?> type) {
        if (BindMethod.forType(type) == BindMethod.VALUE_OBJECT) {
            return new ConfigurationPropertiesValueObjectBeanDefinition(this.beanFactory, beanName, type);
        } else {
            // 可以看到使用 常规的 BeanDefinition 包装的
            GenericBeanDefinition definition = new GenericBeanDefinition();
            definition.setBeanClass(type);
            return definition;
        }
    }
}

2.5  ScannedGenericBeanDefinition 、AnnotatedGenericBeanDefinition 、ConfigurationClassBeanDefinition 

这三个就比较牛逼了,它们不仅继承了常规,还实现了注解的接口,都是常规以及注解的结合体。

那我们刚看的 Controller、Service、Mapper什么的都包装成了 ScannedGenericBeanDefinition ,@Configuration以及一些自动装配类都包装成了 @AnnotatedGenericBeanDefinition ,@Configuration 里的@Bean 都包装成了 ConfigurationClassBeanDefinition 。

他们是怎么注入的,这就要看一个比较重要的大杀器类 ConfigurationClassPostProcessor,这个家伙着实牛逼。如下图:

首先扫描出基础包下以及 SPI 自动配置类下的很多类逐步分析加载他们的 BeanDefinition。

这里主要看看我们 Controller、Service的过程,源码就不带着大家一步一步看了哈,我这里画了个图来看下:

2.6  RootBeanDefinition、ChildBeanDefinition

这两个目前用的比较少了,我这里拿这两个举个例子,体会一下,仅举例,GenericBeanDefinition 同样也可以的。

public static void main(String[] args) {
    ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
    DefaultListableBeanFactory beanFactory = ((DefaultListableBeanFactory) applicationContext.getBeanFactory());
    // 注册一个 root 的  name=root code=root
    RootBeanDefinition beanDefinition = new RootBeanDefinition();
    beanDefinition.setBeanClass(AccountPo.class);
    beanDefinition.getPropertyValues().add("name", "root");
    beanDefinition.getPropertyValues().add("code", "root");
    beanFactory.registerBeanDefinition("person", beanDefinition);
    // 注册一个儿子的 继承 person root型的 形成父子关系
    ChildBeanDefinition childBeanDefinition = new ChildBeanDefinition("person");
    childBeanDefinition.setBeanClass(AccountPo.class);
    // 设置自己个性的 name=child
    childBeanDefinition.getPropertyValues().add("name", "child");
    beanFactory.registerBeanDefinition("child", childBeanDefinition);
    AccountPo accountPo = beanFactory.getBean("child", AccountPo.class);
    // 打印下儿子
    System.out.println(accountPo);
}

继承了父亲的属性还能个性化自己的。

3  问题

3.1  我们的启动类包装成了什么 BeanDefinition?

这个它什么注入的 BeanDefinition,我有点忘了,我就在这里打了个断点:

通过 AnnotatedBeanDefinitionReader 注解的 Reader 注入进来的,可以看到包装成了 AnnotatedGenericBeanDefinition:

那么也可以看到通过 AnnotatedBeanDefinitionReader 注入的 Bean 基本都是 AnnotatedGenericBeanDefinition 类型的。

3.2  getMergedLocalBeanDefinition

回到我最初的疑惑,就是 doGetBean 的时候,有一个 getMergedLocalBeanDefinition 的操作,有一个合并的操作,他会把一些 BeanDefinition 用 RootBeanDefinition 包装一下:

我们自己定义的一些Bean 都被包装了一层,这个具体是为什么?我百度了一下说是为了兼容以前老版本的 RootBeanDefinition 什么的。这块就不太清楚了哈,有知道的还望告知哈。

4  小结

好啦,关于 BeanDefinition 我就看到这里哈,有理解不对的地方欢迎指正哈。

标签:String,Nullable,Spring,void,Bean,深入,type,BeanDefinition
From: https://www.cnblogs.com/kukuxjx/p/18060839

相关文章

  • 基于vue+springboot高校宿舍管理系统
    本项目是一款基于springBoot的高校宿舍管理系统。本系统主要功能包含:(1)基本信息管理基本信息分为学生信息和宿舍信息两部分,其功能是负责维护这些信息,对它们进行增删查改等操作。宿舍分配管理(2)根据给定的宿舍信息与学生信息,按照一定的规则自动地给还未分配宿舍的学生分配宿舍,......
  • SpringBoot3.x使用Druid
    SpringBoot3.x使用DruidBiliBili视频官网:druid/druid-spring-boot-starteratmaster·alibaba/druid参考文章SpringBoot3集成Druid连接池详解起步安装最新版本查询:https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-3-starter注意:Druid配......
  • VSCode 配置 Spring Boot 项目开发环境
    神器IDEA在升级到2023之后越发卡顿,EDU邮箱也不能用了,照现在这个JDK版本的升级速度,神器不升级也不行,需要开始物色替代品.其它IDE我用得少,VSCode还是比较熟悉的,可以作为备选项.两三年前曾经试过配置Java环境,存在不少问题作罢.最近搜了下相关的文章,感觉VSCode对Ja......
  • java springboot拦截器的实现及用法
     1.前景,有时候我们在不同的地方需要使用用户的信息,我们可以使用threadLocal存储信息,这样我们在在这个线程随时使用用户信息了,不用自己在写一段冗余代码了,这时候使用拦截器就很不错 2.实现1.实现HandlerInterceptor2.重写实现方法  preHandle:在业务处理器处理请......
  • springboot3开发文档
    版本号jdk17springboot3mysql8.0mavenidea自带springboot依赖注入pom.xml命名规范:官方提供的场景:命名为:spring-boot-starter-*第三方提供场景:命名为:*-spring-boot-starter父工程坐标对各种常用依赖的版本进行了管理,我们的项目需要以这个项目为父工程,这样我们就......
  • spring-security源码-FilterChainProxy
    FilterChainProxy内部存储了我们各个HttpSecurty生成的SecurityFilterChain。FilterChainProxy实现了ServletFilter接口。只真正的入口org.springframework.security.web.FilterChainProxy.doFilterpublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,F......
  • spring-security源码-如何初始化SecurityFilterChain到Servlet
    1.SecurityFilterChain是由HttpSecurty根据各个config配置生成的FilterSecurityFilterChain是接口,默认实现是由DefaultSecurityFilterChainSecurityFilterChain只充当描述的作用,描述哪些url走这批filterpublicfinalclassDefaultSecurityFilterChainimplementsSecurityF......
  • SpringMVC声明式事务
    事务并发、传播性、隔离级别(重难点)导读:本节重点在于多线程并发环境下的事务处理、和数据库在并发环境下的表锁和行锁。案例:在新增图书的时候,肯定需要先新增作者。SpringMVC声明式事务事务分两种:编程式事务、声明式事务Connectionconnconn.setAutoCommit(false)conn.comm......
  • spring-boot spring-security oauth2 /oauth/token报401,403 问题
    2024-03-1012:20:55.281INFO58776---[nio-8002-exec-2]o.s.web.servlet.DispatcherServlet:InitializingServlet'dispatcherServlet'2024-03-1012:20:55.283INFO58776---[nio-8002-exec-2]o.s.web.servlet.DispatcherServlet:Completedi......
  • spring-security源码阅读-总结(二十六)
    spring-security很重?身边一提到spring-security,都觉得很重,宁愿自己写个filter快速实现认证,确实如此吗,spring-security本质也是基于servlet-filter作为切入点。作为框架,把正常验证流程差异化的地方都封装抽象出来了。我们只需要根据他的每个差异化的地方完成我们自己的配置就行......