手写Spring-IOC

161 阅读6分钟

小傅哥手撸spring-ioc部分学习 bugstack.cn/

创建简单的bean容器

什么是容器

官方文档是这么定义的

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans

Spring IOC容器就是一个org.springframework.context.ApplicationContext的实例化对象,容器负责了实例化,配置以及装配一个bean。

小傅哥在设计中说到凡是可以存放数据的具体数据结构实现,都可以称之为容器。例如:ArrayList、LinkedList、HashSet 等,Spring这里适合HashMap。

简单的spring 容器功能

container-magic.png

最简单的Spring Bean容器实现,需要Bean的定义、注册、获取三个基本步骤;

定义:BeanDefinition(bean__BeanDefinition)目前没有config,只存放Object的bean信息

注册:hashmap存放bean的信息(BeanDefinition放入容器)

获取:通过key获取bean(BeanDefinition__Bean)

类图

small-spring-spring01.drawio.png

职责:

​ BenDefinition:pojo的bean抽象,目前只有Object的属性

​ BeanFactory: bean的容器

为什么要不直接注册pojo,而使用BenDefinition

spring封装管理你所创建的对象,封装后的对象叫bean,bean扩展了你的对象,你所创建的对象的所有属性和方法是bean中的一个子集。spring所有的功能都是围绕这个bean展开的,这个bean在spring中具体的名字就是BeanDefinition。 Spring 中通常以这两种方式定义一个 Bean:面向资源(XML、Properties)面向注解

BeanDefinition 主要包含一下信息:

  1. Bean的类名(class 属性)
  2. Bean行为配置类,如作用域、自动绑定模式、生命周期回调等
  3. 其他Bean引用
  4. 配置设置,比如Bean属性

流程图

small-spring-spring-01-图.drawio.png

实现 Bean 的定义、注册、获取

功能描述

定义

1.BeanDefinition 属性Object 改成Class

注册

1.把 BeanDefinition 注册到容器中

获取

1.考虑单例对象二次从缓存获取

2.获取失败返回异常

3.class的实例化(需要获取BeanDefinition)

模板模式

定义

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

封装不变部分,扩展可变部分,更好的复用和拓展。

​ AbstractBeanFactory定义getBean的流程,定义一些钩子函数,就是上面所说的算法的骨架。

​ 子类DefaultListableBeanFactory 负责获取BeanDefinition,实现抽象方法getBeanDefinition

​ 子类AbstractAutowireCapableBeanFactory负责实例化bean,实现抽象方法createBean

类图

small-spring-spring-02-bean容器.drawio.png

职责划分

BeanDefinition的注册

​ 接口定义BeanDefinitionRegistry、BeanDefinition

Bean的获取

​ BeanFactory

单例的获取与注册

​ SingletonBeanRegistry 和 DefaultSingletonBeanRegistry

模板模式定义获取bean的流程

​ AbstractBeanFactory

负责class的实例化

​ AbstractAutowireCapableBeanFactory(使用反射)

注册BeanDefinition与获取BeanDefinition的实现

​ DefaultListableBeanFactory

getBean流程中的异常

​ 实例化class 和getBeanDefinition()会报异常 BeansException

流程图

small-spring-02-流程图.drawio.png

对象实例化策略

功能描述

Spring支持以下三种方法实例化Bean

  1. 构造方法
  2. 通过静态工厂方法
  3. 通过实例工厂方法

本节内容实现构造方法,无参和有参(有参的筛选构造器的逻辑有待完善)

实例化方法

@Slf4j
public class ApiTest {
    
        @Test
    public void test_newInstance() throws IllegalAccessException, 
    InstantiationException {
        Class<User> aClass = User.class;
        //实例化默认构造方法,User必须无参构造函数,否则将抛异常
        //newInstance方法就是大家所说的反射。事实上Class的newInstance方法内部调用Constructor的newInstance方法。
        User user = aClass.newInstance();
        log.info("User newInstance:{}", user);
    }
   
    
    @Test
    public void test_constructor() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<User> aClass = User.class;

        // 取得指定带int和String参数构造函数,该方法是私有构造private
        Constructor<User> declaredConstructor = aClass.getDeclaredConstructor(int.class, String.class);
        //由于是private必须设置可访问
        declaredConstructor.setAccessible(true);

        User user = declaredConstructor.newInstance(11, "黑猫");
        log.info("User DeclaredConstructor:{}", user);

        //获取带String参数的public构造函数
        Constructor<User> constructor = aClass.getConstructor(String.class);
        User user2 = constructor.newInstance("黑猫2");
        log.info("User Constructor:{}", user2);

    }    
    
    
  //在 Spring 内部有两个字节码提升的框架,ASM(过于底层,直接操作字节码)和 CGLIB(相对于前者更加简便)
    @Test
    public void test_cglib(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(User.class);
        enhancer.setCallback(new NoOp() {
            @Override
            public int hashCode() {
                return super.hashCode();
            }
        });
        Object obj = enhancer.create(new Class[]{String.class}, new Object[]{"黑猫"});
        log.info("cglib:{}",obj);
    }

}


public class User {

    private int age;
    private String name;

    public User() {
        super();
    }

    public User(String name) {
        super();
        this.name = name;
    }

    /**
     * 私有构造
     *
     * @param age
     * @param name
     */
    private User(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

类图

small-spring-spring-03-实例化策略.drawio.png

流程图

small-spring-03-流程图.drawio.png

注入属性和依赖对象

功能描述

依赖注入,类中的属性填充,包括基础属性和对象属性

根据官网介绍,依赖注入主要分为两种方式

  1. 构造函数注入
  2. Setter方法注入,通过反射调用set方法

类图

small-spring-spring-04-属性填充.drawio.png

BeanReference:bean的引用

PropertyValues:属性集合

PropertyValue:属性

流程图

small-spring-04-流程图.drawio.png

资源加载器解析文件并注册

功能描述

Spring 中通常以这两种方式定义一个 Bean:面向资源(XML、Properties)面向注解。本章把bean的定义放在Xml里面,解析完xml变成BeanDefinition,并且注册到BeanFactory

类图

small-spring-spring-05-资源加载解析.drawio.png

流程图

small-spring-05-流程图.drawio.png

应用上下文

功能描述

对BeanFactory的功能拓展

ApplicationContext,整合DefaultListableBeanFactoryXmlBeanDefitionReader,拓展BeanFactory的功能

对Bean的拓展

BeanFactoryPostProcessor

在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制。在Context的refresh中执行BeanFactoryPostProcessor的接口

BeanPostProcessor

BeanPostProcessor 是在 Bean对象实例化之后修改Bean对象,也可以替换 Bean对象。

在Context的refresh中注册BeanPostProcessor的接口,放入BeanFactory,在bean初始化方法中调用。

类图

small-spring-spring-06-上下文.drawio.png

流程图

small-spring-06-流程图.drawio.png

初始化和销毁方法

功能描述

本章节实现bean初始化和销毁回调的前2种实现

Bean初始化回调

  1. 实现InitializingBean接口
  2. 使用Bean标签中的init-method属性
  3. 使用@PostConstruct注解

bean的销毁回调

  1. 实现DisposableBean接口
  2. 使用Bean标签中的destroy-method属性
  3. 使用@PreDestroy注解

类图

small-spring-07-流程图.drawio.png

流程图

small-spring-07-流程图.drawio.png

Aware 感知容器对象

功能描述

所有的Aware接口都是为了能让我们拿到容器中相关的资源

比如BeanNameAware,可以让我们拿到Bean的名称,ApplicationContextAware 可以让我们拿到整个容器,

BeanFactoryAware,可以让我们拿到BeanFactory,BeanClassLoaderAware 可以让我们拿到容器的类加载器。

类图

small-spring-spring-08.drawio.png

流程图

small-spring-08-流程图.drawio.png

对象作用域和 FactoryBean

功能描述

对象作用域:单例和原型;一个单例的bean意味着,这个bean只会容器创建一次,一个原型的bean意味着,每次我们使用时都会重新创建这个bean。

FactoryBean是容器的拓展点,主要用来定制化Bean的创建逻辑,当我们实例化一个Bean的逻辑很复杂的时候,使用FactoryBean是很必要的,这样可以规避我们去使用冗长的XML配置。有些三方包可以用这个方法注入spring,@Bean也是基于FactoryBean的。

类图

small-spring-spring-09.drawio.png

流程图

small-spring-09-流程图.drawio.png

容器事件和事件监听器

功能描述

Spring中的事件监听机制的简单实现

类图

small-spring-spring-10.drawio.png

流程图

small-spring-10-流程图.drawio.png