3 定義解析器相關類
3.1 BeanDefinitionReader介面
BeanDefinitionReader是用來解析配置檔案並在登入檔中註冊bean的資訊。定義了兩個規範:
-
獲取登入檔的功能,讓外界可以透過該物件獲取登入檔物件。
-
載入配置檔案,並註冊bean資料。
/**
* @version v1.0
* @ClassName: BeanDefinitionReader
* @Description:
* 用來解析配置檔案的,而該介面只是定義了規範
*/
public interface BeanDefinitionReader {
//獲取登入檔物件
BeanDefinitionRegistry getRegistry();
//載入配置檔案並在登入檔中進行註冊
void loadBeanDefinitions(String configLocation) throws Exception;
}
3.2 XmlBeanDefinitionReader類
XmlBeanDefinitionReader類是專門用來解析xml配置檔案的。該類實現BeanDefinitionReader介面並實現介面中的兩個功能。
/**
* @version v1.0
* @ClassName: XmlBeanDefinitionReader
* @Description: 針對xml配置檔案進行解析的類
*/
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
//宣告登入檔物件
private BeanDefinitionRegistry registry;
public XmlBeanDefinitionReader() {
this.registry = new SimpleBeanDefinitionRegistry();
}
@Override
public BeanDefinitionRegistry getRegistry() {
return registry;
}
public void loadBeanDefinitions(String configLocation) throws Exception {
//使用dom4j進行xml配置檔案的解析 須需在pom檔案裡面引入dom4j 1.6.1版本
SAXReader reader = new SAXReader();
//獲取類路徑下的配置檔案
InputStream is = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation);
Document document = reader.read(is);
//根據Document物件獲取根標籤物件 (beans)
Element rootElement = document.getRootElement();
//獲取根標籤下所有的bean標籤物件
List<Element> beanElements = rootElement.elements("bean");
//遍歷集合
for (Element beanElement : beanElements) {
//獲取id屬性
String id = beanElement.attributeValue("id");
//獲取class屬性
String className = beanElement.attributeValue("class");
//將id屬性和class屬性封裝到BeanDefinition物件中
//1,建立BeanDefinition
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setId(id);
beanDefinition.setClassName(className);
//建立MutablePropertyValues物件
MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
//獲取bean標籤下所有的property標籤物件
List<Element> propertyElements = beanElement.elements("property");
for (Element propertyElement : propertyElements) {
String name = propertyElement.attributeValue("name");
String ref = propertyElement.attributeValue("ref");
String value = propertyElement.attributeValue("value");
PropertyValue propertyValue = new PropertyValue(name,ref,value);
mutablePropertyValues.addPropertyValue(propertyValue);
}
//將mutablePropertyValues物件封裝到BeanDefinition物件中
beanDefinition.setPropertyValues(mutablePropertyValues);
//將beanDefinition物件註冊到登入檔中
registry.registerBeanDefinition(id,beanDefinition);
}
}
}
4 IOC容器相關類
4.1 BeanFactory介面
在該介面中定義IOC容器的統一規範即獲取bean物件。
public interface BeanFactory {
//根據bean物件的名稱獲取bean物件
Object getBean(String name) throws Exception;
//根據bean物件的名稱獲取bean物件,並進行型別轉換
<T> T getBean(String name, Class<? extends T> clazz) throws Exception;
}
4.2 ApplicationContext介面
該介面的所以的子實現類對bean物件的建立都是非延時的,所以在該介面中定義 refresh()
方法,該方法主要完成以下兩個功能:
-
載入配置檔案。
-
根據登入檔中的BeanDefinition物件封裝的資料進行bean物件的建立。
//定義非延時載入功能
public interface ApplicationContext extends BeanFactory {
//進行配置檔案載入並進行物件建立
void refresh() throws IllegalStateException, Exception;
}
4.3 AbstractApplicationContext類
-
作為ApplicationContext介面的子類,所以該類也是非延時載入,所以需要在該類中定義一個Map集合,作為bean物件儲存的容器。
-
宣告BeanDefinitionReader型別的變數,用來進行xml配置檔案的解析,符合單一職責原則。
BeanDefinitionReader型別的物件建立交由子類實現,因為只有子類明確到底建立BeanDefinitionReader哪兒個子實現類物件。
public abstract class AbstractApplicationContext implements ApplicationContext {
protected BeanDefinitionReader beanDefinitionReader;
//用來儲存bean物件的容器 key儲存的是bean的id值,value儲存的是bean物件
protected Map<String, Object> singletonObjects = new HashMap<String, Object>();
//儲存配置檔案的路徑
protected String configLocation;
public void refresh() throws IllegalStateException, Exception {
//載入BeanDefinition
beanDefinitionReader.loadBeanDefinitions(configLocation);
//初始化bean
finishBeanInitialization();
}
//bean的初始化
private void finishBeanInitialization() throws Exception {
//獲取登入檔物件
BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
//獲取BeanDefinition物件
String[] beanNames = registry.getBeanDefinitionNames();
for (String beanName : beanNames) {
//進行bean的初始化
getBean(beanName);
}
}
}
注意:該類finishBeanInitialization()方法中呼叫getBean()方法使用到了模板方法模式。
4.4 ClassPathXmlApplicationContext類
該類主要是載入類路徑下的配置檔案,並進行bean物件的建立,主要完成以下功能:
-
在構造方法中,建立BeanDefinitionReader物件。
-
在構造方法中,呼叫refresh()方法,用於進行配置檔案載入、建立bean物件並儲存到容器中。
-
重寫父介面中的getBean()方法,並實現依賴注入操作。
/**
* @version v1.0
* @ClassName: ClassPathXmlApplicationContext
* @Description: IOC容器具體的子實現類
* 用於載入類路徑下的xml格式的配置檔案
*/
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
public ClassPathXmlApplicationContext(String configLocation) {
this.configLocation = configLocation;
//構建解析器物件
beanDefinitionReader = new XmlBeanDefinitionReader();
try{
this.refresh();
} catch (Exception e) {
}
}
//根據bean物件的名稱獲取bean物件
public Object getBean(String name) throws Exception {
//判斷物件容器中是否包含指定名稱的bean物件,如果包含,直接返回即可,如果不包含,需要自行建立
Object obj = singletonObjects.get(name);
if (obj != null) {
return obj;
}
//獲取BeanDefinition物件
BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
BeanDefinition beanDefinition = registry.getBeanDefinition(name);
//獲取bean資訊中的className
String className = beanDefinition.getClassName();
//透過反射建立物件
Class<?> clazz = Class.forName(className);
Object beanObj = clazz.newInstance();
//進行依賴注入操作
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues) {
//獲取name屬性值
String propertyName = propertyValue.getName();
//獲取value屬性
String value = propertyValue.getValue();
//獲取ref屬性
String ref = propertyValue.getRef();
if(ref != null && !"".equals(ref)) {
//獲取依賴的bean物件
Object bean = getBean(ref);
//拼接方法名
String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
//獲取所有的方法物件
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (methodName.equals(method.getName())) {
//執行該setter方法
method.invoke(beanObj,bean);
}
}
}
if(value != null && !"".equals(value)) {
//拼接方法名
String methodName = StringUtils.getSetterMethodByFieldName(propertyName);
//獲取method物件
Method method = clazz.getMethod(methodName, String.class);
method.invoke(beanObj,value);
}
}
//在返回beanObj物件之前,將該物件儲存到map容器中
singletonObjects.put(name,beanObj);
return beanObj;
}
public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
Object bean = getBean(name);
if(bean == null) {
return null;
}
return clazz.cast(bean);
}
}
/**
* @version v1.0
* @ClassName: StringUtils
*/
public class StringUtils {
private StringUtils() {
}
// userDao ==> setUserDao
public static String getSetterMethodByFieldName(String fieldName) {
String methodName = "set" + fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);
return methodName;
}
}
如此已經完成,只需對這個工程執行mvn install打包成一個jar,透過pom檔案引入到其他工程(例如:spring使用回顧)中即可進行測試。
// 這是另外一個工程
// 引用的類均來自於前面自定義的類
public static void main(String[] args) throws Exception {
//1,建立spring的容器物件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
//2,從容器物件中獲取userService物件
UserService userService = applicationContext.getBean("userService", UserService.class);
//3,呼叫userService方法進行業務邏輯處理
userService.add();
}