運用反射機制和自定義注解模擬實作IOC容器,使其具有自動加載、自動裝配和根據全限定類名獲取Bean的功能,
一. 實作原理
1-1 IOC容器的本質
IOC容器可理解為是一個map,其中的一個entry可理解為一個component(組件),entry中的key為beanId(全限定類名),entry中的value為bean(類對應的物件);

具體的體現為:

1-2 自動加載
-
自動加載是指IOC容器會自動加載被@Component等(以下用@Component為例)注解標記的類,使其成為IOC容器中的一個組件;
-
@Component注解加在一個類前,表示此類被IOC容器管理,成為IOC容器中的一個組件;
-
在自動加載時,容器會自動掃描給定包路徑所對應的包及其子包下的所有類,判斷類是否是介面,如果不是介面就再判斷是否被@Component標記,如果被標記了就將其添加到IOC容器中,key為該類的全限定類名,value為該類的物件,具體流程如圖:

1-3 自動裝配
-
自動裝配是指IOC容器會自動裝配容器中各個bean中被@Autowired注解標記屬性的屬性值;
-
@Autowired注解加在類中的一個屬性前,表示此屬性的值需要被自動裝配;
-
待自動加載完成后,容器會根據keySet中的全限定類名遍歷容器中各個類的各個屬性,判斷屬性是否被@Autowired注解標記,如果被標記了,就會根據屬性的型別的全限定類名(beanId)從容器中找到對應的bean,然后將找到的bean 的參考賦值給對應屬性(模擬bean的scope為singleton),具體的流程如圖:

二. 具體實作
2-1 模擬情形
IOC容器自動加載com.hutao.springioc包及其子包下的所有類,并自動完成各類對應bean的屬性裝配,
2-2 目錄結構

-
Autowired, Component為自定義注解;
-
Cat, Dog, User為物體類;
-
IocContainer為IOC容器;
-
Demo為測驗,
2-3 實作
Autowired.java
package com.hutao.springioc.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
Component.java
package com.hutao.springioc.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface Component { }
Cat.java
package com.hutao.springioc.model; import com.hutao.springioc.annotation.Component; @Component public class Cat { public void mew(){ System.out.println("Meow Meow Meow..."); } }
Dog.java
package com.hutao.springioc.model; import com.hutao.springioc.annotation.Component; @Component public class Dog { public void bark(){ System.out.println("Wow wow wow..."); } }
User.java
package com.hutao.springioc.model; import com.hutao.springioc.annotation.Autowired; import com.hutao.springioc.annotation.Component; @Component public class User { @Autowired private Dog dog; @Autowired private Cat cat; public void chat(){ System.out.println("This is my dog and cat."); dog.bark(); cat.mew(); } }
IocContainer.java
package com.hutao.springioc; import com.hutao.springioc.annotation.Autowired; import com.hutao.springioc.annotation.Component; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.net.URL; import java.util.*; /** * 模擬Ioc容器,實作自動加載組件、自動裝配、根據類的全限定名獲取Bean * * @author Hutao * @createDate 2020/11/14 */ public class IocContainer { //Ioc容器 存盤的鍵值對為 <類的完全限定名稱,該類的一個物件> private Map<String, Object> container = new HashMap<String, Object>(); //Ioc容器可掃描到該包及其子包下的所有類 private String packageName; public IocContainer(String packageName) { this.packageName = packageName; try{ //添加組件到容器 loadComponent(); //裝配組件 assemble(); }catch (Exception e){ e.printStackTrace(); } } /** * 將制定包及其子包下的所有組件加載到容器中 * * @author Hutao * @createDate 2020/11/14 */ private void loadComponent() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { //用于獲取包對應的URL,而URL中的分隔符為“/”, 所以將包路徑的分隔符“.” 用“/”代替 String packagePath = packageName.replace(".","/"); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); URL resource = classLoader.getResource(packagePath); //通過獲取此資源的協議名稱,判斷包名對應的資源型別 String protocol = resource.getProtocol(); if(!"file".equals(protocol)){ //只測驗protocol為file的情況,其他情況還有jar等 return; } //獲取了指定包及其子包下所對應的所有以suffix(.class)結尾的檔案路徑 List<String> filePathList = listFilePath(resource.getPath()); //獲取類的完全限定名稱 List<String> fullClassNameList = listFullClassName(filePathList); //加載component到容器中 for (String fullClassName : fullClassNameList) { addToContainer(fullClassName); } } /** * 獲取指定檔案夾下所有以.class結尾檔案的抽象路徑名的規范路徑 * @param directoryPath 指定檔案夾的路徑 * @return 如獲取到:包含符合條件的檔案的規范路徑的List * 如未獲取到:空的List * * @author Hutao * @createDate 2020/11/14 */ private List<String> listFilePath(String directoryPath) throws IOException { List<String> filePathList = new ArrayList<String>(); //引數校驗 if(null==directoryPath){ return filePathList; } File directoryFile = new File(directoryPath); if(!directoryFile.isDirectory()){ return filePathList; } String filePath = null; File[] files = directoryFile.listFiles(); for (File file : files) { if(!file.isDirectory()){ filePath = file.getCanonicalPath(); if(filePath.endsWith(".class")){ filePathList.add(filePath); } } else{ //遞回呼叫 filePathList.addAll(listFilePath(file.getCanonicalPath())); } } return filePathList; } /** * 根據.class檔案的規范路徑獲取其類的全限定名 * @param filePathList .class檔案的規范路徑List * @return 如獲取到:包含類的全限定名的的List * 如未獲取到:空的List * * @author Hutao * @createDate 2020/11/14 */ private List<String> listFullClassName(List<String> filePathList){ List<String> fullClassNameList = new ArrayList<String>(); if(null==packageName||null==filePathList){ return fullClassNameList; } String packagePath = packageName.replace(".","\\"); for (String filePath : filePathList) { fullClassNameList.add(filePath.substring(filePath.indexOf(packagePath),filePath.indexOf(".class")).replace("\\",".")); } return fullClassNameList; } /** * 根據類的全限定名判斷該類是否被標記為容器的組件,如果是則將組件添加到容器中 * @param fullClassName 類的全限定名 * * @author Hutao * @createDate 2020/11/14 */ private void addToContainer(String fullClassName) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class classObject = Class.forName(fullClassName); if(!classObject.isInterface() &&null!=classObject.getAnnotation(Component.class)){ //如果掃描的Class物件不是介面型別且有@Component注解,就將對應的component裝配到容器中 container.put(fullClassName,classObject.newInstance()); } } /** * 自動裝配組件的屬性值 * * @author Hutao * @createDate 2020/11/14 */ private void assemble() throws IllegalAccessException, ClassNotFoundException { Set<Map.Entry<String, Object>> entrySet = container.entrySet(); for (Map.Entry<String, Object> entry : entrySet) { Class classObj = Class.forName(entry.getKey()); Field[] declaredFieldArray = classObj.getDeclaredFields(); for (Field field : declaredFieldArray) { if(null!=field.getAnnotation(Autowired.class)){ //如果屬性被@Autowired注解標注,則根據屬性的型別名進行自動裝配 field.setAccessible(true); String beanId = field.getType().getName(); field.set(entry.getValue(),container.get(beanId)); } } } } /** * 根據類的全限定名從Ioc容器中獲取對應的Bean * @param fullClassName * @return * * @author Hutao * @createDate 2020/11/14 */ public Object getBean(String fullClassName){ if(null==fullClassName){ return null; } return container.get(fullClassName); } }
Demo.class
package com.hutao.springioc; import com.hutao.springioc.model.User; import java.io.IOException; public class Demo { public static void main(String[] args) throws IOException, ClassNotFoundException { IocContainer iocContainer = new IocContainer("com.hutao.springioc"); User user = (User)iocContainer.getBean(User.class.getName()); user.chat(); } }
測驗結果為:
//console輸出: This is my dog and cat. Wow wow wow... Meow Meow Meow... Process finished with exit code 0
注意:
1. 以上IOC容器的實作原理只是基本的原理,甚至未查看原始碼進行驗證,僅用于初步理解;
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/228026.html
標籤:其他
