Spring源码-IOC部分-自定义IOC容器及Bean解析注册【4】
实验环境:spring-framework-5.0.2、jdk8、gradle4.3.1
- Spring源码-IOC部分-容器简介【1】
- Spring源码-IOC部分-容器初始化过程【2】
- Spring源码-IOC部分-Xml Bean解析注册过程【3】
- Spring源码-IOC部分-自定义IOC容器及Bean解析注册【4】
- Spring源码-IOC部分-Bean实例化过程【5】
- Spring源码-IOC部分-Spring是如何解决Bean循环依赖的【6】
上文介绍了一些常用容器及初始化、Bean解析过程,源码里面的实现比较复杂,如果我们想要自己实现一个IOC容器,自定义bean配置文件该怎么做呢?
其实只要了解核心思路实现起来是很简单的,只需3步:
1)继承AbstractApplicationContext
2)读取配置文件,封装成beanDefinition对象,存入beanDefinitionMap中
3)调用refresh方法
1、定义配置文件 新建一个文件,格式如下:`user`是bean的名字,`beans.User`是bean的类路径,其中的`id`、`name`是字段属性值
user=beans.User{id:001,name:小明}
user2=beans.User2{id:002,name:小明2}
package beans;
import org.springframework.stereotype.Component;
@Component
public class User {
private int id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
2、既然我们定义好了配置文件格式,那么就需要一个BeanDefinitionReader来针对这类文件进行解析
package context;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
/**
* 自定义BeanDefinitionReader
*/
public class MyBeanDefinitionReader extends AbstractBeanDefinitionReader {
protected final Log logger = LogFactory.getLog(getClass());
public MyBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 解析配置文件
List<BeanDef> beanDefList = parseResource(resource);
beanDefList.forEach(beanDef -> {
// 构造BeanDefinition
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(beanDef.getClassPath());
// 填充参数
beanDef.getParamMap().forEach(beanDefinitionBuilder::addPropertyValue);
// 注册
super.getBeanFactory().registerBeanDefinition(beanDef.getId(),
beanDefinitionBuilder.getBeanDefinition());
});
return beanDefList.size();
}
private List<BeanDef> parseResource(Resource resource) {
List<BeanDef> beanDefList = new ArrayList<>();
BufferedReader reader = null;
try {
reader = new BufferedReader(new BufferedReader(new InputStreamReader(resource.getInputStream())));
String line = reader.readLine();
while (!StringUtils.isBlank(line)) {
BeanDef beanDef = buildBeanDef(line);
if (!beanDefList.contains(beanDef)) {
beanDefList.add(beanDef);
} else {
throw new RuntimeException("bean定义重复" + beanDef);
}
line = reader.readLine();
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
return beanDefList;
}
private BeanDef buildBeanDef(String line) {
BeanDef beanDef = new BeanDef();
String[] split = line.split("=");
String id = split[0];
beanDef.setId(id);
String source = split[1];
if (source.contains("{") && source.contains("}")) {
int begin = source.indexOf("{");
int end = source.indexOf("}");
String classPath = source.substring(0, begin);
beanDef.setClassPath(classPath);
String params = source.substring(begin + 1, end);
String[] paramArray = params.split(",");
for (String param : paramArray) {
String[] kv = param.split(":");
beanDef.getParamMap().put(kv[0], kv[1]);
}
} else {
beanDef.setClassPath(source);
}
return beanDef;
}
public static class BeanDef {
private String id;
private String classPath;
private Map<String, Object> paramMap = new HashMap<>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassPath() {
return classPath;
}
public void setClassPath(String classPath) {
this.classPath = classPath;
}
public Map<String, Object> getParamMap() {
return paramMap;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BeanDef beanDef = (BeanDef) o;
return id.equals(beanDef.id) &&
classPath.equals(beanDef.classPath);
}
@Override
public int hashCode() {
return Objects.hash(id, classPath);
}
@Override
public String toString() {
return "BeanDef{" +
"id='" + id + '\'' +
", classPath='" + classPath + '\'' +
", paramMap=" + paramMap +
'}';
}
}
}
3、现在我们可以读取bean配置文件,然后解析成BeanDefinition注册到容器中,于是我们再准备一个容器
package context;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* 自定义容器实现
*/
public class MyApplicationContext extends AbstractApplicationContext {
private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建自定义bean解析器,传入bean注册器beanFactory
private final MyBeanDefinitionReader definitionReader = new MyBeanDefinitionReader(beanFactory);
public MyApplicationContext(String resourcePath) {
this.definitionReader.loadBeanDefinitions(new ClassPathResource(resourcePath));
this.refresh();
}
/**
* 设置父容器,比如spring mvc容器作为子容器,父容器是spring容器
*/
@Override
public void setParent(@Nullable ApplicationContext parent) {
super.setParent(parent);
this.beanFactory.setParentBeanFactory(getInternalParentBeanFactory());
}
@Override
public void setId(String id) {
super.setId(id);
}
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
this.beanFactory.setAllowBeanDefinitionOverriding(allowBeanDefinitionOverriding);
}
public void setAllowCircularReferences(boolean allowCircularReferences) {
this.beanFactory.setAllowCircularReferences(allowCircularReferences);
}
//---------------------------------------------------------------------
// ResourceLoader / ResourcePatternResolver override if necessary
//---------------------------------------------------------------------
@Override
public Resource getResource(String location) {
return super.getResource(location);
}
@Override
public Resource[] getResources(String locationPattern) throws IOException {
return super.getResources(locationPattern);
}
@Override
public void setClassLoader(@Nullable ClassLoader classLoader) {
super.setClassLoader(classLoader);
}
@Override
@Nullable
public ClassLoader getClassLoader() {
return super.getClassLoader();
}
//---------------------------------------------------------------------
// Implementations of AbstractApplicationContext's template methods
//---------------------------------------------------------------------
@Override
protected final void refreshBeanFactory() throws IllegalStateException {
this.beanFactory.setSerializationId(getId());
}
@Override
protected void cancelRefresh(BeansException ex) {
this.beanFactory.setSerializationId(null);
super.cancelRefresh(ex);
}
@Override
protected final void closeBeanFactory() {
this.beanFactory.setSerializationId(null);
}
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
public final DefaultListableBeanFactory getDefaultListableBeanFactory() {
return this.beanFactory;
}
@Override
public AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException {
assertBeanFactoryActive();
return this.beanFactory;
}
// 子类重写
@Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) {
if (beanFactory instanceof DefaultListableBeanFactory) {
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
String[] beanDefinitionNames = factory.getBeanDefinitionNames();
System.out.println("step4.1 postProcessBeanFactory子类重写,已经加载beanDefinitionNames " + Arrays.asList(beanDefinitionNames));
}
}
// 子类重写
@Override
protected void onRefresh() throws BeansException {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
System.out.println("step9.1 onRefresh子类重写,已经加载beanDefinitionNames " + Arrays.asList(beanDefinitionNames));
}
}
4、测试一下bean能否正常注入到容器中
public class Main {
public static void main(String[] args) {
testMyApplicationContext();
}
private static void testMyApplicationContext() {
String path = "/config/mycontext.txt";
MyApplicationContext context = new MyApplicationContext(path);
System.out.println(context.getBean("user"));
context.close();
}
}

参考资料:
《Spring5核心原理与30个类手写》作者 谭勇德
《Spring源码深度解析》作者 郝佳
《Spring技术内幕》作者 计文柯
Spring源码-IOC部分-自定义IOC容器及Bean解析注册【4】的更多相关文章
- Spring源码分析(四)容器的基础XmlBeanFactory
摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 经过Spring源码分析(二)容器基本用法和Spring源码分析(三)容 ...
- Spring源码剖析4:其余方式获取Bean的过程分析
原型Bean加载过程 之前的文章,分析了非懒加载的单例Bean整个加载过程,除了非懒加载的单例Bean之外,Spring中还有一种Bean就是原型(Prototype)的Bean,看一下定义方式: 1 ...
- Spring学习之——手写Spring源码V2.0(实现IOC、D、MVC、AOP)
前言 在上一篇<Spring学习之——手写Spring源码(V1.0)>中,我实现了一个Mini版本的Spring框架,在这几天,博主又看了不少关于Spring源码解析的视频,受益匪浅,也 ...
- spring源码学习之路---IOC初探(二)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...
- Spring 源码学习(1)—— 容器的基本实现
最近在读Spring的源码,参考的是郝佳的<Spring源码深度解析>,这里把一些学习心得分享一下,总结的地方可能还有一些不完善,希望大家指教 IoC(控制反转)是Spring的特性之一, ...
- Spring 源码学习 04:初始化容器与 DefaultListableBeanFactory
前言 在前一篇文章:创建 IoC 容器的几种方式中,介绍了四种方式,这里以 AnnotationConfigApplicationContext 为例,跟进代码,看看 IoC 的启动流程. 入口 从 ...
- Spring源码分析(三)容器核心类
摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 在上一篇文章中,我们熟悉了容器的基本用法.在这一篇,我们开始分析Spri ...
- Spring 源码学习(1) —— 自定义标签
Spring 工作流程是先加载解析xml配置文件:配置文件中存在默认的标签,也可以自定义标签.解析默认标签调用: private void parseDefaultElement(Element el ...
- Spring源码情操陶冶-自定义节点的解析
本文承接前文Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,特开辟出一块新地来啃啃这块有意思的骨头 自定义节 ...
随机推荐
- python学习第四天:python基础(字符编码和乱码到底咋回事儿)
字符编码 这得从字符编码开始说起: 字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题.因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理. 最早的计算机在设计时采 ...
- 基于MCRA-OMLSA的语音降噪(二):实现
上篇文章(基于MCRA-OMLSA的语音降噪(一):原理)讲了基于MCRA-OMLSA降噪的原理,本篇讲怎么做软件实现.软件实现有多种方式.单纯看降噪效果可用python,因为python有丰富的库可 ...
- Kernel PCA for Novelty Detection
目录 引 主要内容 的选择 数值实验 矩形框 spiral 代码 Hoffmann H. Kernel PCA for novelty detection[J]. Pattern Recognitio ...
- MA8601升级版 PL2586|USB HUB 工控级芯片方案PL2586|可直接替代FE1.1S芯片方案
MA8601升级版 PL2586|USB HUB 工控级芯片方案PL2586|可直接替代FE1.1S芯片方案 旺玖在2022年新推出的一款USB HUB 芯片其性能和参数可以完全替代FE1.1S,是M ...
- 编写Java程序,利用List维护用户信息
返回本章节 返回作业目录 需求说明: 将新增的用户信息添加到List集合. 用户信息包括用户编号.姓名和性别. 按照姓名和性别查找用户信息. 实现思路: 创建类UserInfo,在该类中定义3个Str ...
- 每天学一点——python变量、常量与数字类型
python变量.常量与数字类型 常量 (一句话能概括先讲它) 严格来讲,python中除了π与N就没有不变的量 所以,在python中我们识别常量是看它是否全大写(如下图) 变量 变量,顾名思义,就 ...
- URL中使用IPv4,IPv6和主机名
在浏览器的Http请求的URL中如何使用IPv4,IPv6和主机名, 因为IPv6的地址需要加[],导致用法有点区别, 下面通过具体的例子总结一下不同情况下的用法. 1.假设有台Linux主机名配置如 ...
- NOSQL数据库之MongoDB
一.NoSQL概述 如今,大多数的计算机系统(包括服务器.PC.移动设备等)都会产生庞大的数据量.其实,早在2012年的时候,全世界每天产生的数据量就达到了2.5EB(艾字节,).这些数据有很大一部 ...
- vue js格式化数字为金额格式
/** * @description 格式化金额 * @param number:要格式化的数字 * @param decimals:保留几位小数 默认0位 * @param decPoint:小数点 ...
- iframe 去除边框 背景透明等设置 待修改
<iframe name="file_frame" src="UploadFile.jsp" frameborder=no border=0 marg ...