分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
最近一直想看spring的源码,但是奈何水平太低,庞杂的源代码令我一阵阵的头晕。
很有幸,在网上看到了黄亿华大神的<<1000行代码读懂Spring(一)- 实现一个基本的IoC容器>>
觉得相当不错,就以他的代码为基础,自己又写了一个IoC容器(基本上都是黄的代码,我只改了一部分)
原网页如下
http://my.oschina.net/flashsword/blog/192551
特此声明,本文不能算严格意义上的原创,只能算是黄文章的再次解读吧。
开工
如下的代码不需要解释了吧。
来测试一下,以HelloWorldServiceImpl为例
测试代码如下
很简单,打印出了hello;
ok 至此 我们最简单的IoC就搭建完成了。
现在我们就一步一步地完善我们的容器
解决办法很简单,在AbstractBeanFactory/getBean()方法返还bean之前加上如下代码即可。
if (bean==null) {
bean=beanDefinition.getBeanClass().newInstance();
private String text;
private int a;
public void helloWorld(){
System.out.println(text+a+" ss");
}
既然有参数,那我们就设计一个PropertyValue
下来就是在BeanDefinition里面增加一个List<PropertyValue> pvs=new ArrayList<PropertyValue>,毕竟不能限制一个类只有一个属性吧。
一个类中不会只有一个参数,那必然就是List了。好像说的有道理,目前我们是人为地给pvs里面加数据,用add()方法,如果一个类中,有重复的属性呢?
开玩笑,java里能出现两个变量同名吗?
当然java类里是不存在的,可我们得知道成型的spring可是从xml里面读取数据的
如果我写成这样 怎么办?
因此我们得再加入一个类PropertyValues
因而在BeanDefinition里加入private PropertyValues propertyValues;即可
相应的getBean方法也要变
大家看到了关键问题在creatBean方法上
ok,看看测试代码
测试结果
Hello World!15 ss
如下
private OutputService out;
public void helloWorld3(){
out.output(text);
}
OutputService的output方法很简单就是输出text的内容。
那么下来,理所应当的我们会设计出一个参考类
对于BeanReference,我们可以按下面的方式使用
看到第四步注册bean的时候,大家应该想到如果有n个bean,我就得调用registerBeanDefinition方法n次吗?
目前就只能是这个方法了,技术用for循环,beanDefinition的名字也没办法,现在毕竟是模拟,各个变量的名字都是由人输入的,以后会从xml中读,就简单多了。
下面的麻烦的代码大家应该也能猜处理,就是creatBean部分。
还是上面的问题,如果不区分是引用类型还是基本类型可以不?
property里面的value是object类型的,如果我们给里面放的是int,直接set到bean里面,可是这个object要是BeanReference呢,还得取出BeanReference里面的value,然后在循环getbean()。你们说不区分能行吗?
测试结果
Hello World! with referencebean
很有幸,在网上看到了黄亿华大神的<<1000行代码读懂Spring(一)- 实现一个基本的IoC容器>>
觉得相当不错,就以他的代码为基础,自己又写了一个IoC容器(基本上都是黄的代码,我只改了一部分)
原网页如下
http://my.oschina.net/flashsword/blog/192551
特此声明,本文不能算严格意义上的原创,只能算是黄文章的再次解读吧。
开工
如下的代码不需要解释了吧。
step1
package com.myspring.factory;public interface BeanFactory { Object getBean(String name) throws Exception;}
package com.myspring.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方法}
package com.myspring.factory;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import com.myspring.bean.BeanDefinition;public class AbstractBeanFactory implements BeanFactory { //存放Factory里的所有bean的详细信息 //可以理解为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里 *这里只是将beanDefinition的定义放入"注册表"(beanDefinitionMap) 至于beanDefinition是否有错误 以后再说 这里不管 **/ public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception { beanDefinitionMap.put(name, beanDefinition); beanDefinitionNames.add(name); //为什么现在不检测beanDefinition的细节 例如有没有bean? layzload }}
来测试一下,以HelloWorldServiceImpl为例
package com.myspring;public class HelloWorldServiceImpl { public void helloWorld2() { System.out.println("hello"); }}
测试代码如下
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(); }
很简单,打印出了hello;
ok 至此 我们最简单的IoC就搭建完成了。
现在我们就一步一步地完善我们的容器
step2
第一步的时候,beanDefinition里面我们直接放入了bean,向下面这个,我们放入classname会如何?@Test 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();
}
step3
继续走,如果我们在HelloWorldServiceImpl里面有简单的参数怎么办,示意代码如下private String text;
private int a;
public void helloWorld(){
System.out.println(text+a+" ss");
}
既然有参数,那我们就设计一个PropertyValue
package com.myspring.bean;/** * 用于bean的属性注入 */public class PropertyValue { private final String name; private final Object value; // 省略get/set 后文对简单的get/set方法将直接省略 不再说明}
下来就是在BeanDefinition里面增加一个List<PropertyValue> pvs=new ArrayList<PropertyValue>,毕竟不能限制一个类只有一个属性吧。
一个类中不会只有一个参数,那必然就是List了。好像说的有道理,目前我们是人为地给pvs里面加数据,用add()方法,如果一个类中,有重复的属性呢?
开玩笑,java里能出现两个变量同名吗?
当然java类里是不存在的,可我们得知道成型的spring可是从xml里面读取数据的
如果我写成这样 怎么办?
<bean id="userService" class="com.bjsxt.services.UserService" > <property name="userDao" bean="u" /> <property name="userDao" bean="u" /> </bean>
因此我们得再加入一个类PropertyValues
package com.myspring.bean;import java.util.ArrayList;import java.util.List;/** * 包装一个对象所有的PropertyValue。<br/> * 为什么封装而不是直接用List?因为可以封装一些操作。 */public class PropertyValues { private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>(); public PropertyValues() { } public void addPropertyValue(PropertyValue pv) { //TODO:这里可以对于重复propertyName进行判断,直接用list没法做到 // System.out.println(pv.getName()+pv.getValue()); 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); // 可不可以只传一个beandefinition // 在方法里在beanDefinition.getBean(); // 这样还可以少传递一个对象呢? return bean; }
大家看到了关键问题在creatBean方法上
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(); // System.out.println("par "+list.get(i).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(); Field field = bean.getClass().getDeclaredField(fieldName); // getDeclaredField是获得所有的字段(不仅仅是public) field.setAccessible(true); // 这一步必须有 field.set(bean, value); field.setAccessible(false); // 这一步必须有 } } }
ok,看看测试代码
public void Step3() throws Exception { // 1.初始化beanfactory BeanFactory beanFactory = new AbstractBeanFactory(); // 2.bean定义 BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setBeanClassName("com.myspring.HelloWorldServiceImpl"); // 3.设置属性 PropertyValues propertyValues = new PropertyValues(); propertyValues.addPropertyValue(new PropertyValue("text","Hello World!")); propertyValues.addPropertyValue(new PropertyValue("a",new Integer(15))); beanDefinition.setPropertyValues(propertyValues); // 4.注册bean ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition); HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory .getBean("helloworld"); h.helloWorld(); }
测试结果
Hello World!15 ss
step4
上面的类里面的成员变量依然是int,double等的基本变量(String 在这里也算基本变量),如果我在类里面加一个引用变量呢?如下
private OutputService out;
public void helloWorld3(){
out.output(text);
}
OutputService的output方法很简单就是输出text的内容。
那么下来,理所应当的我们会设计出一个参考类
package com.myspring;public class BeanReference { private String name; private Object bean;}
对于BeanReference,我们可以按下面的方式使用
public void Step4() throws Exception { // 1.初始化beanfactory BeanFactory beanFactory = new AbstractBeanFactory(); // 2.bean定义 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); // 3.设置属性 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); // 4.注册bean ((AbstractBeanFactory)beanFactory).registerBeanDefinition("helloworld", beanDefinition); ((AbstractBeanFactory)beanFactory).registerBeanDefinition("out", beanDefinition2); HelloWorldServiceImpl h = (HelloWorldServiceImpl) beanFactory .getBean("helloworld"); h.helloWorld3(); }
看到第四步注册bean的时候,大家应该想到如果有n个bean,我就得调用registerBeanDefinition方法n次吗?
目前就只能是这个方法了,技术用for循环,beanDefinition的名字也没办法,现在毕竟是模拟,各个变量的名字都是由人输入的,以后会从xml中读,就简单多了。
下面的麻烦的代码大家应该也能猜处理,就是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(); Object referenceBean=getBean(beanName); //循环调用getBean 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(); Field field = bean.getClass().getDeclaredField(fieldName); // getDeclaredField是获得所有的字段(不仅仅是public) field.setAccessible(true); // 这一步必须有 field.set(bean, value); field.setAccessible(false); // 这一步必须有 } }
还是上面的问题,如果不区分是引用类型还是基本类型可以不?
property里面的value是object类型的,如果我们给里面放的是int,直接set到bean里面,可是这个object要是BeanReference呢,还得取出BeanReference里面的value,然后在循环getbean()。你们说不区分能行吗?
测试结果
Hello World! with referencebean