编写一个实现 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; //注册对象信息Mapprotected SourceReader reader; //对象注册读取器public AbstractBeanFactory(String filePath){this.filePath = filePath;}/*** 该方法为抽象方法,需有子类类实现,用于指定使用什么样的注册读取器* @param reader 指定的注册读取器*/protected abstract void setSourceReader(SourceReader reader);// 从注册读取器中读取,注册对象的信息MAPpublic void registerBeans(){this.container = this.reader.loadBeans(filePath);}// 实现BeanFactory定义的根据名称获取指定对象的方法@Overridepublic 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 blocke.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;}@Overridepublic 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()); //添加注册读取器,此处的注册读取器为XMLSourceReaderthis.registerBeans(); //加载注册的对象信息}// 设置注册读取器@Overrideprotected 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{/*** 实现读取注册对象信息方法* 此处只是模拟测试使用,感兴趣的同学可以自己书写通过配置文件读取的实现*/@Overridepublic Map<String, BeanInfo> loadBeans(String filePath) {//初始化一个对象信息Map<String,BeanInfo> beans = new HashMap<String,BeanInfo>(); //初始化一个对象信息MapBeanInfo beanInfo=new BeanInfo();File filename = new File("bean.txt"); // 要读取以上路径的input。txt文件InputStreamReader reader = null; // 建立一个输入流对象readertry {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()); //添加注册读取器,此处的注册读取器为XMLSourceReaderthis.registerBeans(); //加载注册的对象信息} // 设置注册读取器@Overrideprotected 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 {/*** 实现读取注册对象信息方法* 此处只是模拟测试使用,感兴趣的同学可以自己书写通过配置文件读取的实现*/@Overridepublic Map<String, BeanInfo> loadBeans(String filePath) {//初始化一个对象信息Map<String,BeanInfo> res = new HashMap<String,BeanInfo>(); //初始化一个对象信息Maptry {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;}
}}