Bean工厂接口类:
public interface BeanFactory { Object getBean(String name) throws Exception; }
Bean包装类:
/** * Bean的内容及元数据,保存在BeanFactory中,包装Bean的实体 */ public class BeanDefinition { private Object bean; private Class<?> beanClass; private String beanClassName; public BeanDefinition() { } public BeanDefinition(Object object) { this.bean=object; } public void setBeanClassName(String beanClassName) { this.beanClassName = beanClassName; try { this.beanClass = Class.forName(beanClassName); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //省略部分get/set方法 }
Bean工厂实现类:
public class AbstractBeanFactory implements BeanFactory { // 存放Factory里的所有Bean的详细信息 private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(); // 存放Factory里的所有Bean的Name private final List<String> beanDefinitionNames = new ArrayList<String>(); @Override public Object getBean(String name) throws Exception { BeanDefinition beanDefinition = beanDefinitionMap.get(name); if (beanDefinition == null) { throw new IllegalArgumentException("No bean named " + name + " is defined"); } Object bean = beanDefinition.getBean(); return bean; } /** * 将新加入的BeanDefinition注册到BeanDefinitionMap中 */ public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception { beanDefinitionMap.put(name, beanDefinition); beanDefinitionNames.add(name); } }
至此,一个简单的IOC容器搭建完成。
测试一下:
public class HelloWorldServiceImpl { public void helloWorld2() { System.out.println("hello"); } }
第一步:
BeanDefinition直接根据Bean对象实例化。
public void Step1() throws Exception {
BeanFactory beanFactory = new AbstractBeanFactory();
BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldServiceImpl());
// 注册
((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld",beanDefinition);
HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld");
h.helloWorld2();
}
打印结果:helloworld
第二步:
BeanDefinition根据className实例化。
public void Step2() throws Exception {
BeanFactory beanFactory = new AbstractBeanFactory();
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl");
// 注册
((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld",beanDefinition);
HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld");
h.helloWorld2();
}
然后再在AbstractBeanFactory.getBean()方法返还bean之前加上如下代码:
if (bean == null) { bean = beanDefinition.getBeanClass().newInstance(); }
第三步:
如果我们在HelloWorldServiceImpl里面有简单的参数怎么办?如:private String text; private int a; public void helloWorld() { System.out.println(text + a + " ss"); }既然有参数,那我们就设计一个PropertyValue:
/** * 用于Bean的属性注入 */ public class PropertyValue { private final String name; private final Object value; // 省略get,set方法 }然后在BeanDefinition里面增加一个List<PropertyValue> pvs = new ArrayList<PropertyValue>,毕竟不能限制一个类只有一个属性。
如果一个类中,有重复的属性,如在spring的配置文件中这样配置:
<bean id="userService" class="com.bjsxt.services.UserService" > <property name="userDao" bean="u" /> <property name="userDao" bean="u" /> </bean>因此我们需要再加入一个类PropertyValues
/** * 包装一个对象所有的PropertyValue。<br/> */ public class PropertyValues { private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>(); public PropertyValues() { } public void addPropertyValue(PropertyValue pv) { // TODO 这里可以对重复propertyName进行判断 this.propertyValueList.add(pv); } public List<PropertyValue> getPropertyValues() { return this.propertyValueList; } }因而在BeanDefinition里加入private PropertyValues propertyValues;即可。相应的getBean方法也要变。
@Override public Object getBean(String name) throws Exception { BeanDefinition beanDefinition = beanDefinitionMap.get(name); if (beanDefinition == null) { throw new IllegalArgumentException("No bean named " + name + " is defined"); } Object bean = beanDefinition.getBean(); if (bean == null) { bean = beanDefinition.getBeanClass().newInstance(); } creatBean(bean, beanDefinition); return bean; }
public void creatBean(Object bean, BeanDefinition beanDefinition) throws Exception { if (beanDefinition.getPropertyValues() != null) creatBeanWithProperty(bean, beanDefinition); } } public void creatBeanWithProperty(Object bean, BeanDefinition beanDefinition) throws Exception{ int size = beanDefinition.getPropertyValues().getPropertyValues().size(); List<PropertyValue> list = beanDefinition.getPropertyValues().getPropertyValues(); for (int i = 0; i <size ; i++) { if(list.get(i).getValue() instanceof BeanReference){ String beanName=((BeanReference)list.get(i).getValue()).getName(); Object referenceBean = getBean(beanName); String ms="set" + Character.toUpperCase(list.get(i).getName().charAt(0)) + list.get(i).getName().substring(1); Method m = bean.getClass().getDeclaredMethod(ms, referenceBean.getClass()); m.invoke(bean, referenceBean); } else { String fieldName = list.get(i).getName(); Object value = list.get(i).getValue(); // getDeclaredField是获得所有的字段(不仅仅是public) Field field = bean.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(bean, value); field.setAccessible(false); } } }
测试一下:
public void Step3() throws Exception { BeanFactory beanFactory = new AbstractBeanFactory(); BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl"); PropertyValues propertyValues = new PropertyValues(); propertyValues.addPropertyValue(new PropertyValue("text","Hello World!")); propertyValues.addPropertyValue(new PropertyValue("a",new Integer(15))); beanDefinition.setPropertyValues(propertyValues); ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition); HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld"); h.helloWorld(); }
测试结果:Hello World!15 ss
第四步:
上面的类里面的成员变量依然是int,double等的基本变量(String 在这里也算基本变量),如果我在类里面加一个引用变量呢?
private OutputService out; public void helloWorld3(){ out.output(text); }
OutputService的output方法很简单就是输出text的内容。
那么下来,理所应当的我们会设计出一个参考类:
public class BeanReference { private String name; private Object bean; }对于BeanReference,我们可以按下面的方式使用
public void Step4() throws Exception { BeanFactory beanFactory = new AbstractBeanFactory(); BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl"); BeanDefinition beanDefinition2 = new BeanDefinition(); beanDefinition2.setBeanClassName("com.myspring.OutputService"); BeanReference beanReference = new BeanReference("outPutService"); beanReference.setBean(beanDefinition2); PropertyValues propertyValues = new PropertyValues(); propertyValues.addPropertyValue(new PropertyValue("text","Hello World! with referencebean")); propertyValues.addPropertyValue(new PropertyValue("a",new Integer(15))); propertyValues.addPropertyValue(new PropertyValue("out",beanReference)); beanDefinition.setPropertyValues(propertyValues); ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition); ((AbstractBeanFactory)beanFactory).registerBeanDefinition("out", beanDefinition2); HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory.getBean("helloworld"); h.helloWorld3(); }
下面的是creatBean部分
int size = beanDefinition.getPropertyValues().getPropertyValues().size(); for (int i = 0; i <size ; i++) { List<PropertyValue> list = beanDefinition.getPropertyValues().getPropertyValues(); if (list.get(i).getValue() instanceof BeanReference) { String beanName = list.get(i).getName(); // 循环调用getBean Object referenceBean = getBean(beanName); String ms = "set" + Character.toUpperCase(beanName.charAt(0)) + beanName.substring(1); Method m = bean.getClass().getDeclaredMethod(ms, referenceBean.getClass()); m.invoke(bean, referenceBean); } else { String fieldName = list.get(i).getName(); Object value = list.get(i).getValue(); // getDeclaredField是获得所有的字段(不仅仅是public) Field field = bean.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(bean, value); field.setAccessible(false); } }
测试结果:Hello World! with referencebean
第五步:
public void Step5() throws Exception { XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(); xmlBeanDefinitionReader.loadBeanDefinitions("bin/resources/tinyioc.xml"); BeanFactory beanFactory = new AbstractBeanFactory(); for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getBeanDefinitionMap().entrySet()) { ((AbstractBeanFactory)beanFactory).registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); } HelloWorldServiceImpl helloWorldService = (HelloWorldServiceImpl) beanFactory.getBean("helloWorldService"); helloWorldService.helloWorld3(); }在java中,获取文件有两种方式:
Class类下的getResource(String path)方法与ClassLoader类下的getResource(String path)方法。
ClassLoader.getResource()参数中不带"/"。默认就是根路径(在Eclipse中,根路径就是工程下的bin文件夹,在默认情况下,Eclipse会把项目中的src下的内容拷贝到bin下,因此也可以理解为根目录就是src目录)。
Class.getResource()可以带"/"也可以不带。一旦带了‘/’就默认从根路径(就是bin 就是src)下查找了;如果没有‘/’就从这个类本身的那个路径下查找。
XmlBeanDefinitionReader的主要作用就是读取xml,然后转换成一个个BeanDefinition,再存储进BeanDefinitionMap,再创建一个BeanFactory,将BeanDefinitionMap中的记录一个一个再注册一遍。
public class XmlBeanDefinitionReader { private Map<String, BeanDefinition> beanDefinitionMap; public XmlBeanDefinitionReader(){ beanDefinitionMap = new HashMap<String, BeanDefinition>(); } public void loadBeanDefinitions(String local) throws IOException, ParserConfigurationException, SAXException { InputStream is = new FileInputStream(local); parseNode(is); } }
/** * 通过InputStream 获得每一个bean * @param is * @throws ParserConfigurationException * @throws SAXException * @throws IOException */ public void parseNode(InputStream is) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory domfac = DocumentBuilderFactory.newInstance(); DocumentBuilder domBuilder = domfac.newDocumentBuilder(); // 默认是工程目录 Document doc = domBuilder.parse(is); Element root = doc.getDocumentElement(); NodeList beans = root.getChildNodes(); for (int i = 0; i < beans.getLength(); i++) if (beans.item(i) instanceof Element) { Element el=(Element)beans.item(i); parseElement(el); } is.close(); } /** * 分析每一个bean的id class * @param el */ public void parseElement(Element el){ String id = el.getAttribute("id"); String classPath=el.getAttribute("class"); BeanDefinition bd=new BeanDefinition(); bd.setBeanClassName(classPath); parseProperties(el,bd); beanDefinitionMap.put(id, bd); } /** * 分析每一个bean的参数 并加入到beandefinition的property里面 * @param el * @param bd */ public void parseProperties(Element el,BeanDefinition bd){ NodeList bl=el.getElementsByTagName("property"); for (int i = 0; i < bl.getLength(); i++) if (bl.item(i) instanceof Element) { Element property=(Element)bl.item(i); String name=property.getAttribute("name"); if (property.getAttribute("ref")!="") { BeanReference br=new BeanReference(property.getAttribute("ref")); PropertyValue pV=new PropertyValue(name,br); bd.getPropertyValues().addPropertyValue(pV); } if (property.getAttribute("value")!="") { String value=property.getAttribute("value"); PropertyValue pV=new PropertyValue(name, value); bd.getPropertyValues().addPropertyValue(pV); } } } public Map<String, BeanDefinition> getBeanDefinitionMap() { return beanDefinitionMap; }
第六步:
对比XmlBeanDefinitionReader与AbstractBeanFactory,就能发现两个类里面都有beanDefinitionMap。
另外,在第五步中
BeanFactory beanFactory = new AbstractBeanFactory(); for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getBeanDefinitionMap().entrySet()) { ((AbstractBeanFactory)beanFactory).registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); }
按照使用者与创建者分离的原则,初始化注册的代码出现在客户端不合适。
public void Step7() throws Exception { ApplicationContext ac = new ApplicationContext("bin/resources/tinyioc.xml"); HelloWorldServiceImpl helloWorldService = (HelloWorldServiceImpl) ac.getBean("helloWorldService"); helloWorldService.helloWorld3(); }
把XmlBeanDefinitionReader与AbstractBeanFactory合起来,也就是说要把getBean与loadBeanDefinitions装到一个类里面去。
public class ApplicationContext implements BeanFactory { private AbstractBeanFactory abf = new AbstractBeanFactory(); public ApplicationContext(String local) throws Exception { loadBeanDefinitions(abf,local); } protected void loadBeanDefinitions(AbstractBeanFactory beanFactory,String configLocation) throws Exception { XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(); xmlBeanDefinitionReader.loadBeanDefinitions(configLocation); for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getBeanDefinitionMap().entrySet()) { beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue()); } } @Override public Object getBean(String id) { Object obj=null; try { obj = abf.getBean(id); } catch (Exception e) { e.printStackTrace(); } return obj; } }
AbstractBeanFactory与ApplicationContext都继承了BeanFactory,然后我们不直接用BeanFactory,而是让ApplicationContext中装一个AbstractBeanFactory,这不就是最简单的代理模式么?
这里还有一些部分与TinySpring不同:
1 Resources部分
在我完成的代码里,读取xml的时候直接就是一个InputStream,TinySpring的方式是有一个Resource类,同时还有一个LoadResource类用来加载资源,当然实现的内部机理都是inputstream;
2 接口问题
我一直认为,良好的代码是一次一次重构出来的,依我现在的水平,确实不能够很清晰地说出,分了那么多层接口,抽象类的实际作用,因此在我的代码里各个部分都很"薄弱"(只有一层)
3 类的加载
有两种方式一种直接加载,一种延迟加载,TinySpring最开始的那几个step还是在getBean的时候才newInstance的,但是到后面所以的类都直接加载了。
protected void onRefresh() throws Exception{ beanFactory.preInstantiateSingletons(); } public void preInstantiateSingletons() throws Exception { for (Iterator<String> it = this.beanDefinitionNames.iterator(); it.hasNext();) { String beanName = (String) it.next(); getBean(beanName); } }
4 单例模式
TinySpring中一个bean默认只会加载一次,第二次getBean()的时候会取出之前已经creat的那个;
public Object getBean(String name) throws Exception { BeanDefinition beanDefinition = beanDefinitionMap.get(name); if (beanDefinition == null) { throw new IllegalArgumentException("No bean named " + name + " is defined"); } Object bean = beanDefinition.getBean(); // 查找beanDefinition if (bean == null) { bean = doCreateBean(beanDefinition); bean = initializeBean(bean, name); beanDefinition.setBean(bean); } return bean; } protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception { Object bean = createBeanInstance(beanDefinition); // 写入beanDefinition beanDefinition.setBean(bean); applyPropertyValues(bean, beanDefinition); return bean; }
我写的代码中,没有上面的步骤,因此即使第二次get一个已经get过得bean,仍然会产生一个新的bena!
转:http://blog.csdn.net/dlf123321/article/details/39994071,http://blog.csdn.net/dlf123321/article/details/40017593