基于Java反射技术实现简单IOC容器

 更新时间:2020年07月07日 10:44:19   作者:Haidnor  
这篇文章主要介绍了基于Java反射技术实现简单IOC容器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

前言

首先思考一个问题,如果你正在做一个复杂的系统,一个系统模块内有几百个功能业务类,这些类需要使用同一些对象来进行工作。那么,你会怎样去管理这些通用且一样的对象呢?

学习过Spring的朋友会知道,Spring框架为此提供了一种非常先进的思想,即IOC(控制反转)。Spring可以理解为一个工厂,负责对象的创建和对象间关系的维护。IoC即控制反转,简单说就是之前需要使用new的方式创建对象,而Spring框架会从XML文件中根据配置的信息来创建对象,然后放进它自己的容器之中。在程序要使用到该对象的时候,自动注入。

下面就来做一个最简单的IOC容器。

1.创建一个实体类,比如学生类,汽车类

2.创建XML文件配置对象的信息

3.编写一个IOC容器类。这个类工作起来,首先加载XML文件,扫描自己配置的对象信息,之后使用反射技术创建对象,最后将这些

对象放进自己的Map集合中(容器)。外部想要调用这些对象,那么就使用Map的键,来拿到这个集合中对应的值(对象)。

编写一个喜闻乐见的Student学生类。

我做的比较简单,没有使用get() set()方法。

后面使用反射技术可以强制给 private 修饰的属性赋值

package cn.haidnor.bean;

public class Student {
  /** 学生姓名 */
  private String name;
  /** 学生性别 */
  private String gender;
  /** 学生年龄 */
  private int age;
  
  @Override
  public String toString() {
    return "Student{" +
        "name='" + name + '\'' +
        ", gender='" + gender + '\'' +
        ", age=" + age +
        '}';
  }
}

创建XML文件,配置对象信息

  • id 表示在IOC容器(Map)的键
  • class 表示对象类的全类名
  • name 表示对象的各种属性名
  • property下的文本节点表示该属性的值
<?xml version="1.0" encoding="UTF-8"?>
<beans>
  <bean id="stu1" class="cn.haidnor.bean.Student">
    <property name="name">Lucy</property>
    <property name="age">18</property>
    <property name="gender">female</property>
  </bean>

  <bean id="stu2" class="cn.haidnor.bean.Student">
    <property name="name">Tom</property>
    <property name="age">21</property>
    <property name="gender">male</property>
  </bean>

  <bean id="stu3" class="cn.haidnor.bean.Student">
    <property name="name">LiLi</property>
    <property name="age">23</property>
    <property name="gender">female</property>
  </bean>
</beans>

编写IOC容器类

1.首先根据XML中的配置文件,生成学生对象

2.所有的对象都放入到一个Map中

3.提供一个getBean()的方法,传入配置文件中的id,返回对应的对象

package cn.haidnor.core;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;


public class SpringIOC {

  /**
   * 配置文件地址
   */
  private static final String CONFIGURATION_PATH = "resources/applicationContext.xml";

  /**
   * ioc容器
   */
  private static Map<String, Object> ioc = new HashMap<>();

  static {
    initialization();
  }

  /**
   * 从 ioc 容器中获取指定 bean
   *
   * @param name 需要获取的 bean 的 id, 对应 XML 配置文件中的 bean id
   * @return bean
   */
  public static Object getBean(String name) {
    return ioc.get(name);
  }

  /**
   * 初始化容器
   */
  private static void initialization() {
    Document document = null;

    try {
      DocumentBuilderFactory bdf = DocumentBuilderFactory.newInstance();
      DocumentBuilder documentBuilder = bdf.newDocumentBuilder();
      document = documentBuilder.parse(CONFIGURATION_PATH);
    } catch (Exception e) {
      e.printStackTrace();
    }

    NodeList beanNodes = document.getElementsByTagName("bean");

    for (int i = 0; i < beanNodes.getLength(); i++) {
      Node node = beanNodes.item(i);
      reloadBean(node);
    }
  }

  /**
   * 装载 benn
   *
   * @param beanNode xml 文件 bean 根节点
   */
  private static void reloadBean(Node beanNode) {
    Element bean = (Element) beanNode;

    String id = bean.getAttribute("id");      // IOC 容器中 bean 的名字
    String beanClass = bean.getAttribute("class"); // 全类名

    // 每个 bean 节点下的全部 property 节点
    NodeList childNodes = beanNode.getChildNodes();
    Map<String, String> attributeMap = reloadAttribute(childNodes);

    // 使用反射构造 bean 对象
    Object instance = creatBean(beanClass, attributeMap);

    // 将所有的 bean 对象放入容器中
    ioc.put(id, instance);
  }

  /**
   * 加载 bean 的属性值
   *
   * @param attributeNodes 所有的属性 property 节点
   * @return Map 属性的名字和值集合
   */
  private static Map<String, String> reloadAttribute(NodeList attributeNodes) {
    Map<String, String> keyValue = new HashMap<>();
    for (int i = 0; i < attributeNodes.getLength(); i++) {
      Node filed = attributeNodes.item(i);
      if (filed.getNodeType() == Node.ELEMENT_NODE) {
        Element element = (Element) filed;
        String fileName = element.getAttribute("name");
        String value = element.getFirstChild().getNodeValue();
        keyValue.put(fileName, value);
      }
    }
    return keyValue;
  }

  /**
   * 构造bean对象
   *
   * @param className 全类名
   * @param attributes 每个对象的属性和
   * @return Object 构造完成的 bean 对象
   */
  private static Object creatBean(String className, Map<String, String> attributes) {
    Object instance = null;
    try {
      Class<?> clazz = Class.forName(className);
      instance = clazz.newInstance();
      Field[] fields = clazz.getDeclaredFields();

      for (Field field : fields) {
        setFiledValue(instance, field, attributes);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return instance;
  }

  /**
   * 为实例对象的属性赋值
   *
   * @param instance  实例对象
   * @param field   属性字段对象
   * @param attributes 属性名与属性值的 Map 集合
   */
  private static void setFiledValue(Object instance, Field field, Map<String, String> attributes) {
    // 忽略 field 权限检查
    field.setAccessible(true);

    String type = field.getType().toString();
    String name = field.getName();

    try {
      switch (type) {
        case "char":
          field.setChar(instance, attributes.get(name).charAt(0));
          break;

        case "class java.lang.Boolean":
        case "boolean":
          field.setBoolean(instance, Boolean.parseBoolean(attributes.get(name)));
          break;

        case "class java.lang.Byte":
        case "byte":
          field.setByte(instance, Byte.parseByte(attributes.get(name)));
          break;

        case "class java.lang.Float":
        case "float":
          field.setFloat(instance, Float.parseFloat(attributes.get(name)));
          break;

        case "class java.lang.Integer":
        case "int":
          field.setInt(instance, Integer.parseInt(attributes.get(name)));
          break;

        case "class java.lang.Long":
        case "long":
          field.setLong(instance, Long.parseLong(attributes.get(name)));
          break;

        case "class java.lang.Short":
        case "short":
          field.setShort(instance, Short.parseShort(attributes.get(name)));
          break;

        default:
          field.set(instance, attributes.get(name));
          break;
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

最后编写测试类

  • 不使用new的方式创建学生对象
  • 使用ioc容器getBean()方法获取对象
  • 调用对象的复写的toString()方法
package cn.haidnor.test;

import cn.haidnor.bean.Student;
import cn.haidnor.core.SpringIOC;

public class Test {
  public static void main(String[] args) {
    // 不使用 new 的方式创建对象, 从容器中获取
    Student stu1 = (Student) SpringIOC.getBean("stu3");
    // 调用学生类的方法,打印信息
    System.out.println(stu1.toString());
  }
}

运行结果,控制台打印输出的内容

Student{name='LiLi', gender='female', age=23}

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

相关文章

  • Struts2 漏洞分析及如何提前预防

    Struts2 漏洞分析及如何提前预防

    2016年4月26日,Struts2发布一份安全公告,CVE编号 CVE-2016-3081。这是自2012年Struts2命令执行漏洞大规模爆发之后,该服务时隔四年再次爆发大规模漏洞。该漏洞也是今年目前爆出的最严重安全漏洞。本文分析了漏洞的原理危害影响防护等内容。
    2016-05-05
  • Spring Boot 整合单机websocket的步骤 附github源码

    Spring Boot 整合单机websocket的步骤 附github源码

    websocket 是一个通信协议,通过单个 TCP 连接提供全双工通信,这篇文章主要介绍了Spring Boot 整合单机websocket的步骤(附github源码),需要的朋友可以参考下
    2021-10-10
  • SpringBoot整合Quartz实现动态配置的代码示例

    SpringBoot整合Quartz实现动态配置的代码示例

    这篇文章将介绍如何把Quartz定时任务做成接口,实现以下功能的动态配置添加任务,修改任务,暂停任务,恢复任务,删除任务,任务列表,任务详情,文章通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • 详解Java如何实现与JS相同的Des加解密算法

    详解Java如何实现与JS相同的Des加解密算法

    这篇文章主要介绍了如何在Java中实现与JavaScript相同的DES(Data Encryption Standard)加解密算法,确保在两个平台之间可以无缝地传递加密信息,希望对大家有一定的帮助
    2025-04-04
  • 在2023idea中实现SpringBoot的IoC和AOP的方法

    在2023idea中实现SpringBoot的IoC和AOP的方法

    这篇文档详细介绍了如何在Spring Boot中实现IoC(控制反转)和AOP(面向切面编程),深入探讨了AOP的基本概念,包括AOP的作用、优势以及实现方式,最后,它提到了AOP的注解,如@Aspect、@Pointcut、@Before、@After、@AfterReturning、@AfterThrowing和@Around
    2024-11-11
  • SpringBatch数据读取的实现(ItemReader与自定义读取逻辑)

    SpringBatch数据读取的实现(ItemReader与自定义读取逻辑)

    本文主要介绍了SpringBatch数据读取的实现, 文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-04-04
  • 浅谈Java数组的一些使用方法及堆栈存储

    浅谈Java数组的一些使用方法及堆栈存储

    下面小编就为大家带来一篇浅谈Java数组的一些使用方法及堆栈存储。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • Java中RedissonClient基本使用指南

    Java中RedissonClient基本使用指南

    RedissonClient 是一个强大的 Redis 客户端,提供了丰富的功能和简单的 API,本文就来介绍一下Java中RedissonClient基本使用指南,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • Struts2实现文件上传时显示进度条功能

    Struts2实现文件上传时显示进度条功能

    这篇文章主要为大家详细介绍了Struts2实现文件上传时显示进度条功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • Spring JPA联表查询之OneToOne源码详解

    Spring JPA联表查询之OneToOne源码详解

    这篇文章主要为大家介绍了Spring JPA联表查询之OneToOne源码详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04

最新评论