设计一个含有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!