编写一个实现 IOC 功能的简单 Spring 框架,包含对象注册、对象管理、及暴 露给外部获取对象的功能,并编写测试程序。扩展注册器的方式,要求采用 XML 和 txt 文件。
源代码
package myspring;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 最顶层的IOC实现
* 该类负责从注册器中取出注册对象
* 实现从对象描述信息转换为对象实例的过程、
* 实现根据名称获取对象的方法
*/
public abstract class AbstractBeanFactory implements BeanFactory {
private String filePath; //注册文件路径
private Map<String,BeanInfo> container; //注册对象信息Map
protected SourceReader reader; //对象注册读取器
public AbstractBeanFactory(String filePath){
this.filePath = filePath;
}
/**
* 该方法为抽象方法,需有子类类实现,用于指定使用什么样的注册读取器
* @param reader 指定的注册读取器
*/
protected abstract void setSourceReader(SourceReader reader);
// 从注册读取器中读取,注册对象的信息MAP
public void registerBeans(){
this.container = this.reader.loadBeans(filePath);
}
// 实现BeanFactory定义的根据名称获取指定对象的方法
@Override
public Object getBean(String name) {
BeanInfo beaninfo = this.container.get(name); //根据对象名获取该对象的描述信息
if(beaninfo == null){ //如果容器中不存在该对象的描述信息,则返回null,此处可以抛开一个异常
return null;
}else{ //根据对象信息,解析并生成指定对象实例,返回给用户
return this.parseBean(beaninfo);
}
}
/**
* 解析并生成对象实例
* 该方法主要通过反射完成,步骤如下:
* 1.根据类名,加载指定类,并获取该类的貌似Class对象clazz
* 2.使用Class对象clazz实例化该类,获取一个对象,注意,这儿实例化对象时,采用的无参构造方法,因此要求注册的对象必须含有无参构造方法
* 3.逐个设置对象字段的值,这儿采用setter Method方式,而不是直接使用Field对象的原因是,用户有可能在setter对象中,对注入的值进行额外处理,如格式化等
* 4.返回对象实例
* @param beaninfo 指定对象的描述信息
* @return
*/
protected Object parseBean(BeanInfo beaninfo){
Class clazz;
try {
clazz = Class.forName(beaninfo.getType()); //根据对象的全类名,指定类
Object bean = clazz.newInstance(); //使用注册对象的无参构造函数,实例化对象实例
Method[] methods = clazz.getMethods(); //获取该类声明的所有公共方法,其实Spring获取的是所有方法,包括非公有的
for(String property : beaninfo.getProperties().keySet()){ //遍历对象的所有属性,进行赋值
String setter = "set" + StringUtil.firstCharToUp(property); //获取属性的setter方法名称
for(Method method : methods){ //遍历该类的所有公有方法
String methodName = method.getName(); //获取方法名称
if(methodName.equals(setter)){ //比较方法名与属性的setter方法名是否相同,如果相同则进行赋值
Object value = beaninfo.getProperties().get(property); //从对象描述信息中获取该属性的值
method.invoke(bean,value); //通过反射对属性进行赋值
continue; //对下一属性赋值
}
}
}
return bean; //返回指定的对象
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
package myspring;
/**
* IOC容器的顶层接口
*/
public interface BeanFactory {
/**
* 根据对象的名称标识来获取对象实例
* @param name 对象名称,即对象描述信息中的对象标识
* @return 指定名称的对象实例
*/
Object getBean(String name);
}
package myspring;
import java.util.HashMap;
import java.util.Map;
//该类用于描述注册在容器中的对象
public class BeanInfo {
private String id; //对象的标识
private String type; //对象的类型,即全类名
private Map<String,Object> properties = new HashMap<String,Object>(); //对象的属性及值得集合
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Map<String,Object> getProperties() {
return properties;
}
public void setProperties(Map<String, Object> properties) {
this.properties = properties;
}
public void addProperty(String name, Object value){
this.properties.put(name, value);
}
}
package myspring;
public class Bootstrap {
public static void main(String[] args) {
BeanFactory factory = new TXTContext("bean.txt");
Speakable s = (Speakable)factory.getBean("Person");
s.speak("Lesson one!");
}
}
package myspring;
public class Person implements Speakable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void speak(String message) {
System.out.println( this.name + " say: " + message);
}
}
package myspring;
import java.util.Map;
/**
* 注册读取器接口
* 负责从读取用户注册的对象
* 继承该接口的类可以实现多种读取方式,如从配置文件中读取,根据标注读取,从网络中读取等
*/
public interface SourceReader {
/**
* 读取用户注册的对象信息
* @param filePath 读取录取
* @return 注册对象信息Map
*/
Map<String,BeanInfo> loadBeans(String filePath);
}
package myspring;
public interface Speakable {
public void speak(String message);
}
package myspring;
public class StringUtil {
/*public static void main(String[] args) {
System.out.println(StringUtil.firstCharToUp(str));
}*/
public static String firstCharToUp(String str){
char ch[]=str.toCharArray();
char ch1=Character.toUpperCase(ch[0]);
ch[0]=ch1;
String s=new String(ch);
return s;
}
public static String firstCharToLower(String str){
char ch[]=str.toCharArray();
char ch1=Character.toLowerCase(ch[0]);
ch[0]=ch1;
String s=new String(ch);
return s;
}
}
package myspring;
public class TXTContext extends AbstractBeanFactory {
/**
* 上下文的构造方法
* 该方法中指明注册读取器
* 并在构造该方法时一次性的加载注册的对象
* @param filePath
*/
public TXTContext(String filePath) {
super(filePath);
this.setSourceReader(new TXTSourceReader()); //添加注册读取器,此处的注册读取器为XMLSourceReader
this.registerBeans(); //加载注册的对象信息
}
// 设置注册读取器
@Override
protected void setSourceReader(SourceReader reader) {
this.reader = reader;
}
}
package myspring;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
public class TXTSourceReader implements SourceReader{
/**
* 实现读取注册对象信息方法
* 此处只是模拟测试使用,感兴趣的同学可以自己书写通过配置文件读取的实现
*/
@Override
public Map<String, BeanInfo> loadBeans(String filePath) {
//初始化一个对象信息
Map<String,BeanInfo> beans = new HashMap<String,BeanInfo>(); //初始化一个对象信息Map
BeanInfo beanInfo=new BeanInfo();
File filename = new File("bean.txt"); // 要读取以上路径的input。txt文件
InputStreamReader reader = null; // 建立一个输入流对象reader
try {
reader = new InputStreamReader(
new FileInputStream(filename));
BufferedReader br = new BufferedReader(reader); // 建立一个对象,它把文件内容转成计算机能读懂的语言
String line = "";
line = br.readLine();
while (line != null) {
String[] t=line.split("=");
System.out.println(t[0]+t[1]);
if(t[0].equals("id"))
{
beanInfo.setId(t[1]);
}else if(t[0].equals("class"))
{
beanInfo.setType(t[1]);
}else beanInfo.addProperty(t[0],t[1]);
line = br.readLine();
}
} catch (FileNotFoundException fileNotFoundException) {
fileNotFoundException.printStackTrace();
} catch (IOException ioException)
{
ioException.printStackTrace();
}
beans.put(beanInfo.getId(),beanInfo);
return beans; //返回对象信息MAP
}
package myspring;
public class XMLContext extends AbstractBeanFactory{
/**
* 上下文的构造方法
* 该方法中指明注册读取器
* 并在构造该方法时一次性的加载注册的对象
* @param filePath
*/
public XMLContext(String filePath) {
super(filePath);
this.setSourceReader(new XMLSourceReader()); //添加注册读取器,此处的注册读取器为XMLSourceReader
this.registerBeans(); //加载注册的对象信息
}
// 设置注册读取器
@Override
protected void setSourceReader(SourceReader reader) {
this.reader = reader;
}
}
package myspring;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.xpath.XPath;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* XML注册读取器
* 该类继承了注册读取器接口,并模拟实现了读取注册对象信息的方法
*/
public class XMLSourceReader implements SourceReader {
/**
* 实现读取注册对象信息方法
* 此处只是模拟测试使用,感兴趣的同学可以自己书写通过配置文件读取的实现
*/
@Override
public Map<String, BeanInfo> loadBeans(String filePath) {
//初始化一个对象信息
Map<String,BeanInfo> res = new HashMap<String,BeanInfo>(); //初始化一个对象信息Map
try {
SAXBuilder builder = new SAXBuilder();
Document doc = null;
doc = (Document) builder.build(new File("book.xml")); XPath xpath = null; xpath = XPath.newInstance("//bean");
List beans = null;beans = xpath.selectNodes(doc); Iterator i = beans.iterator();
while (i.hasNext()) {
BeanInfo beaninfo = new BeanInfo();
Element bean = (Element) i.next();
String id = bean.getAttributeValue("id");
String cls = bean.getAttributeValue("class");
beaninfo.setId(id);
beaninfo.setType(cls);
List<Element> list = bean.getChildren("property");
for (Element el : list) {
if (el.getAttribute("name") != null) {
beaninfo.addProperty(el.getAttributeValue("name"),el.getAttributeValue("value"));
}
}
res.put(beaninfo.getId(),beaninfo);
}
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
}
}