zoukankan      html  css  js  c++  java
  • 实现IOC功能的简单Spring框架

    需求分析

    设计一个含有IOC的简单Spring,要求含有对象注册、对象管理以及暴露给外部的获取对象功能。

    项目设计

    1. 对于注册的对象用一个类BeanInfo来描述其信息,包括对象标识、全类名以及属性名与值的Map。
    2. 对于IOC容器设定一个顶层接口BeanFactory,定义通过对象标识获取对象示例的方法getBean(String id)AbstractBeanFactory实现该接口,在该类中实现解析生成目标对象,以及获取对象方法,并在该类中添加注册器接口,以便能从注册器中读取注册的对象。
    3. 对于注册器,提供一个顶层接口SourceReader,并在其中添加加载用户注册的对象的方法loadBeans(String filePath)。本项目中使用读取XML的方式,从XML中读取出注册的对象,并把它们封装成BeanInfo放入Map中。
    4. 设定一个上下文XMLContext,继承AbstractBeanFactory,负责选择使用哪种注册方式,并决定何时加载注册的对象。

    代码实现

    BeanInfo类

    package ex1;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
    * 该类用于描述注册在容器中的对象
    */
    public class BeanInfo {
    	private String id; //对象ID,名字
    	private String type; //全类名
    	private Map<String, Object> properties = new HashMap<>(); //属性名与值的映射集合
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public Map<String, Object> getProperties() {
            return properties;
        }
    
        public void setProperties(Map<String, Object> properties) {
            this.properties = properties;
        }
    
        public void addProperty(String key,Object value) {
            properties.put(key,value);
        }
    }
    

    BeanFactory接口

    package ex1;
    
    public interface BeanFactory {
        /**
         * 根据对象的名称标识来获取对象实例
         * @param id 对象名称,即对象描述信息中的对象标识
         * @return 指定名称的对象实例
         */
        Object getBean(String id);
    }
    

    AbstractBeanFactory类

    package ex1;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Map;
    /**
     * 最顶层的IOC实现
     * 该类负责从注册器中取出注册对象
     * 实现从对象描述信息转换为对象实例的过程
     * 实现根据名称获取对象的方法
     */
    public abstract class AbstractBeanFactory implements BeanFactory {
        private String filePath;  //注册文件路径
        private Map<String, BeanInfo> container; //注册对象信息Map(IOC容器)
        protected SourceReader reader; //对象注册读取器
    
    
        public AbstractBeanFactory(String filePath) {
            this.filePath = filePath;
        }
    
        /**
         * 抽象方法,需由子类实现,用于指定使用什么样的注册读取器
         * @param reader 指定的注册读取器
         */
        protected abstract void setReader(SourceReader reader);
    
        //从注册读取器中读取注册对象的信息Map
        public void registerBeans() {
            this.container = this.reader.loadBeans(this.filePath);
        }
    
        //实现BeanFactory定义的根据名称获取指定对象的方法
        @Override
        public Object getBean(String id) {
            BeanInfo beanInfo = this.container.get(id); //根据对象名称获取该对象的描述信息
            if (beanInfo == null) {
                return null;
            } else {
                //根据对象信息,解析并生存指定对象实例,返回给用户
                return this.parseBean(beanInfo);
            }
        }
    
        /**
         * 解析并生成对象实例
         * 该方法主要通过反射完成,步骤如下:
         * 1.根据类名,加载指定类,并获取该类的Class对象clazz
         * 2.使用clazz实例化该类,获取一个对象,注意,这里采用无参构造方法
         * 3.逐个设置对象子段的值,这里采用setter Method方式,而不是直接使用Field对象
         * 4.返回对象实例
         * @param beanInfo 指定对象的描述信息
         * @return
         */
        protected Object parseBean(BeanInfo beanInfo) {
            Class clazz;
            Object bean = null;
            try {
                clazz = Class.forName(beanInfo.getType()); //根据对象的全类名,指定类
                bean = clazz.newInstance(); //使用注册对象的无参构造函数,实例化对象
                Method[] methods = clazz.getMethods(); //获取所有公共方法(其实Spring获取的是所有方法,包括非公有是)
                for (String property : beanInfo.getProperties().keySet()) {
                    //首字母大写
                    String name = property.toUpperCase().charAt(0) + property.toLowerCase().substring(1);
                    //获取属性的setter方法名称(命名规范)
                    String setter = "set" + name;
                    for (Method method : methods) {
                        if (method.getName().equals(setter)) {
                            Object value = beanInfo.getProperties().get(property);
                            method.invoke(bean, value); //通过反射对属性赋值
                            break;
                        }
                    }
                }
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return bean;
        }
    }
    

    SourceReader接口

    package ex1;
    
    import java.util.Map;
    /**
     * 注册读取器接口
     * 复制读取用户注册的对象
     * 继承该接口的类可以实现多种读取方式,如从配置文件中读取,根据标注读取,从网络中读取等
     */
    public interface SourceReader {
        /**
         * 读取用户注册的对象信息
         * @param filePath 注册路径
         * @return 注册对象信息Map
         */
        Map<String, BeanInfo> loadBeans(String filePath);
    }
    

    XMLSourceReader类

    这里实现从xml配置文件中读取。需要用到dom4j包,用来解析XML文件。本项目中XML文件放置在根目录下,内容如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans>
        <bean id="Person" class="ex1.Person">
            <property name="name" value="fang"/>
        </bean>
    </beans>
    

    实现代码

    package ex1;
    
    import org.dom4j.Attribute;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    /**
     * XML注册读取器
     * 该类继承了注册读取器接口,并模拟实现了读取注册对象信息的方法
     */
    public class XMLSourceReader implements SourceReader {
        /**
         * 实现读取注册对象信息方法
         * 自己编写通过配置文件读取的实现
         */
        @Override
        public Map<String, BeanInfo> loadBeans(String filePath) {
            BeanInfo info = new BeanInfo(); //注册对象的info
            InputStream is = XMLContext.class.getClassLoader().getResourceAsStream(filePath);//获取xml文件
            SAXReader reader = new SAXReader();
            Map<String,BeanInfo> beanMap = new HashMap<>();
            try {
                Document document = reader.read(is);
                Element root = document.getRootElement(); //获取根标签,这里是beans
                //遍历所有bean
                for(Iterator iterator = root.elementIterator("bean");iterator.hasNext();){
                    Element element = (Element)iterator.next();
                    //获取id和class
                    Attribute id = element.attribute("id");
                    Attribute clazzName = element.attribute("class");
                    info.setId(id.getText());
                    info.setType(clazzName.getText());
                    //遍历该bean的property
                    for(Iterator it=element.elementIterator("property");it.hasNext();){
                        Element tmp = (Element)it.next();
                        //获取name和value
                        Attribute name = tmp.attribute("name");
                        Attribute value = tmp.attribute("value");
                        info.addProperty(name.getText(),value.getText());
                    }
                    beanMap.put(id.getText(),info);
                }
            } catch (DocumentException e) {
                e.printStackTrace();
            }
            return beanMap;
        }
    }
    

    XMLContext类

    package ex1;
    
    public class XMLContext extends AbstractBeanFactory {
        /**
         * 上下文的构造方法
         * 该方法中指明注册读取器
         * 并在构造该方法时一次性加载注册的对象
         * @param filePath
         */
        public XMLContext(String filePath) {
            super(filePath);
            this.setReader(new XMLSourceReader());//添加注册读取器
            this.registerBeans(); //加载注册的对象信息
        }
    
        //设置注册读取器
        @Override
        protected void setReader(SourceReader reader) {
            this.reader = reader;
        }
    }
    

    测试类

    Speakable接口

    package ex1;
    
    public interface Speakable {
        void speak(String message);
    }
    

    Person类

    package ex1;
    
    public class Person implements Speakable {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void speak(String message) {
            System.out.println(this.name + " say: " + message);
        }
    }
    

    Bootstrap类

    package ex1;
    
    public class Bootstrap {
        public static void main(String[] args) {
            BeanFactory beanFactory = new XMLContext("bean.xml");
            Speakable speakable = (Speakable) beanFactory.getBean("Person");
            speakable.speak("Experience One!");
        }
    }
    

    运行结果

    fang say: Experience One!
  • 相关阅读:
    分布式缓存技术之Redis_03分布式redis
    Spring 二、400行代码手写初体验Spring V1.0版本
    Spring 一、各级架构与依赖关系
    Java正则表达式基础学习
    JAVA开发:SpringBoot多数据源配置
    Spring 单例模式实现源码分析
    Spring 使用的设计模式用哪些
    Spring之@Autowired和@Resource
    Spring的优缺点
    MySQL支持的事物隔离级别以及悲观锁和乐观锁原理和应用场景
  • 原文地址:https://www.cnblogs.com/fht-litost/p/9880437.html
Copyright © 2011-2022 走看看