Erlo

java代码实现简易版IOC容器,含IOC容器实现步骤分解

2019-07-18 11:02:56 发布   445 浏览  
页面报错/反馈
收藏 点赞

一、需求

  实现一个简易的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

 

登录查看全部

参与评论

评论留言

还没有评论留言,赶紧来抢楼吧~~

手机查看

返回顶部

给这篇文章打个标签吧~

棒极了 糟糕透顶 好文章 PHP JAVA JS 小程序 Python SEO MySql 确认