本次代碼實現了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;
}
}