參看資料:《Spring揭祕》
DefaultListableBeanFactory看的糊里糊塗的,這篇博客寫一下我對Spring ioc的理解,肯定有錯誤的地方,希望各位指點,不要噴我哈(o.o)。如果你是小白,你可能需要看一下java的反射機制
首先,如果你想要通過配置文件的方法來給bean配置,
你可以就需要四個類,
第一個是BeanDefinition,這個類中保存有配置文件中的每一個配置的bean的信息,最終會通過BeanDefinition提供的信息來生產bean.
第二個是BeanReader,這個類讀取配置文件,並把相應的信息轉換成BeanDefinition。
第三個是BeanRegist,這個類的作用,就是保存BeanDefinition,BeanDefinition保存bean的信息,BeanRegist保存所有的BeanDefinition(配置文件的每個bean都會有一個相對應的BeanDefinition)。
第四纔是我們的BeanFactory,所有的類最後都是通過BeanFactory跟程序員打交道的。
現在我們可以試着來實現一個簡單的ioc容器:我們用.properties來所配置文件,用java的Properties類來讀取我們的配置文件。
首先是配置文件:
bean_name=com.myioc.Book
book_name=ceshi
book_author=ceshizhe
/*然後是我們的實體類*/
package com.myioc;
public class Book {
private String book_name;
private String book_author;
public String getBook_name() {
return book_name;
}
public void setBook_name(String book_name) {
this.book_name = book_name;
}
public String getBook_author() {
return book_author;
}
public void setBook_author(String book_author) {
this.book_author = book_author;
}
@Override
public String toString() {
return "Book [book_name=" + book_name + ", book_author=" + book_author
+ "]";
}
}
現在需要我們的BeanDefinition
import java.util.Properties;
public class BeanDefinition {
/*我用一個String來保存bean的名字*/
private String beanName;
/*用Properties來保存配置的信息*/
private Properties propertieslist;
/*setter,getter方法,用來設置屬性,不然怎麼保存*/
/*爲了測試,把toString方法重寫*/
@Override
public String toString() {
return "BeanDefinition [beanName=" + beanName + ", propertieslist="
+ propertieslist + "]";
}
}
BeanReader
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
public class MyReader {
public void loadfile(String classpath,MyRegister myRegister) {
/*這個地方有點繞,reader不但要讀取文件,還要將從文件讀取到的BeanDefinition註冊到Register中,這樣Register才能管理Bean*/
Properties properties = new Properties();
/*給定classpath將配置文件讀入*/
try {
File file = new File(classpath);
FileInputStream inStream = new FileInputStream(file);
/*讀取配置文件*/
properties.load(inStream);
} catch(Exception e) {
System.out.println("配置文件沒找到");
}
/*生成一個BeanDefinition*/
BeanDefinition beanDefinition = new BeanDefinition();
/*設置Bean的名字*/
beanDefinition.setBeanName(properties.getProperty("bean_name"));
/*properties中保存了屬性名字和對應的名字,直接保存就行了*/
beanDefinition.setPropertieslist(properties);
/*向傳入的myRegister註冊Beandefinition*/
myRegister.registerBeanDefinition(beanDefinition);
}
}
然後是BeanRegister
import java.util.HashMap;
import java.util.Map;
public class MyRegister {
private Map<String, BeanDefinition> beanDefinitions = new HashMap<>();
/*將beanDefinition放入自己的Map中管理*/
public void registerBeanDefinition(BeanDefinition beanDefinition) {
beanDefinitions.put(beanDefinition.getBeanName(), beanDefinition);
}
/*方便測試*/
public Map<String, BeanDefinition> getBeanDefinitions() {
return beanDefinitions;
}
public void setBeanDefinitions(Map<String, BeanDefinition> beanDefinitions) {
this.beanDefinitions = beanDefinitions;
}
/*這個方式是方便BeanFactory從Regist中取得BeanDefinition然後生成實例的*/
public Object getBeanDefinition(String beanName) {
return beanDefinitions.get(beanName);
}
}
/*Register和BeanFactory的關係,Spring揭祕一書中講的很好,就好比圖書館與圖書館裏面的書架的關係,借書雖然是跟圖書館打交道,但是書本是放在書架上的,這裏我們的BeanFactory充當圖書館的角色,Register充當書架的角色。*/
接下來就是我們的BeanFactory
package com.myioc;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
public class MyBeanFactory {
/*圖書館裏面肯定有書架放書,Reader也可以有其他的寫法,但是這裏爲了測試方便就放在BeanFactory中了*/
private MyRegister myRegister = new MyRegister();
private MyReader myReader = new MyReader();
public MyBeanFactory(String classpath) {
myReader.loadfile(classpath,myRegister);
/*將配置文件的路徑和Rrgister給Reader,Reader就會找到配置文件,並且將讀取到的信息註冊給Register*/
}
public Object getBean(String beanName) {
BeanDefinition beanDefinition = (BeanDefinition)myRegister.getBeanDefinition(beanName);
if(beanDefinition != null) {
try {
/*得到beanName的Class對象*/
Class cl = Class.forName(beanDefinition.getBeanName());
/*得到beanDefinition中保存所有的屬性值*/
Properties properties = beanDefinition.getPropertieslist();
/*得到properties的key值*/
Set<Object> keySet = properties.keySet();
Iterator<Object> si = keySet.iterator();
/*生成實例*/
Object newObject = cl.newInstance();
String field;
/*遍歷,通過setter方法給 beanName的屬性賦值*/
while(si.hasNext()) {
/*遍歷keyset中的值*/
field = (String)si.next();
if( !"bean_name".equals(field) && field != null) {
try {
Method m = cl.getMethod(makeMethod(field), String.class);
m.invoke(newObject, properties.getProperty(field));
} catch(Exception e) {
System.out.println("沒有"+field+"的set方法");
}
}
}
return newObject;
} catch(Exception e){
System.out.println("forname錯誤");
}
}
return null;
}
/*得到field的set方法*/
private String makeMethod(String field) {
if(field!=null) {
field = field.substring(0, 1).toUpperCase() + field.substring(1);
return "set"+field;
}
return null;
}
}
就這樣 看起來很簡單,那是因爲我們實現的是簡單的ioc更本不能在實際中使用,Sping在實現DefaultListableBeanFactory的時候考慮了很多東西。
現在我們可以測試了
package com.myioc;
public class Test {
public static void main(String ar[]) {
MyBeanFactory bf = new MyBeanFactory("src/Myioc.properties");
Book b = (Book)bf.getBean("com.myioc.Book");
System.out.println(b);
}
}
文件結構:
運行結果: