ioc框架 java_从零开始实现一个简易的Java MVC框架(三)--实现IOC
Spring中的IOC
IoC全稱(chēng)是Inversion of Control,就是控制反轉(zhuǎn),他其實(shí)不是spring獨(dú)有的特性或者說(shuō)也不是java的特性,他是一種設(shè)計(jì)思想。而DI(Dependency Injection),即依賴(lài)注入就是Ioc的一種實(shí)現(xiàn)方式。關(guān)于Ioc和DI的具體定義和優(yōu)缺點(diǎn)等大家可以自行查找資料了解一下,這里就不詳細(xì)贅述,總之spring的IoC功能很大程度上便捷了我們的開(kāi)發(fā)工作。
在實(shí)現(xiàn)我們的Ioc之前,我們先了解一下spring的依賴(lài)注入,在spring中依賴(lài)注入有三種方式,分別是:
接口注入(Interface Injection)
設(shè)值方法注入(Setter Injection)
構(gòu)造注入(Constructor Injection)
@Component
public class ComponentA {
@Autowired // 1.接口注入
private ComponentB componentB;
@Autowired // 2.設(shè)值方法注入
public void setComponentB(ComponentB componentB) {
this.componentB = componentB;
}
@Autowired // 3.構(gòu)造注入
public ComponentA(ComponentB componentB) {
this.componentB = componentB;
}
}
循環(huán)依賴(lài)注入
如果只是實(shí)現(xiàn)依賴(lài)注入的話(huà)實(shí)際上很簡(jiǎn)單,只要利用java的反射原理將對(duì)應(yīng)的屬性‘注入’進(jìn)去就可以了。但是必須要注意一個(gè)問(wèn)題,那就是循環(huán)依賴(lài)問(wèn)題。循環(huán)依賴(lài)就是類(lèi)之間相互依賴(lài)形成了一個(gè)循環(huán),比如A依賴(lài)于B,同時(shí)B又依賴(lài)于A,這就形成了相互循環(huán)。
// ComponentA
@Component
public class ComponentA {
@Autowired
private ComponentB componentB;
}
// ComponentB
@Component
public class ComponentB {
@Autowired
private ComponentA componentA;
}
那么在spring中又是如何解決循環(huán)依賴(lài)問(wèn)題的呢,我們大致說(shuō)一下原理。
如果要?jiǎng)?chuàng)建一個(gè)類(lèi),先把這個(gè)類(lèi)放進(jìn)'正在創(chuàng)建池'中,通過(guò)反射等創(chuàng)建實(shí)例,創(chuàng)建成功的話(huà)就把這個(gè)實(shí)例放入創(chuàng)建池中,并移除'正在創(chuàng)建池'中的這個(gè)類(lèi)。每當(dāng)實(shí)例中有依賴(lài)需要注入的話(huà),就從創(chuàng)建池中找對(duì)應(yīng)的實(shí)例注入進(jìn)去,如果沒(méi)有找到實(shí)例,則先創(chuàng)建這個(gè)依賴(lài)。
利用了這個(gè)正在創(chuàng)建的中間狀態(tài)緩存,讓Bean的創(chuàng)建的時(shí)候即使有依賴(lài)還沒(méi)有實(shí)例化,可以先把Bean放進(jìn)這個(gè)中間狀態(tài),然后跑去創(chuàng)建那個(gè)依賴(lài),假如那個(gè)依賴(lài)的類(lèi)又依賴(lài)與這個(gè)Bean,那么只要在'正在創(chuàng)建池'中再把這個(gè)Bean拿出來(lái),注入到這個(gè)依賴(lài)中,就可以保證Bean的依賴(lài)能夠?qū)嵗瓿伞T倩仡^來(lái)把這個(gè)依賴(lài)注入到Bean中,那么這個(gè)Bean也實(shí)例化完成了,就把這個(gè)Bean從'正在創(chuàng)建池'移到'創(chuàng)建完成池'中,就解決了循環(huán)依賴(lài)問(wèn)題。
雖然spring巧妙的避免了循環(huán)依賴(lài)問(wèn)題,但是事實(shí)上構(gòu)造注入是無(wú)法避免循環(huán)依賴(lài)問(wèn)題的。因?yàn)樵趯?shí)例化ComponentA的構(gòu)造函數(shù)的時(shí)候必須得到ComponentB的實(shí)例,但是實(shí)例化ComponentB的構(gòu)造函數(shù)的時(shí)候又必須有ComponentA的實(shí)例。這兩個(gè)Bean都不能通過(guò)反射實(shí)例化然后放到'正在創(chuàng)建池',所以無(wú)法解決循環(huán)依賴(lài)問(wèn)題,這時(shí)候spring就會(huì)主動(dòng)拋出BeanCurrentlyInCreationException異常避免死循環(huán)。
* 注意,前面講的這些都是基于spring的單例模式下的,如果是多例模式會(huì)有所不同,大家有興趣可以自行了解。
實(shí)現(xiàn)IOC
現(xiàn)在可以開(kāi)始實(shí)現(xiàn)IOC功能了
增加注解
先在zbw.ioc包下創(chuàng)建一個(gè)annotation包,然后再創(chuàng)建一個(gè)Autowired的注解。這個(gè)注解的Target只有一個(gè)ElementType.FIELD,就是只能注解在屬性上。意味著我們目前只實(shí)現(xiàn)接口注入的功能。這樣可以避免構(gòu)造注入造成的循環(huán)依賴(lài)問(wèn)題無(wú)法解決,而且接口注入也是用的最多的方式了。如果想要實(shí)現(xiàn)設(shè)值方式注入大家可以自己去實(shí)現(xiàn),實(shí)現(xiàn)原理幾乎都一樣。
package com.zbw.ioc.annotation;
import ...
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
實(shí)現(xiàn)IOC類(lèi)
package com.zbw.ioc;
import ...
@Slf4j
public class Ioc {
/**
* Bean容器
*/
private BeanContainer beanContainer;
public Ioc() {
beanContainer = BeanContainer.getInstance();
}
/**
* 執(zhí)行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注解,則對(duì)其注入
final Class> fieldClass = field.getType();
Object fieldValue = getClassInstance(fieldClass);
if (null != fieldValue) {
ClassUtil.setField(field, targetBean, fieldValue);
} else {
throw new RuntimeException("無(wú)法注入對(duì)應(yīng)的類(lèi),目標(biāo)類(lèi)型:" + fieldClass.getName());
}
}
}
}
}
/**
* 根據(jù)Class獲取其實(shí)例或者實(shí)現(xiàn)類(lèi)
*/
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;
});
}
/**
* 獲取接口的實(shí)現(xiàn)類(lèi)
*/
private Class> getImplementClass(final Class> interfaceClass) {
return beanContainer.getClassesBySuper(interfaceClass)
.stream()
.findFirst()
.orElse(null);
}
}
在知道IOC的原理之后發(fā)現(xiàn)其實(shí)真的是非常簡(jiǎn)單,這里用了幾十行的代碼就實(shí)現(xiàn)了IOC的功能。
首先在Ioc類(lèi)構(gòu)造的時(shí)候先獲取到我們之前已經(jīng)單例化的BeanContainer容器。
然后在doIoc()方法中就是正式實(shí)現(xiàn)IOC功能的了。
遍歷在BeanContainer容器的所有Bean
對(duì)每個(gè)Bean的Field屬性進(jìn)行遍歷
如果某個(gè)Field屬性被Autowired注解,則調(diào)用getClassInstance()方法對(duì)其進(jìn)行注入
getClassInstance()會(huì)根據(jù)Field的Class嘗試從Bean容器中獲取對(duì)應(yīng)的實(shí)例,如果獲取到則返回該實(shí)例,如果獲取不到,則我們認(rèn)定該Field為一個(gè)接口,我們就調(diào)用getImplementClass()方法來(lái)獲取這個(gè)接口的實(shí)現(xiàn)類(lèi)Class,然后再根據(jù)這個(gè)實(shí)現(xiàn)類(lèi)Class在Bean容器中獲取對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)實(shí)例。
測(cè)試用例
為了測(cè)試我們的Ioc和之前寫(xiě)的BeanContainer編寫(xiě)正確,我們寫(xiě)一下測(cè)試用例測(cè)試一下。
先在pom.xml添加junit的依賴(lài)
...
4.12
...
junit
junit
${junit.version}
test
然后在test包下添加DoodleController、DoodleService、DoodleServiceImpl三個(gè)類(lèi)方便測(cè)試
// 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";
}
}
再編寫(xiě)IocTest的測(cè)試用例
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()方法里的字符串,說(shuō)明DoodleController中的DoodleService已經(jīng)成功注入了DoodleServiceImpl。那么我們的IOC的功能也完成了。
總結(jié)
以上是生活随笔為你收集整理的ioc框架 java_从零开始实现一个简易的Java MVC框架(三)--实现IOC的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: centos7下双网卡绑定(bondin
- 下一篇: java 链表实现堆栈_《Java数据结