手动模拟实现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源码的童鞋以帮助,抛砖引玉。实力有限,欢迎各位专家同仁批评指正。