1. 程式人生 > >自己實現IOC容器,java程式碼實現簡易版IOC容器,IOC容器實現的步驟分解

自己實現IOC容器,java程式碼實現簡易版IOC容器,IOC容器實現的步驟分解

一、需求

  實現一個簡易的IOC容器,管理Bean,從IOC容器的BeanFactory中獲取例項,從而取代自己new例項的做法。

二、實現步驟分析

三、具體程式碼實現

  自定義註解類 MyComponent 和 MyAutowired:

 1 package MyIOCAndMyAop.Annotations;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 @Target(ElementType.TYPE)
 9 @Retention(RetentionPolicy.RUNTIME)
10 public @interface MyComponent {
11     
12 } 
 1 package MyIOCAndMyAop.Annotations;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 @Target(ElementType.FIELD)
 9 @Retention(RetentionPolicy.RUNTIME)
10 public @interface MyAutowired {
11     
12 }

 

  MyIOC容器的實現:

 

    自己實現簡單的IOC容器,來管理bean:BeanFactory<String, Object>,String為全類名,Object為通過類載入器載入進來的Class物件反射建立的bean。

 

  1 package MyIOCAndMyAop;
  2 
  3 import java.io.File;
  4 import java.lang.annotation.Annotation;
  5 import java.lang.reflect.Field;
  6 import java.lang.reflect.InvocationTargetException;
  7 import java.lang.reflect.Method;
  8 import java.net.MalformedURLException;
  9 import java.net.URL;
 10 import java.net.URLClassLoader;
 11 import java.util.ArrayList;
 12 import java.util.HashMap;
 13 import java.util.Map;
 14 import MyIOCAndMyAop.Annotations.MyAutowired;
 15 import MyIOCAndMyAop.Annotations.MyComponent;
 16 
 17 public class MyIOC {
 18 
 19     // beanFactory 要宣告為類變數,因為它不能被GC回收:
 20     private static HashMap<String, Object> beanFactory = new HashMap<>();
 21     
 22     /**
 23      * 隨著MyIOC類被載入到記憶體進行初始化,就會執行其靜態程式碼塊
 24      * @param args
 25      */
 26     static {
 27         init();
 28     }
 30     
 31     /**
 32      * 獲取BeanFactory
 33      * @return
 34      */
 35     public static HashMap<String, Object> getBeanFactory(){
 36         return beanFactory;
 37     }
 38     
 39     /**
 40      * 根據全類名更新BeanFactory中的bean
 41      * @param typeName
 42      * @param proxyInstance
 43      */
 44     public static void updateBeanFromBeanFactory(String typeName, Object proxyInstance) {
 45         beanFactory.put(typeName, proxyInstance);
 46     }
 47     
 48     /**
 49      * 通過全類名獲得對應的例項
 50      * @param completeClassName
 51      * @return
 52      */
 53     public static Object getBean(String completeClassName) {
 54         return beanFactory.get(completeClassName);
 55     }
 56     
 57     public static void init() {
 58         HashMap<String, Class> loadedClazzList;//<全類名, Class物件>
 59         try {
 60             //1.載入指定的類
 61             File file = new File("C:\\workplace\\test\\bin");//!!!這裡寫死了路徑不合適,可以做改進
 62             loadedClazzList = loadAllClazz(file);
 63             
 64             //2.例項化並放入IOC容器中:對於那些有註解的類,做例項化
 65             newInstance(loadedClazzList);
 66             
 67             // 3.完成依賴注入
 68             autoWired();
 69             
 70             // 4.測試:找到beanFactory中的某個bean,並執行其某個方法 ===> 這裡有個問題,只能執行指定的方法,所以beanFactory中的所有bean都得有這個方法,這裡先這麼做了,但這明顯不合理。
 71 //            test();
 72         } catch (Exception e) {
 73             e.printStackTrace();
 74         }
 75     }
 76     
 77     public static void test() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 78         for(Map.Entry<String, Object> entry : beanFactory.entrySet()) {
 79 //            System.out.println(entry.getKey() + " ---> ");
 80             Method method = entry.getValue().getClass().getMethod("test");
 81             method.invoke(entry.getValue());
 82         }
 83     }
 84     
 85     /**
 86      * 對BeanFactory中管理的所有bean完成依賴注入。
 87      * 交給IOC容器管理的類,需要注入成員變數,如果該成員變數是自定義的類,該類也是需要交給IOC容器管理的。
 88      * @throws IllegalAccessException 
 89      * @throws IllegalArgumentException 
 90      * @throws MalformedURLException 
 91      * @throws ClassNotFoundException 
 92      */
 93     public static void autoWired() throws IllegalArgumentException, IllegalAccessException, ClassNotFoundException, MalformedURLException {
 94         for(Map.Entry<String, Object> entry :  beanFactory.entrySet()) {
 95             Field[] fields = entry.getValue().getClass().getDeclaredFields();//!!!getFields():只能獲取到執行時類中及其父類中宣告為public的屬性;getDeclaredFields():獲取執行時類本身宣告的所有的屬性
 96             for(Field field : fields) {
 97                 Annotation[] annotations = field.getAnnotations();
 98                 for(int i = 0; i < annotations.length; i++) {
 99                     if(annotations[i].annotationType() == MyAutowired.class) {
100                         //從beanFactory中找到相應的bean,賦值給該成員變數,以完成依賴注入。
101                         Object object = beanFactory.get(field.getType().getTypeName());
102 //                        System.out.println(field.getType().getTypeName());//MyIOCAndMyAop.bean.Student
103                         //通過Field(反射)為成員變數賦值:
104                         field.setAccessible(true);
105                         field.set(entry.getValue(), object);
106                     }
107                 }
108             }
109         }
110     }
111     
112     /**
113      * 例項化: 放到loadedClazzlist集合中的Class物件都是需要做例項化的(加了@MyComponent註解的類)
114      */
115     public static void newInstance(HashMap<String, Class> loadedClazzList) throws InstantiationException, IllegalAccessException, ClassNotFoundException, MalformedURLException {
116         for(Map.Entry<String, Class> entry : loadedClazzList.entrySet()) {
117             beanFactory.put(entry.getKey(), entry.getValue().newInstance());
118         }
119     }
120     
121     /**
122      * 載入指定路徑下的類。
123      * 類載入:javase/src/classLoader/a01helloworld/A03GetExtClassLoader
124      * @return 類載入器載入進來的指定路徑下的所有Class物件
125      * @throws IllegalAccessException 
126      * @throws InstantiationException 
127      */
128     public static HashMap<String, Class> loadAllClazz(File file) throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException {
129         //用於存放類載入器載入進來的Class物件<全類名, Class物件>
130         HashMap<String, Class> loadedClazzList = new HashMap<>();
131         
132         URL[] urls = new URL[]{file.toURI().toURL()};
133         URLClassLoader classLoader = new URLClassLoader(urls);
134         
135         ArrayList<String> allCompleteClassName = getAllCompleteClassName(file);
136         
137         for(String element : allCompleteClassName) {
138             Class<?> clazz = classLoader.loadClass(element);
139             Annotation[] annotations = clazz.getAnnotations();// !!!拿到Class物件的時候,就進行篩選出有註解的Class再放到容器中,而不是把指定路徑下的所有類都載入進來。
140             for(int i = 0; i < annotations.length; i++) {
141                 if(annotations[i].annotationType() == MyComponent.class) {
142                     loadedClazzList.put(element, clazz);//得到各個類物件了
143                 }
144             }
145         }
146         return loadedClazzList;
147     }
148     
149     /**
150      * 得到allNeedLoadClassFiles中所有要載入的class檔案的全類名
151      */
152     private static ArrayList<String> getAllCompleteClassName(File file) {
153         // 所有要載入的class的全類名,如:classLoader.a02myclassloader.bean.Bean
154         ArrayList<String> completeClassNames = new ArrayList<>();
155         // 用於存放指定路徑下所有要載入的class檔案
156         ArrayList<File> allNeedLoadClassFiles = new ArrayList<File>();
157         
158         getAllNeedLoadClassFile(file, allNeedLoadClassFiles);
159         
160         for (File element : allNeedLoadClassFiles) {
161             String filePath = element.getPath().replace("\\", ".");
162             
163             if(filePath.endsWith(".class")) {
164                 //filePath.indexOf("bin.")+4:"bin."之後。filePath.lastIndexOf(".class"):".class"之前,該方法是從後往前找,效能更高。
165                 String completeClassName = filePath.substring(filePath.indexOf("bin.")+4, filePath.lastIndexOf(".class"));
166                 completeClassNames.add(completeClassName);
167             }
168         }
169         return completeClassNames;
170     }
171     
172     /**
173      * 通過遞迴獲取指定路徑下所有要載入的class檔案
174      * 遞迴:javase/src/recursion/A_PrintFolder
175      * @param file
176      */
177     private static ArrayList<File> getAllNeedLoadClassFile(File file, ArrayList<File> allNeedLoadClassFiles) {
178         if(!file.exists()) {//!!!這裡要多一層判斷
179             return allNeedLoadClassFiles;
180         }
181         
182         if (file.isDirectory()) {//是資料夾
183             File[] listFiles = file.listFiles();
184             for (File element : listFiles) {
185                 getAllNeedLoadClassFile(element, allNeedLoadClassFiles);
186             }
187         } else {//是檔案
188             allNeedLoadClassFiles.add(file);
189         }
190         return allNeedLoadClassFiles;
191     }
192 }

&n