一、需求
实现一个简易的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 }