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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

ioc框架 java_从零开始实现一个简易的Java MVC框架(三)--实现IOC

發布時間:2023/12/13 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ioc框架 java_从零开始实现一个简易的Java MVC框架(三)--实现IOC 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Spring中的IOC

IoC全稱是Inversion of Control,就是控制反轉,他其實不是spring獨有的特性或者說也不是java的特性,他是一種設計思想。而DI(Dependency Injection),即依賴注入就是Ioc的一種實現方式。關于Ioc和DI的具體定義和優缺點等大家可以自行查找資料了解一下,這里就不詳細贅述,總之spring的IoC功能很大程度上便捷了我們的開發工作。

在實現我們的Ioc之前,我們先了解一下spring的依賴注入,在spring中依賴注入有三種方式,分別是:

接口注入(Interface Injection)

設值方法注入(Setter Injection)

構造注入(Constructor Injection)

@Component

public class ComponentA {

@Autowired // 1.接口注入

private ComponentB componentB;

@Autowired // 2.設值方法注入

public void setComponentB(ComponentB componentB) {

this.componentB = componentB;

}

@Autowired // 3.構造注入

public ComponentA(ComponentB componentB) {

this.componentB = componentB;

}

}

循環依賴注入

如果只是實現依賴注入的話實際上很簡單,只要利用java的反射原理將對應的屬性‘注入’進去就可以了。但是必須要注意一個問題,那就是循環依賴問題。循環依賴就是類之間相互依賴形成了一個循環,比如A依賴于B,同時B又依賴于A,這就形成了相互循環。

// ComponentA

@Component

public class ComponentA {

@Autowired

private ComponentB componentB;

}

// ComponentB

@Component

public class ComponentB {

@Autowired

private ComponentA componentA;

}

那么在spring中又是如何解決循環依賴問題的呢,我們大致說一下原理。

如果要創建一個類,先把這個類放進'正在創建池'中,通過反射等創建實例,創建成功的話就把這個實例放入創建池中,并移除'正在創建池'中的這個類。每當實例中有依賴需要注入的話,就從創建池中找對應的實例注入進去,如果沒有找到實例,則先創建這個依賴。

利用了這個正在創建的中間狀態緩存,讓Bean的創建的時候即使有依賴還沒有實例化,可以先把Bean放進這個中間狀態,然后跑去創建那個依賴,假如那個依賴的類又依賴與這個Bean,那么只要在'正在創建池'中再把這個Bean拿出來,注入到這個依賴中,就可以保證Bean的依賴能夠實例化完成。再回頭來把這個依賴注入到Bean中,那么這個Bean也實例化完成了,就把這個Bean從'正在創建池'移到'創建完成池'中,就解決了循環依賴問題。

雖然spring巧妙的避免了循環依賴問題,但是事實上構造注入是無法避免循環依賴問題的。因為在實例化ComponentA的構造函數的時候必須得到ComponentB的實例,但是實例化ComponentB的構造函數的時候又必須有ComponentA的實例。這兩個Bean都不能通過反射實例化然后放到'正在創建池',所以無法解決循環依賴問題,這時候spring就會主動拋出BeanCurrentlyInCreationException異常避免死循環。

* 注意,前面講的這些都是基于spring的單例模式下的,如果是多例模式會有所不同,大家有興趣可以自行了解。

實現IOC

現在可以開始實現IOC功能了

增加注解

先在zbw.ioc包下創建一個annotation包,然后再創建一個Autowired的注解。這個注解的Target只有一個ElementType.FIELD,就是只能注解在屬性上。意味著我們目前只實現接口注入的功能。這樣可以避免構造注入造成的循環依賴問題無法解決,而且接口注入也是用的最多的方式了。如果想要實現設值方式注入大家可以自己去實現,實現原理幾乎都一樣。

package com.zbw.ioc.annotation;

import ...

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Autowired {

}

實現IOC類

package com.zbw.ioc;

import ...

@Slf4j

public class Ioc {

/**

* Bean容器

*/

private BeanContainer beanContainer;

public Ioc() {

beanContainer = BeanContainer.getInstance();

}

/**

* 執行Ioc

*/

public void doIoc() {

for (Class> clz : beanContainer.getClasses()) { //遍歷Bean容器中所有的Bean

final Object targetBean = beanContainer.getBean(clz);

Field[] fields = clz.getDeclaredFields();

for (Field field : fields) { //遍歷Bean中的所有屬性

if (field.isAnnotationPresent(Autowired.class)) {// 如果該屬性被Autowired注解,則對其注入

final Class> fieldClass = field.getType();

Object fieldValue = getClassInstance(fieldClass);

if (null != fieldValue) {

ClassUtil.setField(field, targetBean, fieldValue);

} else {

throw new RuntimeException("無法注入對應的類,目標類型:" + fieldClass.getName());

}

}

}

}

}

/**

* 根據Class獲取其實例或者實現類

*/

private Object getClassInstance(final Class> clz) {

return Optional

.ofNullable(beanContainer.getBean(clz))

.orElseGet(() -> {

Class> implementClass = getImplementClass(clz);

if (null != implementClass) {

return beanContainer.getBean(implementClass);

}

return null;

});

}

/**

* 獲取接口的實現類

*/

private Class> getImplementClass(final Class> interfaceClass) {

return beanContainer.getClassesBySuper(interfaceClass)

.stream()

.findFirst()

.orElse(null);

}

}

在知道IOC的原理之后發現其實真的是非常簡單,這里用了幾十行的代碼就實現了IOC的功能。

首先在Ioc類構造的時候先獲取到我們之前已經單例化的BeanContainer容器。

然后在doIoc()方法中就是正式實現IOC功能的了。

遍歷在BeanContainer容器的所有Bean

對每個Bean的Field屬性進行遍歷

如果某個Field屬性被Autowired注解,則調用getClassInstance()方法對其進行注入

getClassInstance()會根據Field的Class嘗試從Bean容器中獲取對應的實例,如果獲取到則返回該實例,如果獲取不到,則我們認定該Field為一個接口,我們就調用getImplementClass()方法來獲取這個接口的實現類Class,然后再根據這個實現類Class在Bean容器中獲取對應的實現類實例。

測試用例

為了測試我們的Ioc和之前寫的BeanContainer編寫正確,我們寫一下測試用例測試一下。

先在pom.xml添加junit的依賴

...

4.12

...

junit

junit

${junit.version}

test

然后在test包下添加DoodleController、DoodleService、DoodleServiceImpl三個類方便測試

// DoodleController

package com.zbw.bean;

@Controller

@Slf4j

public class DoodleController {

@Autowired

private DoodleService doodleService;

public void hello() {

log.info(doodleService.helloWord());

}

}

// DoodleService

package com.zbw.bean;

public interface DoodleService {

String helloWord();

}

// DoodleServiceImpl

package com.zbw.bean;

@Service

public class DoodleServiceImpl implements DoodleService{

@Override

public String helloWord() {

return "hello word";

}

}

再編寫IocTest的測試用例

package com.zbw.ioc;

import ...

@Slf4j

public class IocTest {

@Test

public void doIoc() {

BeanContainer beanContainer = BeanContainer.getInstance();

beanContainer.loadBeans("com.zbw");

new Ioc().doIoc();

DoodleController controller = (DoodleController) beanContainer.getBean(DoodleController.class);

controller.hello();

}

}

看到在DoodleController中輸出了DoodleServiceImpl的helloWord()方法里的字符串,說明DoodleController中的DoodleService已經成功注入了DoodleServiceImpl。那么我們的IOC的功能也完成了。

總結

以上是生活随笔為你收集整理的ioc框架 java_从零开始实现一个简易的Java MVC框架(三)--实现IOC的全部內容,希望文章能夠幫你解決所遇到的問題。

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