編寫一個實現 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;}
}}