Spring源码探究2.0

Spring IOC 探究

概述

BeanFactory 接口,典型功能有:

  • getBean

到底什么是 BeanFactory

  • 它是 ApplicationContext 的父接口
  • 它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能,【组合】是指 ApplicationContext 的一个重要成员变量就是 BeanFactory

一个key value

beanFactory内存储着bean对象

BeanFactory 能干点啥

  • 表面上只有 getBean
  • 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能,都由它的实现类提供
  • 例子中通过反射查看了它的成员变量 singletonObjects,内部包含了所有的单例 bean
java 复制代码
ublic class A01 {
    private static final Logger log = LoggerFactory.getLogger(A01.class);
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException {
        ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);    
        /*
            2. BeanFactory 能干点啥
                - 表面上只有 getBean
                - 实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供
                BeanFactory (接口)      ← 定义 getBean 等
    ↑
ListableBeanFactory (接口)   ← 定义 getBeansOfType 等
    ↑
ConfigurableBeanFactory (接口)  ← 提供注册作用域等功能
    ↑
HierarchicalBeanFactory (接口) ← 父子容器
    ↑
DefaultListableBeanFactory (实现类)
    ↑
    ├── 继承 DefaultSingletonBeanRegistry (管理单例缓存)
    ├── 继承 AbstractAutowireCapableBeanFactory (负责创建 Bean 实例)
    └── 实现上述接口们

         */
        Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
 ConfigurableApplicationContext (接口)
└── AbstractApplicationContext
└── GenericApplicationContext
└── DefaultListableBeanFactory
└── AbstractAutowireCapableBeanFactory
└── AbstractBeanFactory
└── DefaultSingletonBeanRegistry ← ← ← 你要的类!   private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
        
        
        
        singletonObjects.setAccessible(true);
        //獲取私有字段并且允許訪問
        
        //獲取容器内的所有bean 同時過濾掉被component修飾的bean
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
        map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
                .forEach(e -> {
                    System.out.println(e.getKey() + "=" + e.getValue());
                });

SpringApplication.run(...) 是 Spring Boot 中的启动方法:

  • 它会启动一个 Spring 应用上下文(ApplicationContext);
  • 会进行 Spring 的自动配置(根据 classpath 中的依赖自动加载配置);
  • 会扫描并加载 Spring Bean;
  • 会初始化各种自动装配的组件、运行 @ComponentScan 等;
  • 最后会启动一个 Tomcat Web 服务器(如果是 Web 项目)

容器是 BeanFactory,但单例Bean的缓存是在 DefaultSingletonBeanRegistry 中管理的。

也就是说,BeanFactory 是接口,DefaultListableBeanFactory 是实现类,而它内部继承了 DefaultSingletonBeanRegistryDefaultSingletonBeanRegistry 才真正维护了单例Bean的缓存。

graph 复制代码
一个受 Spring 管理的 bean,生命周期主要阶段有

1. 创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象
2. 依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系
3. 初始化:回调各种 Aware 接口,调用对象的各种初始化方法
4. 销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)
   * prototype 对象也能够销毁,不过需要容器这边主动调用

我们要实现的就是对应的后置处理器的功能

ps:spring源码基本还回去了

自写探究

单一职责原则

前文
层级 组件 核心职责 设计模式 关键说明
容器底层 Map 容器 存储 Bean --- Map<String, Object> / Map<String, BeanDefinition>
工厂层 BeanFactory 创建和获取 Bean 工厂模式 getBean() 的真正执行者
上下文层 ApplicationContext 对外统一入口 门面模式 持有 BeanFactory 引用
解析层 BeanDefinitionReader 解析配置 策略模式 不关心 Bean 如何创建
元数据层 BeanDefinition 描述 Bean 信息 元信息模式 类名、作用域、依赖
配置来源 xml / yml / annotation / properties 定义 Bean 配置策略 多配置形式统一为 BeanDefinition
实例层 Bean 实例 真实对象 反射 Class.newInstance()
代理层 原生 Bean / 代理 Bean 增强功能 代理模式 AOP、事务等
包装层 BeanWrapper 包装 Bean 包装器模式 持有 Bean 引用
缓存层 IOC 容器缓存 复用 Bean 缓存思想 单例池
开写
java 复制代码
public class GPDispatcherServlet extends HttpServlet {
    private GPApplicationContext applicationContext;
    
        @Override
    public void init(ServletConfig config) throws ServletException {

        //初始化Spring核心IoC容器
        applicationContext = new GPApplicationContext(config.getInitParameter("contextConfigLocation"));

        //==============MVC部分==============
        //5、初始化HandlerMapping
        doInitHandlerMapping();

        System.out.println("GP Spring framework is init.");
    }

    }
复制代码
GPApplicationContext {


}

GPApplicationContext 成员变量职责对应表

变量名 类型 在你这套框架中的作用 Spring 中的对应角色 设计含义
reader GPBeanDefinitionReader 读取并解析配置 BeanDefinitionReader 配置解析器
beanDefinitionMap Map<String, GPBeanDefinition> 存储 Bean 元信息 beanDefinitionMap IOC 的"设计图仓库"
factoryBeanInstanceCache Map<String, GPBeanWrapper> 缓存已创建的 BeanWrapper singletonObjects / earlySingletonObjects 一级 IOC 容器(实例缓存)
factoryBeanObjectCache Map<String, Object> 缓存原始 Bean 对象 singletonFactories(简化版) 原始对象缓存

三大核心 Map

Map 存的是什么 何时放 作用
beanDefinitionMap BeanDefinition 容器启动时 描述 Bean,不创建
factoryBeanObjectCache 原生 Bean 实例化后 保存最原始对象
factoryBeanInstanceCache BeanWrapper DI 前 对外提供 Bean

BeanDefinition 管"是什么",Object 管"创建",BeanWrapper 管"使用"。

方法级变量

getBean(String beanName)

populateBean(...)

instantiateBean(...)

复制代码
GPApplicationContext构造探究

思路

gpservlet传来配置,构造负责解析 GPBeanDefinitionReader,返回 GPBeanDefinition 描述对应的bean元信息

GPBeanDefinition 的存在让容器可以:

  • 先收集所有 Bean 的描述

  • 什么时候创建,由容器决定

    package com.gupaoedu.vip.spring.framework.beans.config;

    /**

    • Created by Tom.
      */
      public class GPBeanDefinition {
      private String factoryBeanName;
      private String beanClassName; //Bean 对应的 Java 类全限定名

      public String getFactoryBeanName() {
      return factoryBeanName;
      }

      public void setFactoryBeanName(String factoryBeanName) {
      this.factoryBeanName = factoryBeanName;
      }

      public String getBeanClassName() {
      return beanClassName;
      }

      public void setBeanClassName(String beanClassName) {
      this.beanClassName = beanClassName;
      }
      }

java 复制代码
        //1、加载配置文件
        reader = new GPBeanDefinitionReader(configLocations);

            //2、解析配置文件,封装成BeanDefinition
            List<GPBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
GPBeanDefinitionReader 探究

初始化,扫描文件下的文件,找到需要加载的类。保存进regitryBeanClasses

java 复制代码
public class GPBeanDefinitionReader {

    //保存扫描的结果
    private List<String> regitryBeanClasses = new ArrayList<String>();
    private Properties contextConfig = new Properties();
}
复制代码
public GPBeanDefinitionReader(String... configLocations) {
    doLoadConfig(configLocations[0]);

    //扫描配置文件中的配置的相关的类
    doScanner(contextConfig.getProperty("scanPackage"));
}
java 复制代码
doLoadConfig---
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation.replaceAll("classpath:",""));
try {
    contextConfig.load(is);
} 
loadBeanDefinitions 探究
变量 本质含义 用在什么时候 是否唯一
factoryBeanName Bean 在 IOC 中的名字 / Key getBean()@Autowired 不一定
beanClassName Bean 的 真实类(全限定名) 反射创建实例 唯一
BeanDefintion缓存
java 复制代码
配置解析完成
   ↓
生成 BeanDefinition
   ↓
doRegistBeanDefinition   ←(你现在这一步)
   ↓
getBean()
   ↓
实例化 + DI

一个 BeanDefinition,两个 key --一 个 BeanDefinition 可以被多个 key 指向"。

复制代码
beanDefinitionMap
├── "userService" ----------------┐
├── "com.xxx.UserService" --------┼──> 同一个 BeanDefinition
├── "com.xxx.UserServiceImpl" ----┘
查的词 实际指向
"userService" UserServiceImpl 的配置
"com.xxx.UserService" UserServiceImpl 的配置
"com.xxx.UserServiceImpl" UserServiceImpl 的配置

因为 DI 查找 Bean 有多种方式

@GPAutowired("userService")

@GPAutowired

private UserService userService;

field.getType().getName()

doAutowrited
java 复制代码
   private void doAutowrited() {
        //调用getBean()
        //这一步,所有的Bean并没有真正的实例化,还只是配置阶段
        for (Map.Entry<String,GPBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            getBean(beanName);
        }
    }
java 复制代码
private void doAutowrited() {
    Set<GPBeanDefinition> processed = new HashSet<>();

    for (GPBeanDefinition bd : beanDefinitionMap.values()) {
        if (processed.contains(bd)) continue;
        getBean(bd.getFactoryBeanName());
        processed.add(bd);
    }
}

可以去重,可以不去

查看getbean-

getbean

判断容器内有没有wrapper. 没有的化 用classname 拿到配置信息,反射创建,wrapper包装。然后保存ioc容器

-再执行依赖注入

-这个类的创建需要依赖注入

-再返回对象

复制代码
//4、保存到IoC容器
factoryBeanInstanceCache.put(beanName,beanWrapper);

执行依赖注入
执行依赖注入
java 复制代码
private void populateBean(String beanName, GPBeanDefinition beanDefinition, GPBeanWrapper beanWrapper) {
        //可能涉及到循环依赖?
        //A{ B b}
        //B{ A b}
        //用两个缓存,循环两次
        //1、把第一次读取结果为空的BeanDefinition存到第一个缓存
        //2、等第一次循环之后,第二次循环再检查第一次的缓存,再进行赋值

        Object instance = beanWrapper.getWrapperInstance();

        Class<?> clazz = beanWrapper.getWrappedClass();

        //在Spring中@Component
        if(!(clazz.isAnnotationPresent(GPController.class) || clazz.isAnnotationPresent(GPService.class))){
            return;
        }

        //把所有的包括private/protected/default/public 修饰字段都取出来
        for (Field field : clazz.getDeclaredFields()) {
            if(!field.isAnnotationPresent(GPAutowired.class)){ continue; }

            GPAutowired autowired = field.getAnnotation(GPAutowired.class);

            //如果用户没有自定义的beanName,就默认根据类型注入
            String autowiredBeanName = autowired.value().trim();
            if("".equals(autowiredBeanName)){
                //field.getType().getName() 获取字段的类型
                autowiredBeanName = field.getType().getName();
            }

            //暴力访问
            field.setAccessible(true);

            try {
                if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){
                    continue;
                }
                //ioc.get(beanName) 相当于通过接口的全名拿到接口的实现的实例
                field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
            }
        }

    }

扫描是有顺序的,但 DI 不能依赖扫描顺序;

如果依赖的 Bean 还没创建好,注入会"悄悄失败",字段保持 null

相关推荐
涵涵(互关)2 小时前
添加了 @TableId(type = IdType.AUTO) 但仍生成超大 ID
数据库·spring·mybatis
Star Learning Python2 小时前
30道经典java面试题
java·开发语言
程序员鱼皮2 小时前
你的 IP 归属地,是咋被挖出来的?
前端·后端·计算机·程序员·互联网·编程经验
运维@小兵2 小时前
Spring AI入门
java·人工智能·spring
Geoking.2 小时前
【设计模式】策略模式(Strategy)详解:把 if-else 变成可切换的算法
java·设计模式·策略模式
代码改变生活-1202 小时前
idea 清除缓存之后重启项目编译失败
java·缓存·intellij-idea
fisher_sky2 小时前
流媒体服务mediamtx和FFMpeg工具链联合实验
后端
Microsoft Word2 小时前
HashMap面试题总结
java·开发语言
stillaliveQEJ2 小时前
【MyBatis】DML映射
java·mybatis