日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

框架源码系列四:手写Spring-配置(为什么要提供配置的方法、选择什么样的配置方式、配置方式的工作过程是怎样的、分步骤一个一个的去分析和设计)...

發布時間:2025/5/22 javascript 33 豆豆

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

經過前面的手寫Spring IOC、手寫Spring DI、手寫Spring AOP,我們知道要創建一個bean對象,需要用戶先定義好bean,然后注冊到bean工廠才能創建一個bean對象。代碼如下:

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. 注解

三、配置方式的工作過程是怎樣的

?

四、分步驟一個一個的去分析和設計

1. 定義xml標準和注解標準

首先我們需要理清楚定義xml標準、定義注解標準的目的是什么,定義它們的目的是讓用戶可以使用它們去配置bean定義和標注bean定義。那么

問題1:bean定義需要指定些什么信息呢?

  需要指定的信息我們可以從之前寫的BeanDefinition里面看到

?

可以看到bean定義需要上面的這些信息

如果使用xml配置的方式,我們需要為上面的這些信息定義一個DTD(Document Type Definition)文件或者XSD(XML Schemas Definition)文件,具體實現利用了Spring提供的可擴展Schema機制實現,實現方式查看我前面的文章:

dubbo系列三:dubbo源碼分析(dubbo框架是如何跟spring進行整合的,消費者獲取的代理實例是如何創建的呢、生產者怎么把內容注冊到注冊中心的,然后消費者是如何獲取這個信息的、dubbo負載均衡策略)

然后用戶根據提供的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定義的時候通過反射獲取這些信息

package com.dn.spring.context.config.annotation;import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;import com.study.spring.beans.BeanDefinition;@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Component {String value() default "";String name() default "";String scope() default BeanDefinition.SCOPE_SINGLETION;String factoryMethodName() default "";String factoryBeanName() default "";String initMethodName() default "";String destroyMethodName() default ""; }

注意:注解?Component里面沒有定義 1)指定類 需要的元素,因為通過在類上加上@Component就能獲取到類的名稱了

前面的8)到 9)我們可以定義三個注解@Autowired、@Qualifier、@Value,其中@Autowired用來指定屬性依賴的bean依賴或者有多個構造函數時指定使用哪個構造函數;@Qualifier用來指定bean依賴的具體bean,比如一個類有多個bean,可以指定具體的一個bean,如@Qualifier("cbean01") CBean cb;@Value用來指定屬性依賴的非bean依賴。

@Autowired代碼:

package com.study.spring.context.config.annotation;import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD,ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired {/*** Declares whether the annotated dependency is required.* <p>* Defaults to {@code true}.*/boolean required() default true; }

@Qualifier代碼:

package com.study.spring.context.config.annotation;import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Qualifier {String value() default "";}

@Value代碼:

package com.study.spring.context.config.annotation;import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Value {String value(); }

4個注解的使用示例代碼:

package com.study.spring.samples;import com.study.spring.context.config.annotation.Autowired; import com.study.spring.context.config.annotation.Component; import com.study.spring.context.config.annotation.Qualifier; import com.study.spring.context.config.annotation.Value;@Component(initMethodName = "init", destroyMethodName = "destroy") public class ABean {private String name;private CBean cb;@Autowiredprivate DBean dbean;@Autowiredpublic ABean(@Value("mike") String name, @Qualifier("cbean01") CBean cb) {super();this.name = name;this.cb = cb;System.out.println("調用了含有CBean參數的構造方法");}public ABean(String name, CCBean cb) {super();this.name = name;this.cb = cb;System.out.println("調用了含有CCBean參數的構造方法");}public ABean(CBean cb) {super();this.cb = cb;} }

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

同樣的我們需要分析用戶指定xml配置文件的位置和指定掃描的包的目的是什么,目的是讓提供方去加載xml文件和掃描包下的類,那么

問題1:怎么指定?

  我們需要為用戶提供一種方式

問題2:下面這些事情在哪里做好?

這里所做的事情是解析xml配置和反射獲取bean定義注解、創建bean定義、向bean工廠注冊bean定義,他不是bean工廠的事,我們應該單獨定義接口和類完成這些事

?

無論是XmlApplicationContext還是AnnotationApplicationContext都要使用BeanFactory和BeanDefinitionRegistry,所以可以進一步優化類圖

?

現在用戶要使用我們的框架需要知道下面的接口和類:

那么讓用戶只需要知道ApplicationContext接口及其子類是否對用戶更簡單?

  是的,這里我們可以用到前面學習的設計模式——外觀模式,即提供一個新的外觀,用戶只需要知道要調用哪些接口和類,而不需要知道具體的實現。正確的做法是把上面類圖中的BeanFactory和ApplicationContext合并到一起

?

?3.怎么加載xml文件的配置?怎么掃描用戶指定包下的類?

?

3.1 加載用戶指定的xml配置文件

思考1:xml的來源會有多種嗎?

  會?

?

那么它們的加載方式一樣嗎?

  不一樣

對于xml解析來說,從加載過程它希望獲得什么??

?  xml解析希望獲得流InputStream

我們希望能加載不同來源的xml,向解析xml配置提供統一的使用接口,那么該如何來設計接口和類呢?

?  讓解析xml配置面向接口編程,不同的xml來源都實現該接口,提供不同的實現去加載xml配置文件最終都生產出一個InputStream

?

問題:這里我們定義不同的Resource類對應不同的xml來源,誰去負責分辨創建他們的對象?因為用戶給定的是一個一個的字符串(這對他們來說是最簡單的方式)

?  這個分辨字符串 ,創建對應的Resource對象的工作就是加載xml配置文件,這個事情有ApplicationContext來做。

  這里就需要使用前面學過的設計模式工廠模式了:根據不同的字符串創建不同的對象

?  給ApplicationContext定義一個加載xml配置文件的行為ResourceLoader::

?

問題:怎么分辨字符串?

  定義一套規則

工廠根據不同的前綴來區分,創建不同的Resource對象

3.2 注解的方式如何掃描

掃描過程如下:

到指定的包目錄下找出所有的類文件(包含子孫包下的)

根據上面的掃描過程分析,我們需要定義一個正則表達式的匹配器來看掃描到的路徑是否匹配用戶指定的掃描包的路徑:

思考:如果要掃描的是com.study下所有service包下的類,現在滿足嗎?

?  com.study/**/service/*? ? ?這種寫法是ant path表達式

這時就需要在PathMatcher匹配器的基礎上擴展一個ant path表達式的匹配器了:

?

思考:掃到了指定包下的class文件,我們最終需要的是什么?

?  我們最終需要的是類名,因為要拿類名去獲取bean定義信息、創建bean定義、注冊bean定義到bean工廠

  那么這里我們需要設計什么樣的接口和類呢?

?  在加載xml文件的時候存放掃描到的類可以嗎?

    不可以

?

思考:掃描的事情是由AnnotationApplicationContext這個類來做還是外包給其他的類來做?

?    外包給ClassPathBeandefinitionScanner這個類來做

說明:

掃描外包給ClassPathBeandefinitionScanner這個類來做,ClassPathBeandefinitionScanner里面的scan方法掃描完成以后得到bean定義信息,然后創建bean定義,把bean定義通過-registry : BeanDefinitionRegistry注冊到bean工廠

思考:在哪里啟動掃描調用ClassPathBeandefinitionScanner的scan方法?

  在AnnotationApplicationContext的構造方法里面調用scan方法

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

?

問題1:加載和掃描的輸出是什么?

? ? ?加載和掃描的輸出是Resource

?說明:

上面這張圖紅色部分要做的事情就是解析xml、反射獲取注解,然后得到Resource讀取bean定義信息,把bean定義信息注冊到bean工廠里面去創建bean對象,由此我們可以做下面的設計:

?說明:

BeanDefinitionReader負責解析xml、反射獲取注解得到Resource,然后AbstractBeanDefinitionReader從Resource里面獲取bean定義信息、創建bean定義、注冊bean定義到bean工廠,具體的實現交給XmlBeanDefinitionReader和AnnotationBeanDefinitionReader這兩個讀取器來做

?那么誰應該持有BeanDefinitionReader呢?請看下面完整的類圖:

?

下面根據上面完整的類圖開始寫代碼:

先定義接口,再定義抽象類、最后再定義具體類,把架子搭起來,然后再開始寫具體的業務邏輯

?完整代碼獲取地址:

?https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v4

轉載于:https://www.cnblogs.com/leeSmall/p/10073931.html

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的框架源码系列四:手写Spring-配置(为什么要提供配置的方法、选择什么样的配置方式、配置方式的工作过程是怎样的、分步骤一个一个的去分析和设计)...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。