手动模拟实现Spring IOC功能(基于javaConfig风格)

  以下文中spring特指spring frameWork项目,不含其它:如spring cloud等。

  作为刚开始研究spring源码的小白,对于spring两大核心功能之一的IOC,虽说大致了解了Bean的注册与实例化过程、PostProcessors对于bean的实例化的干预等,还是觉得自己要手动实践下IOC功能,算是考验下自己是否对spring源码有了初步的入门。此次模拟基于annotation风格,也比较符合现在spring的开发主流趋势。由于是手动实现,当然不能通过springboot构建项目,采用idea或eclipse创建一个普通的web工程吧。按照开发的先后顺序吧。

  自定义的几个注解

  熟悉基于配置的风格的童鞋都知道,spring里面最最常用的注解@ComponentScan,@Component和@Autowired。稍稍说下这三个注解的作用,@ComponentScan:对于指定的目录下的Bean的扫描。@Component:描述一个普通的pojo,告知spring上下文这个pojo需要被spring容器所管理,也就是spring 中的bean。@Autowired:Bean中属性的自动注入,默认注入是按照bytype。那么,照葫芦画瓢,对应的定义了下面三个注解

 1 @Retention(RetentionPolicy.RUNTIME)
 2 public @interface MyAutowired {
 3     String value() default "";
 4 }
 5 
 6 @Retention(RetentionPolicy.RUNTIME)
 7 public @interface MyComponent {
 8     String value();
 9 }
10 
11 @Retention(RetentionPolicy.RUNTIME)
12 public @interface MyComponentScan {
13     String value();
14 }

  注意,每个自定义注解至少应该添加注解@Retention,并且指定value是runtime。@Retention是用来修饰注解的注解,也就是元注解。RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。

  定义几个pojo

  一个是配置类MyConfigure,用于指定扫描路径,功能单一。加上自定义的注解@MyComponentScan。指定扫描com.entity路径下的类。

1 package com.entity;
2 import com.annotate.MyComponentScan;
3 
4 @MyComponentScan("com.entity")
5 public class MyConfigure {
6 
7 }

  两个简单的实体类Order和User,加上自定义注解@MyComponent。其中Order中有个成员属性User,加入注解@MyAutowired。

 1 package com.entity;
 2 
 3 import com.annotate.MyAutowired;
 4 import com.annotate.MyComponent;
 5 
 6 @MyComponent("myComponent")
 7 public class Order {
 8     @MyAutowired
 9     private User user;
10 
11     public void print(){
12         System.out.println("This is order");
13     }
14 
15     public void printUser(){
16         this.user.printMessage();
17     }
18 }
 1 package com.entity;
 2 
 3 import com.annotate.MyComponent;
 4 
 5 @MyComponent("myComponent")
 6 public class User {
 7     public void print(){
 8         System.out.println("This is user");
 9     }
10 
11     public void printMessage(){
12         System.out.println("This is user from order");
13     }
14 }

  spring上下文的模拟

  MySpringApplicationContext实现的spring上下文,主要包含了根据路径扫描的注册Bean方法和getBean过程,也包含一个主要的成员属性map,用于存放上下文中被注册并实例化的Bean。

 1 package com.myspring;
 2 
 3 import com.annotate.MyAutowired;
 4 import com.annotate.MyComponent;
 5 import com.annotate.MyComponentScan;
 6 
 7 import java.io.File;
 8 import java.lang.reflect.Field;
 9 import java.util.HashMap;
10 import java.util.Iterator;
11 import java.util.Map;
12 
13 public class MySpringApplicationContext {
14     /**
15      * 存放bean的map(模仿spring beanfactory 中的map)
16      */
17     private HashMap<String,Object> beanFactoryMap;
18 
19     public MySpringApplicationContext(Class clazz){
20         beanFactoryMap = new HashMap<String, Object>();
21         this.packageScan(clazz);
22     }
23 
24 
25     public void packageScan(Class configureClazz){
26         //获取扫描路径
27         MyComponentScan myComponentScan = (MyComponentScan) configureClazz.getAnnotation(MyComponentScan.class);
28         if(myComponentScan == null){
29             return;
30         }
31         /**
32          * 获取待扫描的目录
33          */
34         String packageName = myComponentScan.value();
35         String rootPath = this.getClass().getResource("/").getPath();
36         String packagePath = packageName.replaceAll("\\.","/");
37         /**
38          * 获取扫描目录的绝对地址
39          */
40         String classFilePath = rootPath + packagePath;
41         File file = new File(classFilePath);
42         String[] files = file.list();
43         if(files != null && files.length>0){
44             for(String subFile:files){
45                 try {
46                     String beanName = subFile.split("\\.")[0];
47                     Class clazz = Class.forName(packageName +"."+ beanName);
48                     if(clazz.isAnnotationPresent(MyComponent.class)){
49                         Object object = clazz.newInstance();
50                         beanFactoryMap.put(beanName.toLowerCase(),object);
51                     }
52                 } catch (Exception e) {
53                     e.printStackTrace();
54                 }
55             }
56         }
57         //处理注入的问题(模拟@Autowired)
58         if(!beanFactoryMap.isEmpty()){
59             Iterator iterator = beanFactoryMap.entrySet().iterator();
60             while (iterator.hasNext()){
61                 try {
62                     Map.Entry entry = (Map.Entry) iterator.next();
63                     Class clazz = entry.getValue().getClass();
64                     Field[] fields = clazz.getDeclaredFields();
65                     if(fields != null && fields.length > 0){
66                         for(Field field:fields){
67                             field.setAccessible(true);
68                             MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
69                             if(myAutowired != null){
70                                 field.set(entry.getValue(),beanFactoryMap.get(field.getName()));
71                             }
72                         }
73                     }
74                 } catch (Exception e) {
75                     e.printStackTrace();
76                 }
77             }
78         }
79 
80     }
81 
82     public Object getBean(String beanName){
83         return this.beanFactoryMap.get(beanName);
84     }
85 
86 }

  测试

  主要测试内容是Bean的成功注册并实例化,Bean的成功注入。

 1 package com.test;
 2 
 3 import com.entity.User;
 4 import com.myspring.MySpringApplicationContext;
 5 import com.entity.MyConfigure;
 6 import com.entity.Order;
 7 public class Test {
 8     public static void main(String[] args) {
 9         MySpringApplicationContext mySpringApplicationContext = new MySpringApplicationContext(MyConfigure.class);
10         User user = (User) mySpringApplicationContext.getBean("user");
11         user.print();
12         Order order = (Order) mySpringApplicationContext.getBean("order");
13         order.print();
14         /**
15          * 用于验证Order中是否成功注入User对象
16          */
17         order.printUser();
18     }
19 }

  结果输出

1 This is user
2 This is order
3 This is user from order

  以上是手动实现spring IOC功能的全过程,当然这只是spring机制的皮毛的皮毛,希望给有欲望去了解,但还未实施了解spring源码的童鞋以帮助,抛砖引玉。实力有限,欢迎各位专家同仁批评指正。

 

posted @ 2019-11-19 19:55  剑眉枉凝  阅读(...)  评论(...编辑  收藏