Spring实现一个简单的SpringIOC容器

接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的不到位,说白了,就是仅仅停留在会用的阶段,有一颗想读源码的心于是买了一本计文柯的《Spring技术内幕》,第二章没看完,就被我扔一边了,看的那是相当痛苦,深深觉得自己资质尚浅,能力还不够,昨天在网上碰巧看到一个实现简单的SpringIOC容器的视频教程,于是跟着做了一遍,竟然相当顺利,至少每一行代码都能理解,于是细心整理了一番,放在这里.

主要思想:

提到IOC,第一反应就是控制反转,我以前以为SpringIOC就是控制反转,控制反转就是SpringIOC,当然这种理解是错误的,控制反转是一种思想,一种模式,而Spring的IOC容器是实现了这种思想这种模式的一个载体.

使用过Spring的人都熟知,SpringIOC容器可以在对象生成或初始化时就直接将数据注入到对象中,如果对象A的属性是另一个对象B,还可以将这个对象B的引用注入到注入到A的数据域中.

如果在初始化对象A的时候,对象B还没有进行初始化,而A又需要对象B作为自己的属性,那么就会用一种递归的方式进行注入,这样就可以把对象的依赖关系清晰有序的建立起来.

IOC容器解决问题的核心就是把创建和管理对象的控制权从具体的业务对象手中抢过来.由IOC容器来管理对象之间的依赖关系,并由IOC容器完成对象的注入.这样就把应用从复杂的对象依赖关系的管理中解放出来,简化了程序的开发过程.

下图是这个简单IOC容器的类图(原谅我真没学过UML,凑合看吧):

程序中所有的Bean之间的依赖关系我们是放在一个xml文件中进行维护的,就是applicationContext.xml  

ConfigManager类完成的功能是读取xml,并将所有读取到有用的信息封装到我们创建的一个Map<String,Bean>集合中,用来在初始化容器时创建bean对象.

定义一个BeanFactory的接口,接口中有一个getBean(String name)方法,用来返回你想要创建的那个对象.

然后定义一个该接口的实现类ClassPathXmlApplicationContext.就是在这个类的构造方法中,初始化容器,通过调用ConfigManager的方法返回的Map集合,通过反射和内省一一创建bean对象.这里需要注意,对象的创建有两个时间点,这取决与bean标签中scope属性的值:  

  1. 如果scope="singleton",那么对象在容器初始化时就已创建好,用的时候只需要去容器中取即可.
  2. 如果scope="prototype",那么容器中不保存这个bean的实例对象,每次开发者需要使用这个对象时再进行创建.

使用的主要知识点:

  1. dom4j解析xml文件
  2. xpath表达式(用于解析xml中的标签)
  3. java反射机制
  4. 内省(获取Bean属性的set方法进行赋值)

项目结构图及介绍如下:

项目需要的jar包与项目结构已经在上图中介绍了,这个项目所能实现的功能如下:

1. IOC容器能管理对象的创建以及对象之间的依赖关系.

2. 能够实现数据的自动类型转换(借助BeanUtils).

3. 能够实现scope="singleton"和scope="prototype"的功能,即能够控制对象是否为单例.  

下面介绍代码部分:

application.xml:

<?xml version="1.0" encoding="utf-8"?>
<beans>
  <bean name="student" class="com.wang.entity.Student" >
    <property name="name" value="123"></property>
  </bean>

  <bean name="teacher" class="com.wang.entity.Teacher">
    <property name="student" ref="student"></property>
  </bean>
  <bean name="person" class="com.wang.entity.Person" scope="prototype">
    <property name="teacher" ref="teacher"></property>
    <property name="student" ref="student"></property>
  </bean>

</beans>

实体类Student,Teacher,Person:

package com.wang.entity;
//Student类
public class Student {
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}
/************************************/
package com.wang.entity;
//Teacher类
public class Teacher {

  private Student student;

  public Student getStudent() {
    return student;
  }

  public void setStudent(Student student) {
    this.student = student;
  }

}
/************************************/
package com.wang.entity;
//Person类
public class Person {

  private Student student;
  private Teacher teacher;

  public Student getStudent() {
    return student;
  }
  public void setStudent(Student student) {
    this.student = student;
  }
  public Teacher getTeacher() {
    return teacher;
  }
  public void setTeacher(Teacher teacher) {
    this.teacher = teacher;
  }
}

用于封装Bean标签信息的Bean类:

package com.wang.config;

import java.util.ArrayList;
import java.util.List;

public class Bean {

  private String name;
  private String className;
  private String scope="singleton";
  private List<Property> properties=new ArrayList<Property>();

  public String getScope() {
    return scope;
  }

  public void setScope(String scope) {
    this.scope = scope;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getClassName() {
    return className;
  }

  public void setClassName(String className) {
    this.className = className;
  }

  public List<Property> getProperties() {
    return properties;
  }

  public void setProperties(List<Property> properties) {
    this.properties = properties;
  }

}

用与封装Bean子标签property内容的Property类:

package com.wang.config;

public class Property {

  private String name;
  private String value;
  private String ref;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getValue() {
    return value;
  }
  public void setValue(String value) {
    this.value = value;
  }
  public String getRef() {
    return ref;
  }
  public void setRef(String ref) {
    this.ref = ref;
  }

}

ConfigManager类:

package com.wang.config.parse;

import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Test;

import com.wang.config.Bean;
import com.wang.config.Property;

public class ConfigManager {

  private static Map<String,Bean> map=new HashMap<String,Bean>(); 

  //读取配置文件并返回读取结果
  //返回Map集合便于注入,key是每个Bean的name属性,value是对应的那个Bean对象
  public static Map<String, Bean> getConfig(String path){
    /*dom4j实现
     * 1.创建解析器
     * 2.加载配置文件,得到document对象
     * 3.定义xpath表达式,取出所有Bean元素
     * 4.对Bean元素继续遍历
     *   4.1将Bean元素的name/class属性封装到bean类属性中
     *   4.2获得bean下的所有property子元素
     *   4.3将属性name/value/ref分装到类Property类中
     * 5.将property对象封装到bean对象中
     * 6.将bean对象封装到Map集合中,返回map
      */
    //1.创建解析器
    SAXReader reader=new SAXReader();
    //2.加载配置文件,得到document对象
    InputStream is = ConfigManager.class.getResourceAsStream(path);
    Document doc =null;
    try {
       doc = reader.read(is);
    } catch (DocumentException e) {
      e.printStackTrace();
      throw new RuntimeException("请检查您的xml配置是否正确");
    }
    // 3.定义xpath表达式,取出所有Bean元素
    String xpath="//bean";

    //4.对Bean元素继续遍历
    List<Element> list = doc.selectNodes(xpath);
    if(list!=null){
      //4.1将Bean元素的name/class属性封装到bean类属性中

       // 4.3将属性name/value/ref分装到类Property类中
      for (Element bean : list) {
        Bean b=new Bean();
        String name=bean.attributeValue("name");
        String clazz=bean.attributeValue("class");
        String scope=bean.attributeValue("scope");
        b.setName(name);
        b.setClassName(clazz);
        if(scope!=null){
          b.setScope(scope);
        }
         // 4.2获得bean下的所有property子元素
        List<Element> children = bean.elements("property");

         // 4.3将属性name/value/ref分装到类Property类中
        if(children!=null){
          for (Element child : children) {
            Property prop=new Property();
            String pName=child.attributeValue("name");
            String pValue=child.attributeValue("value");
            String pRef=child.attributeValue("ref");
            prop.setName(pName);
            prop.setRef(pRef);
            prop.setValue(pValue);
            // 5.将property对象封装到bean对象中
            b.getProperties().add(prop);
          }
        }
        //6.将bean对象封装到Map集合中,返回map
        map.put(name, b);
      }
    }

    return map;
  }

}

 BeanFactory接口:

package com.wang.main;

public interface BeanFactory {
  //核心方法getBean
  Object getBean(String name);
}

ClassPathXmlApplicationContext类:

package com.wang.main;

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

import org.apache.commons.beanutils.BeanUtils;
import org.junit.Test;

import com.wang.config.Bean;
import com.wang.config.Property;
import com.wang.config.parse.ConfigManager;
import com.wang.entity.Student;
//import com.wang.utils.BeanUtils;
import com.wang.utils.BeanUtil;

public class ClassPathXmlApplicationContext implements BeanFactory {

  // 获得读取的配置文件中的Map信息
  private Map<String, Bean> map;
  // 作为IOC容器使用,放置sring放置的对象
  private Map<String, Object> context = new HashMap<String, Object>();

  public ClassPathXmlApplicationContext(String path) {
    // 1.读取配置文件得到需要初始化的Bean信息
    map = ConfigManager.getConfig(path);
    // 2.遍历配置,初始化Bean
    for (Entry<String, Bean> en : map.entrySet()) {
      String beanName = en.getKey();
      Bean bean = en.getValue();

      Object existBean = context.get(beanName);
      // 当容器中为空并且bean的scope属性为singleton时
      if (existBean == null && bean.getScope().equals("singleton")) {
        // 根据字符串创建Bean对象
        Object beanObj = createBean(bean);

        // 把创建好的bean对象放置到map中去
        context.put(beanName, beanObj);
      }
    }

  }

  // 通过反射创建对象
  private Object createBean(Bean bean) {
    // 创建该类对象
    Class clazz = null;
    try {
      clazz = Class.forName(bean.getClassName());
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
      throw new RuntimeException("没有找到该类" + bean.getClassName());
    }
    Object beanObj = null;
    try {
      beanObj = clazz.newInstance();
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException("没有提供无参构造器");
    }
    // 获得bean的属性,将其注入
    if (bean.getProperties() != null) {
      for (Property prop : bean.getProperties()) {
        // 注入分两种情况
        // 获得要注入的属性名称
        String name = prop.getName();
        String value = prop.getValue();
        String ref = prop.getRef();
        // 使用BeanUtils工具类完成属性注入,可以自动完成类型转换
        // 如果value不为null,说明有
        if (value != null) {
          Map<String, String[]> parmMap = new HashMap<String, String[]>();
          parmMap.put(name, new String[] { value });
          try {
            BeanUtils.populate(beanObj, parmMap);
          } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("请检查你的" + name + "属性");
          }
        }

        if (ref != null) {
          // 根据属性名获得一个注入属性对应的set方法
          // Method setMethod = BeanUtil.getWriteMethod(beanObj,
          // name);

          // 看一看当前IOC容器中是否已存在该bean,有的话直接设置没有的话使用递归,创建该bean对象
          Object existBean = context.get(prop.getRef());
          if (existBean == null) {
            // 递归的创建一个bean
            existBean = createBean(map.get(prop.getRef()));
            // 放置到context容器中
            // 只有当scope="singleton"时才往容器中放
            if (map.get(prop.getRef()).getScope()
                .equals("singleton")) {
              context.put(prop.getRef(), existBean);
            }
          }
          try {
            // setMethod.invoke(beanObj, existBean);
              //通过BeanUtils为beanObj设置属性
            BeanUtils.setProperty(beanObj, name, existBean);
          } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("您的bean的属性" + name
                + "没有对应的set方法");
          }

        }

      }
    }

    return beanObj;
  }

  @Override
  public Object getBean(String name) {
    Object bean = context.get(name);
    // 如果为空说明scope不是singleton,那么容器中是没有的,这里现场创建
    if (bean == null) {
      bean = createBean(map.get(name));
    }

    return bean;
  }

}

最后就是一个测试类TestBean:

 package com.wang.main;

import org.junit.Test;

import com.wang.entity.Person;
import com.wang.entity.Student;
import com.wang.entity.Teacher;

public class TestBean {

  @Test
  public void func1(){

    BeanFactory bf=new ClassPathXmlApplicationContext("/applicationContext.xml");
    Person s=(Person)bf.getBean("person");
    Person s1=(Person)bf.getBean("person");
    System.out.println(s==s1);
    System.out.println(s1);
    Student stu1=(Student) bf.getBean("student");
    Student stu2=(Student) bf.getBean("student");
    String name=stu1.getName();
    System.out.println(name);
    System.out.println(stu1==stu2);
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 浅析Java的Spring框架中IOC容器容器的应用

    Spring容器是Spring框架的核心.容器将创建对象,它们连接在一起,配置它们,并从创建到销毁管理他们的整个生命周期.在Spring容器使用依赖注入(DI)来管理组成应用程序的组件.这些对象被称为Spring Beans. 容器获得其上的哪些对象进行实例化,配置和组装通过阅读提供的配置元数据的说明.配置元数据可以通过XML,Java注释或Java代码来表示.下面的图是Spring如何工作的高层次图. Spring IoC容器是利用Java的POJO类和配置元数据的产生完全配置和可执行的系统或

  • 用java的spring实现一个简单的IOC容器示例代码

    要想深入的理解IOC的技术原理,没有什么能比的上我们自己实现它.这次我们一起实现一个简单IOC容器.让大家更容易理解Spring IOC的基本原理. 这里会涉及到一些java反射的知识,如果有不了解的,可以自己去找些资料看看. 注意 在上一篇文章,我说,启动IOC容器时,Spring会将xml文件里面配置的bean扫描并实例化,其实这种说法不太准确,所以我在这里更正一下,xml文件里面配置的非单利模式的bean,会在第一次调用的时候被初始化,而不是启动容器的时候初始化.但是我们这次要做的例子是容

  • 深入理解Java的Spring框架中的IOC容器

    Spring IOC的原型 spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功完成了依赖的反转:从主类的对依赖的主动管理反转为了spring容器对依赖的全局控制. 这样做的好处是什么呢? 当然就是所谓的"解耦"了,可以使得程序的各模块之间的关系更为独立,只需要spring控制这些模块之间的依赖关系并在容器启动和初始化的过程中将依据这些依赖关系创建.管理和维护这些模块就好,如果需要改变模块间的依赖关系的话,甚至都不需要改变程序代码,只需要将

  • Spring实现一个简单的SpringIOC容器

    接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的不到位,说白了,就是仅仅停留在会用的阶段,有一颗想读源码的心于是买了一本计文柯的<Spring技术内幕>,第二章没看完,就被我扔一边了,看的那是相当痛苦,深深觉得自己资质尚浅,能力还不够,昨天在网上碰巧看到一个实现简单的SpringIOC容器的视频教程,于是跟着做了一遍,竟然相当顺利,至少每一行代码都能理解,于是细心整理了一番,放在这里. 主要思想: 提到

  • Spring boot实现一个简单的ioc(1)

    前言 跳过废话,直接看正文 之前参与开发的几个spring的项目,用的版本都3.x, 最近忽然发现spring 5 都快上线了,于是赶紧去关注了下spring的最新动态.发现了spring-boot这个好东西(终于可以从各种错综复杂的xml配置文件中解放出来了!). 在学习了目前最新的1.5.2.RELEASE版spring-boot官方文档之后,我决定仿照spring-boot的项目结构以及部分注解,写一个简单的ioc容器,一方面为了加深自己对ioc的理解,另一方面也为了以后在开发一些个人项目

  • Spring boot实现一个简单的ioc(2)

    前言 跳过废话,直接看正文 仿照spring-boot的项目结构以及部分注解,写一个简单的ioc容器. 测试代码完成后,便正式开始这个ioc容器的开发工作. 正文 项目结构 实际上三四个类完全能搞定这个简单的ioc容器,但是出于可扩展性的考虑,还是写了不少的类. 因篇幅限制,接下来只将几个最重要的类的代码贴出来并加以说明,完整的代码请直接参考https://github.com/clayandgithub/simple-ioc. SimpleAutowired 代码 import java.la

  • 一个简单的Spring容器初始化流程详解

    前言 首先我们初始化一个最简单的容器,用这个容器研究初始化的流程. 下面就是一个再简单不过的IoC容器了,该容器包含了一个名为beanA的bean,我们初始化容器后,取出该Bean,并调用方法. public class BeanA { private String testStr = "Test"; public BeanA(){ System.out.println("Running A"); } public void sayHello(){ System.o

  • 使用Spring来创建一个简单的工作流引擎

    文章来源:matrix 作者:Steve Dodge 摘要 spring是支持控制反转编程机制的一个相对新的框架.本文把spring作为简单工作流引擎,将它用在了更加通用的地方.在对工作流简单介绍之后,将要介绍在基本工作流场景中基于Spring的工作流API的使用. 许多J2EE应用程序要求在一个和主机分离的上下文中执行处理过程.在许多情况下,这些后台的进程执行多个任务,一些任务依赖于以前任务的状态.由于这些处理任务之间存在相互依赖的关系,使用一套基于过程的方法调用常常不能满足要求.开发人员能够

  • Spring之借助Redis设计一个简单访问计数器的示例

    为什么要做一个访问计数?之前的个人博客用得是卜算子做站点访问计数,用起来挺好,但出现较多次的响应很慢,再其次就是个人博客实在是访问太少,数据不好看

  • 如何使用 Dockerfile 创建一个简单容器

    目录 Dockerfile 写一个 Dockerfile 优化 Dockerfile container shell Dockerfile Dockerfile 是用于指导 docker 创建自定义 image 的一系列指令,是用于创建 image 的蓝图. 现在有一个简单的 node 项目( 其中Dockerfile 是后面加的,初始项目没有此文件): 要运行此代码,一般的做法是,先在本机安装 node,然后在项目文件夹中先后运行: npm install 以及: node server.js

  • Spring与Dubbo搭建一个简单的分布式详情

    目录 一.zookeeper 环境安装搭建 二.实现服务接口 dubbo-interface 1. dubbo-interface 项目创建 2. 创建接口类 3. 将项目打成 jar 包供其他项目使用 三.实现服务提供者 dubbo-provider 1. dubbo-provider 项目创建 2. pom 文件引入相关依赖 3. 在 application.properties 配置文件中配置 dubbo 相关信息 4. 实现接口 5. 服务提供者启动类编写 四.实现服务消费者 dubbo

  • spring ioc的简单实例及bean的作用域属性解析

    IoC(Inversion if Control)-控制反转是Spring俩大核心技术之一,IoC一般分为俩种类型:依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup) 使用示例: 1.新建工程并导入Spring相关jar包. 2.新建数据访问层及业务逻辑层 代码结构: 代码示例: /** * 实体Bean * @author BC * */ public class User { private Integer id; private

随机推荐