手写配置解析

一、为什么要提供配置的方法

经过前面的手写Spring IOC、手写Spring DI、手写Spring AOP,我们知道要创建一个bean对象,需要用户先定义好bean,然后注册到bean工厂才能创建一个bean对象。代码如下:

https://www.cnblogs.com/leeSmall/p/10073931.html

        static PreBuildBeanFactory bf = new PreBuildBeanFactory();
        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(ABean.class);
        List<Object> args = new ArrayList<>();
        args.add("abean01");
        args.add(new BeanReference("cbean"));
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("abean", bd);

        bd = new GenericBeanDefinition();
        bd.setBeanClass(CBean.class);
        args = new ArrayList<>();
        args.add("cbean01");
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("cbean", bd);

那么如果我们上面的过程换成配置的方式会是什么样的呢?

    <bean id="abean" class="com.study.spring.samples.ABean">
        <constructor-arg type="String" value="abean01"></constructor-arg>
        <constructor-arg ref="cbean"></constructor-arg>
    </bean>
    <bean id="cbean" class="com.study.spring.samples.CBean">
        <constructor-arg type="String" value="cbean01"></constructor-arg>
    </bean>

经过上面的创建bean对象的过程由ava代码转为xml配置的方式,可以看出使用配置有如下优势:

1. 因为我们是写框架的,提供配置的方式,别人使用更简单,改动更加灵活

2. 要新增修改东西时不需要改代码

二、选择什么样的配置方式

用过Spring的朋友都知道,配置方式有两种:

1. xml

2. 注解

三、配置方式的工作过程是怎样的

img

四、分步骤一个一个的去分析和设计

1. 定义xml标准和注解标准

img

首先我们需要理清楚定义xml标准、定义注解标准的目的是什么,定义它们的目的是让用户可以使用它们去配置bean定义和标注bean定义。那么

问题1:bean定义需要指定些什么信息呢?

需要指定的信息我们可以从之前写的BeanDefinition里面看到

img

可以看到bean定义需要上面的这些信息

如果使用xml配置的方式,我们需要为上面的这些信息定义一个DTD(Document Type Definition)文件或者XSD(XML Schemas Definition)文件,具体实现利用了Spring提供的可扩展Schema机制实现

然后用户根据提供的DTD文件定义bean定义需要的信息即可:

    <bean id="abean" class="com.study.spring.samples.ABean" init-method="init" 
        destroy-method="destroy" scope="prototype" >
        <constructor-arg type="String" value="abean01"></constructor-arg>
        <constructor-arg ref="cbean"></constructor-arg>
        <property name="name" value="leSmall"></property>
        <property name="age" value="18"></property>
    </bean>

问题2:如果使用注解的方式,需要定义一些什么注解?

从需要的bean定义信息里面我们可能需要做如下的步骤:

1) 指定类

2)指定beanName

3)指定scope

4)指定工厂bean

5)指定工厂方法

6)指定init method

7)指定销毁方法

8)指定构造参数依赖

9)指定属性依赖

前面的1)到 7)我们可以定义一个注解@Component,里面持有1)到 7)需要的bean定义的信息,在创建bean定义的时候通过反射获取这些信息

2. 用户怎么指定配置的xml文件的位置?用户怎么指定要扫描的包?

img

同样的我们需要分析用户指定xml配置文件的位置和指定扫描的包的目的是什么,目的是让提供方去加载xml文件和扫描包下的类,那么

问题1:怎么指定?

我们需要为用户提供一种方式

问题2:下面这些事情在哪里做好?

img

这里所做的事情是解析xml配置和反射获取bean定义注解、创建bean定义、向bean工厂注册bean定义,他不是bean工厂的事,我们应该单独定义接口和类完成这些事

img

无论是XmlApplicationContext还是AnnotationApplicationContext都要使用BeanFactory和BeanDefinitionRegistry,所以可以进一步优化类图

img

现在用户要使用我们的框架需要知道下面的接口和类:

img

那么让用户只需要知道ApplicationContext接口及其子类是否对用户更简单?

是的,这里我们可以用到前面学习的设计模式——外观模式,即提供一个新的外观,用户只需要知道要调用哪些接口和类,而不需要知道具体的实现。正确的做法是把上面类图中的BeanFactory和ApplicationContext合并到一起

img

3.怎么加载xml文件的配置?怎么扫描用户指定包下的类?

img

3.1 加载用户指定的xml配置文件

思考1:xml的来源会有多种吗?

img

那么它们的加载方式一样吗?

不一样

对于xml解析来说,从加载过程它希望获得什么?

xml解析希望获得流InputStream

我们希望能加载不同来源的xml,向解析xml配置提供统一的使用接口,那么该如何来设计接口和类呢?

让解析xml配置面向接口编程,不同的xml来源都实现该接口,提供不同的实现去加载xml配置文件最终都生产出一个InputStream

img

问题:这里我们定义不同的Resource类对应不同的xml来源,谁去负责分辨创建他们的对象?因为用户给定的是一个一个的字符串(这对他们来说是最简单的方式)

这个分辨字符串 ,创建对应的Resource对象的工作就是加载xml配置文件,这个事情有ApplicationContext来做。

这里就需要使用前面学过的设计模式工厂模式了:根据不同的字符串创建不同的对象

给ApplicationContext定义一个加载xml配置文件的行为ResourceLoader::

img

问题:怎么分辨字符串?

定义一套规则

img

工厂根据不同的前缀来区分,创建不同的Resource对象

3.2 注解的方式如何扫描

扫描过程如下:

到指定的包目录下找出所有的类文件(包含子孙包下的)

img

根据上面的扫描过程分析,我们需要定义一个正则表达式的匹配器来看扫描到的路径是否匹配用户指定的扫描包的路径:

img

思考:如果要扫描的是com.study下所有service包下的类,现在满足吗?

com.study/**/service/* 这种写法是ant path表达式

这时就需要在PathMatcher匹配器的基础上扩展一个ant path表达式的匹配器了:

img

思考:扫到了指定包下的class文件,我们最终需要的是什么?

我们最终需要的是类名,因为要拿类名去获取bean定义信息、创建bean定义、注册bean定义到bean工厂

那么这里我们需要设计什么样的接口和类呢?

在加载xml文件的时候存放扫描到的类可以吗?

不可以

img

思考:扫描的事情是由AnnotationApplicationContext这个类来做还是外包给其他的类来做?

外包给ClassPathBeandefinitionScanner这个类来做

img

说明:

扫描外包给ClassPathBeandefinitionScanner这个类来做,ClassPathBeandefinitionScanner里面的scan方法扫描完成以后得到bean定义信息,然后创建bean定义,把bean定义通过-registry : BeanDefinitionRegistry注册到bean工厂

思考:在哪里启动扫描调用ClassPathBeandefinitionScanner的scan方法?

在AnnotationApplicationContext的构造方法里面调用scan方法

4. 提供方解析xml配置文件、提供方反射获取bean定义注解

img

问题1:加载和扫描的输出是什么?

加载和扫描的输出是Resource

img

说明:

上面这张图红色部分要做的事情就是解析xml、反射获取注解,然后得到Resource读取bean定义信息,把bean定义信息注册到bean工厂里面去创建bean对象,由此我们可以做下面的设计:

img

说明:

BeanDefinitionReader负责解析xml、反射获取注解得到Resource,然后AbstractBeanDefinitionReader从Resource里面获取bean定义信息、创建bean定义、注册bean定义到bean工厂,具体的实现交给XmlBeanDefinitionReader和AnnotationBeanDefinitionReader这两个读取器来做

那么谁应该持有BeanDefinitionReader呢?请看下面完整的类图:

img

下面根据上面完整的类图开始写代码:

tractBeanDefinitionReader从Resource里面获取bean定义信息、创建bean定义、注册bean定义到bean工厂,具体的实现交给XmlBeanDefinitionReader和AnnotationBeanDefinitionReader这两个读取器来做**

那么谁应该持有BeanDefinitionReader呢?请看下面完整的类图:

[外链图片转存中…(img-fPVm7Sum-1586693761188)]

下面根据上面完整的类图开始写代码:

先定义接口,再定义抽象类、最后再定义具体类,把架子搭起来,然后再开始写具体的业务逻辑

文章来自:
https://www.cnblogs.com/leeSmall/p/10073931.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值