技术JavaSpring面向对象手写 Spring IoC - BeanDefinition 加载与解析
penjc前文:
面向过程手写 Spring IoC
手写 Spring IoC 模块设计
手写 Spring IoC 容器 —— BeanDefinition 加载与解析
引言
在 Spring IoC 容器中,BeanDefinition 负责描述 Bean 的基本信息,如类名、作用域、初始化方法等。
本文先实现 Spring IoC 的核心模块之一:BeanDefinition 加载与解析。
主要功能
- 资源加载:从 XML 文件中读取配置信息。
- 解析 XML:解析 XML 文件内容并转换为
BeanDefinition
对象。
- 注册 BeanDefinition:将解析后的
BeanDefinition
存储到 BeanDefinitionRegistry
。
1. 资源加载
Spring 允许从多种来源(如 Classpath、文件系统、网络等)加载 XML 配置文件。这里我们定义 Resource
接口,提供统一的资源访问方式。
1.1 资源接口
1 2 3 4 5 6
| / * 提供对资源的操作功能(资源可能存在于磁盘、网络、classpath等) */ public interface Resource { InputStream getResource(); }
|
1.2 Classpath 资源实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class ClasspathResource implements Resource { private String location;
public ClasspathResource(String location) { this.location = location; }
@Override public InputStream getResource() { if (location.startsWith("classpath:")) { location = location.substring(10); } return this.getClass().getClassLoader().getResourceAsStream(location); } }
|
设计思路:
Resource
作为抽象接口,提供不同的资源加载方式。
ClasspathResource
具体实现了从 类路径 加载资源的功能。
2. XML 解析
2.1 XML 解析器 XmlBeanDefinitionReader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class XmlBeanDefinitionReader { private BeanDefinitionRegistry registry;
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) { this.registry = registry; }
public void loadBeanDefinitions(Resource resource) { InputStream inputStream = resource.getResource(); doLoadBeanDefinitions(inputStream); }
private void doLoadBeanDefinitions(InputStream inputStream) { Document document = DocumentUtils.getDocument(inputStream); BeanDefinitionDocumentReader documentReader = new DefaultBeanDefinitionDocumentReader(registry); documentReader.registerBeanDefinitions(document.getRootElement()); } }
|
设计思路:
XmlBeanDefinitionReader
负责解析 XML 配置文件。
loadBeanDefinitions
方法支持 Resource
作为参数,解耦资源的获取方式。
doLoadBeanDefinitions
通过 DocumentUtils
解析 XML,并委托 BeanDefinitionDocumentReader
进行解析。
3. BeanDefinition 解析
3.1 解析 XML 并注册 BeanDefinition
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader { private BeanDefinitionRegistry registry;
public DefaultBeanDefinitionDocumentReader(BeanDefinitionRegistry registry) { this.registry = registry; }
@Override public void registerBeanDefinitions(Element rootElement) { List<Element> elements = rootElement.elements(); for (Element element : elements) { if ("bean".equals(element.getName())) { parseDefaultElement(element); } else { parseCustomElement(element); } } }
private void parseCustomElement(Element element) { }
private void parseDefaultElement(Element beanElement) { try { String id = beanElement.attributeValue("id"); String name = beanElement.attributeValue("name"); String clazzName = beanElement.attributeValue("class"); String initMethod = beanElement.attributeValue("init-method"); String scope = beanElement.attributeValue("scope", "singleton");
String beanName = id == null ? name : id; Class<?> clazzType = Class.forName(clazzName); beanName = beanName == null ? clazzType.getSimpleName() : beanName;
BeanDefinition beanDefinition = new BeanDefinition(clazzName, beanName); beanDefinition.setInitMethod(initMethod); beanDefinition.setScope(scope);
List<Element> propertyElements = beanElement.elements(); for (Element propertyElement : propertyElements) { parsePropertyElement(beanDefinition, propertyElement); } registry.registerBeanDefinition(beanName, beanDefinition); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
|
设计思路:
registerBeanDefinitions
遍历 XML 的 <bean>
标签。
parseDefaultElement
解析 bean
标签的属性,并转换为 BeanDefinition
。
parsePropertyElement
解析 property
标签。
registry
为 BeanDefinitionRegistry
,负责注册 BeanDefinition
。
3.2 解析 Bean 属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private void parsePropertyElement(BeanDefinition beanDefinition, Element propertyElement) { if (propertyElement == null) return;
String name = propertyElement.attributeValue("name"); String value = propertyElement.attributeValue("value"); String ref = propertyElement.attributeValue("ref");
if (value != null && ref != null) return;
PropertyValue pv = null; if (value != null) { TypedStringValue typedValue = new TypedStringValue(value); Class<?> targetType = ReflectUtils.getTypeByFieldName(beanDefinition.getClazzName(), name); typedValue.setTargetType(targetType); pv = new PropertyValue(name, typedValue); } else if (ref != null) { pv = new PropertyValue(name, new RuntimeBeanReference(ref)); } beanDefinition.addPropertyValue(pv); }
|
设计思路:
value
属性表示直接赋值,ref
表示引用另一个 Bean。
TypedStringValue
解决 String 转换为具体类型的问题。
RuntimeBeanReference
处理 Bean 之间的依赖。