眾說周知当宴,Spring是一個(gè)具有強(qiáng)大的依賴注入功能的Java框架。本篇文章將介紹筆者自己動(dòng)手寫的一個(gè)輕量級(jí)依賴注入框架冰沙,實(shí)現(xiàn)jsr330并兼容jsr330注解哑芹。
完整代碼托管在github中,可以點(diǎn)擊https://github.com/bdqfork/spring-toy 查看完整項(xiàng)目垫释。
另筆者開發(fā)經(jīng)驗(yàn)不足丝格,歡迎大家指正批評(píng)。
需求簡介
- 可以使用注解標(biāo)記類為組件棵譬,并自動(dòng)掃描包路徑显蝌,識(shí)別組件類。
- 獲取注解信息订咸,將組件類注冊(cè)到容器中曼尊,供以后的訪問使用。
- 解析組件之間的依賴關(guān)系脏嚷,初始化組件類骆撇,并注入相關(guān)依賴。
- 從容器中獲取組件類的實(shí)例父叙,并正常調(diào)用相應(yīng)的方法神郊。
更多更具體的細(xì)節(jié)需求,可以查看jsr330趾唱。jsr330對(duì)依賴注入作了一個(gè)詳細(xì)的說明涌乳,但并未給出實(shí)現(xiàn),筆者嘗試根據(jù)jsr330的定義甜癞,作出了自己的實(shí)現(xiàn)夕晓。
項(xiàng)目框架
整個(gè)項(xiàng)目大致分為以下幾個(gè)包:
其中,
- annotation包中定義了一些容器所需要的注解带欢,比如Component运授,Service等注解烤惊。
- container包是容器的主要實(shí)現(xiàn),負(fù)責(zé)處理容器的相關(guān)功能吁朦,如依賴注入等柒室。
- context包定義了上下文環(huán)境,負(fù)責(zé)掃描組件逗宜,以及依賴解析等過程雄右。
- exception包定義了項(xiàng)目所需的異常。
- proxy包定義了兩種動(dòng)態(tài)代理的方式纺讲,一種是Jdk的動(dòng)態(tài)代理實(shí)現(xiàn)擂仍,另一種是CGlib方式。
- utils包定義了一些工具類熬甚。
功能實(shí)現(xiàn)
注解定義
在進(jìn)行核心功能實(shí)現(xiàn)之前逢渔,首先定義相關(guān)的注解,筆者參考了Spring的注解定義乡括,作出了如下注解肃廓。
定義 | 功能 | 參數(shù)定義 |
---|---|---|
@Component | 用于標(biāo)記組件類,被標(biāo)記的類將會(huì)被添加到容器中管理诲泌。 | String value()盲赊,用于指定類名,默認(rèn)為""敷扫。 |
@Repositorty | 用于標(biāo)記組件類哀蘑,被標(biāo)記的類將會(huì)被添加到容器中管理,這是一個(gè)領(lǐng)域定義葵第,其功能和Component一致绘迁。 | String value(),用于指定類名羹幸,默認(rèn)為""脊髓。 |
@Service | 用于標(biāo)記組件類,被標(biāo)記的類將會(huì)被添加到容器中管理栅受,這是一個(gè)領(lǐng)域定義,其功能和Component一致恭朗。 | String value()屏镊,用于指定類名,默認(rèn)為""痰腮。 |
@Controller | 用于標(biāo)記組件類而芥,被標(biāo)記的類將會(huì)被添加到容器中管理,這是一個(gè)領(lǐng)域定義膀值,其功能和Component一致棍丐。 | String value()误辑,用于指定類名,默認(rèn)為""歌逢。 |
@Scope | 用于標(biāo)記組件類巾钉,用于表示,注冊(cè)到容器中的類秘案,其實(shí)例是否為單例砰苍。 | String value(),用于表示是否單例阱高,其值在ScopeType類中定義赚导。 |
@AutoWired | 標(biāo)記待注入的字段,構(gòu)造函數(shù)赤惊,setter方法等吼旧。 | boolean required(),用于表示是否一定需要注入未舟。 |
@Qualifier | 限定器浇揩,可以與AutoWired一起使用,標(biāo)記待注入依賴名牍蜂。 | String value()陷虎,待注入依賴名。 |
BeanFactory | 用于替換@AutoWired的一個(gè)接口魂角,可以實(shí)現(xiàn)相同的功能昵济。 | T,待注入依賴的類型野揪。 |
由于同時(shí)筆者兼容了jsr330访忿,jsr330官方給出了一些注解,功能上與上文定義的注解可以進(jìn)行相互替換斯稳,在此也進(jìn)行描述海铆。
注解 | 功能 | 備注 |
---|---|---|
@Named | 可以作為@Component等組件標(biāo)記注解的替換,標(biāo)記一個(gè)組件類挣惰。 | 也可以作為@Qualifier的替換卧斟,標(biāo)記待注入依賴名。 |
@Singleton | 可以替換@Scope(“singleton”)憎茂,被標(biāo)記的類的實(shí)例將會(huì)是單例珍语。 | |
@Inject | 可以和@AutoWired替換,標(biāo)記待注入的字段竖幔,構(gòu)造函數(shù)板乙,setter方法等。 | 不支持required拳氢,其標(biāo)記的依賴必須注入募逞,否則拋出異常蛋铆。 |
Provider | 與BeanFactory等價(jià),可以互相替換放接。 |
注解掃描
在相關(guān)注解的定義完成之后刺啦,需要進(jìn)行掃描,將標(biāo)記有@Component等注解的類掃描出來透乾,以進(jìn)行下一步的處理洪燥。
整個(gè)掃描的過程實(shí)際上是對(duì)類進(jìn)行掃描,可以通過Java的ClassLoader來掃描類路徑乳乌,將類加載進(jìn)一個(gè)集合中捧韵。這個(gè)過程的部分代碼如下,完整代碼可以在utils包下的ReflectUtil中查看汉操。
private static final String FILE_PROTOCOL = "file";
private static final String JAR_PROTOCOL = "jar";
private static final String SUFFIX = ".class";
/**
* 根據(jù)包名獲取獲取Class
*
* @param packageName
* @return
*/
public static Set<Class<?>> getClasses(String packageName) {
if (packageName == null || "".equals(packageName)) {
return Collections.emptySet();
}
//將包名改為相對(duì)路徑
String packagePath = packageName.replace(".", "/");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<Class<?>> classes = new HashSet<>();
try {
//掃描包路徑再来,返回資源的枚舉
Enumeration<URL> dirs = classLoader.getResources(packagePath);
while (dirs.hasMoreElements()) {
URL fileUrl = dirs.nextElement();
String filePath = fileUrl.getPath();
//判斷資源類型
if (FILE_PROTOCOL.equals(fileUrl.getProtocol())) {
//處理文件類型的Class
classes.addAll(getClassesByFilePath(filePath, packagePath));
} else if (JAR_PROTOCOL.equals(fileUrl.getProtocol())) {
//處理Jar包中的Class
JarURLConnection jarURLConnection = (JarURLConnection) fileUrl.openConnection();
JarFile jarFile = jarURLConnection.getJarFile();
classes.addAll(getClassesByJar(jarFile));
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
通過ClassLoader加載指定包路徑下的所有資源,然后對(duì)Class進(jìn)行加載即可磷瘤,需要注意的是jar包里面的Class加載的方式有些許不同芒篷。
容器的實(shí)現(xiàn)
容器這個(gè)功能可以說是依賴注入的核心之一了,容器是對(duì)所有組件的管理采缚,基本上所有的功能都圍繞著容器來開展针炉。
最簡單的容器可能就是一個(gè)Map<String,Object>了,網(wǎng)上很多的文章都是基于這個(gè)類型實(shí)現(xiàn)的簡單的依賴注入扳抽。然而篡帕,這種方式有很多的缺陷。例如贸呢,使用這種方式實(shí)現(xiàn)的容器镰烧,其存儲(chǔ)的大都是待注入對(duì)象的直接實(shí)例,也就是說獲取的對(duì)象實(shí)例大都是單例的形式楞陷,這就導(dǎo)致了一個(gè)問題怔鳖,當(dāng)需要返回的實(shí)例是一個(gè)新的實(shí)例的時(shí)候,這種實(shí)現(xiàn)方式就無法滿足了固蛾。一方面是因?yàn)榻嶂矗琈ap里只保存了一個(gè)實(shí)例,另一方面是因?yàn)榉祷匦碌膶?shí)例艾凯,需要重新將依賴注入到新的實(shí)例中昌犹。
因此,要使用更高級(jí)的方式進(jìn)行實(shí)現(xiàn)览芳。看過Spring源碼的同學(xué)鸿竖,應(yīng)該了解到BeanDefinition沧竟。BeanDefinition是對(duì)Bean的一個(gè)描述铸敏,我們可以定義一個(gè)BeanDefinition。它描述了一個(gè)Bean的類型悟泵,名稱杈笔,是否需要單例等信息。使用Map<String,BeanDefinition>等方式來作為容器糕非,這樣上文描述的問題就迎刃而解了蒙具。
筆者定義的BeanDefinition如下:
public class BeanDefinition {
/**
* Class類
*/
private Class<?> clazz;
/**
* Bean的名稱
*/
private String name;
/**
* 單實(shí)例
*/
private Object instance;
/**
* 是否單例
*/
private boolean isSingleton;
/**
* 依賴信息提供者
*/
private InjectorProvider injectorProvider;
}
獲取對(duì)象實(shí)例的方法如下:
/**
* 獲取對(duì)象實(shí)例,如果bean是單例的朽肥,則每次都返回同一個(gè)實(shí)例禁筏,如果不是,則每次都創(chuàng)建一個(gè)新的實(shí)例衡招。
*
* @return Object
*/
public Object getInstance() throws InjectedException {
if (isSingleton) {
return getSingleInstance();
}
return newBean();
}
private Object getSingleInstance() throws InjectedException {
if (instance == null) {
synchronized (Object.class) {
if (instance == null) {
instance = newBean();
}
}
}
return instance;
}
private Object newBean() throws InjectedException {
Object instance = injectorProvider.doInject(this);
Class<?>[] classes = clazz.getInterfaces();
if (classes.length != 0) {
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler();
return jdkInvocationHandler.newProxyInstance(instance);
} else {
CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
return cglibMethodInterceptor.newProxyInstance(instance);
}
}
這樣一來篱昔,容器的實(shí)現(xiàn)就簡單多了。BeanDefinition的完整代碼始腾,可以查閱container包下的BeanDefinition類州刽。
下面貼出容器的代碼實(shí)現(xiàn),只貼出注冊(cè)部分浪箭,完整代碼請(qǐng)查看container包下的BeanContainer類穗椅。
public class BeanContainer {
private Map<String, BeanDefinition> beans = new HashMap<>();
public void register(String beanName, BeanDefinition beanDefinition) throws ConflictedBeanException {
if (beans.containsKey(beanName)) {
throw new ConflictedBeanException(String.format("the entity named: %s has conflicted ! ", beanName));
}
beans.put(beanName, beanDefinition);
}
}
通過調(diào)用register方法,即可將Bean注冊(cè)到容器中奶栖。
注冊(cè)組件
組件的注冊(cè)過程很簡單匹表,掃描包路徑,獲取所有組件的Class驼抹。然后根據(jù)jsr330的要求桑孩,檢測(cè)組件是否可注冊(cè),將可注冊(cè)的組件注冊(cè)到容器中即可框冀。代碼如下:
private void scan() throws SpringToyException {
Set<Class<?>> candidates = new HashSet<>();
for (String scanPath : scanPaths) {
candidates.addAll(ReflectUtil.getClasses(scanPath));
}
for (Class<?> candidate : candidates) {
if (candidate.isAnnotation() || candidate.isInterface() || Modifier.isAbstract(candidate.getModifiers())) {
continue;
}
String name = getComponentName(candidate);
if (name != null) {
boolean isSingleton = false;
Scope scope = candidate.getAnnotation(Scope.class);
if (scope != null) {
if (ScopeType.SINGLETON.equals(scope.value())) {
isSingleton = true;
} else if (!ScopeType.PROTOTYPE.equals(scope.value())) {
throw new SpringToyException("the value of scope is error !");
}
} else if (candidate.getAnnotation(Singleton.class) != null) {
isSingleton = true;
}
if ("".equals(name)) {
name = this.beanNameGenerator.generateBeanName(candidate);
}
BeanDefinition beanDefinition = new BeanDefinition(candidate, isSingleton, name);
beanDefinition.setInjectorProvider(new InjectorProvider(candidate, this.beanNameGenerator));
beanContainer.register(beanDefinition.getName(), beanDefinition);
}
}
Map<String, BeanDefinition> beanDefinationMap = beanContainer.getBeanDefinations();
Resolver resolver = new Resolver(beanContainer);
for (Map.Entry<String, BeanDefinition> entry : beanDefinationMap.entrySet()) {
resolver.resolve(entry.getValue());
}
}
完整的代碼在context包下的AnnotationApplicationContext類中可以查看流椒。
依賴信息的管理
通過上文的介紹,我們使用BeanDefinition描述了一個(gè)組件Bean的基本信息明也,但是我們還有一樣重要的信息沒有描述——組件依賴信息宣虾。組件類之間是有著依賴的關(guān)系的,BeanDefinition并沒有描述組件類的依賴信息温数,為了要完整的描述組件類的信息绣硝,引入InjectorData來描述依賴注入信息。
InjectorData是一個(gè)接口撑刺,可以有多種實(shí)現(xiàn)鹉胖,其定義如下:
public interface InjectorData {
/**
* 設(shè)置注入的bean
*
* @param bean
*/
void setBean(BeanDefinition bean);
/**
* 返回依賴的bean
*
* @return
*/
BeanDefinition getBean();
/**
* 設(shè)置依賴的默認(rèn)名稱
*
* @param defaultName
*/
void setDefaultName(String defaultName);
/**
* 獲取依賴的默認(rèn)名稱
*
* @return
*/
String getDefaultName();
/**
* 獲取指定的依賴的名稱
*
* @return
*/
String getRefName();
/**
* 獲取依賴的類型
*
* @return
*/
Class<?> getType();
/**
* 判斷依賴是否匹配
*
* @param beanDefinition
* @return
*/
boolean isMatch(BeanDefinition beanDefinition);
/**
* 是否必須
*
* @return
*/
boolean isRequired();
/**
* 設(shè)置是否是注入器
*
* @param provider
*/
void setProvider(boolean provider);
/**
* 是否是注入器
*
* @return
*/
boolean isProvider();
/**
* 設(shè)置注入器類型
*
* @param providedType
*/
void setProvidedType(Class<?> providedType);
}
根據(jù)具體實(shí)現(xiàn)類的不同,可以用來描述不同的依賴注入信息,包括字段依賴注入信息甫菠,參數(shù)注入信息挠铲。繼承關(guān)系圖如下:
其中,抽象父類中實(shí)現(xiàn)了一些通用的方法寂诱,部分代碼如下拂苹,省略一些get,set方法:
public abstract class AbstractInjectorData implements InjectorData {
/**
* 默認(rèn)依賴名稱
*/
private String defalultName;
/**
* 指定依賴名稱
*/
private String refName;
/**
* 依賴的BeanDefination實(shí)例
*/
private BeanDefinition bean;
/**
* 是否必須
*/
private boolean isRequired;
/**
* 是否是Provider或者BeanFactory依賴
*/
private boolean isProvider;
/**
* Provider或者BeanFactory提供的依賴類
*/
private Class<?> providedType;
public AbstractInjectorData(String defalultName, String refName, boolean isRequired) {
this.defalultName = defalultName;
this.refName = refName;
this.isRequired = isRequired;
}
@Override
public void setDefaultName(String defaultName) {
this.defalultName = defaultName;
}
@Override
public String getDefaultName() {
return defalultName;
}
@Override
public String getRefName() {
return refName;
}
@Override
public void setBean(BeanDefinition bean) {
this.bean = bean;
}
@Override
public BeanDefinition getBean() {
return this.bean;
}
@Override
public boolean isRequired() {
return isRequired;
}
@Override
public boolean isMatch(BeanDefinition beanDefinition) {
if (refName != null && refName.equals(beanDefinition.getName())) {
return true;
} else if (defalultName.equals(beanDefinition.getName())) {
return true;
} else {
Class<?> type = getType();
return beanDefinition.isType(type);
}
}
@Override
public void setProvider(boolean provider) {
isProvider = provider;
}
@Override
public boolean isProvider() {
return isProvider;
}
@Override
public void setProvidedType(Class<?> providedType) {
this.providedType = providedType;
}
protected Class<?> getProvidedType() {
return providedType;
}
}
其子類實(shí)現(xiàn)也是很簡單痰洒,將標(biāo)記的依賴字段或者參數(shù)瓢棒,傳入相應(yīng)的依賴描述里面保存下來即可:
/**
* bean的依賴信息
*
* @author bdq
* @date 2019-02-12
*/
public class FieldInjectorData extends AbstractInjectorData {
private Field field;
public FieldInjectorData(String defalultName, String refName, boolean required, Field field) {
super(defalultName, refName, required);
this.field = field;
}
@Override
public Class<?> getType() {
if (isProvider()) {
return getProvidedType();
}
return field.getType();
}
public Field getField() {
return field;
}
}
/**
* @author bdq
* @date 2019-02-13
*/
public class ParameterInjectorData extends AbstractInjectorData {
private Parameter parameter;
public ParameterInjectorData(String defalultName, String refName, boolean required, Parameter parameter) {
super(defalultName, refName, required);
this.parameter = parameter;
}
@Override
public Class<?> getType() {
if (isProvider()) {
return getProvidedType();
}
return parameter.getType();
}
}
依賴注入器
什么是依賴注入器,依賴注入器是筆者自己定義的一個(gè)接口Injector丘喻,它的功能是負(fù)責(zé)管理依賴信息脯宿,進(jìn)行依賴注入。之所以定義這個(gè)接口仓犬,是因?yàn)橐蕾囎⑷胗兄N場(chǎng)景:字段注入嗅绰,構(gòu)造器注入,方法注入搀继。不同的注入方式有不同的實(shí)現(xiàn)方式窘面,于是引入Injector,分別實(shí)現(xiàn)對(duì)應(yīng)三種場(chǎng)景的注入器叽躯,Injector的實(shí)現(xiàn)應(yīng)該持有對(duì)應(yīng)的注入信息财边。
Injector接口的定義如下:
/**
* @author bdq
* @date 2019-02-14
*/
public interface Injector {
/**
* 判斷當(dāng)前bean是否依賴beanDefination,如果是点骑,返回true酣难,否則返回false
*
* @param beanDefinition
* @return boolean
*/
boolean hasDependence(BeanDefinition beanDefinition);
/**
* 注入依賴
*
* @param instance
* @param beanDefinition
* @return
* @throws InjectedException
*/
Object inject(Object instance, BeanDefinition beanDefinition) throws InjectedException;
}
繼承關(guān)系圖如下:
AbstractInjector是Injector的抽象實(shí)現(xiàn)類,實(shí)現(xiàn)了一些通用的方法黑滴,代碼如下:
/**
* @author bdq
* @date 2019-02-14
*/
public abstract class AbstractInjector implements Injector {
protected List<InjectorData> injectorDatas;
public AbstractInjector(List<InjectorData> injectorDatas) {
this.injectorDatas = injectorDatas;
}
@Override
public boolean hasDependence(BeanDefinition beanDefinition) {
for (InjectorData injectorData : injectorDatas) {
if (injectorData.isMatch(beanDefinition)) {
return true;
}
}
return false;
}
}
ConstructorInjector的功能是進(jìn)行構(gòu)造函數(shù)的注入憨募,產(chǎn)生對(duì)象實(shí)例,主要代碼如下:
/**
* 構(gòu)造器注入
*
* @param beanDefinition
* @return
* @throws ConstructorInjectedException
*/
public Object inject(BeanDefinition beanDefinition) throws ConstructorInjectedException {
return inject(null, beanDefinition);
}
@Override
public Object inject(Object instance, BeanDefinition beanDefinition) throws ConstructorInjectedException {
if (constructor != null) {
if (injectorDatas != null && injectorDatas.size() > 0) {
List<Object> args = new LinkedList<>();
//遍歷構(gòu)造函數(shù)的參數(shù)依賴信息
for (InjectorData injectorData : injectorDatas) {
BeanDefinition bean = injectorData.getBean();
try {
if (bean != null) {
//判斷是否是Provider
if (injectorData.isProvider()) {
//添加實(shí)例到Provider參數(shù)
args.add(new ObjectFactory<>(bean.getInstance()));
} else {
//添加實(shí)例作為參數(shù)
args.add(bean.getInstance());
}
}
} catch (InjectedException e) {
throw new ConstructorInjectedException(String.format("failed to inject entity: %s by constructor!", beanDefinition.getName()), e);
}
}
try {
if (args.size() > 0) {
//反射調(diào)用構(gòu)造器袁辈,構(gòu)造對(duì)象實(shí)例
instance = constructor.newInstance(args.toArray());
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new ConstructorInjectedException(String.format("failed to inject entity: %s by constructor!", beanDefinition.getName()), e);
}
}
}
return instance;
}
另外兩個(gè)注入器的實(shí)現(xiàn)原理與之類似菜谣,只不過反射調(diào)用的方法不同罷了,就不再貼出代碼了晚缩。
依賴解析
依賴注入器實(shí)現(xiàn)了依賴注入的過程尾膊,而依賴解析的過程并沒有體現(xiàn)。依賴解析的步驟荞彼,筆者認(rèn)為不屬于注入器功能上的定義冈敛,依賴注入器應(yīng)該只關(guān)注于進(jìn)行依賴注入,所以筆者將這部分代碼放在了Resolver中鸣皂。
在Resolver中抓谴,會(huì)對(duì)依賴進(jìn)行解析暮蹂,查詢依賴的Bean,設(shè)置依賴信息齐邦。其主要代碼如下:
public void resolve(BeanDefinition beanDefinition) throws SpringToyException {
//如果已經(jīng)解析過了椎侠,則返回
if (beanDefinition.isResolved()) {
return;
}
//優(yōu)先解析父類
Class<?> superClass = beanDefinition.getClazz().getSuperclass();
if (superClass != null && superClass != Object.class) {
for (BeanDefinition bean : beanContainer.getBeans(superClass).values()) {
if (bean != beanDefinition) {
//遞歸解析父類
resolve(bean);
}
}
}
InjectorProvider injectorProvider = beanDefinition.getInjectorProvider();
if (injectorProvider != null) {
//如果有構(gòu)造器注入,則先解析構(gòu)造器注入依賴
if (injectorProvider.getConstructorParameterDatas() != null) {
for (InjectorData parameterInjectorData : injectorProvider.getConstructorParameterDatas()) {
doResolve(beanDefinition, injectorProvider, parameterInjectorData, parameterInjectorData.isRequired());
}
}
//如果有字段注入措拇,則解析字段注入依賴
if (injectorProvider.getFieldInjectorDatas() != null) {
for (InjectorData fieldInjectorData : injectorProvider.getFieldInjectorDatas()) {
doResolve(beanDefinition, injectorProvider, fieldInjectorData, fieldInjectorData.isRequired());
}
}
//如果有方法注入,則解析方法注入依賴
if (injectorProvider.getMethodInjectorAttributes() != null) {
for (MethodInjectorAttribute methodInjectorAttribute : injectorProvider.getMethodInjectorAttributes()) {
if (methodInjectorAttribute.getParameterInjectorDatas() != null) {
for (InjectorData parameterInjectorData : methodInjectorAttribute.getParameterInjectorDatas()) {
doResolve(beanDefinition, injectorProvider, parameterInjectorData, methodInjectorAttribute.isRequired());
}
}
}
}
}
beanDefinition.setResolved(true);
}
private void doResolve(BeanDefinition beanDefinition, InjectorProvider injectorProvider, InjectorData injectorData, boolean isRequired) throws UnsatisfiedBeanException {
BeanDefinition ref = null;
Map<String, BeanDefinition> beanDefinationMap = beanContainer.getBeanDefinations();
//判斷依賴組件是否存在慎宾,先查找指定名稱的依賴丐吓,如果不存在,則按找默認(rèn)名稱去查找趟据,仍然不存在券犁,則再按類型匹配
if (injectorData.getRefName() != null && beanDefinationMap.containsKey(injectorData.getRefName())) {
ref = beanDefinationMap.get(injectorData.getRefName());
} else if (beanDefinationMap.containsKey(injectorData.getDefaultName())) {
ref = beanDefinationMap.get(injectorData.getDefaultName());
} else {
for (BeanDefinition bean : beanDefinationMap.values()) {
if (bean.isType(injectorData.getType())) {
ref = bean;
break;
} else if (bean.isSubType(injectorData.getType())) {
ref = bean;
break;
}
}
}
//判斷依賴是否存在,如果不存在汹碱,則拋出異常粘衬。如果依賴存在,但有相互引用的情況咳促,也拋出異常
if (ref == null) {
if (isRequired) {
throw new UnsatisfiedBeanException("unsatisfied entity , the entity named " + injectorData.getType() + " don't exists");
}
} else if (beanDefinition == ref || injectorProvider.hasDependence(beanDefinition)) {
throw new UnsatisfiedBeanException("unsatisfied entity , there two entity ref each other !");
} else {
//設(shè)置依賴信息
injectorData.setBean(ref);
}
}
至此稚新,一個(gè)簡單的依賴注入框架完成了。這個(gè)框架還有很多需要完善的地方跪腹,比如效率的優(yōu)化褂删,更多安全性的檢查等等。
如果對(duì)筆者的框架感興趣的冲茸,可以點(diǎn)擊:https://github.com/bdqfork/spring-toy 查看完整代碼屯阀,運(yùn)行example進(jìn)行測(cè)試,希望大家批評(píng)指正轴术。