以下文中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。那么,照葫芦画瓢,对应的定义了下面三个注解

 @Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
String value() default "";
} @Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
String value();
} @Retention(RetentionPolicy.RUNTIME)
public @interface MyComponentScan {
String value();
}

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

  定义几个pojo

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

 package com.entity;
import com.annotate.MyComponentScan; @MyComponentScan("com.entity")
public class MyConfigure { }

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

 package com.entity;

 import com.annotate.MyAutowired;
import com.annotate.MyComponent; @MyComponent("myComponent")
public class Order {
@MyAutowired
private User user; public void print(){
System.out.println("This is order");
} public void printUser(){
this.user.printMessage();
}
}
 package com.entity;

 import com.annotate.MyComponent;

 @MyComponent("myComponent")
public class User {
public void print(){
System.out.println("This is user");
} public void printMessage(){
System.out.println("This is user from order");
}
}

  spring上下文的模拟

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

 package com.myspring;

 import com.annotate.MyAutowired;
import com.annotate.MyComponent;
import com.annotate.MyComponentScan; import java.io.File;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; public class MySpringApplicationContext {
/**
* 存放bean的map(模仿spring beanfactory 中的map)
*/
private HashMap<String,Object> beanFactoryMap; public MySpringApplicationContext(Class clazz){
beanFactoryMap = new HashMap<String, Object>();
this.packageScan(clazz);
} public void packageScan(Class configureClazz){
//获取扫描路径
MyComponentScan myComponentScan = (MyComponentScan) configureClazz.getAnnotation(MyComponentScan.class);
if(myComponentScan == null){
return;
}
/**
* 获取待扫描的目录
*/
String packageName = myComponentScan.value();
String rootPath = this.getClass().getResource("/").getPath();
String packagePath = packageName.replaceAll("\\.","/");
/**
* 获取扫描目录的绝对地址
*/
String classFilePath = rootPath + packagePath;
File file = new File(classFilePath);
String[] files = file.list();
if(files != null && files.length>0){
for(String subFile:files){
try {
String beanName = subFile.split("\\.")[0];
Class clazz = Class.forName(packageName +"."+ beanName);
if(clazz.isAnnotationPresent(MyComponent.class)){
Object object = clazz.newInstance();
beanFactoryMap.put(beanName.toLowerCase(),object);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//处理注入的问题(模拟@Autowired)
if(!beanFactoryMap.isEmpty()){
Iterator iterator = beanFactoryMap.entrySet().iterator();
while (iterator.hasNext()){
try {
Map.Entry entry = (Map.Entry) iterator.next();
Class clazz = entry.getValue().getClass();
Field[] fields = clazz.getDeclaredFields();
if(fields != null && fields.length > 0){
for(Field field:fields){
field.setAccessible(true);
MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
if(myAutowired != null){
field.set(entry.getValue(),beanFactoryMap.get(field.getName()));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
} } public Object getBean(String beanName){
return this.beanFactoryMap.get(beanName);
} }

  测试

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

 package com.test;

 import com.entity.User;
import com.myspring.MySpringApplicationContext;
import com.entity.MyConfigure;
import com.entity.Order;
public class Test {
public static void main(String[] args) {
MySpringApplicationContext mySpringApplicationContext = new MySpringApplicationContext(MyConfigure.class);
User user = (User) mySpringApplicationContext.getBean("user");
user.print();
Order order = (Order) mySpringApplicationContext.getBean("order");
order.print();
/**
* 用于验证Order中是否成功注入User对象
*/
order.printUser();
}
}

  结果输出

 This is user
This is order
This is user from order

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

手动模拟实现Spring IOC功能(基于javaConfig风格)的更多相关文章

  1. 模拟实现Spring IoC功能

    为了加深理解Spring 今天自己写了一个模拟的Spring.... 步骤: 1.利用jdom解析bean.xml(pull,sax也能够,我这里用了jdom) 2.先解析全部的<bean/&g ...

  2. Spring IOC之基于注解的容器配置

    Spring配置中注解比XML更好吗?基于注解的配置的介绍提出的问题是否这种途径比XML更好.简单来说就是视情况而定. 长一点的答案是每一种方法都有自己的长处也不足,而且这个通常取决于开发者决定哪一种 ...

  3. 自定义模拟一个Spring IOC容器

    一.模拟一个IOC容器: 介绍:现在,我们准备使用一个java project来模拟一个spring的IOC容器创建对象的方法,也就是不使用spring的jar自动帮助我们创建对象,而是通过自己手动书 ...

  4. Spring IOC之基于JAVA的配置

    基础内容:@Bean 和 @Configuration 在Spring中新的支持java配置的核心组件是 @Configuration注解的类和@Bean注解的方法. @Bean注解被用于表明一个方法 ...

  5. Spring - IoC(8): 基于 Annotation 的配置

    除了基于 XML 的配置外,Spring 也支持基于 Annotation 的配置.Spring 提供以下介个 Annotation 来标注 Spring Bean: @Component:标注一个普 ...

  6. 自己模拟实现spring IOC原理

    1.1.IoC是什么 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对 ...

  7. Spring IOC 剖析

    模拟实现 Spring Ioc 控制反转功能 使用 => 原理 => 源码 => 模拟实现 使用:了解 原理:熟悉 源码 And 模拟实现: 精通 对照 Spring 功能点 Spr ...

  8. 使用 Spring 2.5 注释驱动的 IoC 功能(转)

    基于注释(Annotation)的配置有越来越流行的趋势,Spring 2.5 顺应这种趋势,提供了完全基于注释配置 Bean.装配 Bean 的功能,您可以使用基于注释的 Spring IoC 替换 ...

  9. Spring MVC 笔记--配置基于JavaConfig

    主要使用基于 JavaConfig 方式配置 配置 DispatcherServlet 通过继承抽象类AbstractAnnotationConfigDispatcherServletInitiali ...

随机推荐

  1. 你好,Go语言

    本文是「vangoleo的Go语言学习笔记」系列文章之一. 官网: http://www.vangoleo.com/go/hello-golang/ 我在2015年第一次接触Go语言,完成了Hello ...

  2. PHP 输出XML字符串

    <?php echo htmlentities( $xml_string);?>

  3. Python基础-语法知识

    ——编程语言的发展史 机器语言 优点:执行速度够快 缺点:开发效率非常低 汇编语言 优点:执行效率相较于机器语言略低 缺点:开发效率相较于机器语言略高 高级语言 C.C++.C#.java.PHP.p ...

  4. C语言I作业06

    问题 答案 这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-4/homework/9888 我在 ...

  5. 《Effective Java》 读书笔记(五)使用依赖注入取代原本的资源依赖

    相信接触过Spring的同学,对于依赖注入并不陌生. 刚开始在听说这个名字的时候,一直不明白到底什么叫依赖注入,后来才发现,依赖注入一直都存在我们日常代码中,只是我们没有刻意的把它提出来,然后再取这样 ...

  6. 题解【洛谷】CF134A

    题解 CF134A [Average Numbers] 这题就是简单的模拟. 只不过要优化一下常数什么的 思路: 为了不浪费时间总是取平均数,直接用一个 S 储存总和,每次都减去 a_i​ 再除以 n ...

  7. [校内自测 NOIP模拟题] chenzeyu97要请客(单调栈)

    题目描述 chenzeyu97的家可以看成是一个n*m的矩阵,每块区域都有独一无二的海拔高度h(h>0)!其最大值为n*m. 现在他要选择一个子矩阵摆放一张桌子,在他眼里,这样摆放桌子的美观度为 ...

  8. 『图论』有向图强连通分量的Tarjan算法

    在图论中,一个有向图被成为是强连通的(strongly connected)当且仅当每一对不相同结点u和v间既存在从u到v的路径也存在从v到u的路径.有向图的极大强连通子图(这里指点数极大)被称为强连 ...

  9. php判断是不是https的方法

    php判断是不是https的方法<pre> public function is_https() { if (!empty($_SERVER['HTTPS']) && st ...

  10. 使用websocketpp进行websocket通信

    websocketpp介绍 websocketpp是一个只有头文件的支持websocket协议的C++开源库,支持websocket客户端和服务器功能,网络传输模块基于boost::asio 提供 s ...