1.写在前面
我之前已经手动模拟Spring的源码,但是只是IOC的一小部分,今天打算将剩下的IOC的一部分给模拟完,尽量做到和Spring的IOC的容器做到差不多,就是基础的功能有,没有Spring的IOC的源码做的那么好,因为Spring的源码是真的太好了。
2.step4-读取xml配置来初始化bean
前面我模拟的Spring的IOC容器,已经可以支持简单的Bean的生成,同时也支持属性的注入了。但是这还完全布不够,我们都知道早期的Spring是支持读取xml文件的。我们先来看下我们步骤三的测试的代码,具体的代码如下:
package com.ys.ioc;
import com.ys.ioc.factory.AutowireCapableBeanFactory;
import com.ys.ioc.factory.BeanFactory;
import org.junit.Test;
public class BeanFactoryTest {
@Test
public void test() throws Exception {
// 1.初始化BeanFactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
// 2.Bean定义
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("com.ys.ioc.HelloWorldService");
// 3.设置属性
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!"));
beanDefinition.setPropertyValues(propertyValues);
// 4.生成Bean
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
// 5.获取Bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
这段初始化的代码是又臭又长,当然Spring也支持这种方式的编程,但是这种方式写出来的BeanDefinition
太多太长了,不好维护,那么Spring提供了什么解决方案呢?Spring提供了两种解决办法,一种是通过xml的方式来定义BeanDefinition
,一种是通过注解来定义BeanDefinition
。笔者就模拟通过xml的方式来定义BeanDefinition
。首先我们还是先来看下BeanDefinition
类,具体的代码如下:
package com.ys.ioc;
public class BeanDefinition {
private Object bean;
private String beanClassName;
private PropertyValues propertyValues = new PropertyValues();
private Class beanClass;
public BeanDefinition() {
}
public Object getBean() {
return bean;
}
public void setBean(Object bean) {
this.bean = bean;
}
public String getBeanClassName() {
return beanClassName;
}
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
try {
this.beanClass = Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public PropertyValues getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(PropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
public Class getBeanClass() {
return beanClass;
}
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
}
BeanDefinition
类和原来是一样的,继续看下PropertyValues
和PropertyValue
的代码,具体的代码如下:
package com.ys.ioc;
public class PropertyValue {
private final String name;
private final Object value;
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
package com.ys.ioc;
import java.util.ArrayList;
import java.util.List;
public class PropertyValues {
private List<PropertyValue> propertyValues = new ArrayList<>();
public void addPropertyValue(PropertyValue propertyValue){
this.propertyValues.add(propertyValue);
}
public List<PropertyValue> getPropertyValues() {
return this.propertyValues;
}
}
这两个类和原来的还是一样的。没有任何的变化。我们再来看下BeanFactory
接口的定义,具体的如下:
package com.ys.ioc.factory;
import com.ys.ioc.BeanDefinition;
public interface BeanFactory {
Object getBean(String beanName);
void registerBeanDefinition(String name, BeanDefinition definition) throws Exception;
}
同样也是没有变化的。我们再来看下AbstractBeanFactory
类,这是一个抽象类,同时是实现BeanFactory
接口的,具体的代码如下:
package com.ys.ioc.factory;
import com.ys.ioc.BeanDefinition;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class AbstractBeanFactory implements BeanFactory {
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
@Override
public Object getBean(String beanName) {
return beanDefinitionMap.get(beanName).getBean();
}
@Override
public void registerBeanDefinition(String name, BeanDefinition definition) throws Exception{
Object bean = doCreateBean(definition);
definition.setBean(bean);
beanDefinitionMap.put(name,definition);
}
protected abstract Object doCreateBean(BeanDefinition definition) throws Exception;
}
代码和原来的是一样的,我们再来看下AutowireCapableBeanFactory
,这个类继承AbstractBeanFactory
,具体的代码如下:
package com.ys.ioc.factory;
import com.ys.ioc.BeanDefinition;
import com.ys.ioc.PropertyValue;
import java.lang.reflect.Field;
public class AutowireCapableBeanFactory extends AbstractBeanFactory {
@Override
protected Object doCreateBean(BeanDefinition definition) throws Exception {
Object bean = createBeanInstance(definition);
applyPropertyValues(bean, definition);
return bean;
}
private void applyPropertyValues(Object bean, BeanDefinition definition) throws Exception {
for (PropertyValue propertyValue : definition.getPropertyValues().getPropertyValues()) {
Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
declaredField.setAccessible(true);
declaredField.set(bean,propertyValue.getValue());
}
}
private Object createBeanInstance(BeanDefinition definition) throws Exception {
return definition.getBeanClass().newInstance();
}
}
代码还是和原来的一样的。下面就要看我们读取xml文件的代码了,我们先将我们的资源抽成一个接口Resource
,具体的代码如下:
package com.ys.ioc.io;
import java.io.InputStream;
public interface Resource {
InputStream getInputStream()throws Exception;
}
我们在写一个UrlResource
来实现这个Resource
接口,具体的代码如下:
package com.ys.ioc.io;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class UrlResource implements Resource {
private final URL url;
public UrlResource(URL url) {
this.url = url;
}
@Override
public InputStream getInputStream() throws Exception {
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
return urlConnection.getInputStream();
}
}
这个类主要是通过我们给的url,来获取输入流。然后我们再来写一个ResourceLoader
用来获取传入一个地址,来获取Resource
,具体的代码如下:
package com.ys.ioc.io;
import java.net.URL;
public class ResourceLoader {
public Resource getResource(String location) {
URL resource = this.getClass().getClassLoader().getResource(location);
return new UrlResource(resource);
}
}
通过url来获取输入流,这部分的代码已经写完了,接下来就来写我们的读取器,就是读取xml的配置文件解析成BeanDefinition
,先看我们的定义的读取器BeanDefinitionReader
的接口定义,具体的代码如下:
package com.ys.ioc;
public interface BeanDefinitionReader {
void loadBeanDefinitions(String location) throws Exception;
}
我们先定义一个抽象类AbstractBeanDefinitionReader
来实现BeanDefinitionReader
,具体的代码如下:
package com.ys.ioc;
import com.ys.ioc.io.ResourceLoader;
import java.util.HashMap;
import java.util.Map;
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
//用来存从配置文件中读取的BeanDefinition
private Map<String,BeanDefinition> registry;
private ResourceLoader resourceLoader;
public AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
this.registry = new HashMap<>();
this.resourceLoader = resourceLoader;
}
public Map<String, BeanDefinition> getRegistry() {
return registry;
}
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
}
下面我们就来实现真正读取xml文件的和解析xml文件的类了,具体的代码如下:
package com.ys.ioc.xml;
import com.ys.ioc.AbstractBeanDefinitionReader;
import com.ys.ioc.BeanDefinition;
import com.ys.ioc.PropertyValue;
import com.ys.ioc.io.ResourceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.InputStream;
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
super(resourceLoader);
}
@Override
public void loadBeanDefinitions(String location) throws Exception {
//获取给定文件的输入流
InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
//加载BeanDefinition
doLoadBeanDefinitions(inputStream);
}
protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
Document document = documentBuilder.parse(inputStream);
//解析bean
registerBeanDefinitions(document);
inputStream.close();
}
private void registerBeanDefinitions(Document document) {
Element root = document.getDocumentElement();
//解析bean,从根节点开始
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 property = element.getElementsByTagName("property");
for (int i = 0; i < property.getLength(); i++) {
Node node = property.item(i);
if (node instanceof Element) {
Element propertyEle = (Element) node;
String name = propertyEle.getAttribute("name");
String value = propertyEle.getAttribute("value");
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
}
}
}
}
上面的代码就是解析xml文件,然后将对应的值,设置到BeanDefinition
中去,至此整个读取和解析xml文件就讲完了。
接下来就去书写我们的测试类,首先我们先来测试ResourceLoader
,具体的测试代码如下:
package com.ys.ioc.io;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class ResourceLoaderTest {
@Test
public void test() throws Exception {
ResourceLoader resourceLoader = new ResourceLoader();
Resource resource = resourceLoader.getResource("tinyioc.xml");
InputStream inputStream = resource.getInputStream();
Assert.assertNotNull(inputStream);
}
}
这儿我们利用了断言,判断这个输入流不为空,这儿如果测试通过了,这儿控制台就不会有异常。具体的测试结果如下:
可以看到我们的测试是通过的,证明我们写的代码是没有问题的。还是来看下配置文件吧,具体的代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/com.ys.ioc.beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:com.ys.ioc.context="http://www.springframework.org/schema/com.ys.ioc.context"
xsi:schemaLocation="
http://www.springframework.org/schema/com.ys.ioc.beans http://www.springframework.org/schema/com.ys.ioc.beans/spring-com.ys.ioc.beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/com.ys.ioc.context http://www.springframework.org/schema/com.ys.ioc.context/spring-com.ys.ioc.context-2.5.xsd">
<bean name="helloWorldService" class="com.ys.ioc.HelloWorldService">
<property name="text" value="Hello World!"></property>
</bean>
</beans>
然后再来看下HelloWorldService
这个类,具体的代码如下:
package com.ys.ioc;
public class HelloWorldService {
private String text;
public void helloWorld(){
System.out.println(text);
}
public void setText(String text) {
this.text = text;
}
}
这个时候,我们需要来测试一下,我们的XmlBeanDefinitionReader
,测试的代码如下:
package com.ys.ioc.xml;
import com.ys.ioc.BeanDefinition;
import com.ys.ioc.io.ResourceLoader;
import org.junit.Assert;
import org.junit.Test;
import java.util.Map;
public class XmlBeanDefinitionReaderTest {
@Test
public void test() throws Exception {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
Map<String, BeanDefinition> registry = xmlBeanDefinitionReader.getRegistry();
Assert.assertTrue(registry.size() > 0);
}
}
上面也是利用了断言,判断读取到BeanDefinition的长度大于0,这儿如果没有问题,控制台应该是通过的,具体的测试结果如下:
这个时候,需要测试我们写的核心代码了,具体的测试代码如下:
package com.ys.ioc;
import com.ys.ioc.factory.AutowireCapableBeanFactory;
import com.ys.ioc.factory.BeanFactory;
import com.ys.ioc.io.ResourceLoader;
import com.ys.ioc.xml.XmlBeanDefinitionReader;
import org.junit.Test;
import java.util.Map;
public class BeanFactoryTest {
@Test
public void test() throws Exception {
// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory并注册bean
BeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
测试的结果如下:
可以看到这儿我们测试是成功了。至此我们的第四步就完成了。
3.为bean注入bean
使用xml配置之后,似乎里我们熟知的Spring更近了一步!但是现在有一个大问题没有解决:我们无法处理bean之间的依赖,无法将bean注入到bean中,所以它无法称之为完整的IOC容器!如何实现呢?我们定义一个BeanReference
,来表示这个属性是对另一个bean的引用。这个在读取xml的时候初始化,并在初始化bean的时候,进行解析和真实bean的注入。BeanReference
代码如下:
package com.ys.ioc;
public class BeanReference {
private String name;
private Object bean;
public BeanReference(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object getBean() {
return bean;
}
public void setBean(Object bean) {
this.bean = bean;
}
}
接下来我们只需要看下变换的部分,由于篇幅的原因,不变的我这儿就不展示了,同时为了解决循环依赖的问题,我们使用lazy-init的方式,将createBean
的事情放到getBean
的时候才执行,备注:原来是在registerBeanDefinition
方法中,是不是一下子方便很多?这样在注入bean的时候,如果该属性对应的bean找不到,那么就先创建!因为总是先创建后注入,所以不会存在两个循环依赖的bean创建死锁的问题。具体的代码如下:
package com.ys.ioc.factory;
import com.ys.ioc.BeanDefinition;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class AbstractBeanFactory implements BeanFactory{
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
//用来存所有的bean的name
private final List<String> beanDefinitionNames = new ArrayList<>();
@Override
public Object getBean(String name) throws Exception {
//获取BeanDefinition
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null){
throw new IllegalArgumentException("No bean named " + name + " is defined");
}
//获取BeanDefinition对应的Bean
Object bean = beanDefinition.getBean();
//如果bean为空,我们就出创建
if (bean == null){
bean = doCreateBean(beanDefinition);
}
return bean;
}
@Override
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
beanDefinitionMap.put(name, beanDefinition);
beanDefinitionNames.add(name);
}
protected abstract Object doCreateBean(BeanDefinition beanDefinition) throws Exception;
public void preInstantiateSingletons() throws Exception{
for (Iterator<String> it = this.beanDefinitionNames.iterator(); it.hasNext();){
String beanName = it.next();
getBean(beanName);
}
}
}
package com.ys.ioc.factory;
import com.ys.ioc.BeanDefinition;
import com.ys.ioc.BeanReference;
import com.ys.ioc.PropertyValue;
import java.lang.reflect.Field;
public class AutowireCapableBeanFactory extends AbstractBeanFactory{
@Override
protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception{
Object bean = createBeanInstance(beanDefinition);
beanDefinition.setBean(bean);
applyPropertyValues(bean, beanDefinition);
return bean;
}
private Object createBeanInstance(BeanDefinition beanDefinition) throws Exception{
return beanDefinition.getBeanClass().newInstance();
}
private void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception{
for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()){
Field field = bean.getClass().getDeclaredField(propertyValue.getName());
field.setAccessible(true);
Object value = propertyValue.getValue();
//为bean注入bean
if (value instanceof BeanReference){
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getName());
}
field.set(bean,value);
}
}
}
至此代码就修改完成了,于是开始测试,我们先来看下配置文件,具体的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/com.ys.ioc.beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:com.ys.ioc.context="http://www.springframework.org/schema/com.ys.ioc.context"
xsi:schemaLocation="
http://www.springframework.org/schema/com.ys.ioc.beans http://www.springframework.org/schema/com.ys.ioc.beans/spring-com.ys.ioc.beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/com.ys.ioc.context http://www.springframework.org/schema/com.ys.ioc.context/spring-com.ys.ioc.context-2.5.xsd">
<bean name="outputService" class="com.ys.ioc.OutputService">
<property name="helloWorldService" ref="helloWorldService"></property>
</bean>
<bean name="helloWorldService" class="com.ys.ioc.HelloWorldService">
<property name="text" value="Hello World!"></property>
<property name="outputService" ref="outputService"></property>
</bean>
</beans>
然后再看我们在配置文件中定义的两个bean,具体的代码如下:
package com.ys.ioc;
import org.junit.Assert;
public class OutputService {
private HelloWorldService helloWorldService;
public void output(String text){
Assert.assertNotNull(helloWorldService);
System.out.println(text);
}
public void setHelloWorldService(HelloWorldService helloWorldService) {
this.helloWorldService = helloWorldService;
}
}
package com.ys.ioc;
public class HelloWorldService {
private String text;
private OutputService outputService;
public HelloWorldService() {
System.out.println("HelloWorldService.HelloWorldService");
}
public void helloWorld(){
outputService.output(text);
}
public void setText(String text) {
this.text = text;
}
public void setOutputService(OutputService outputService) {
this.outputService = outputService;
}
}
最后看下我们的测试代码:
package com.ys.ioc;
import com.ys.ioc.factory.AbstractBeanFactory;
import com.ys.ioc.factory.AutowireCapableBeanFactory;
import com.ys.ioc.io.ResourceLoader;
import com.ys.ioc.xml.XmlBeanDefinitionReader;
import org.junit.Test;
import java.util.Map;
public class BeanFactoryTest {
@Test
public void testLazy() throws Exception {
// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory并注册bean
AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
System.out.println("before getBean()");
// 3.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
System.out.println("after getBean()");
helloWorldService.helloWorld();
}
@Test
public void testPreInstantiate() throws Exception {
// 1.读取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory并注册bean
AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
// 3.初始化bean
beanFactory.preInstantiateSingletons();
// 4.获取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
上面的测试类第一个方法是测试懒加载,只有在调用getBean
的时候这个类才会创建,于是我在HelloWorldService初始化的方法中添加一行打印语句,看调用registerBeanDefinition
方法后,bean有没有创建,具体的测试结果如下:
从测试结果中可以得出我们的懒加载是起作用了。于是我们测试第二个方法,就是初始化bean的方法。由于我们调用的helloWorld
方法,是调用outputService.output(text);
而这个方法中我们加了断言,表示helloWorldService
不为空。这也是一个循环依赖的例子,只要这个例子通过了,那我们的IOC容器中的bean注入bean,就可以了,测试结果如下:
从测试结果,可以得出我们的IOC的容器已经差不多了。
4.step6-ApplicationContext登场
现在BeanFactory的功能齐全了,但是使用起来有点麻烦。于是我们引入熟悉的ApplicationContext
接口,并在AbstractApplicationContext
的refresh()
方法中进行bean的初始化工作。ApplicationContext接口的代码如下:
package com.ys.ioc.context;
import com.ys.ioc.beans.factory.BeanFactory;
public interface ApplicationContext extends BeanFactory {
}
同时我们对BeanFactory
接口也做了一些修改,具体的代码如下:
package com.ys.ioc.beans.factory;
public interface BeanFactory {
Object getBean(String name) throws Exception;
}
接下来我们再来看AbstractApplicationContext
抽象类的代码,具体的代码如下:
package com.ys.ioc.context;
import com.ys.ioc.beans.factory.AbstractBeanFactory;
public abstract class AbstractApplicationContext implements ApplicationContext{
protected AbstractBeanFactory beanFactory;
public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void refresh() throws Exception{}
@Override
public Object getBean(String name) throws Exception {
return beanFactory.getBean(name);
}
}
再书写ClassPathXmlApplicationContext
类对AbstractApplicationContext
进行继承,同时完成refresh
方法的开发,具体的代码如下:
package com.ys.ioc.context;
import com.ys.ioc.beans.BeanDefinition;
import com.ys.ioc.beans.factory.AbstractBeanFactory;
import com.ys.ioc.beans.factory.AutowireCapableBeanFactory;
import com.ys.ioc.beans.io.ResourceLoader;
import com.ys.ioc.beans.xml.XmlBeanDefinitionReader;
import java.util.Map;
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
private String configLocation;
public ClassPathXmlApplicationContext(String configLocation) throws Exception {
this(configLocation, new AutowireCapableBeanFactory());
}
public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception {
super(beanFactory);
this.configLocation = configLocation;
refresh();
}
@Override
public void refresh() throws Exception {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
}
}
这样我们这部分的代码就书写完成了,继续看我们测试的部分,原来的配置文件没有变,我们只需要看测试类,具体的代码如下:
package com.ys.ioc.context;
import com.ys.ioc.HelloWorldService;
import org.junit.Test;
public class ApplicationContextTest {
@Test
public void test() throws Exception {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
helloWorldService.helloWorld();
}
}
测试结果如下:
可以看到我们的测试是没有问题的。
5.写在最后
至此我们简单模拟Spring的IOC容器的源码就完成了,后面会模拟Aop。本篇博客主要参考《tiny-spring》开源项目书写。