Spring源码-自定义IOC容器及Bean解析注册【4】
实验环境:spring-framework-5.0.2、jdk8、gradle4.3.1
- Spring源码-IOC容器简介【1】
- Spring源码-IOC容器初始化过程【2】
- Spring源码-Xml Bean解析注册过程【3】
- Spring源码-自定义IOC容器及Bean解析注册【4】
- Spring源码-Bean实例化过程【5】
- Spring源码-Spring是如何解决Bean循环依赖的【6】
- Spring源码-循环依赖-用实例证明去掉二级缓存会出现什么问题【7】
- Spring源码-AOP是如何实现代理的【8】
上文介绍了一些常用容器及初始化、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技术内幕》作者 计文柯
本文来自博客园,作者:wzyy,转载请注明原文链接:https://www.cnblogs.com/wwzyy/p/15860179.html
分类:
探索Spring之美
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】Flutter适配HarmonyOS 5知识地图,实战解析+高频避坑指南
【推荐】凌霞软件回馈社区,携手博客园推出1Panel与Halo联合会员
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· C#/.NET/.NET Core优秀项目和框架2025年4月简报
· 如何把ASP.NET Core WebApi打造成Mcp Server
· 为什么AI多轮对话那么傻?
· 排行榜的5种实现方案!
· windows11 安装WSL2详细过程