• 设计模式(三十一)----综合应用-自定义Spring框架-自定义Spring IOC-定义解析器、IOC容器相关类


    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 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 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 getBean(String name, Class 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 getBean(String name, Class 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();
        }

     

     



    如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Java标识符和关键字,Java常量的定义和分类
    Linux 时间操作及其同步
    443-C++基础语法(121-130)
    启动服务提供者报 zookeeper not connected错
    2022年100道最新软件测试面试题,常见面试题及答案汇总
    【从零开始学习 SystemVerilog】1.1、SystemVerilog 概述
    ant-design中form组件的item在typescript环境下自定义提示文字(Form.Item的tooltip属性)及提示图标
    Golang 中的 String、rune 和 byte
    TensorFlow实战教程(十九)-Keras搭建循环神经网络分类案例及RNN原理详解
    RHCE第一天练习题
  • 原文地址:https://www.cnblogs.com/xiaoyh/p/16563381.html