手动模拟实现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。那么,照葫芦画瓢,对应的定义了下面三个注解
@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风格)的更多相关文章
- 模拟实现Spring IoC功能
为了加深理解Spring 今天自己写了一个模拟的Spring.... 步骤: 1.利用jdom解析bean.xml(pull,sax也能够,我这里用了jdom) 2.先解析全部的<bean/&g ...
- Spring IOC之基于注解的容器配置
Spring配置中注解比XML更好吗?基于注解的配置的介绍提出的问题是否这种途径比XML更好.简单来说就是视情况而定. 长一点的答案是每一种方法都有自己的长处也不足,而且这个通常取决于开发者决定哪一种 ...
- 自定义模拟一个Spring IOC容器
一.模拟一个IOC容器: 介绍:现在,我们准备使用一个java project来模拟一个spring的IOC容器创建对象的方法,也就是不使用spring的jar自动帮助我们创建对象,而是通过自己手动书 ...
- Spring IOC之基于JAVA的配置
基础内容:@Bean 和 @Configuration 在Spring中新的支持java配置的核心组件是 @Configuration注解的类和@Bean注解的方法. @Bean注解被用于表明一个方法 ...
- Spring - IoC(8): 基于 Annotation 的配置
除了基于 XML 的配置外,Spring 也支持基于 Annotation 的配置.Spring 提供以下介个 Annotation 来标注 Spring Bean: @Component:标注一个普 ...
- 自己模拟实现spring IOC原理
1.1.IoC是什么 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对 ...
- Spring IOC 剖析
模拟实现 Spring Ioc 控制反转功能 使用 => 原理 => 源码 => 模拟实现 使用:了解 原理:熟悉 源码 And 模拟实现: 精通 对照 Spring 功能点 Spr ...
- 使用 Spring 2.5 注释驱动的 IoC 功能(转)
基于注释(Annotation)的配置有越来越流行的趋势,Spring 2.5 顺应这种趋势,提供了完全基于注释配置 Bean.装配 Bean 的功能,您可以使用基于注释的 Spring IoC 替换 ...
- Spring MVC 笔记--配置基于JavaConfig
主要使用基于 JavaConfig 方式配置 配置 DispatcherServlet 通过继承抽象类AbstractAnnotationConfigDispatcherServletInitiali ...
随机推荐
- SpringBoot整合RabbitMq(二)
本文序列化和添加package参考:https://www.jianshu.com/p/13fd9ff0648d RabbitMq安装 [root@topcheer ~]# docker ...
- 解开HTTPS的神秘面纱
在说HTTP前,一定要先介绍一下HTTP,这家伙应该不用过多说明了,大家每天都在用,每一次HTTP请求,都是一次TCP连接.遗憾的是,请求的内容在TCP报文中是明文传输的,任何人截取到请求都可以读取其 ...
- GCC中,可以使用未声明过的函数
今天代码中使用了一个函数,这个函数也是自定义的,但是还没来得及声明和定义,可以编译时竟然未报错,网上查了下果然,GCC中可以使用未声明的函数http://bbs.csdn.net/topics/390 ...
- networkx整理
1.基础知识 1.1.介绍 networkx在2002年5月产生,是一个用Python语言开发的图论与复杂网络建模工具,内置了常用的图与复杂网络分析算法,可以方便的进行复杂网络数据分析.仿真建模等工作 ...
- apply 、 call 、 bind的用法
1.apply 方法 apply:调用一个对象的一个方法,用另一个对象替换当前对象.例如:B.apply(A, arguments);即A对象应用B对象的方法. apply方法最多只能有两个参数——新 ...
- 用GitLab Runner自动部署GitBook并不难
相信很多程序员喜欢用 GitBook 来写电子书.教程或者博客,看了不少文章,貌似都缺少说明如何将 GitBook 部署到版本库,并自动在服务器上 build,然后将生成的静态网站部署到云服务器上. ...
- Flink中接收端反压以及Credit机制 (源码分析)
先上一张图整体了解Flink中的反压 可以看到每个task都会有自己对应的IG(inputgate)对接上游发送过来的数据和RS(resultPatation)对接往下游发送数据, 整个反压机制通 ...
- 前端技术之:使用webpack构建React程序配置方法
package.json中需要引入以下依赖: 开发依赖: "@babel/core": "^7.1.2", "@babel/preset-env& ...
- 还不会用FindBugs?你的代码质量很可能令人堪忧
前言 项目中代码质量,往往需要比较有经验的程序员的审查来保证.但是随着项目越来越大,代码审查会变得越来越复杂,需要耗费越来越多的人力.而且程序员的经验和精力都是有限的,能审查出问题必定有限.而在对代码 ...
- Kong03-Nginx、OpenResty、Kong 的基本概念和区别联系
Nginx.OpenRestry.Kong 这三个项目关系比较紧密: Nginx 是模块化设计的反向代理软件,C语言开发: OpenResty 是以 Nginx 为核心的 Web 开发平台,可以解析执 ...