设计模式终章----手写IOC容器

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式终章----手写IOC容器相关的知识,希望对你有一定的参考价值。


Spring核心架构

Spring大约有20个模块,由1300多个不同的文件构成

这些模块可以分为:

核心容器,AOP和设备支持,数据访问和集成,Web组件,通信报文和集成测试,下面是Spring框架的总体架构图:

核心容器由beans,core,context和expression(Spring Expression Language,SPEL)4个模块组成

要点一:

  • spring-beans和spring-core模块是Spring框架的核心模块,包含了控制反转(IOC)和依赖注入(DI).
  • BeanFactory使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码进行了分离。
  • BeanFactory属于延时加载,也就是说在实例化容器对象后并不会自动实例化Bean,只有当Bean被使用时,BeanFatory才会对该Bean进行实例化与依赖关系的装配.

要点二:

  • spring-context模块架构与核心模块之上,扩展了BeanFactory,为它添加了Bean生命周期控制,框架事件体系及资源加载透明化等功能。
  • 此外,此模块还提供了许多企业支持,如邮件访问,远程访问,任务调度。
  • ApplicationContext是该模块的核心接口,它的超类是BeanFactory.

要点三;

  • spring-context-support模块是对Spring IOC容器及IOC子容器的扩展支持

要点四:

  • spring-context-indexer模块是Spring的类管理组件和Classpath扫描组件

要点五:

  • spring-expression模块是统一表达式语言EL的扩展模块,可以查询,管理运行中的对象,同时也可以方便地调用对象方法,以及操作数组,集合等。
  • 它的语法类似于传统EL,但提供了额外的功能,最出色的要数函数调用和简单的字符串模板函数。
  • EL的特性是基于Spring产品的需求而设计的,可以非常方便地同Spring IOC进行交互

Bean概述


Spring IOC相关接口分析

BeanFactory接口


这三个接口共同定义了Bean的集合,Bean之间的关系及Bean行为。

最基本的IOC容器接口是BeanFactory,来看一下它的源码

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
     //根据Bean的名称,获取IOC容器中的Bean对象 
    Object getBean(String var1) throws BeansException;
   //根据Bean的名称,获取IOC容器中的Bean对象,并指定获取到的Bean对象的类型,这样我们使用时,就不需要进行强制类型转换
    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    <T> T getBean(Class<T> var1, Object... var2) throws BeansException;

    <T> ObjectProvider<T> getBeanProvider(Class<T> var1);

    <T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
   //判断容器中是否包含指定名称的Bean
    boolean containsBean(String var1);
   //根据Bean的名称判断是否是单例
    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
    //是否是多实例Bean 
    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
  
    boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    @Nullable
    Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

在BeanFactory里只对IOC容器的基本行为做了定义,根本不关心你的Bean是如何定义及加载的。

正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。

BeanFactory有一个很重要的子接口,就是ApplicationContext接口,该接口主要来规范容器中的bean对象是非延时加载的,即在创建容器对象的时候就对Bean进行初始化,并存储到一个容器中


要知道工厂是如何产生对象的,我们需要看具体的IOC容器实现,Spring提供了许多IOC容器实现,比如:

  • ClasspathXmlApplicationContext :根据类路径加载xml配置文件,并创建IOC容器对象
  • FileSystemXmlApplicationContext:根据系统路径加载xml配置文件,并创建IOC容器对象
  • AnnotationConfigApplicationContext:加载注解类配置,并创建IOC容器

BeanDefinition接口

Spring IOC容器管理我们定义的各种Bean对象及其相互关系,而Bean对象在Spring实现中是以BeanDefinition来描述的,如下面的配置文件

<bean id="userDao" class="com.dao.impl.UserDaoImpl"></bean>

bean标签还有很多属性: scope,init-method,destory-method等


BeanDefinitionReader接口



BeanDefinitionReader接口定义的功能:

public interface BeanDefinitionReader {
//获取BeanDefinitionRegistry 注册器对象
    BeanDefinitionRegistry getRegistry();

    @Nullable
    ResourceLoader getResourceLoader();

    @Nullable
    ClassLoader getBeanClassLoader();

    BeanNameGenerator getBeanNameGenerator();

//下面的loadBeanDefinitions都是从指定的资源中加载bean定义信息
    int loadBeanDefinitions(Resource var1) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(Resource... var1) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(String var1) throws BeanDefinitionStoreException;

    int loadBeanDefinitions(String... var1) throws BeanDefinitionStoreException;
}

BeanDefinitionRegistry 接口

BeanDefinitionReader用来解析bean定义,并封装BeanDefinition对象,而我们定义的配置文件中定义了很多Bean标签,所以就有一个问题,解析的BeanDefinition对象存储到哪儿?

答案就是BeanDefinition的注册中心,而该注册中心顶层接口就是BeanDefinitionRegistry

public interface BeanDefinitionRegistry extends AliasRegistry {
//往注册表中注册bean,即bean定义加载后被封装成的BeanDefinition对象
    void registerBeanDefinition(String var1, BeanDefinition var2) throws BeanDefinitionStoreException;
//从注册表删除指定名称的bean
    void removeBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
//获取注册表中指定名称的Bean
    BeanDefinition getBeanDefinition(String var1) throws NoSuchBeanDefinitionException;
//判断注册表中是否已经注册了指定名称的bean,即BeanDefinition对象
    boolean containsBeanDefinition(String var1);
//获取注册表中所有bean的名称
    String[] getBeanDefinitionNames();
//获取注册表中注册的bean的个数
    int getBeanDefinitionCount();
//判断当前的bean名称在注册表中是否已经在使用了
    boolean isBeanNameInUse(String var1);
}


SimpleBeanDefinitionRegistry—简单的bean注册中心

public class SimpleBeanDefinitionRegistry extends SimpleAliasRegistry implements BeanDefinitionRegistry {
//重点是这个beanDefinitionMap ---作为容器存放beanDefinition对象
//将beanDefinition对象放入map集合的过程就称为注册bean
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(64);


    public SimpleBeanDefinitionRegistry() {
    }

//下面都是重写父类的方法
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
        Assert.hasText(beanName, "'beanName' must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
....
}

DefaultListableBeanFactory探究

该类中也有一个属性是用来注册bean的

    private final Map<String, BeanDefinition> beanDefinitionMap;

创建容器

ClassPathXmlApplicationContext对Bean配置资源的载入是从refresh()方法开始的。

refresh()方法是一个模板方法,规定了IOC容器的启动流程,有些逻辑要交给器其子类实现。

他对Bean配置资源进行载入,ClassPathXmlApplicationContext通过调用父类AbstractApplicationContext的refresh()方法启动整个IOC容器对Bean定义的载入过程.


手写SpringIOC容器

现在要对下面的配置文件进行解析,并自定义Spring框架的IOC对涉及到的对象进行管理

<?xml version="1.0" encoding="UTF-8"?>

<beans>

<bean id="userService" class="com.pojo.UserService">

<property name="userDao" ref="userDao"></property>

<bean id="userDao" class="com.pojo.UserDao"></bean>

</beans>

定义Bean相关的Pojo类

PropertyValue类

用于封装bean的属性,体现到上面就是封装bean标签的子标签的property标签数据

每个PropertyValue实例对象,封装一条property标签里面的数据

//用来封装bean标签下的property标签下的属性
//name属性,ref属性:给引用类型赋值,value属性:给基本数据类型及String类型属性赋值
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PropertyValue
{
    private  String name;
    private String ref;
    private String value;
}

MultablePropertyValues类

一个bean标签可以有多个property子标签,所以再定义一个MultablePropertyValues类,用来存储并管理多个PropertyValue对象

//用来存储和管理多个PropertyValue对象
public class MultablePropertyValues implements
        Iterable<PropertyValue> {
//定义list集合对象,用来存储propertyvlaue对象
    private  final List<PropertyValue> propertyValueList;
    //用final修饰的变量只能赋值一次,并且必须在构造方法结束前进行赋值
    public MultablePropertyValues()
       {
           propertyValueList=new ArrayList<>();
       }
    public MultablePropertyValues(List<PropertyValue> valueList)
    {
        if(valueList==null)
        {
         propertyValueList=new ArrayList<>();
        }
        else
        {
            propertyValueList=new ArrayList<>();
        }
    }
    //获取所有PropertyValue对象,以数组形式返回
    public PropertyValue[] getPropertyValues()
    {
        //将集合转换为数组并返回即可
        //指定返回数组的类型
        return propertyValueList.toArray(new PropertyValue[0]);
    }
    //根据name属性值获取PropertyValue对象
    public PropertyValue getPropertyValue(String propertyName)
    {
        //遍历集合
        for (PropertyValue propertyValue : propertyValueList) {
            if(propertyValue.getName().equals(propertyName))
            {
             return propertyValue;
            }
        }
        return null;
    }
    //判断集合是否为空
    public boolean isEmpty()
    {
        return propertyValueList.isEmpty();
    }
    //添加PropertyValue对象
    public MultablePropertyValues addPropertyValue(PropertyValue pv)
    {
        //判断传递进来的PropertyValue对象,是否和集合中已有的重复了,如果重复了,就记性覆盖操作
        for(int i=0;i<propertyValueList.size();i++)
        {
            PropertyValue propertyValue = propertyValueList.get(i);
             if(propertyValue.getName().equals(pv.getName()))
             {
                 //进行覆盖操作
                 propertyValueList.set(i,propertyValue);
                 return this;//实现链式编程
             }
        }
        //没有就直接添加
        propertyValueList.add(pv);
        return this;
    }
    //判断是否有指定name属性值的对象
    public boolean contains(String propertyName)
    {
       return getPropertyValue(propertyName)!=null;
    }
    //获取迭代器对象
    @Override
    public Iterator<PropertyValue> iterator()
    {
        //调用list集合里面获取迭代器的方法
        return propertyValueList.iterator();
    }
}

BeanDenfinition类

BeanDenfinition用来封装bean信息的,主要包含id(即对象的名称),class(需要交由spring管理类的全类名)及子标签property数据

//用来封装bean标签数据
//id属性,class属性,property子标签数据
@Data
public class BeanDefinition
{
       private String id;
       private String className;
       private  MultablePropertyValues propertyValues;
       //利用无参构造对PropertyValues里面的list集合进行初始化
   public BeanDefinition()
    {
        propertyValues=new MultablePropertyValues();
    }
}

定义注册表相关类

BeanDefinitionRegistry接口

BeanDefinitionRegistry接口定义了注册表相关操作,定义了如下功能:

  • 注册BeanDefinition到注册表中
  • 从注册表中删除指定名称的BeanDefinition对象
  • 根据名称从注册表中获取BeanDefinition对象
  • 判断注册表中是否含有指定名称的BeanDefinition对象
  • 获取注册表中BeanDefinition对象的个数
  • 获取注册表中所有BeanDefinition对象的名称
public interface BeanDefinitionRegistry
{
    //往注册表中注册bean,即bean定义加载后被封装成的BeanDefinition对象
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
    //从注册表删除指定名称的bean
    void removeBeanDefinition(String beanName);
    //获取注册表中指定名称的Bean
    BeanDefinition getBeanDefinition(String beanName);
    //判断注册表中是否已经注册了指定名称的bean,即BeanDefinition对象
    boolean containsBeanDefinition(String beanName);
    //获取注册表中所有bean的名称
    String[] getBeanDefinitionNames();
    //获取注册表中注册的bean的个数
    int getBeanDefinitionCount();
    //判断当前的bean名称在注册表中是否已经在使用了
    boolean isBeanNameInUse(String beanName);
}

SimpleBeanDefinitionRegistry–注册表接口的子实现类

public class SimpleBeanDefinitionRegistry implements  BeanDefinitionRegistry{

    //定义一个容器,用来存储BeanDefinition对象
    private Map<String,BeanDefinition> beanDefinitionMap=new HashMap<>();

    //往注册表中注册bean,即bean定义加载后被封装成的BeanDefinition对象
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName,beanDefinition);
    }

    //从注册表删除指定名称的bean
    @Override
    public void removeBeanDefinition(String beanName) {
beanDefinitionMap.remove(beanName);
    }

    //获取注册表中指定名称的Bean
    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return beanDefinitionMap.get(beanName);
    }

    //判断注册表中是否已经注册了指定名称的bean,即BeanDefinition对象
    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitionMap.containsKey(beanName);
    }

    //获取注册表中所有bean的名称
    @Override
    public String[] getBeanDefinitionNames()
    {
        //获取key的set集合,再将set集合,转换为数组,数组的类型为string
        return beanDefinitionMap.keySet().toArray(new String[0]);
    }

    //获取注册表中注册的bean的个数
    @Override
    public int getBeanDefinitionCount() {
        return beanDefinitionMap.size();
    }

    //判断当前的bean名称在注册表中是否已经在使用了
    @Override
    public boolean isBeanNameInUse(String beanName) {
        return beanDefinitionMap.containsKey(beanName);
    }
}

定义解析器相关类

BeanDefinitionReader接口

BeanDefinitionReader是用来解析配置文件并在注册表中注册bean信息,定义了两个规范:

  • 获取注册表的功能,让外界可以通过该对象获取注册表对象
  • 加载配置文件,并注册bean数据
//用来解析配置文件的,该接口只是定义了规范
public以上是关于设计模式终章----手写IOC容器的主要内容,如果未能解决你的问题,请参考以下文章

手写Spring的IOC容器和DI依赖注入

Spring IOC :相关接口分析手写简易 Spring IOC

Spring IOC :相关接口分析手写简易 Spring IOC

Spring IOC :相关接口分析手写简易 Spring IOC

30个类手写Spring核心原理之Ioc顶层架构设计

spring