各位老少爷们,赶紧系紧安全带,菜鸟开车,防止车速过快。言归正传,本篇文章呢,主要是来探讨一下Spring管理bean的原理。
如上xml文件所示,通常我们在使用Spring管理系统应用的组件时,在Spring的核心配置文件中,配置上需要管理组件的信息就可以了,剩下的就都交于Spring框架了。但是我们不要满足于用,更要知道其中的原理。知己知彼,百战不殆。为了了解Spring框架如何管理bean的,接下来我们简单模仿一下Spring 容器。
package com.marswzp.test.beanbase;
/**
* bean 对象基本定义
* @author Administrator
*
*/
public class BeanDefintion {
private String id;
private String className;
public BeanDefintion(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;
}
}
package com.marswzp.test.ioc;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import com.marswzp.test.beanbase.BeanDefintion;
/**
*
* 容器:模拟Spring容器
* @author Administrator
*
*/
public class MyTestClassPathXMLApplicationContext {
//存放BeanDefintion 对象的集合
private List
beanDefintions = new ArrayList
();
//存放实例化后的bean对象
private Map
singletons = new HashMap
();
/**
* 容器构造方法:用于读取配置文件
* @param resource
*/
public MyTestClassPathXMLApplicationContext(String resourceName){
//读取配置文件,并将bean的配置信息放入集合中
this.readXML(resourceName);
//实例化bean
this.instanceBean();
}
/**
* 根据xml资源名称读取xml文件
* @param resourceName
*/
public void readXML(String resourceName){
//采用dom4j 的SAX方式读取xml配置文件
SAXReader saxReader = new SAXReader();
Document document = null;
try {
URL xmlPath = this.getClass().getClassLoader().getResource(resourceName);//获取资源路径
document = saxReader.read(xmlPath);//读取路径的文件,实例化xml文档对象
/**
* 采用xpath 匹配查询xml文档节点
*/
//增加XPath的命名空间
Map
nsMap = new HashMap
(); nsMap.put("ns", "http://www.springframework.org/schema/beans"); XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean的查询路径 //设置 命名空间 xsub.setNamespaceURIs(nsMap); //匹配解析xml文档对象,返回beans.xml文件中bean配置信息节点 List
beans = xsub.selectNodes(document); //遍历获取bean配置信息,实例化BeanDefintion对象 for(Element bean : beans){ String id = bean.attributeValue("id");//id属性值 String clazz = bean.attributeValue("class");//class属性值 //创建BeanDefintion 对象 BeanDefintion beanDefintion = new BeanDefintion(id,clazz); //bean配置对象方法集合中 beanDefintions.add(beanDefintion); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 通过 反射集合实例化bean */ private void instanceBean() { for(BeanDefintion beanDefintion : beanDefintions){ try { if(beanDefintion.getClassName() !=null && !"".equals(beanDefintion.getClassName().trim())){ singletons.put(beanDefintion.getId(), Class.forName(beanDefintion.getClassName()).newInstance()); } } catch (Exception e) { e.printStackTrace(); } } } /** * 根据id获取bean对象 * @param id * @return */ public Object getBean(String key){ return singletons.get(key); } }
MyTestClassPathXMLApplicationContext类模仿了Spring 容器,仔细分析其中的代码,可以知道,在这个容器类中,我主要做了以下几个步骤:
1. 读取xml配置文件, 数据封装到BeanDefinition。
2. 将BeanDefinition对象存入list。
3. 遍历list, 通过反射产生对象并存入map。
4. 调用getBean方法返回一个bean对象, 此bean对象为单例。
读取xml配置文件时,我采用的是dom4j 的sax方法,并且通过xpath来匹配xml文档中的节点。因此我们需要在项目中引入两个jar包:dom4j-1.6.1.jar、jaxen-1.1-beta-6.jar。将这两个jar扔到项目的lib文件夹下即可。当我们解析完成xml文档,并且将xml文档中<bean>节点所配置的信息封装到BeanDefinition对象中时,接下来容器会通过这些封装的配置信息,通过反射技术,实例化bean对象,并存入一个Map集合。容器提供了一个getBean方法,在容器外部可以获取到实例化后的bean对象。
容器类已经模仿好了,那么接下来需要进行实例测试了。事先我定义了一个接口,并且编写实现类实现了这个接口,代码如下。
/**
*学生接口
* @author Administrator
*
*/
public interface StudentService {
public void save();
public void say();
}
import com.marswzp.service.StudentService;
/**
* 接口实现类
* @author Administrator
*
*/
public class StudentServiceImpl implements StudentService {
public void save() {
System.out.println("我是一个保存方法");
}
public void say() {
System.out.println("我是一个学生,我要抓住凹凸曼!!!");
}
}
编写测试用例,我采用的是单元测试框架junit4进行测试单元测试,测试用例代码如下。
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.marswzp.service.PersonService;
import com.marswzp.service.StudentService;
import com.marswzp.test.ioc.MyTestClassPathXMLApplicationContext;
/**
* 测试类
* @author Administrator
*
*/
public class JunitTest {
@Before
public void star() {
System.out.println("开始测试");
}
@After
public void end() {
System.out.println("测试结束");
}
@Test
public void testMyIoc(){
/**
* 模仿IOC
*/
MyTestClassPathXMLApplicationContext context = new MyTestClassPathXMLApplicationContext("beans.xml");
StudentService studentService = (StudentService)context.getBean("studentService");
studentService.say();
}
}
以上就是通过模仿Spring容器管理bean的过程,可以让我们可以更深入的理解Spring管理bean对象的机制,当然以上的模仿只是做简单的模仿,Spring 的容器提供了更多的功能和更多的行为,需要你慢慢地去理解和使用。