紧接上一篇文章《轻松了解Spring中的控制反转和依赖注入》讲解了SpringIOC和DI的基本概念,这篇文章我们模拟一下SpringIOC的工作机制,使我们更加深刻的理解其中的工作。
类之间的结构图如下
以下是代码
BeanFactor接口:在Spring源码中的定义是:持有对一定数量的Bean的定义,同时每个Bean都被唯一标识的对象(类),需要实现这个接口。根据对Bean的定义,该工厂将会返回一个包含Bean定义的对象的独立实例(原型设计模式),或者单例共享(一个不错的单例设计模式,)范围是整个工厂的范围(也可以理解成是整个容器下),返回哪种类型的实例依赖于Bean工厂的配置:API是相同的。因为Spring2.0中扩大了依赖范围,可以根据具体应用上下问(如在Web环境中的请求和会话),BeanFactory是应用程序组件的中央注册中心和集中配置。简单的来说该接口定义了获取Bean的方法,由子类去实现。
package ioc.factory; /** * Created by zzf on 2016/10/26. */ public interface BeanFactory { /** * 根据对象的ID标识获取对象实例 * @param name * @return */ Object getBean(String name); }
BeanInfo类:使用Hash Map进行存储Bean的信息,注意主要是存储了Bean定义的Class类路径,这样才能通过取得type从而利用反射实例化所定义的Bean。
package ioc.info; import java.lang.Object; import java.util.HashMap; import java.util.Map; /** * Created by zzf on 2016/10/26. */ 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 object) { this.properties.put(name, object); } }
Person:xml文件中定义的Bean
package ioc.bean; /** * Created by zzf on 2016/10/26. */ public class Person { private String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
AbstractBeanFactory接口:是实现BeanFactory接口的抽象基类。实现获取Bean定义的方法。是实现逻辑的最关键一个类,注册、读取、分析、注入都是在这个类上的方法调用的,具体可以根据方法查找。
package ioc.factory; import ioc.info.BeanInfo; import ioc.reader.SourceReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; /** * Created by zzf on 2016/10/26. * * 最顶层的IOC实现 * 该类负责从注册器中取出注册对象 * 实现从对象描述信息转换为对象实例的过程 * 实现根据名称获取对象的方法 * */ public abstract class AbstractBeanFactory implements BeanFactory { private String filePath; private Map<String,BeanInfo> container; protected SourceReader reader; public AbstractBeanFactory(String filePath){ this.filePath=filePath; } /** * 由子类决定如果使用什么样的注册读取器 * 这里使用了模板方法,父类定义抽象方法,但交给子类自由去设计方法内容 * @param reader */ protected abstract void setReader(SourceReader reader); //注册bean public void registerBeans(){ this.container=this.reader.loadBeans(filePath); } @Override public Object getBean(String name) { BeanInfo beanInfo=this.container.get(name); if(beanInfo==null){ return null; }else { return this.parseBean(beanInfo); } } /** * 解析生成并生成对象实例 * 主要通过反射完成 * 根据类名,加载指定类,并取得该类的Class对象 * 使用Class对象实例化该类,获取一个对象。 * 逐个设置对象字段的值,这里采用setter Method方式 * * @param beanInfo 指定对象的描述信息 * @return */ protected Object parseBean(BeanInfo beanInfo){ Class clazz; try { //加载Bean的实例 clazz=Class.forName(beanInfo.getType()); Object bean=clazz.newInstance(); //获取该对象下的所有方法,包括私有 Method[] methods=clazz.getDeclaredMethods(); for (String property:beanInfo.getProperties().keySet()){ String setter="set"+ firstCharToUp(property); for(Method method:methods){ String methodName=method.getName(); if(methodName.equals(setter)){ Object value=beanInfo.getProperties().get(property); //使用反射调用set方法 method.invoke(bean,value); } } } return bean; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } private String firstCharToUp(String property) { System.out.println(property); char [] c=property.toCharArray(); String first=String.valueOf(c[0]).toUpperCase(); c[0]=first.charAt(0); System.out.println(String.valueOf(c)); return String.valueOf(c); } } package ioc.context; import ioc.factory.AbstractBeanFactory; import ioc.reader.SourceReader; import ioc.reader.XMLSourceReader; /** * Created by zzf on 2016/10/26. * 上下文的构造方法 * 该方法中指明注册读取器(这里采用的XML,读者可以根据兴趣去实现另外的方式如注解) * 并在构造该方法时一次性加载注册的对象 */ public class XMLContext extends AbstractBeanFactory{ public XMLContext(String filePath){ super(filePath); this.setReader(new XMLSourceReader()); super.registerBeans(); } @Override protected void setReader(SourceReader reader) { super.reader=reader; } }
SourceReader:设计顶层读取器的抽象方法
package ioc.reader; import ioc.info.BeanInfo; import java.util.Map; /** * Created by zzf on 2016/10/26. * 注册读取器接口 * 负责读取用户注册的对象 * 继承该接口的类可以实现多种读取方式,如从配置文件中读取,根据标注读取,从网络中读取等等 */ public interface SourceReader { Map<String,BeanInfo> loadBeans(String filePath); }
XMLContext:XML的上下文,继承了AbstractBeanFactory,里面比较重要的方法是setReader(),在父类中该方法是抽象方法, 这样的做的意义是交由子类实现自己所想要的读取方式。
package ioc.context; import ioc.factory.AbstractBeanFactory; import ioc.reader.SourceReader; import ioc.reader.XMLSourceReader; /** * Created by zzf on 2016/10/26. * 上下文的构造方法 * 该方法中指明注册读取器(这里采用的XML,读者可以根据兴趣去实现另外的方式如注解) * 并在构造该方法时一次性加载注册的对象 */ public class XMLContext extends AbstractBeanFactory{ public XMLContext(String filePath){ super(filePath); this.setReader(new XMLSourceReader()); super.registerBeans(); } @Override protected void setReader(SourceReader reader) { super.reader=reader; } }
XmlContext类:继承了AbstractBeanFactory抽象类,进行Bean的注册和注册XML的读取器。
package ioc.reader; import ioc.info.BeanInfo; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.InputStream; import java.io.Reader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * Created by zzf on 2016/10/26. * 使用 dom4j进行Xml的读取操作 */ public class XMLSourceReader implements SourceReader { @Override public Map<String, BeanInfo> loadBeans(String filePath) { //读取指定的配置文件 SAXReader reader = new SAXReader(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); //从class目录下获取指定的xml文件 InputStream ins = classLoader.getResourceAsStream(filePath); Document doc = null; try { doc = reader.read(ins); } catch (DocumentException e) { e.printStackTrace(); } //获得根节点 Element root = doc.getRootElement(); Map<String,BeanInfo>beanInfoMap=new HashMap<String, BeanInfo>(); //遍历bean for (Iterator i = root.elementIterator("bean"); i.hasNext();){ Element element = (Element) i.next(); //获取bean的属性id和class Attribute id = element.attribute("id"); Attribute cls = element.attribute("class"); try { //利用Java反射机制,通过class的名称获取Class对象 Class bean=Class.forName(cls.getText()); //获取对应class的信息 java.beans.BeanInfo info= Introspector.getBeanInfo(bean); //获取其属性描述 PropertyDescriptor [] propertyDescriptors=info.getPropertyDescriptors(); Method method; Object object=bean.newInstance(); BeanInfo beanInfo=new BeanInfo(); for(Iterator iterator=element.elementIterator("property");iterator.hasNext();){ Element foo2= (Element) iterator.next(); //获取该property的name属性 Attribute name = foo2.attribute("name"); String value = null; //获取该property的子元素value的值 for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) { Element node = (Element) ite1.next(); value = node.getText(); break; } System.out.println("name:"+name.getText()+"value"+value); for (int j=0;j<propertyDescriptors.length;j++){ if(propertyDescriptors[j].getName().equalsIgnoreCase(name.getText())){ method=propertyDescriptors[j].getWriteMethod(); //利用Java的反射极致调用对象的某个set方法,并将值设置进去 method.invoke(object,value); //将获取的对象属性信息存入我们自定义的BeanInfo当中 beanInfo.addProperty(name.getText(),value); } } beanInfo.setId(id.getText()); beanInfo.setType(cls.getText()); beanInfoMap.put(id.getText(),beanInfo); } return beanInfoMap; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IntrospectionException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } return null; } }
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="person" class="ioc.bean.Person"> <property name="username"> <value>zzf</value> </property> <property name="password"> <value>12345678</value> </property> </bean> </beans>
package ioc; import ioc.bean.Person; import ioc.context.XMLContext; import ioc.factory.BeanFactory; /** * Created by zzf on 2016/10/26. */ public class test { public static void main(String[] args) { BeanFactory factory=new XMLContext("configuration/config.xml"); Person person= (Person) factory.getBean("person"); System.out.println(person.getPassword()); System.out.println(person.getUsername()); } }
执行main方法后,控制台成功输出xml所定义的属性。
再结合我第一篇所讲的SpringIOC概念讲解,相信读者应该能够清晰的认识的IOC的作用和大概的实现过程。