zoukankan      html  css  js  c++  java
  • 实现极简IoC容器

    创建Apple类
    public class Apple {
        private String title;
        private String color;
        private String origin;
    
        public Apple(){
    
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        public String getOrigin() {
            return origin;
        }
    
        public void setOrigin(String origin) {
            this.origin = origin;
        }
    }
    编写applicationContext.xml文件
    <?xml version="1.0" encoding="UTF-8" ?>
    <beans>
        <bean id="sweetApple" class="com.imooc.spring.ioc.entity.Apple">
            <property name="title" value="红富士"/>
            <property name="color" value="红色"/>
            <property name="origin" value="欧洲"/>
        </bean>
    </beans>
    创建ApplicationContext接口,接口里创建getBean抽象类
    public interface ApplicationContext {
        public Object getBean(String beanId);
    }
    创建ClassPathXmlApplicationContext实现类,实现ApplicationContext接口。
    public class ClassPathXmlApplicationContext implements ApplicationContext {
        private Map iocContainer = new HashMap();
        public ClassPathXmlApplicationContext(){
            try {
                String filePath = this.getClass().getResource("/applicationContext.xml ").getPath();
            }catch (Exception e){
    
            }
        }
        public Object getBean(String beanId){
            return null;
        }
    }
    作为java使用Map对象保存beanId和所对应的对象,Mao是一个键值对的结构,作为键就对应了beanId,作为值就对应了容器创建过程中所产生的对象。
    创建一个HashMap作为IoC容器
    private Map iocContainer = new HashMap();
    下一步就是在实例化ClassPathXmlApplicationContext对象过程中去加载处理xml文件。在ClassPathXmlApplicationContext构造方法中,在对象初始化的时候读取刚才编写的applicationContext.xml文件。
    getResource方法用于从classpath下获取指定的文件资源。之后通过getPath得到这个文件的路径。这样就能得到配置文件的物理地址了。
    public ClassPathXmlApplicationContext(){
            try {
                String filePath = this.getClass().getResource("/applicationContext.xml ").getPath();
            }catch (Exception e){
    
            }
        }
    但是作为这个地址如果中间包含中文,可能出现路径找不到的问题,所以还需要url的解码 
    filePath = new URLDecoder().decode(filePath,"UTF-8");
    接下来作为获取到的xml路径如何对xml进行解析?
    需要引入dom4j和jaxen,在pom.xml中加入依赖。
    Dom4j是java的xml解析组件。Jaxen是Xpath表达式解释器。Dom4j底层是依赖Jaxen的,所以都要引入
        <dependencies>
            <dependency>
                <groupId>org.dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>2.1.1</version>
            </dependency>
    
            <dependency>
                <groupId>jaxen</groupId>
                <artifactId>jaxen</artifactId>
                <version>1.1.6</version>
            </dependency>
        </dependencies>
    利用Dom4j提供的SAXReader对象去加载解析filePath对应的xml。reader对象中提供了一个read方法用于读取指定文件。这里新创建一个File,将filePath传入其中,代表了所对应的文件对象。然后再提供给reader进行读取解析。这样就能得到xml对应的文档对象document。所有的文档数据都包含在document对象中。
            try {
                String filePath = this.getClass().getResource("/applicationContext.xml ").getPath();
                filePath = new URLDecoder().decode(filePath,"UTF-8");
                SAXReader reader = new SAXReader();
                Document document = reader.read(new File(filePath));
            }catch (Exception e){
    
            }
    之后的工作就是按照xml格式,依次进行读取。首先文档的根节点是beans,beans下面拥有若干bean标签,先把它提取出来。document.getRootElement()得到根节点,然后在根节点下用selectNodes(),selectNodes是按照指定的Xpath表达式来得到节点的集合。直接传入bean就会将当前根节点下所有bean子标签获取了。返回的是一个List集合,集合中每个对象都是一个Node节点。
    List<Node> beans = document.getRootElement().selectNodes("bean");
    接下来for循环遍历。作为Node有多种子类型,作为每一个beans,它的实际类型为Element(元素)。在遍历过程中,每一个bean标签都包含了两个属性,一个id,一个class,需要读取出来。使用ele.attributeValue()读取当前节点对应的属性。就将xml两个属性值提取了出来
                for (Node node : beans){
                    Element ele = (Element) node;
                    String id = ele.attributeValue("id");
                    String className = ele.attributeValue("class");
                }
    但是作为这两个属性,用于实例化对象。如何对Apple实例化呢?
    就可以使用反射技术了。Class.forName()用于加载指定的类,将className传入,便可以得到与之对应的类对象了。通过newInstance便可以通过默认构造方法创建Apple类的实例。
                    Class c = Class.forName(className);
                    Object obj = c.newInstance();
    这时候beanId有了对象也有了,下面只需要利用iocContainer中的put方法将id和obj放入其中,就相当于IoC容器对刚才新创建的对象赋予了beanId进行管理
            try {
                String filePath = this.getClass().getResource("/applicationContext.xml ").getPath();
                filePath = new URLDecoder().decode(filePath,"UTF-8");
                SAXReader reader = new SAXReader();
                Document document = reader.read(new File(filePath));
                List<Node> beans = document.getRootElement().selectNodes("bean");
                for (Node node : beans){
                    Element ele = (Element) node;
                    String id = ele.attributeValue("id");
                    String className = ele.attributeValue("class");
                    Class c = Class.forName(className);
                    Object obj = c.newInstance();
                    iocContainer.put(id,obj);
                }
                System.out.println("IoC容器初始化完毕");
            }catch (Exception e){
                e.printStackTrace();
            }
    getBean方法,利用iocContainer.get方法对指定的beanId进行提取,并且进行返回就可以了
        public Object getBean(String beanId){
            return iocContainer.get(beanId);
        }
    这样就完成了一个简单的IoC容器
    验证。创建测试类Application
    在Application中调用getBean传入beanId打印出来地址,在ClassPathXmlApplicationContext中打印iocContainer地址。两者地址相同
     
    接下来在ioc容器中,通过set方法进行属性注入。还是通过反射来完成
     
    在获取到对象以后,通过ele.selectNodes得到每一个bean标签下的每一个property属性标签。且同样使用List<Node>进行保存
        List<Node> properties = ele.selectNodes("property");
    这个List集合中就包含了bean中的三个属性name和value数据。之后再利用for循环对属性进行遍历。
                    for (Node p : properties){
                        Element property = (Element)p;
                        
                    }
    之后提取每一个属性中的name和value
                    for (Node p : properties){
                        Element property = (Element)p;
                        String propName = property.attributeValue("name");
                        String propValue = property.attributeValue("value");
                    }
    属性名字和对应的值获取到以后,如何在运行时动态注入呢?
    基于property这种形式完成动态注入,是通过属性的set方法完成的。而set方法命名的规则,前面是set,后面增加属性名,属性名首字母大写。只要按照这个规则组织set方法,就可以利用反射进行动态调用。
    首先获取set方法名
                        String setMethodName = "set" + propName.substring(0,1).toUpperCase() + propName.substring(1);
    之后通过方法名完成调用。反射中的getMethod方法。
                        // 获取Method对象
                        Method setMethod = c.getMethod(setMethodName, String.class);
                        // 通过set方法注入数据
                        setMethod.invoke(obj,propValue);
    动态注入就完成了。
     

    附件列表

     https://files.cnblogs.com/files/sx1011/s06.zip

  • 相关阅读:
    D django 用户认证系统
    vim 跳到指定行
    django 的auth.authenticate返回为None
    git fetch 的简单用法:更新远程代码到本地仓库
    sql语句查询出表里符合条件的第二条记录的方法
    你一定喜欢看的 Webpack 2.× 入门实战
    webpack 从入门到工程实践
    入门Webpack,看这篇就够了
    教程
    常用浏览器如何设置代理服务器上网(图文教程)
  • 原文地址:https://www.cnblogs.com/sx1011/p/13673714.html
Copyright © 2011-2022 走看看