手寫一個(gè)HTTP框架:兩個(gè)類實(shí)現(xiàn)基本的IoC功能
jsoncat:https://github.com/Snailclimb/jsoncat?(仿 Spring Boot 但不同于 Spring Boot 的一個(gè)輕量級(jí)的 HTTP 框架)
國(guó)慶節(jié)的時(shí)候,我就已經(jīng)把 jsoncat 的?IoC 功能給寫了,具體可以看這篇文章《手寫“SpringBoot”近況:IoC模塊已經(jīng)完成》 。
今天這篇文章就來(lái)簡(jiǎn)單分享一下自己寫 IoC 的思路與具體的代碼實(shí)現(xiàn)。

IoC (Inverse of Control:控制反轉(zhuǎn)) 和 AOP(Aspect-Oriented Programming:面向切面編程) 可以說(shuō)是 Spring 框架提供的最核心的兩個(gè)功能。但凡是了解過(guò) Spring 的小伙伴,那肯定對(duì)這個(gè)兩個(gè)概念非常非常了解。不了解的小伙伴,可以查看《面試被問(wèn)了幾百遍的 IoC 和 AOP ,還在傻傻搞不清楚?》這篇通俗易懂的文章。
考慮到這篇文章要手寫 Spring 框架的 IoC 功能,所以,我這里還是簡(jiǎn)單介紹一下 IoC 。如果你不太清楚 IoC 這個(gè)概念,一定要搞懂之后再看后面具體的代碼實(shí)現(xiàn)環(huán)節(jié)。
IoC 介紹
IoC(Inverse of Control:控制反轉(zhuǎn))是一種設(shè)計(jì)思想,也就是 將原本在程序中手動(dòng)創(chuàng)建對(duì)象的控制權(quán)交由Spring框架來(lái)管理。 IoC 在其他語(yǔ)言中也有應(yīng)用,并非 Spring 特有。
IoC 容器
IoC 容器是用來(lái)實(shí)現(xiàn) IoC 的載體,被管理的對(duì)象就被存放在IoC容器中。IoC 容器在 Spring 中實(shí)際上就是個(gè)Map(key,value),Map 中存放了各種被管理的對(duì)象。
IoC 解決了什么問(wèn)題
將對(duì)象之間的相互依賴關(guān)系交給 IoC 容器來(lái)管理,并由 IoC 容器完成對(duì)象的注入。這樣可以很大程度上簡(jiǎn)化應(yīng)用的開發(fā),把應(yīng)用從復(fù)雜的依賴關(guān)系中解放出來(lái)。IoC 容器就像是一個(gè)工廠一樣,當(dāng)我們需要?jiǎng)?chuàng)建一個(gè)對(duì)象的時(shí)候,只需要配置好配置文件/注解即可,完全不用考慮對(duì)象是如何被創(chuàng)建出來(lái)的。?
IoC 和 DI 別再傻傻分不清楚
IoC(Inverse of Control:控制反轉(zhuǎn))是一種設(shè)計(jì)思想 或者說(shuō)是某種模式。這個(gè)設(shè)計(jì)思想就是 將原本在程序中手動(dòng)創(chuàng)建對(duì)象的控制權(quán),交由 Spring 框架來(lái)管理。 IoC 在其他語(yǔ)言中也有應(yīng)用,并非 Spring 特有。IoC 容器是 Spring 用來(lái)實(shí)現(xiàn) IoC 的載體, IoC 容器實(shí)際上就是個(gè) Map(key,value),Map 中存放的是各種被管理的對(duì)象。
IoC 最常見以及最合理的實(shí)現(xiàn)方式叫做依賴注入(Dependency Injection,簡(jiǎn)稱 DI)。
并且,老馬(Martin Fowler)在一篇文章中提到將 IoC 改名為 DI,原文如下,原文地址:https://martinfowler.com/articles/injection.html 。

IoC實(shí)現(xiàn)思路
?注意 :以下思路未涉及解決循環(huán)依賴的問(wèn)題!
開始代碼實(shí)現(xiàn)之前,我們先簡(jiǎn)單聊聊實(shí)現(xiàn) IoC 的思路,搞清楚了思路之后,實(shí)現(xiàn)起來(lái)就非常簡(jiǎn)單了。
掃描指定包下的特定注解比如 @Component標(biāo)記的類,并將這些類保存起來(lái)。遍歷所有被特定注解比如 @Component標(biāo)記的類,然后將這些類通過(guò)反射實(shí)例化并通過(guò)一個(gè) Map 保存起來(lái),Map 的 key 為類名,value為類對(duì)象。再一次遍歷所有被特定注解比如 @Component標(biāo)記的類,并獲取類中所有的字段,如果類被@Autowired注解標(biāo)記的話,就進(jìn)行第 4 步。通過(guò)字段名 key,從bean容器中獲取對(duì)應(yīng)的對(duì)象 value。 判斷獲取到的對(duì)象是否為接口。如果是接口的話,需要獲取接口對(duì)應(yīng)的實(shí)現(xiàn)類,然后再將指定的實(shí)現(xiàn)類的實(shí)例化對(duì)象通過(guò)反射賦值給指定對(duì)象。如果不是接口的話,就直接將獲取到的對(duì)象通過(guò)反射賦值給指定對(duì)象。
IoC 實(shí)現(xiàn)核心代碼
核心注解
@Autowired :注解對(duì)象
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public?@interface?Autowired?{
}
@Component :聲明對(duì)象被IoC容器管理
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public?@interface?Component?{
????String?name()?default?"";
}
@Qualifier: 指定注入的bean(當(dāng)接口有多個(gè)實(shí)現(xiàn)類的時(shí)候需要使用)
@Target({ElementType.FIELD,?ElementType.METHOD,?ElementType.PARAMETER,?ElementType.TYPE,?ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public?@interface?Qualifier?{
????String?value()?default?"";
}
工具類
簡(jiǎn)單封裝一個(gè)反射工具類。工具類包含3個(gè)后面會(huì)用到的方法:
scanAnnotatedClass()?:掃描指定包下的被指定注解標(biāo)記的類(使用Reflections這個(gè)反射框架一行代碼即可解決掃描獲取指定注解的類)。newInstance(): 傳入 Class 即可返回 Class 對(duì)應(yīng)的對(duì)象。setField():為對(duì)象的指定字段賦值。
@Slf4j
public?class?ReflectionUtil?{
????/**
?????*?scan?the?classes?marked?by?the?specified?annotation?in?the?specified?package
?????*
?????*?@param?packageName?specified?package?name
?????*?@param?annotation??specified?annotation
?????*?@return?the?classes?marked?by?the?specified?annotation?in?the?specified?package
?????*/
????public?static?Set>?scanAnnotatedClass(String?packageName,?Class?extends?Annotation>?annotation)?{
????????Reflections?reflections?=?new?Reflections(packageName,?new?TypeAnnotationsScanner());
????????Set>?annotatedClass?=?reflections.getTypesAnnotatedWith(annotation,?true);
????????log.info("The?number?of?class?Annotated?with??@RestController?:[{}]",?annotatedClass.size());
????????return?annotatedClass;
????}
????/**
?????*?create?object?instance?through?class
?????*
?????*?@param?cls?target?class
?????*?@return?object?created?by?the?target?class
?????*/
????public?static?Object?newInstance(Class>?cls)?{
????????Object?instance?=?null;
????????try?{
????????????instance?=?cls.newInstance();
????????}?catch?(InstantiationException?|?IllegalAccessException?e)?{
????????????log.error("new?instance?failed",?e);
????????}
????????return?instance;
????}
????/**
?????*?set?the?value?of?a?field?in?the?object
?????*
?????*?@param?obj???target?object
?????*?@param?field?target?field
?????*?@param?value?the?value?assigned?to?the?field
?????*/
????public?static?void?setField(Object?obj,?Field?field,?Object?value)?{
????????field.setAccessible(true);
????????try?{
????????????field.set(obj,?value);
????????}?catch?(IllegalAccessException?e)?{
????????????log.error("set?field?failed",?e);
????????????e.printStackTrace();
????????}
????}
??
}
根據(jù)實(shí)現(xiàn)思路寫代碼
?注意 :以下代碼未涉及解決循環(huán)依賴的問(wèn)題!以下是 IoC 實(shí)現(xiàn)的核心代碼,完整代碼地址:https://github.com/Snailclimb/jsoncat 。
1.掃描指定包下的特定注解比如@Component標(biāo)記的類,并將這些類保存起來(lái)。
掃描指定注解@RestController和@Component并保存起來(lái):
public?class?ClassFactory?{
????public?static?final?Map,?Set>>?CLASSES?=?new?ConcurrentHashMap<>();
????//1.掃描指定包下的特定注解比如`@Component`標(biāo)記的類,并將這些類保存起來(lái)
????public?static?void?loadClass(String?packageName)?{
????????Set>?restControllerSets?=?ReflectionUtil.scanAnnotatedClass(packageName,?RestController.class);
????????Set>?componentSets?=?ReflectionUtil.scanAnnotatedClass(packageName,?Component.class);
????????CLASSES.put(RestController.class,?restControllerSets);
????????CLASSES.put(Component.class,?componentSets);
????}
}
2.遍歷所有被特定注解比如@Component標(biāo)記的類,然后將這些類通過(guò)反射實(shí)例化并通過(guò)一個(gè) Map 保存起來(lái),Map 的 key 為類名,value為類對(duì)象。
public?final?class?BeanFactory?{
????public?static?final?Map?BEANS?=?new?ConcurrentHashMap<>(128);
????public?static?void?loadBeans()?{
????????//?2.遍歷所有被特定注解比如?@Component?標(biāo)記的類,然后將這些類通過(guò)反射實(shí)例化并通過(guò)一個(gè)?Map?保存起來(lái),Map?的?key?為類名,value為類對(duì)象
????????ClassFactory.CLASSES.forEach((annotation,?classes)?->?{
????????????if?(annotation?==?Component.class)?{
????????????????//將bean實(shí)例化,?并放入bean容器中
????????????????for?(Class>?aClass?:?classes)?{
????????????????????Component?component?=?aClass.getAnnotation(Component.class);
????????????????????String?beanName?=?"".equals(component.name())???aClass.getName()?:?component.name();
????????????????????Object?obj?=?ReflectionUtil.newInstance(aClass);
????????????????????BEANS.put(beanName,?obj);
????????????????}
????????????}
????????????if?(annotation?==?RestController.class)?{
????????????????for?(Class>?aClass?:?classes)?{
????????????????????Object?obj?=?ReflectionUtil.newInstance(aClass);
????????????????????BEANS.put(aClass.getName(),?obj);
????????????????}
????????????}
????????});
????}
}
3.再一次遍歷所有被特定注解比如@Component標(biāo)記的類,并獲取類中所有的字段,如果類被 @Autowired 注解標(biāo)記的話,就進(jìn)行第 4 步。
public?class?DependencyInjection?{
????public?static?void?dependencyInjection(String?packageName)?{
????????Map?beans?=?BeanFactory.BEANS;
????????if?(beans.size()?==?0)?return;
????????//3.再一次遍歷所有被特定注解比如?@Component 標(biāo)記的類,并獲取類中所有的字段,如果類被?`@Autowired`?注解標(biāo)記的話,就進(jìn)行第 4 步。
????????//?3.1.遍歷bean容器中的所有對(duì)象
????????beans.values().forEach(bean?->?{
????????????//?3.2.獲取對(duì)象所屬的類聲明的所有字段/屬性
????????????Field[]?beanFields?=?bean.getClass().getDeclaredFields();
????????????if?(beanFields.length?==?0)?return;
????????????//3.3.遍歷對(duì)象所屬的類聲明的所有字段/屬性
????????????for?(Field?beanField?:?beanFields)?{
??????????????//3.4.判斷字段是否被?@Autowired?注解標(biāo)記
????????????????if?(beanField.isAnnotationPresent(Autowired.class))?{
????????????????????//4.通過(guò)字段名 key,從bean容器中獲取對(duì)應(yīng)的對(duì)象 value。
????????????????????//4.1.字段對(duì)應(yīng)的類型
????????????????????Class>?beanFieldClass?=?beanField.getType();
????????????????????//4.2.字段對(duì)應(yīng)的類名
????????????????????String?beanName?=?beanFieldClass.getName();
????????????????????if?(beanFieldClass.isAnnotationPresent(Component.class))?{
????????????????????????Component?component?=?beanFieldClass.getAnnotation(Component.class);
????????????????????????beanName?=?"".equals(component.name())???beanFieldClass.getName()?:?component.name();
????????????????????}
????????????????????//4.3.從bean容器中獲取對(duì)應(yīng)的對(duì)象
????????????????????Object?beanFieldInstance?=?beans.get(beanName);
????????????????????//5.判斷獲取到的對(duì)象是否為接口。如果是接口的話,需要獲取接口對(duì)應(yīng)的實(shí)現(xiàn)類,然后再將指定的實(shí)現(xiàn)類的實(shí)例化對(duì)象通過(guò)反射賦值給指定對(duì)象。如果不是接口的話,就直接將獲取到的對(duì)象通過(guò)反射賦值給指定對(duì)象。
????????????????????if?(beanFieldClass.isInterface())?{
????????????????????????//如果是接口,獲取接口對(duì)應(yīng)的實(shí)現(xiàn)類
????????????????????????Set>?subClasses?=?getSubClass(packageName,?beanFieldClass);
????????????????????????//沒有實(shí)現(xiàn)類的話就拋出異常
????????????????????????if?(subClasses.size()?==?0)?{
????????????????????????????throw?new?InterfaceNotHaveImplementedClassException("interface?does?not?have?implemented?class?exception");
????????????????????????}
????????????????????????//實(shí)現(xiàn)類只有一個(gè)話,直接獲取
????????????????????????if?(subClasses.size()?==?1)?{
????????????????????????????Class>?aClass?=?subClasses.iterator().next();
????????????????????????????beanFieldInstance?=?ReflectionUtil.newInstance(aClass);
????????????????????????}
????????????????????????//實(shí)現(xiàn)類多與一個(gè)的話,根據(jù)?Qualifier?注解的值獲取
????????????????????????if?(subClasses.size()?>?1)?{
????????????????????????????Class>?aClass?=?subClasses.iterator().next();
????????????????????????????Qualifier?qualifier?=?beanField.getDeclaredAnnotation(Qualifier.class);
????????????????????????????beanName?=?qualifier?==?null???aClass.getName()?:?qualifier.value();
????????????????????????????beanFieldInstance?=?beans.get(beanName);
????????????????????????}
????????????????????}
????????????????????//?如果最后獲取到的字段對(duì)象為null,就拋出異常
????????????????????if?(beanFieldInstance?==?null)?{
????????????????????????throw?new?CanNotDetermineTargetBeanException("can?not?determine?target?bean");
????????????????????}
????????????????????//通過(guò)反射設(shè)置指定對(duì)象中的指定字段的值
????????????????????ReflectionUtil.setField(bean,?beanField,?beanFieldInstance);
????????????????}
????????????}
????????});
????}
????/**
?????*?獲取接口對(duì)應(yīng)的實(shí)現(xiàn)類
?????*/
????@SuppressWarnings("unchecked")
????public?static?Set>?getSubClass(String?packageName,?Class>?interfaceClass)?{
????????Reflections?reflections?=?new?Reflections(packageName);
????????return?reflections.getSubTypesOf((Class 閑聊
前天晚肝到很晚,把 jsoncat(仿 Spring Boot 但不同于 Spring Boot 的一個(gè)輕量級(jí)的 HTTP 框架) 的攔截器功能重構(gòu)完善了一下。感興趣的小伙伴可以先看一下,后續(xù)會(huì)帶大家看具體實(shí)現(xiàn)過(guò)程,手把手教你實(shí)現(xiàn)(用了責(zé)任鏈模式并參考了 mybatis interceptor 插件機(jī)制的實(shí)現(xiàn))。

jsoncat 相關(guān)文章:《手寫一個(gè)類似SpringBoot的輕量級(jí)HTTP框架》
支持原創(chuàng)!文章有幫助可以點(diǎn)個(gè)「在看」或「分享」,我都會(huì)開心很久!
我是Guide哥,Java后端開發(fā),會(huì)一點(diǎn)前端知識(shí),喜歡烹飪,自由的少年。一個(gè)三觀比主角還正的技術(shù)人。我們下期再見!
