目录
【SSH进阶之路】一步步重构容器实现Spring框架——从一个简单的容器开始(八)
【SSH进阶之路】一步步重构容器实现Spring框架——解决容器对组件的“侵入式”管理的两种方案--主动查找和控制反转(九)
【SSH进阶之路】一步步重构容器实现Spring框架——配置文件+反射实现IoC容器(十)
【SSH进阶之路】一步步重构容器实现Spring框架——彻底封装,实现简单灵活的Spring框架(十一)(已更新)
上上篇博文【SSH进阶之路】一步步重构容器实现Spring框架——从一个简单的容器开始(八),我们为了去掉接
口对具体实现的依赖关系,封装了一个特别简陋的容器。
上篇博文【SSH进阶之路】一步步重构容器实现Spring框架——解决容器对组件的“侵入式”管理的两种方案--主
动查找和控制反转(九),我们利用控制反转,去掉了组件对容器的依赖。
简单配置,反射
上篇博文容器初始化时,使用new的方式来实力化对象,这篇博文我们利用配置文件+反射实力化对象,进一步封
装降低容器和组件的耦合度。下面我们先看一下配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="dao" class="com.tgb.container.dao.impl.Dao4MySqlImpl" />
<bean id="service" class="com.tgb.container.service.impl.ServiceImpl" />
</beans>
看到上面的配置文件,除了命名空间没有,和Spring的配置文件已经很像了,下面我们就使用dom4j或jdom来读取
配置文件,并将配置文件中配置类利用反射实例化。本实例我们使用的jdom,大家也可以使用dom4j试一下。下面我
们看一下读取配置文件的代码:
public interface BeanFactory {
Object getBean(String id);
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;
import com.tgb.container.dao.Dao;
import com.tgb.container.service.Service;
/**
* 从类路径加载配置文件
*
* @author liang
*
*/
public class ClassPathXmlApplicationContext implements BeanFactory {
// 用于存放Bean
private Map<String, Object> beans = new HashMap<String, Object>();
public ClassPathXmlApplicationContext(String fileName) {
this.readXML(fileName);
}
// 解析xml文件,通过反射将配置的beasn放到container中,并实现依赖注入
private void readXML(String fileName) {
// 创建SAXBuilder对象
SAXBuilder saxBuilder = new SAXBuilder();
// 读取资源,获得document对象
Document doc;
try {
doc = saxBuilder.build(this.getClass().getClassLoader().getResourceAsStream(fileName));
// 获取根元素
Element rootEle = doc.getRootElement();
// 从根元素获得所有的子元素,建立元素集合
List listBean = XPath.selectNodes(rootEle, "/beans/bean");
// 遍历根元素的子元素集合,扫描配置文件中的bean
for (int i = 0; i < listBean.size(); i++) {
Element bean = (Element) listBean.get(i);
// 获取id属性值
String id = bean.getAttributeValue("id");
// 获取class属性值
String clazz = bean.getAttributeValue("class");
// 反射,实例化
Object o = Class.forName(clazz).newInstance();
beans.put(id, o);
}
// 依赖管理,这里还不灵活,但是原理是一样的
Service service = (Service) beans.get("service");
Dao dao = (Dao) beans.get("dao");
// 依赖注入,Service实现依赖dao的实现
service.setDao(dao);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 查找组件
*
* @param id
* @return
*/
@Override
public Object getBean(String id) {
return beans.get(id);
}
}
看到上面的代码,我们发现读取配置文件的方法中包含了反射,代码的可读性太差,并且对面向对象的封装不够彻
底,下面我们将bean的实例化以及依赖注入进行进一步的封装。
封装bean的实例化
为了做进一步的封装,我们将配置文件的属性封装成一个javabean,为了存放我们的属性值。如下所示:
public class BeanDefinition {
private String id;
private String className;
public BeanDefinition(String id, String className) {
this.id = id;
this.className = className;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
现在我们就可以把bean的实例化做进一步的封装了。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;
import com.tgb.container.dao.Dao;
import com.tgb.container.service.Service;
/**
* 容器
*
* @author liang
*
*/
public class ClassPathXmlApplicationContext implements BeanFactory {
// 用于存放Bean
private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
// 用于存放Bean的实例
private Map<String, Object> sigletons =new HashMap<String, Object>();
public ClassPathXmlApplicationContext(String fileName) {
this.readXML(fileName);
this.instanceBeans();
this.injectObject();
}
/**
* 依赖注入,为bean对象的属性注入值
* 这里还不灵活,但是原理是一样的
*/
private void injectObject() {
Service service = (Service) this.sigletons.get("service");
Dao dao = (Dao) this.sigletons.get("dao");
//依赖注入,Service实现依赖dao的实现
service.setDao(dao);
}
/**
* 完成bean的实例化
*/
private void instanceBeans() {
for(BeanDefinition beanDefinition : beanDefines){
try {
if(beanDefinition.getClassName() != null && !"".equals(beanDefinition.getClassName().trim())){
sigletons.put(beanDefinition.getId(),Class.forName(beanDefinition.getClassName()).newInstance() );
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 读取xml配置文件
*/
private void readXML(String fileName) {
// 创建SAXBuilder对象
SAXBuilder saxBuilder = new SAXBuilder();
try {
// 读取资源,获得document对象
Document doc = saxBuilder.build(this.getClass().getClassLoader()
.getResourceAsStream(fileName));
// 获取根元素
Element rootEle = doc.getRootElement();
// 从根元素获得所有的子元素,建立元素集合
List listBean = XPath.selectNodes(rootEle, "/beans/bean");
// 遍历根元素的子元素集合,扫描配置文件中的bean
for (int i = 0; i < listBean.size(); i++) {
Element bean = (Element) listBean.get(i);
// 获取id属性值
String id = bean.getAttributeValue("id");
// 获取class属性值
String clazz = bean.getAttributeValue("class");
BeanDefinition beanDefine = new BeanDefinition(id,clazz);
// 将javabean添加到集合中
beanDefines.add(beanDefine);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取bean实例
*/
@Override
public Object getBean(String beanName) {
return this.sigletons.get(beanName);
}
}
我们知道容器不仅负责创建对象,而且可以管理对象的依赖关系,管理对象的生命周期等等。我们仅实现了容器
灵活创建对象的部分,依赖注入部分是由我们手动注入的。 对象的依赖关系还不灵活,但是我们已经能够看到IoC的
影子了,只是形似,还没有达到神似的目标。
下篇博文【SSH进阶之路】一步步重构容器实现Spring框架——彻底封装,实现简单灵活的Spring框架(十一),马
上送上。