实现自己的IOC容器——Winter (一)Bean加载
做了几年 Java 开发每天和 Spring系列框架打交道。虽然各种注解、中间件用的飞起但说实话心里挺虚的。框架帮我们屏蔽了太多细节舒服是舒服了可久而久之感觉自己像个被惯坏的孩子——离了脚手架就不会盖房了。为了治好这份“技术焦虑症”也为了满足那点该死的好奇心我决定自己动手从零开始撸一个简易版的 Spring 容器。不求功能强大只求把那些神秘的面纱亲手揭开。项目地址https://gitee.com/sheevg/winter目前还在持续更新中....我是从《Spring源码深度解析第二版》这本书开始了解Spring框架所以Winter容器也是从解析Xml配置实现Bean注册后面会陆续更新注解驱动、AOP等等。一、解析Xml配置、注册BeanDefinition参考方法XmlBeanDefinitionReader.loadBeanDefinitions(resource)Winter容器是Xml配置文件驱动对配置的解析流程如下1. 加载xml文件成Resource对象2. 将Resource转成Document解析Document将Bean的配置解析成BeanDefinition3. 将解析完成的BeanDefinition放入容器WinterBeanFactory中1. 加载xml文件转成Document对象定义ClassPathResource作为类路径下文件抽象定义getInputStream方法用于读取。定义ResourceLoader用于统一加载文件。import java.io.FileNotFoundException; import java.io.InputStream; /** * 用于解析 classpath 路径下的文件 */ public class ClassPathResource implements Resource{ // 文件路径 private final String path; // 用于加载文件的 private final ClassLoader classLoader; public ClassPathResource(String path){ this(path,null); } public ClassPathResource(String path,ClassLoader classLoader){ // classloader加载时不能以/ 为开头比如/config/xx.xml if(path.startsWith(/)){ path path.substring(1); } this.path path; this.classLoader classLoader ! null ? classLoader : ClassLoader.getSystemClassLoader() ; } Override public InputStream getInputStream() throws FileNotFoundException { InputStream inputStream this.classLoader.getResourceAsStream(this.path); if(inputStream null){ throw new FileNotFoundException(描述: 类路径下的资源 [ this.path ] 不存在无法打开); } return inputStream; } }2. 解析Document成BeanDefintion定义BeanDefinitionReader用于将Resource的输入流转成Document对象使用dom4j转换。public int loadBeanDefinition(Resource resource) throws FileNotFoundException, DocumentException { // 获取输入流 InputStream is resource.getInputStream(); SAXReader reader new SAXReader(); // 将输入流转成 document Document document reader.read(is); // 计算注册了多少 int countBefore beanDefinitionRegistry.getBeanDefinitionCount(); doRegisterBeanDefinition(document.getRootElement()); return beanDefinitionRegistry.getBeanDefinitionCount() - countBefore; }在doRegisterBeanDefinition方法中遍历bean element在用解析配置的委托类BeanDefinitionParserDelegate进行具体解析。最终将bean配置解析为BeanDefinition对象。public void doRegisterBeanDefinition(Element rootElement){ System.out.println(rootElement.getName()); BeanDefinitionParserDelegate parserDelegate new BeanDefinitionParserDelegate(); // 获取所有 bean标签 ListElement beanElements rootElement.elements(bean); beanElements.forEach(e - { BeanDefinitionHolder bdHolder parserDelegate.parseBeanDefinition(e); beanDefinitionRegistry.registerBeanDefinition(bdHolder.getBeanName(),bdHolder.getBeanDefinition()); }); }public BeanDefinitionHolder parseBeanDefinition(Element element) { String id element.attributeValue(id); String className element.attributeValue(class); String initMethod element.attributeValue(init-method); // 构造函数 ListConstructorArg argList new ArrayList(); ListElement constructorArgs element.elements(constructor-arg); for(int i0; iconstructorArgs.size();i){ Element constructorArg constructorArgs.get(i); String argName constructorArg.attributeValue(name); String ref constructorArg.attributeValue(ref); String value constructorArg.attributeValue(value); ConstructorArg ca ConstructorArg.builder() .index(i) .name(argName) .type(StringUtils.isNotBlank(ref)?ConstructorArgEnum.REF:ConstructorArgEnum.VALUE) .value(StringUtils.isNotBlank(ref)?ref:value) .build(); argList.add(ca); } BeanDefinition bd BeanDefinition.builder() .id(id) .beanName(id) .className(className) .initMethodName(initMethod) .argList(argList) // 默认单例 .isSingleton(true) .build(); return new BeanDefinitionHolder(id,bd); }3. 注册BeanDefinition需要在WinterBeanFactory中创建一个本地缓存ConcurrentHashMap将创建好的BeanDefinition放进去即可。private final MapString, BeanDefinition beanDefinitionMap new ConcurrentHashMap(64); Override public void registerBeanDefinition(String beanName, BeanDefinition bd) { this.beanDefinitionMap.put(beanName,bd); }明天更新如何将配置中的Bean注册进容器....