本次代码实现了spring中ioc模块的基本功能
前提:有JavaWeb基础,xml基础
先演示效果,再深入分析源码,后面有源码和说明
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="stu1" className="home.sise.cn.Student">
<property name="number" value="110"/>
<property name="name" value="zhangSan"/>
<property name="age" value="29"/>
<property name="sex" value="male"/>
<property name="teacher" ref="t1"/><!-- ref的值必须是另一个been的id -->
</bean>
<bean id="t1" className="home.sise.cn.Teacher">
<property name="tid" value="120" />
<property name="name" value="liSi" />
<property name="salary" value="123.456" />
</bean>
</beans>
主类
public class Demo {
@Test
public void fun() {
/*
* 1. 创建Bean工厂,创建时需要给工厂指定配置文件
* 2. 从工厂中获取bean对象
*/
BeanFactory bf = new BeanFactory("beans.xml");
Student s1 = (Student)bf.getBean("stu1");
Student s2 = (Student)bf.getBean("stu1");
System.out.println(s1 == s2);
System.out.println(s1.toString());
System.out.println(s1.getTeacher().toString());
}
}
student实体类
public class Student {
private String number;
private String name;
private int age;
private String sex;
private Teacher teacher;
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String toString() {
return "Student [number=" + number + ", name=" + name + ", age=" + age
+ ", sex=" + sex + "]";
}
public Student() {
super();
}
public Student(String number, String name, int age, String sex) {
super();
this.number = number;
this.name = name;
this.age = age;
this.sex = sex;
}
}
Teacher实体类
public class Teacher {
private String tid;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private double salary;
public String getTid() {
return tid;
}
public void setTid(String tid) {
this.tid = tid;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Teacher [tid=" + tid + ", salary=" + salary + "]";
}
public Teacher() {
super();
}
public Teacher(String tid, double salary) {
super();
this.tid = tid;
this.salary = salary;
}
}
运行结果
开始分析(源码后面都会给出,重点都有注释)
代码逻辑
BeanFactory bf = new BeanFactory(“beans.xml”);
在BeanFactory加载时,加载名为bean.xml的配置文件,然后把它里面的所有节点元素通过遍历解析成对象。加载后,整个bean.xml中所有节点对应产生的对象都封装到了BeanFactory中。
这里是把<bean>节点解析成BeanConfig对象
<property>节点解析成PropertyConfig对象
然后把PropertyConfig对象放入BeanConfig对象中
Student s1 = (Student)bf.getBean(“stu1”);
调用BeanFactory的getBean方法,把BeanFactory中名为stu1的BeanConfig对象找到,然后通过判断它是否为单例模式,如果是,从缓存中拿出来,没有则创建对象后放入缓存
如果值是prototype(即多例)则创建一个新的对象。然后返回student对象
在创建student对象时,先用通过传入的(”stu1“),在BeanFactory中找到对应的BeanConfig,然后通过解析BeanConfig中的属性className中的值,利用反射技术,得到student对象实例。并且把BeanConfig中的值封装给stuent对象。
封装时需要还要判断它的ref是否有值,即在beans.xml中的bean节点property下是否有ref属性,如果有,则封装
sutdent的属性时,把beans.xml中名为name的,值为ref的封装给student
如果没有ref属性,则把beans.xml中名为name的,值为value的封装给student
具体源码如下
BeanFactory源码
public class BeanFactory {
// 配置文件的对应体
private Map<String, BeanConfig> bcs = new HashMap<String, BeanConfig>();
// Bean缓存,id是键, Bean是值
private Map<String, Object> beanCache = new HashMap<String, Object>();
public BeanFactory(String xmlName) {
BeanFactoryUtils.load(this, xmlName);
}
// 如果缓存中存在,直接返回
// 如果不存在,创建Bean,放入缓存中,再返回
public Object getBean(String id) {
BeanConfig bc = bcs.get(id);
if(bc == null) {
throw new RuntimeException(id + "不存在!");
}
if(bc.getScope() == null || bc.getScope().equals("singleton")) {
// 如果是单例bean,查看缓存中是否存在,如果存在直接返回
if(beanCache.containsKey(id)) {
return beanCache.get(id);
}
// 如果缓存中不存在,那么创建之,然后放入缓存中,再返回
Object bean = createBean(id);
beanCache.put(id, bean);
return bean;
} else if(bc.getScope().equals("prototype")) {
// 如果是原型bean,那么直接创建,然后返回,不用向入缓存
Object bean = createBean(id);
return bean;
}
throw new RuntimeException("scope只能是singleton或prototype");
}
//创建对象
private Object createBean(String id) {
try {
BeanConfig bc = bcs.get(id);//获取Bean配置对象
Class c = Class.forName(bc.getClassName());
Object bean = c.newInstance();
Map<String, PropertyConfig> pcs = bc.getPropertyConfigMap();
// 遍历所有的PropertyConfig
for(String propName : pcs.keySet()) {
PropertyConfig pc = pcs.get(propName);
if(pc.getRef() != null) {
String ref = pc.getRef();//是不是另一个bean的id
Object refBean = getBean(ref);
BeanUtils.setProperty(bean, pc.getName(), refBean);
} else {
BeanUtils.setProperty(bean, pc.getName(), pc.getValue());
}
}
return bean;
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public void addBeanConfig(BeanConfig bc) {
bcs.put(bc.getId(), bc);
}
public BeanConfig getBeanConfig(String id) {
return bcs.get(id);
}
public Map<String, BeanConfig> getBcs() {
return bcs;
}
public void setBcs(Map<String, BeanConfig> bcs) {
this.bcs = bcs;
}
}
BeanFactoryUtils源码
public class BeanFactoryUtils {
public static void load(BeanFactory factory, String xmlName) {
Document doc = new SAXReader().read(Thread.currentThread().getContextClassLoader()
.getResource(xmlName).getPath();//通过xmlName文件的位置,获取Document节点
List<Element> beanEleList = doc.selectNodes("//bean");//通过xpath表达式获取Document中所有<bean>元素集合
for(Element beanEle : beanEleList) {//遍历<bean>元素
BeanConfig bc = XmlUtils.toBean(beanEle, BeanConfig.class);//把每个<bean>元素解析成一个BeanConfig对象
List<Element> propEleList = beanEle.elements();
// 把所有的PropertyConfig添加到BeanConfig中
for(Element propEle : propEleList) {// 遍历<bean>中每个<property>元素
PropertyConfig pc = XmlUtils.toBean(propEle, PropertyConfig.class);//把每个<property>元素解析成一个PropertyConfig对象
bc.addPropertyConfig(pc);
}
factory.addBeanConfig(bc);
}
}
}
XmlUtils源码
public class XmlUtils {
public static <T> T toBean(Element e, Class<T> clazz) {
try {
Map map = toMap(e);//把传入的Element封装成一个map集合
T bean = clazz.newInstance();
BeanUtils.populate(bean, map);//通过BeanUtils把map中的值映射到bean中
return bean;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static Map<String, String> toMap(Element e) {
Map<String, String> map = new LinkedHashMap<String, String>();
/*
* 循环遍历e的所有属性 循环遍历e的所有子元素(条件:只是纯文本内容的子元素)
*/
/*
* 把e的所有属性添加到map中
*/
List<Attribute> attrs = e.attributes();
for (Attribute a : attrs) {
map.put(a.getName(), a.getValue());
}
/*
* 把e的所有子元素(纯属文本内容的子元素)添加到map中
*/
List<Element> eles = e.elements();
for (Element ele : eles) {
if (ele.isTextOnly()) {
map.put(ele.getName(), ele.getText());
}
}
return map;
}
}
BeanConfig源码
public class BeanConfig {
private String id;
private String className;
private String scope;
public String getScope() {
return scope;
}
public void setScope(String scope) {
if(!scope.equals("singleton") && !scope.equals("prototype")) {
throw new RuntimeException("scope只能是singleton或prototype");
}
this.scope = scope;
}
private Map<String, PropertyConfig> propertyConfigMap =
new LinkedHashMap<String, PropertyConfig>();
// 添加proertyConfig
// propertyConfig.getName()为键, propertyConfig本身是值
public void addPropertyConfig(PropertyConfig propertyConfig) {
propertyConfigMap.put(propertyConfig.getName(), propertyConfig);
}
public PropertyConfig getPropertyConfig(String name) {
return propertyConfigMap.get(name);
}
public Map<String, PropertyConfig> getPropertyConfigMap() {
return propertyConfigMap;
}
public void setPropertyConfigMap(Map<String, PropertyConfig> propertyConfigMap) {
this.propertyConfigMap = propertyConfigMap;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public BeanConfig() {
super();
// TODO Auto-generated constructor stub
}
public BeanConfig(String id, String className) {
super();
this.id = id;
this.className = className;
}
@Override
public String toString() {
return "BeanConfig [id=" + id + ", className=" + className
+ ", propertyConfigMap=" + propertyConfigMap + "]";
}
}
PropertyConfig源码
public class PropertyConfig {
private String name;
private String value;
private String ref;
@Override
public String toString() {
return "PropertyConfig [name=" + name + ", value=" + value + ", ref="
+ ref + "]";
}
public PropertyConfig(String name, String value, String ref) {
super();
this.name = name;
this.value = value;
this.ref = ref;
}
public PropertyConfig() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
}