• Spring底层架构核心概念


    BeanDefinition

    其实底层是个接口

    BeanDefinition表示Bean定义,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如:

    • class,表示Bean类型(其实叫beanClass)
    • scope,表示Bean作用域,单例或原型等
    • lazyInit:表示Bean是否是懒加载
    • initMethodName:表示Bean初始化时要执行的方法
    • destroyMethodName:表示Bean销毁时要执行的方法
    • 还有很多...

    在Spring中,我们经常会通过以下几种方式来定义Bean:

    1. @Bean
    2. @Component(@Service,@Controller)

    这些,我们可以称之声明式定义Bean

    如果底层用的xml,spring也会解析成一个beanDefinition对象

    bean注解解析也会生成beanDefinition

    这里加了Bean注解直接调用这个方法对吗?

    不太对,

    首先会生成BeanDefinition对象,然后放到BeanDefinitionMap里面,再从map里面取出非懒加载的单例bean再拿出来,才会调用这个方法。

    (Spring提供定义bean的方式)

    我们还可以编程式定义Bean,那就是直接通过BeanDefinition,比如:

    也就是说这个编程式定义Bean可以替代@Bean注解的一种写法。

    (但是平常不会这么干,spring底层是这么干的)

    1. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    2. // 生成一个BeanDefinition对象,并设置beanClass为User.class,并注册到ApplicationContext中
    3. AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
    4. beanDefinition.setBeanClass(User.class);
    5. // 还能指定作用域
    6. beanDefinition.setScope("prototype");
    7. // 指定beanName
    8. context.registerBeanDefinition("user", beanDefinition);
    9. System.out.println(context.getBean("user"));

    我们还可以通过BeanDefinition设置一个Bean的其他属性

    1. beanDefinition.setScope("prototype"); // 设置作用域
    2. beanDefinition.setInitMethodName("init"); // 设置初始化方法
    3. beanDefinition.setLazyInit(true); // 设置懒加载

    和申明式事务、编程式事务类似,通过,@Bean,@Component等申明式方式所定义的Bean,最终都会被Spring解析为对应的BeanDefinition对象,并放入Spring容器中。

    被注解的BeanDefinitionReader(BeanDefinition的一个读取器 spring提供的一个工具类)

    AnnotatedBeanDefinitionReader

    可以直接把某个类转换为BeanDefinition,并且会解析该类上的注解,比如

    1. // 先构造一个spring的容器
    2. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    3. // 读到哪里去 肯定读到容器 所以要传参一个context
    4. AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(context);
    5. // 利用了BeanDefinition的一个读取器去注册了一个User
    6. // 将User.class解析为BeanDefinition 也就是最终这个User就会成为BeanDefinition,放在spring容器里
    7. annotatedBeanDefinitionReader.register(User.class);
    8. System.out.println(context.getBean("user"));

    register底层源码:这里只传了一个类User.class

    构造了一个BeanDefinition,类就是传进来的类

    然后还会去判断当前类上有没有@Lazy注解之类的,然后设置到刚才的BeanDefinition中

    注意:它能解析的注解是:@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description

    User也没加注解

    运行正常,说明我们的User是一个bean,报错就说明不是bean

    比如AnnotationConfigApplicationContext底层就调用到了这个

    被注解的BeanDefinitionReader(BeanDefinition的一个读取器 spring提供的一个工具类)

    这个register底层其实就是调用的这个BeanDefinition的一个读取器

    小总结:

    他就是BeanDefinition的一个读取器 ,传给他一个类,他就能解析类上面的注解,然后变成一个BeanDefinition,然后放到spring的容器中,然后就成为了一个bean。

    XmlBeanDefinitionReader

    可以解析xml文件的,解析标签

    1. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    2. XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(context);
    3. int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");
    4. System.out.println(context.getBean("user"));

    这里就不会报错,能拿到bean,底层解析xml

    ClassPathBeanDefinitionScanner

    ClassPathBeanDefinitionScanner是扫描器,但是它的作用和BeanDefinitionReader类似,它可以进行扫描,扫描某个包路径,对扫描到的类进行解析,比如,扫描到的类上如果存在@Component注解,那么就会把这个类解析为一个BeanDefinition,比如:

    1. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    2. context.refresh();
    3. ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(context);
    4. // 定义扫描路径 扫描得到的都是BeanDefinition 然后放到spring容器
    5. scanner.scan("com.zhouyu");
    6. System.out.println(context.getBean("userService"));
    例子:

    在UserService上加了@Component注解

    问:能拿到UserService这个bean吗?

    答:不行 因为这里构造了空的spring容器,没有传配置文件进去,而且没刷新

    加个代码context.refresh();(强制要求,不过就算加了也没丝毫作用)

    这里定义了扫描器,然后把容器传进扫描器里。然后去路径下扫描,发现有@Component注解,然后就生成BeanDefinition,接着会加入到spring容器里,这样就能拿到UserService的bean了

    我们的spring容器 也就是这个context,既可以注册某一个类成为bean

    也可以自己触发去扫描

    用构造器的底层代码

    底层先调用空的构造方法

    再把传进来的类进行注册

    通过AnnotatedBeanDefinitionReader进行注册 注册成为一个BeanDefinition

    再去刷新

    也可以这么操作实现一样的效果

  • 相关阅读:
    MybatisPlus的基础使用
    [附源码]Python计算机毕业设计SSM家纺商品展示平台(程序+LW)
    kali的安装与配置
    go语言基本操作--四
    Java 基础知识面试题总结
    ESP32网络开发实例-TCP服务器数据传输
    【容器网络】跨主通信网络实现方法之host-gw实现原理
    C语言练习题-大数乘法、分解质因数
    面试官随便问几个问题就知道你究竟做没做过微信支付宝支付
    【HCIE】13.VXLAN EVPN
  • 原文地址:https://blog.csdn.net/u014474768/article/details/136612665