手动模拟实现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 ...
随机推荐
- 我发现了Unity3D的2D Light Renderer, 随后就把它抄了过来
. 前几个月,偶然在群里看到有人讨论Unity3D光照,于是我又萌生了一个新的目标----把它抄过来! . 众所周知,3D渲染的整个流水线都跟光照密不可分,相关的技术更是数不甚数,而2D游戏的光照通常 ...
- Linux学习使用Vim
Vim是从 vi 发展出来的一个文本编辑器.vi编辑器是所有Unix及Linux系统下标准的编辑器,他就相当于windows系统中的记事本一样.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序 ...
- python-nmap使用及案例
nmap概念及功能 概念 NMap,也就是Network Mapper,最早是Linux下的网络扫描和嗅探工具包. nmap是一个网络连接端扫描软件,用来扫描网上电脑开放的网络连接端.确定哪些服务运行 ...
- Apache Tomcat服务部署网站
Tomcat是Apache软件基金会 (Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache.Sun 和其他一些公司及个人共同开发而成.由于有了S ...
- QA:无重复字符的最长子串
无重复字符的最长字串 leetcode地址:https://leetcode-cn.com/problems/longest-substring-without-repeating-character ...
- numpy.array 合并和分割
# 导包 import numpy as np numpy.array 的合并 .concatenate() 一维数组 x = np.array([1, 2, 3]) # array([1, 2, 3 ...
- 爬虫之scrapy简介
原始的爬虫流程:效率低.同步.阻塞 scrapy执行流程:效率高.异步.非阻塞 scrapy的概念 scrapy是一个爬虫框架 开发速度快 稳定性高 性能优越 scrapy的流程 1. 爬虫模块(Sp ...
- python学习之【第十六篇】:Python中的常用模块之OS模块、sys模块、random模块
1. OS模块 OS模块是与操作系统交互的一个接口.内部提供了以下方法: os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname& ...
- python学习之【第十一篇】:Python中的文件操作
1.前言 在Python中,对文件的操作主要遵循以下流程: 打开文件,得到文件句柄并赋值给一个变量 通过文件句柄对文件进行操作 关闭文件 2.打开文件 使用open函数,可以打开一个已经存在的文件,或 ...
- 「Luogu 3792」由乃与大母神原型和偶像崇拜
更好的阅读体验 Portal Portal1: Luogu Description 给你一个序列\(a\) 每次两个操作: 修改\(x\)位置的值为\(y\): 查询区间\([l, r]\)是否可以重 ...