轻松了解Spring中的控制反转和依赖注入(二)

  紧接上一篇文章《轻松了解Spring中的控制反转和依赖注入》讲解了SpringIOC和DI的基本概念,这篇文章我们模拟一下SpringIOC的工作机制,使我们更加深刻的理解其中的工作。

  类之间的结构图如下

    

  以下是代码

  

   BeanFactor接口:在Spring源码中的定义是:持有对一定数量的Bean的定义,同时每个Bean都被唯一标识的对象(类),需要实现这个接口。根据对Bean的定义,该工厂将会返回一个包含Bean定义的对象的独立实例(原型设计模式),或者单例共享(一个不错的单例设计模式,)范围是整个工厂的范围(也可以理解成是整个容器下),返回哪种类型的实例依赖于Bean工厂的配置:API是相同的。因为Spring2.0中扩大了依赖范围,可以根据具体应用上下问(如在Web环境中的请求和会话),BeanFactory是应用程序组件的中央注册中心和集中配置。简单的来说该接口定义了获取Bean的方法,由子类去实现。

 

package ioc.factory;

/**
 * Created by zzf on 2016/10/26.
 */
public interface BeanFactory {
    /**
     * 根据对象的ID标识获取对象实例
     * @param name
     * @return
     */
    Object getBean(String name);
}

  BeanInfo类:使用Hash Map进行存储Bean的信息,注意主要是存储了Bean定义的Class类路径,这样才能通过取得type从而利用反射实例化所定义的Bean。

package ioc.info;

import java.lang.Object;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by zzf on 2016/10/26.
 */
public class BeanInfo {
    private String id;
    private String type;
    private Map<String,Object> properties=new HashMap<String,Object>();

    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 name, Object object)
    {
        this.properties.put(name, object);
    }
}

  Person:xml文件中定义的Bean

package ioc.bean;

/**
 * Created by zzf on 2016/10/26.
 */
public class Person {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

 AbstractBeanFactory接口:是实现BeanFactory接口的抽象基类。实现获取Bean定义的方法。是实现逻辑的最关键一个类,注册、读取、分析、注入都是在这个类上的方法调用的,具体可以根据方法查找。

package ioc.factory;

import ioc.info.BeanInfo;
import ioc.reader.SourceReader;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Created by zzf on 2016/10/26.
 *
 * 最顶层的IOC实现
 * 该类负责从注册器中取出注册对象
 * 实现从对象描述信息转换为对象实例的过程
 * 实现根据名称获取对象的方法
 *
 */
public abstract class AbstractBeanFactory implements BeanFactory {
    private  String filePath;
    private Map<String,BeanInfo> container;
    protected SourceReader reader;
    public AbstractBeanFactory(String filePath){
        this.filePath=filePath;

    }

    /**
     * 由子类决定如果使用什么样的注册读取器
     * 这里使用了模板方法,父类定义抽象方法,但交给子类自由去设计方法内容
     * @param reader
     */
    protected abstract void setReader(SourceReader reader);

    //注册bean
    public void registerBeans(){
        this.container=this.reader.loadBeans(filePath);
    }

    @Override
    public Object getBean(String name) {
        BeanInfo beanInfo=this.container.get(name);
        if(beanInfo==null){

            return null;

        }else {
            return this.parseBean(beanInfo);
        }

    }

    /**
     * 解析生成并生成对象实例
     * 主要通过反射完成
     * 根据类名,加载指定类,并取得该类的Class对象
     * 使用Class对象实例化该类,获取一个对象。
     * 逐个设置对象字段的值,这里采用setter Method方式
     *
     * @param beanInfo 指定对象的描述信息
     * @return
     */
    protected  Object parseBean(BeanInfo beanInfo){
        Class clazz;
        try {
            //加载Bean的实例
            clazz=Class.forName(beanInfo.getType());
            Object bean=clazz.newInstance();

            //获取该对象下的所有方法,包括私有
            Method[] methods=clazz.getDeclaredMethods();

            for (String property:beanInfo.getProperties().keySet()){
                String setter="set"+ firstCharToUp(property);
                for(Method method:methods){
                    String methodName=method.getName();

                    if(methodName.equals(setter)){
                        Object value=beanInfo.getProperties().get(property);
                        //使用反射调用set方法
                        method.invoke(bean,value);
                    }
                }
            }
            return bean;

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
            return null;
    }

    private String firstCharToUp(String property) {
        System.out.println(property);
        char [] c=property.toCharArray();
        String first=String.valueOf(c[0]).toUpperCase();
        c[0]=first.charAt(0);
        System.out.println(String.valueOf(c));
        return String.valueOf(c);
    }
}
  

package ioc.context;

import ioc.factory.AbstractBeanFactory;
import ioc.reader.SourceReader;
import ioc.reader.XMLSourceReader;

/**
 * Created by zzf on 2016/10/26.
 * 上下文的构造方法
 * 该方法中指明注册读取器(这里采用的XML,读者可以根据兴趣去实现另外的方式如注解)
 * 并在构造该方法时一次性加载注册的对象
 */
public class XMLContext extends AbstractBeanFactory{

    public XMLContext(String filePath){
        super(filePath);
        this.setReader(new XMLSourceReader());
        super.registerBeans();
    }

    @Override
    protected void setReader(SourceReader reader) {
        super.reader=reader;
    }
}
SourceReader:设计顶层读取器的抽象方法
package ioc.reader;

import ioc.info.BeanInfo;

import java.util.Map;

/**
 * Created by zzf on 2016/10/26.
 * 注册读取器接口
 * 负责读取用户注册的对象
 * 继承该接口的类可以实现多种读取方式,如从配置文件中读取,根据标注读取,从网络中读取等等
 */
public interface SourceReader {
    Map<String,BeanInfo> loadBeans(String filePath);
}

  XMLContext:XML的上下文,继承了AbstractBeanFactory,里面比较重要的方法是setReader(),在父类中该方法是抽象方法, 这样的做的意义是交由子类实现自己所想要的读取方式。

package ioc.context;

import ioc.factory.AbstractBeanFactory;
import ioc.reader.SourceReader;
import ioc.reader.XMLSourceReader;

/**
 * Created by zzf on 2016/10/26.
 * 上下文的构造方法
 * 该方法中指明注册读取器(这里采用的XML,读者可以根据兴趣去实现另外的方式如注解)
 * 并在构造该方法时一次性加载注册的对象
 */
public class XMLContext extends AbstractBeanFactory{

    public XMLContext(String filePath){
        super(filePath);
        this.setReader(new XMLSourceReader());
        super.registerBeans();
    }

    @Override
    protected void setReader(SourceReader reader) {
        super.reader=reader;
    }
}

  XmlContext类:继承了AbstractBeanFactory抽象类,进行Bean的注册和注册XML的读取器。

  

package ioc.reader;

import ioc.info.BeanInfo;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Created by zzf on 2016/10/26.
 * 使用 dom4j进行Xml的读取操作
 */
public class XMLSourceReader implements SourceReader {

    @Override
    public Map<String, BeanInfo> loadBeans(String filePath) {
        //读取指定的配置文件
        SAXReader reader = new SAXReader();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        //从class目录下获取指定的xml文件
        InputStream ins = classLoader.getResourceAsStream(filePath);
        Document doc = null;
        try {
            doc = reader.read(ins);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        //获得根节点
        Element root = doc.getRootElement();
        Map<String,BeanInfo>beanInfoMap=new HashMap<String, BeanInfo>();
        //遍历bean
        for (Iterator i = root.elementIterator("bean"); i.hasNext();){
            Element element = (Element) i.next();
            //获取bean的属性id和class
            Attribute id = element.attribute("id");
            Attribute cls = element.attribute("class");
            try {
                //利用Java反射机制,通过class的名称获取Class对象
                Class bean=Class.forName(cls.getText());
                //获取对应class的信息
                java.beans.BeanInfo info= Introspector.getBeanInfo(bean);
                //获取其属性描述
                PropertyDescriptor [] propertyDescriptors=info.getPropertyDescriptors();
                Method method;
                Object object=bean.newInstance();
                BeanInfo beanInfo=new BeanInfo();
                for(Iterator iterator=element.elementIterator("property");iterator.hasNext();){
                    Element foo2= (Element) iterator.next();
                    //获取该property的name属性
                    Attribute name = foo2.attribute("name");

                    String value = null;
                    //获取该property的子元素value的值
                    for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) {
                        Element node = (Element) ite1.next();
                        value = node.getText();
                        break;
                    }
                    System.out.println("name:"+name.getText()+"value"+value);
                for (int j=0;j<propertyDescriptors.length;j++){
                    if(propertyDescriptors[j].getName().equalsIgnoreCase(name.getText())){
                        method=propertyDescriptors[j].getWriteMethod();
                        //利用Java的反射极致调用对象的某个set方法,并将值设置进去
                        method.invoke(object,value);
                        //将获取的对象属性信息存入我们自定义的BeanInfo当中
                        beanInfo.addProperty(name.getText(),value);
                }
                }

                    beanInfo.setId(id.getText());
                    beanInfo.setType(cls.getText());
                    beanInfoMap.put(id.getText(),beanInfo);
                }
                return beanInfoMap;
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IntrospectionException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }

        return null;
    }
}
  

  

<?xml version="1.0" encoding="UTF-8"?>

<beans>
    <bean id="person" class="ioc.bean.Person">
        <property name="username">
            <value>zzf</value>
        </property>
        <property name="password">
            <value>12345678</value>
        </property>
    </bean>
</beans>

  

package ioc;

import ioc.bean.Person;
import ioc.context.XMLContext;
import ioc.factory.BeanFactory;

/**
 * Created by zzf on 2016/10/26.
 */
public class test {
    public static void main(String[] args) {
        BeanFactory factory=new XMLContext("configuration/config.xml");
        Person person= (Person) factory.getBean("person");
        System.out.println(person.getPassword());
        System.out.println(person.getUsername());
    }
}

 执行main方法后,控制台成功输出xml所定义的属性。

再结合我第一篇所讲的SpringIOC概念讲解,相信读者应该能够清晰的认识的IOC的作用和大概的实现过程。

时间: 2024-10-25 18:19:49

轻松了解Spring中的控制反转和依赖注入(二)的相关文章

轻松了解Spring中的控制反转和依赖注入

我们回顾一下计算机的发展史,从最初第一台计算机的占地面积达170平方米,重达30吨,到现如今的个人笔记本,事物更加轻量功能却更加丰富,这是事物发展过程中的一个趋势,在技术领域中同样也是如此,企业级JavaBean(Enterprise JavaBean ,EJB)在创建之初是非常成功,但是时间一久人们便开始追逐更加方便更加简易和轻量级的技术框架实现,于是Spring就应运而生,并且Sring一直开始不断地涉及到其他领域(这里就不再多详谈了),而Spring的精髓当中就包括控制反转和依赖注入. 浅

Spring框架之控制反转和依赖注入

学Spring框架必须理解控制反转和依赖注入.下面各自举一个例子,来说明. IOC(控制反转):应用本身创建和维护的依赖对象:现在交由外部容器(Spring)来创建和维护:这个控制权的转移: 就叫做控制反转. 第一步:配置applicationContextcreateproject.xml和applicationcontext.xml(总体) <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=

Spring之ioc控制反转(依赖注入)

个人感觉依赖注入比控制反转更好理解,所以下面就只说依赖注入: spring的整体结构示意图: 一.spring 中的概念 beanFactory容器 1.容器是spring框架的核心,容器使用ioc依赖注入来管理所有组成应用系统的组件. 2.spring中的两种容器: beanFactory  这个容器提供了基础的依赖注入支持,而且是延迟加载的,而 applicationcontext是对beanFactory 这个容器的扩展, 3.beanFactory :beanFactory就是一个Bea

浅谈spring框架的控制反转和依赖注入

spring是什么? spring是一个轻量级的控制反转和面向切面编程的开源容器框架. 轻量级是说spring框架本身的体积小. 控制反转(Ioc):它不是技术,而是一种思想,将创建对象的控制权力交给spring框架. 依赖注入(DI):将对象中的属性通过配置文件的方式进行赋值. 面向切面编程(AOP):零散存在于业务层中的功能代码(例如:日志,事务),称为横切面关注点.把一个个横切面关注点放到某个模块中,称为切面. AOP就是把多个方法前后的共同代码抽离出来,使用动态代理机制来控制.好处是降低

Spring 基础 控制反转和依赖注入

Spring框架两个最重要的知识点 1.IOC(控制反转)/DI(依赖注入):把整个项目中的所有对象交给Spring容器管理 IOC:取到对象 DI:对象的装配 依赖注入有两种方式: 1.      属性值注入方式 a.     普通类型 注入方式 b.bean注入方式 测试结果 在使用普通类型方式注入的时候,我们会有一些特殊字符,处理这种特殊字符时 我们会想使特殊字符保持原有的样子,这时候就要使用CDATA,就如上一张图片所示 2.构造参数注值方式 在一般情况,一个类会有一个默认的无参构造函数

spring(3)------控制反转(IOC)/依赖注入(DI)

一,spring核心概念理解 控制反转: 控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理. 所谓的"控制反转"概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器. 没有控制反转这种模式前,你创建一个对象,在什么地方用,你得单独通过关键字new出来用, 但现在可以不用这样,你把new对象的权利交给spring配置文件,通过配置文件来'new', 就是权利的反转,你以前干的事

spring学习总结一----控制反转与依赖注入

spring作为java EE中使用最为广泛的框架,它的设计体现了很多设计模式中经典的原则和思想,所以,该框架的各种实现方法非常值得我们去研究,下面先对spring中最为重要的思想之一----控制反转(依赖注入)进行简单的总结. 一.控制反转与依赖注入的概念 在学习spring框架的时候,我们习惯性地将控制反转和依赖注入放在一起,其实,两者体现的思想原则都是差不多的,只不过控制反转是一种更广泛的程序操作理念,而依赖注入是一种更为具体的实现方法,总的来说,控制反转可以依靠依赖注入来实现. 1.控制

spring的容器(控制反转、依赖注入)

一.spring的容器 ”容器“是spring的一个重要概念,其主要作用是完成创建成员变量,并完成装配. 而容器的特点”控制反转“和”依赖注入“是两个相辅相成的概念. 控制反转:我们在使用一个类型的实例实现某个功能时,需要先new出该类型的一个实例,并赋值给我们声明的某个引用变量,这样我们才能够使用该变量进行操作.而new和赋值本事我们自己的权限,此处便是将该控制权限反转交给了spring. 依赖注入:某个类型要完成一个功能往往需要其他类型的变量来完成,我们在程序中往往通过自己new的方式来完成

Spring理论基础-控制反转和依赖注入

序第一次了解到控制反转(Inversion of Control)这个概念,是在学习Spring框架的时候.IOC和AOP作为Spring的两大特征,自然是要去好好学学的.而依赖注入(Dependency Injection,简称DI)却使得我困惑了挺久,一直想不明白他们之间的联系.控制反转控制反转顾名思义,就是要去反转控制权,那么到底是哪些控制被反转了?在2004年 Martin fowler 大神就提出了 "哪些方面的控制被反转了?" 这个问题,他总结出是依赖对象的获得被反转了.在