手写简易IOC容器

手写简易IOC容器

1. IOC介绍

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

  • 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
  • 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

IOC实现流程图:
在这里插入图片描述
从示意图可以看出,当web容器启动的时候,spring的全局bean的管理器会去xml配置文件中扫描的包下面获取到所有的类,并根据你使用的注解,进行对应的封装,封装到全局的bean容器中进行管理,一旦容器初始化完毕,beanID以及bean实例化的类对象信息就全部存在了,现在我们需要在某个service里面调用另一个bean的某个方法的时候,我们只需要依赖注入进来另一个bean的Id即可,调用的时候,spring会去初始化完成的bean容器中获取即可,如果存在就把依赖的bean的类的实例化对象返回给你,你就可以调用依赖的bean的相关方法或属性等;

IOC的实现原理:https://www.jiansh·u.com/p/ad05cfe7868e

2.简单代码实现

实现简单的IOC可以分为两步
1.读取xml文件
2.通过反射动态的操作对象

package com.example.demo.ioctest;

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.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class MySingleIOC {
   /**
    *     模拟spring容器  用于保存bean的键值对的hashMap
    */
   private Map<String, Object> beanMap = new HashMap<>();

   public MySingleIOC() { }

   /**
    *     有参构造函数 初始化xml
    */
   public MySingleIOC(String xmlName) throws Exception {
       loadbean(xmlName);
   }

   /**
    * 私有方法加载bean
    */
   private void loadbean(String xmlName) throws Exception {
       //第一步 读取 xml文件
       InputStream resourceAsStream = MySingleIOC.class.getClassLoader().getResourceAsStream(xmlName);
       if (resourceAsStream == null) {
           //当找不到xml抛出异常
           throw new FileNotFoundException("该XML文件无法找到:" + xmlName);
       }
       DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
       DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
       Document parse = documentBuilder.parse(resourceAsStream);
       resourceAsStream.close();
       NodeList beanList = parse.getElementsByTagName("bean");
       //遍历bean标签
       for (int i = 0; i < beanList.getLength(); i++) {

           Node node = beanList.item(i);

           if (node instanceof Element) {
               Element el = (Element) node;

               String beanId = el.getAttribute("id");

               String beanClass = el.getAttribute("class");

               //第二步 反射动态获取对象
               Class beanClazz = null;
               try {
                   //加载beanClass
                   beanClazz = Class.forName(beanClass);
               } catch (ClassNotFoundException e) {
                   e.getMessage();
                   return;
               }
               //创建Bean对象
               Object beanObj = beanClazz.newInstance();

               NodeList propertyList = el.getElementsByTagName("property");

               //遍历子标签property
               for (int j = 0; j < propertyList.getLength(); j++) {
                   Node item = propertyList.item(j);
                   if (item instanceof Element) {
                       Element ele = (Element) item;
                       //拿到变量名称
                       String name = ele.getAttribute("name");
                       //拿到变量值(不一定都是String类型下面要做转换)
                       String value = ele.getAttribute("value");

                       //通过反射将bean对象指定属性
                       Field declaredField = beanObj.getClass().getDeclaredField(name);
                       //将私有属性设置为可访问
                       declaredField.setAccessible(true);
                       //获取成员属性的类型名称,若非字符串类型,则需要做相应转换
                       String fieldTypeName = declaredField.getType().getName();

                       Object o = ParamType(fieldTypeName, value);
                       //为该成员属性赋值
                       declaredField.set(beanObj, o);
                       //将该字段属性设置值


                       beanMap.put(beanId, beanObj);

                   }
               }
           }

       }
   }

   /**
    *     转换运行时参数类型(做简单演示,目前只有String Integer和int可扩展)
    */
   private Object ParamType(String fieldTypeName, String value) {
       Object obj = null;
       //判断该成员属性是否为int或Integer类型
       if ("int".equals(fieldTypeName) || "java.lang.Integer".equals(fieldTypeName)) {
           //转换为int类型并为该成员属性赋值
           int intFieldValue = Integer.parseInt(value);
           obj = intFieldValue;

       }//判断该成员属性是否为String类型
       if ("java.lang.String".equals(fieldTypeName)) {
           //为该成员属性赋值
           obj = value;
       }//判断另外类型同理
       //if(){ }
       return obj;
   }

   /**
    * 获取bean
    */
   public Object getBean(String beanName) {
       Object bean = beanMap.get(beanName);
       if (bean == null) {
           throw new IllegalArgumentException("无法实例化该名称的bean,请确定名称是否正确 " + beanName);
       }

       return bean;
   }

}



测试使用bean代码:

public class User {
    private String name;
    private Integer age;
    private String sex;
    //省略get、set方法
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}

public class Home {
    private String country;
    private String province;
    private String homeCity;
    private String workCity;
    
    //省略get、set方法
   
    @Override
    public String toString() {
        return "Home{" +
                "country='" + country + '\'' +
                ", province='" + province + '\'' +
                ", homeCity='" + homeCity + '\'' +
                ", workCity='" + workCity + '\'' +
                '}';
    }
}

bean配置文件MySpring.xml:

<beans>
    <bean id="user" class="com.xiaoli.model.User">
        <property name="name" value="小立"></property>  <!--姓名-->
        <property name="age" value="22"></property>         <!--年龄-->
        <property name="sex" value="男"></property>          <!--性别-->
    </bean>

    <bean id="home" class="com.xiaoli.model.Home">
        <property name="country" value="中国"></property>     <!--国家-->
        <property name="province" value="湖北"></property>     <!--省份-->
        <property name="homeCity" value="宜昌"></property>      <!--家乡城市-->
        <property name="workCity" value="杭州"></property>        <!--工作城市-->
    </bean>
</beans>

演示类:

package com.example.demo.ioctest;

/**
 * @author y001
 */
public class DemoTest {
    public static void main(String[] args) throws  Exception{
        MySingleIOC ioc=new MySingleIOC("MySpring.xml");
        User user = (User)ioc.getBean("user");
        System.out.println(user);
        Home home = (Home)ioc.getBean("home");
        System.out.println(home);

    }
}

IOC使用注解区别简介

注解含义
@Component最普通的组件,可以被注入到spring容器进行管理
@Repository作用于持久层
@Service作用于业务逻辑层
@Controller作用于表现层

@Component, @Service, @Controller, @Repository是spring注解,注解后可以被spring框架所扫描并注入到spring容器来进行管理
@Component是通用注解,其他三个注解是这个注解的拓展,并且具有了特定的功能
@Repository注解在持久层中,具有将数据库操作抛出的原生异常翻译转化为spring的持久层异常的功能。
@Controller层是spring-mvc的注解,具有将请求进行转发,重定向的功能。
@Service层是业务逻辑层注解,这个注解只是标注该类处于业务逻辑层。
用这些注解对应用进行分层之后,就能将请求处理,义务逻辑处理,数据库操作处理分离出来,为代码解耦,也方便了以后项目的维护和开发。

注解对比@Resource@Autowire
注解来源JDKSpring
装配方式优先按名称优先按类型
属性name,typerequired
  • @Resource和@Autowired都可以用来装配bean,都可以用于字段或setter方法。
  • @Autowired默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false。
  • @Resource默认按名称装配,当找不到与名称匹配的bean时才按照类型进行装配。名称可以通过name属性指定,如果没有指定name属性,当注解写在字段上时,默认取字段名,当注解写在setter方法上时,默认取属性名进行装配。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值