設計一個含有IOC的簡單Spring,要求含有對象註冊、對象管理以及暴露給外部的獲取對象功能。java
package ex1; import java.util.HashMap; import java.util.Map; /** * 該類用於描述註冊在容器中的對象 */ public class BeanInfo { private String id; //對象ID,名字 private String type; //全類名 private Map<String, Object> properties = new HashMap<>(); //屬性名與值的映射集合 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 key,Object value) { properties.put(key,value); } }
package ex1; public interface BeanFactory { /** * 根據對象的名稱標識來獲取對象實例 * @param id 對象名稱,即對象描述信息中的對象標識 * @return 指定名稱的對象實例 */ Object getBean(String id); }
package ex1; import java.lang.reflect.InvocationTargetException; 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(IOC容器) protected SourceReader reader; //對象註冊讀取器 public AbstractBeanFactory(String filePath) { this.filePath = filePath; } /** * 抽象方法,需由子類實現,用於指定使用什麼樣的註冊讀取器 * @param reader 指定的註冊讀取器 */ protected abstract void setReader(SourceReader reader); //從註冊讀取器中讀取註冊對象的信息Map public void registerBeans() { this.container = this.reader.loadBeans(this.filePath); } //實現BeanFactory定義的根據名稱獲取指定對象的方法 @Override public Object getBean(String id) { BeanInfo beanInfo = this.container.get(id); //根據對象名稱獲取該對象的描述信息 if (beanInfo == null) { return null; } else { //根據對象信息,解析並生存指定對象實例,返回給用戶 return this.parseBean(beanInfo); } } /** * 解析並生成對象實例 * 該方法主要經過反射完成,步驟以下: * 1.根據類名,加載指定類,並獲取該類的Class對象clazz * 2.使用clazz實例化該類,獲取一個對象,注意,這裏採用無參構造方法 * 3.逐個設置對象子段的值,這裏採用setter Method方式,而不是直接使用Field對象 * 4.返回對象實例 * @param beanInfo 指定對象的描述信息 * @return */ protected Object parseBean(BeanInfo beanInfo) { Class clazz; Object bean = null; try { clazz = Class.forName(beanInfo.getType()); //根據對象的全類名,指定類 bean = clazz.newInstance(); //使用註冊對象的無參構造函數,實例化對象 Method[] methods = clazz.getMethods(); //獲取全部公共方法(其實Spring獲取的是全部方法,包括非公有是) for (String property : beanInfo.getProperties().keySet()) { //首字母大寫 String name = property.toUpperCase().charAt(0) + property.toLowerCase().substring(1); //獲取屬性的setter方法名稱(命名規範) String setter = "set" + name; for (Method method : methods) { if (method.getName().equals(setter)) { Object value = beanInfo.getProperties().get(property); method.invoke(bean, value); //經過反射對屬性賦值 break; } } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return bean; } }
package ex1; import java.util.Map; /** * 註冊讀取器接口 * 複製讀取用戶註冊的對象 * 繼承該接口的類能夠實現多種讀取方式,如從配置文件中讀取,根據標註讀取,從網絡中讀取等 */ public interface SourceReader { /** * 讀取用戶註冊的對象信息 * @param filePath 註冊路徑 * @return 註冊對象信息Map */ Map<String, BeanInfo> loadBeans(String filePath); }
這裏實現從xml配置文件中讀取。須要用到dom4j包,用來解析XML文件。本項目中XML文件放置在根目錄下,內容以下:bootstrap
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="Person" class="ex1.Person"> <property name="name" value="fang"/> </bean> </beans>
實現代碼網絡
package ex1; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * XML註冊讀取器 * 該類繼承了註冊讀取器接口,並模擬實現了讀取註冊對象信息的方法 */ public class XMLSourceReader implements SourceReader { /** * 實現讀取註冊對象信息方法 * 本身編寫經過配置文件讀取的實現 */ @Override public Map<String, BeanInfo> loadBeans(String filePath) { BeanInfo info = new BeanInfo(); //註冊對象的info InputStream is = XMLContext.class.getClassLoader().getResourceAsStream(filePath);//獲取xml文件 SAXReader reader = new SAXReader(); Map<String,BeanInfo> beanMap = new HashMap<>(); try { Document document = reader.read(is); Element root = document.getRootElement(); //獲取根標籤,這裏是beans //遍歷全部bean for(Iterator iterator = root.elementIterator("bean");iterator.hasNext();){ Element element = (Element)iterator.next(); //獲取id和class Attribute id = element.attribute("id"); Attribute clazzName = element.attribute("class"); info.setId(id.getText()); info.setType(clazzName.getText()); //遍歷該bean的property for(Iterator it=element.elementIterator("property");it.hasNext();){ Element tmp = (Element)it.next(); //獲取name和value Attribute name = tmp.attribute("name"); Attribute value = tmp.attribute("value"); info.addProperty(name.getText(),value.getText()); } beanMap.put(id.getText(),info); } } catch (DocumentException e) { e.printStackTrace(); } return beanMap; } }
package ex1; public class XMLContext extends AbstractBeanFactory { /** * 上下文的構造方法 * 該方法中指明註冊讀取器 * 並在構造該方法時一次性加載註冊的對象 * @param filePath */ public XMLContext(String filePath) { super(filePath); this.setReader(new XMLSourceReader());//添加註冊讀取器 this.registerBeans(); //加載註冊的對象信息 } //設置註冊讀取器 @Override protected void setReader(SourceReader reader) { this.reader = reader; } }
package ex1; public interface Speakable { void speak(String message); }
package ex1; 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 ex1; public class Bootstrap { public static void main(String[] args) { BeanFactory beanFactory = new XMLContext("bean.xml"); Speakable speakable = (Speakable) beanFactory.getBean("Person"); speakable.speak("Experience One!"); } }
fang say: Experience One!