一、需求
實作一個簡易的IOC容器,管理Bean,從IOC容器的BeanFactory中擷取執行個體,進而取代自己new執行個體的做法。
二、實作步驟分析
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISM9AnYldnJwAzN9c3PnBnauQ0MlQ0MlcnW3BXbMBTVE50MRpWT0EFRNlHMD10MRRUT4FFVOVTQE9EejRUT1UERNlHM510MRR1T6FFVNZ3YE1UNFRUT5hTeNNTUU9keRRVT2NmMiNnSywEd5ITW110MaZHetlVdO1GT0UERNl3YXJGc5kHT20ESjBjUIF2Lc12bj5SYphXa5VWen5WY35iclN3Ztl2Lc9CX6MHc0RHaiojIsJye.jpg)
三、具體代碼實作
自定義注解類 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 }