一、需求
實現一個簡易的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 }
自己實現AOP 1.0版本,含步驟分解:https://www.cnblogs.com/laipimei/p/11137250.html
自己實現SpringAOP 2.0版本,含實現步驟分解:https://www.cnblogs.com/laipimei/p/11163377.html