Spring框架作为使用最多最广泛的Java框架,直接看源码的话,设计层级复杂,容易陷入其中,摸不清框架思路,艺华大神的tiny-spring通过庖丁解牛抽取Spring精华,一步一步拆解非常好的展现框架设计很好的思路,去体会这个经典框架的设计,前面分析过三步之后,接下来从第四步实现通过XML文件加载bean的实现开始继续分析:
一、实现用xml配置文件方式进行bean读取
1.首先定义读取bean的接口
public interface BeanDefinitionReader {
void loadBeanDefinition(String location) throws Exception;
}
2.抽象类实现BeanDefinitionReader接口,包含registry和resourceLoader
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
@Getter
private Map<String, BeanDefinition> registry;
@Getter
private ResourceLoader resourceLoader;
public AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
this.registry = Maps.newConcurrentMap();
this.resourceLoader = resourceLoader;
}
}
3.ResouceLoader类中包含获取Resource的方法
public class ResourceLoader {
public Resource getResource(String location) {
URL resource = this.getClass().getClassLoader().getResource(location);
return new UrlResource(resource);
}
}
4.XmlBeanDefinitionReader使用Xml方式实现AbstractBeanDefinitionReader
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
super(resourceLoader);
}
@Override
public void loadBeanDefinition(String location) throws Exception {
InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
doLoadBeanDefinitions(inputStream);
}
private void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(inputStream);
registerBeanDefinitions(document);
inputStream.close();
}
private void registerBeanDefinitions(Document document) {
Element root = document.getDocumentElement();
parseBeanDefinitions(root);
}
private void parseBeanDefinitions(Element root) {
NodeList childNodes = root.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node item = childNodes.item(i);
if (item instanceof Element) {
Element element = (Element) item;
processBeanDefinition(element);
}
}
}
private void processBeanDefinition(Element element) {
String name = element.getAttribute("name");
String className = element.getAttribute("class");
BeanDefinition beanDefinition = new BeanDefinition();
processProperty(element, beanDefinition);
beanDefinition.setBeanClassName(className);
getRegistry().put(name, beanDefinition);
}
private void processProperty(Element element, BeanDefinition beanDefinition) {
NodeList propertyNode = element.getElementsByTagName("property");
for (int i = 0; i < propertyNode.getLength(); i++) {
Node node = propertyNode.item(i);
if (node instanceof Element) {
Element propertyElement = (Element) node;
String name = propertyElement.getAttribute("name");
String value = propertyElement.getAttribute("value");
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
}
}
}
}
public interface Resource {
InputStream getInputStream() throws IOException;
}
5.通过URL方式获取输入流实现类UrlResourceLoader
@AllArgsConstructor
public class UrlResource implements Resource {
private final URL url;
@Override
public InputStream getInputStream() throws IOException {
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
return urlConnection.getInputStream();
}
}
6.定义springioc.xml文件,并对于ResouceLoader写UT进行验证
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean name="helloSpringIOCService" class="fun.elricboa.spring.ioc.HelloSpringIOCService">
<property name="text" value="Hello World!"></property>
</bean>
</beans>
public class ResourceLoaderTest {
@Test
public void test() throws IOException {
ResourceLoader resourceLoader = new ResourceLoader();
Resource resource = resourceLoader.getResource("springioc.xml");
InputStream inputStream = resource.getInputStream();
Assert.assertNotNull(inputStream);
}
}
7.测试XmlBeanDefinitionReader
public class XmlBeanDefinitionReaderTest {
@Test
public void test() throws Exception {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinition("springioc.xml");
Map<String, BeanDefinition> registry = xmlBeanDefinitionReader.getRegistry();
Assert.assertTrue(registry.size() > 0);
}
}
二、创建bean实例并注册到beanFactory
public class AutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
Object bean = createBeanInstance(beanDefinition);
applyPropertyValues(bean, beanDefinition);
return bean;
}
private void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {
for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValueList()) {
Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
declaredField.setAccessible(true);
declaredField.set(bean, propertyValue.getValue());
}
}
private Object createBeanInstance(BeanDefinition beanDefinition) throws InstantiationException, IllegalAccessException {
return beanDefinition.getBeanClass().newInstance();
}
}
三、测试
@Test
public void testXMLBeanRegister() throws Exception {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinition("springioc.xml");
BeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
HelloSpringIOCService helloSpringIOCService = (HelloSpringIOCService) beanFactory.getBean("helloSpringIOCService");
helloSpringIOCService.helloSpring();
}
- 第四步就完成了从xml中加载bean生成和获取bean,后续会继续讲解IoC的实现。
- 代码来源:https://github.com/code4craft/tiny-spring