zoukankan      html  css  js  c++  java
  • [spring源码学习]一、IOC简介

    一、程序实例

      假设一个简单地实例,我们有一个人,人可能有姓名,年龄等属性,每天上下班的时候需要坐车,他可能做小轿车,suv等,这样一个场景。我们很容易想到如下代码:

      1、人的对象类,包括两个属性,姓名和车

    package com.zjl.ioc;
    
    public class Person {
        String name;
        Driveable driveable;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Driveable getDriveable() {
            return driveable;
        }
        public void setDriveable(Driveable driveable) {
            this.driveable = driveable;
        }
        
        
        public void drive(){
            System.out.println(this.name+"准备开车");
            this.driveable.drive();
        }
    }

      2、假设我们所有的车子都继承一个可以开走的接口

    package com.zjl.ioc;
    
    public interface Driveable {
        public void drive();
    }

      3、定义一种车,小汽车

    package com.zjl.ioc;
    
    public class Car implements Driveable {
    
        @Override
        public void drive() {
            System.out.println("小汽车被开着跑了");
        }
        
    }

      4、如果一个人,名叫张三,准备开着小汽车走了,我们很容易想到实现代码为

    package com.zjl.ioc;
    
    public class Test {
        public static void main(String[] args) {
            Person person=new Person();
            person.setName("张三");
            person.setDriveable(new Car());
            person.drive();
        }
    }

      5、运行结果

    张三准备开车
    小汽车被开着跑了

      很好,很强大,我们可以看到张三开着小汽车就跑掉了。

      到这里本来就改结束了,不过我们实际开发中的程序并不会如此简单,一个简单的功能,我们需要引入数据库的dateSource,日志记录,交易检查等各种各样的属性,如果都等到具体使用的时候再一个个进行初始化,赋值,这将是一个非常大的工作量。

      那么spring ioc就是将一个类的所有属性在容器中进行初始化后,帮助我们完成所有的赋值操作。我们只需要获得初始化好的实例,就可以使用它所有的方法。

    二、使用spring ioc进行改造

      1、我们使用spring的xml配置,配置bean的信息

        <bean id="person" class="com.zjl.ioc.Person">
            <property name="name" value="zhangsan" />  
            <property name="driveable" ref="car" />
        </bean>  
        <bean id="car" class="com.zjl.ioc.Car">
        </bean> 

      2、再次编写测试

    public class Test {
        public static void main(String[] args) {
            ApplicationContext context=new FileSystemXmlApplicationContext("ioctest.xml");
            Person person=(Person) context.getBean("person");
            person.drive();
        }
    }

      这里我们可以看到,spring主要做了几件事情:

      1、从配置文件中读取了实例所有的配置,使得应用程序完全不需要关心bean有哪些属性,这个配置文件可以是xml或者properties,也可以是通过类的注解方式

      2、帮我们将所有需要用到的bean从容器中获得,bean在容器中的存储方式和实例化过程对客户隐藏

      3、如果一个bean需要在多个类里使用我们仅仅需要每次使用getBean就可以直接获取,不需要反复手工初始化

      4、从日后的学习中我们还可以看到,ioc容器额外做了许多通用的功能,如类的延迟加载,默认执行方法,动态代理,属性继承,单例模式等等许多好用的功能。

    三、简单模拟

      1、分析

      从上边的spring实例,我们可以看到,ioc的加载和初始化过程需要依赖于配置文件,配置文件将每一个类定义为一个bean标签,标签内分为两部分:通用部分和个性化部分

      通用部分主要是:bean的id,class,根据spring配置文件,我们得知还有parent,scope,init-lazy,init-method等许多方法

      个性化部分:每个类的个性化部分都不同,但可以归类为基础类型,引用其他bean,以及一些集合类型-数组,list,set,map等,我们的目前bean比较简单,主要包括一个基础类型,一个引用类型。

      所以我们需要做的事情首先是需要建立一个对象,将所有的配置文件进行解析,进行保存,对于通用属性,作为对象的直接属性,个性化部分使用map进行存储。我们参考spring的命名:

    package com.zjl.ioc;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class BeanDefinition {
        public String id;
        public String className;
        public Map<String,Object> pv =new HashMap<String, Object>();
    }

      2、定义一个配置解析的容器

      我们定义一个beanFactory来进行参数初始化和实例生成,在BeanFactory中定义一个容器,保存所有的bean基本配置信息,为了便于查找,定义为map

    public class BeanFactory {
        Map<String,BeanDefinition> beanDefinitions=new HashMap<String,BeanDefinition>();
        public BeanFactory(){
        init(); } }

      3、解析配置

      有了一个容器后,我们需要将所有的bean的基本参数进行保存,如前文分析中,这些内容可以来自于xml或者properties中,本例子中为了简便,我直接使用硬编码写入

        public void init(){
            //定义person
            BeanDefinition personDefinition=new BeanDefinition();
            personDefinition.id="person";
            personDefinition.className="com.zjl.ioc.Person";
            personDefinition.pv.put("name", "zhangsan");
            personDefinition.pv.put("driveable", "car");
            beanDefinitions.put(personDefinition.id,personDefinition);
            //定义car
            BeanDefinition driveableDefinition=new BeanDefinition();
            driveableDefinition.id="car";
            driveableDefinition.className="com.zjl.ioc.Car";
            beanDefinitions.put(driveableDefinition.id,driveableDefinition);
        }

      实际中可能是这样的: 

        <bean id="person" class="com.zjl.ioc.Person">
            <property name="name" value="zhangsan" />  
            <property name="driveable" ref="car" />
        </bean>  
        <bean id="car" class="com.zjl.ioc.Car">
        </bean> 

      也可能是这样的:

    person.class=con.zjl.ioc.Person
    person.pv.name=zhangsan
    person.pv.driveable=car
    
    car.class=com.zjl.ioc.Car

       4、实例化bean,提供接口

      此处操作主要有两步:

      a、根据已经有的className,将类进行实例化

      b、将配置文件的属性赋值实例化的对象

    public Object getBean(String beanName){
            Object result = null;
            if(beanDefinitions.get(beanName)!=null){
                BeanDefinition beanDefinition=beanDefinitions.get(beanName);
                String className=beanDefinition.className;
                try {
                    result=Class.forName(className).newInstance();
                    setPV(result,beanDefinition);
                } catch (InstantiationException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (SecurityException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (NoSuchFieldException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return result;
            }
            
            return result;

      5、属性赋值

      遍历pv,一个个进行赋值,如果pv中的value不是基础类型,那么判断是否为其他bean,并实例化引用的对象,此处写的比较简单,仅能处理目前的实例,实际中需要判断各种类型,包括基础类型,数组,容器和对象等等。

    private void setPV(Object object, BeanDefinition beanDefinition) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
            Map<String, Object> map=beanDefinition.pv;
            for(String key:map.keySet()){
                Field field=object.getClass().getDeclaredField(key);
                Object value=null;
                //判断如果不是基础类型,那么应该是需要注入其他bean
                if(field.getType().getName()!="java.lang.String"){
                    value=getBean(map.get(key).toString());
                }else {
                    value=map.get(key);
                }
                field.setAccessible(true);
                field.set(object, value);
    //            System.out.println(field.getType());
                
            }

      6、修改测试

    public class Test {
        public static void main(String[] args) {
            BeanFactory beanFactory=new BeanFactory();
            Person person=(Person) beanFactory.getBean("person");
            person.drive();
        }
        
    }

     四、总结

      到此为止,我们简单模拟了一遍spring的ioc处理过程,主要涉及到的知识包括:配置文件解析,反射等知识。

        但是仍有许多改进的地方,如果对属性注入的改进,对bean的判断,如果是单例我们需要另外一个容器保存已经初始化好的bean等一系列内容,这个都将在我学习源码过程中再仔细体会。

  • 相关阅读:
    SQL Server
    SQL Server
    SQL Server
    SQL Server
    SQL Server
    SQL Server
    SQL Server
    ssh保持连接不断开
    如何查看linux是否打开虚拟化
    dd命令详解
  • 原文地址:https://www.cnblogs.com/jyyzzjl/p/5420887.html
Copyright © 2011-2022 走看看