設計模式(三十一)----綜合應用-自定義Spring框架-自定義Spring IOC-定義解析器、IOC容器相關類

2023-04-03 21:02:30

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();
    }