手动模拟实现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 ...
随机推荐
- 20190728_收集的一些链接_关于opencv
https://softwarebydefault.com/tag/pixel-neighbourhood-max/ https://www.cnblogs.com/happystudyeveryda ...
- Ubuntu18.04 安装MySQL(Linux)解决登陆权限问题及Navicat for mysql 中文乱码问题
一.MySQL(Linux)解决登陆权限问题 Ubuntu18.04 安装mysql或者mariadb之后,发现普通用户和远程都没有权限连接. ERROR 1045: Access denied fo ...
- 「考试」小P的生成树
考场上想到一半正解,没想到随机化,不然也许能够$A$掉. 题目所说的其实就是向量加法,求模长最长的向量生成树. 我们考虑对于两个向量,必然在平行边形对角线方向上,他们的投影和是最大的,长度就是对角线长 ...
- CSPS模拟 98
T1 待改 T2 这道题的爆炸充分说明我最近已经颓到一定境界了 考虑到总步数不可能超过n 直接枚举总步数,那么任意时刻对末态的影响就是确定的 T3 两遍最短路,一遍从-1的限制考虑求出允许的最早时间, ...
- 2019.NET Conf China(中国.NET开发者峰会)活动全纪录:.NET技术之崛起,已势不可挡
一.微软走向开放与.NET Core的诞生 当今时代,气象更新,技术飞速发展. 当今时代,开发者大概是最优秀的群体.每一位开发者,无不奋勇向前,努力追寻时代的步伐,以大无畏的精神迎接挑战,紧跟大时代成 ...
- svn:"Update item to this version"、"Revert to this version"及"Revert changes from this version"详细说明
背景: 今天在svn分支上做了一些课题性研究,发现当前的环境版本不稳定, 和领导研究后决定还原到前面的版本以继续进行课题. 问题: 因此遇到了问题,是应该选择“Update item to this ...
- 大数据之路day04_2--经典bug(equals与==比较不同,break的跳出不同)
一.equals与==比较不同 在实现某个人去5个商场去购物,控制台输入是否购物(Y/N)的时候,在比较出了问题,发现无论输入什么都是false,后来查阅资料发现,字符串的比较,==和equals不一 ...
- UiPath之基础知识(一)
各位小伙伴,大家好.在10月份小U的微信订阅号做了一个投票,主题是UiPath目前已经掌握的程度. 从投票的结果来看,有一半以上的人还是刚刚起步,为了帮助刚刚起步的小伙伴,准备陆续发布一些基础性的内容 ...
- [转载]2.2 UiPath条件判断活动Flow Decision的介绍和使用
一.Flow Decision介绍 FlowDecision节点是一个条件节点,它根据指定条件是否成立来控制流程的两个分支. 当条件为True时,流程执行一个分支 当条件为False时,流程执行另外一 ...
- transformer模型简介
Transformer模型由<Attention is All You Need>提出,有一个完整的Encoder-Decoder框架,其主要由attention(注意力)机制构成.论文地 ...