当前位置: 首页 > 编程笔记 >

Spring的Ioc模拟实现详细介绍

澹台承
2023-03-14
本文向大家介绍Spring的Ioc模拟实现详细介绍,包括了Spring的Ioc模拟实现详细介绍的使用技巧和注意事项,需要的朋友参考一下

简单来说就是当自己需要一个对象的时候不需要自己手动去new一个,而是由其他容器来帮你提供;Spring里面就是IOC容器。

例如:

在Spring里面经常需要在Service这个装配一个Dao,一般是使用@Autowired注解:类似如下

public Class ServiceImpl{
  @Autowired
   Dao dao;
   
  public void getData(){
    dao.getData();
  }

在这里未初始化Dao直接使用是会报出空指针异常的,那么在Spring里面的做法就是通过反射来将需要的类帮你加载进来。

关于IOC:我们讲个故事吧!

有一个厨师,他在做一道菜的时候需要某种调味料(bean),可是他正好没有那瓶调味料(bean),这个时候他就必须去制作一瓶调味料(bean)出来。(这就像我们平时需要对象的时候一样:UserDao  userdao=new UserDaoImpl)这个时候厨师的工作就不得不跟制作调味料联系起来,这就是所谓的“耦合”。(耦合程度高通说来说就是,我没了你我活不了了。);

这个时候IOC里的控制反转出来了,它提出,开辟一个仓库(容器),里面放着各种各样的调味料(bean),当你需要某种调味料(bean)的时候只要给出调味料的名字(beanName)就可以直接去拿取,代码类似这样:(UserDao user=仓库.get("调味料名字"));这样你就可以直接拿到了调味料 (bean)而不用等到你没有的时候再去制作一瓶出来。这就是所谓的将控制权转移出来,它将生产调味料(bean)的工作直接让仓库(容器)来制作(生产);

接下来依赖注入出来了,它就更加地强大的了,它提出厨师只要有一个瓶子(private UserDao userdao),并且将瓶子盖打开(方法:setUserDao( UserDao userdao){this.userdao=userdao}即提供对于的set方法 )再给瓶子贴上一个标签注明要放什么材料(在xml文件中配置一下<property name="userdao",ref="xxbean"),那么这个时候你什么都不用做了,仓库(容器)会自动派人帮你将你所需的调味料(bean)放入到瓶子中 ;当你想换另一种调味料的时候只要将瓶子上的标签改成其他比如胡椒粉(这时要在xml文件中更改一下ref的bean就行了),依赖注入提高了代码的灵活性,你需要替换类的实现的时候,不需要去修改只要在xml配置文件里修改一下就行了;

总结一下我的总结:IOC的主要目的就是实现解耦!解耦!解耦!重要的事情说三遍.由厨师的列子来说就是.我是一个厨师我只做菜,我不做调味料~~我不是一个做调味料的,我不跟制作调味料有那么大的关系.   IOC的出现,厨师只要安心做好自己的事情(做菜),需要调味料直接去仓库拿取就行了!那么这样厨师跟调味料的制作之间的关系就分离开来了,这就是解耦!解耦!解耦!使两个东西的关系没那么紧密.

模拟IOC的实现,主要使用的技术是java的反射机制(模拟使用的是架构分为dao层,service层,controller层):

注:欢迎各位大佬的指点,小弟也在学习,说得不好多多包涵~

一.编写dao类,用于测试:

public interface IocDao {
  public void sayhello(); //一个用来测试的接口
}

二.编写dao的实现类:

public class IocDaoImpl implements IocDao {
  @Override
  public void sayhello() {
    // TODO Auto-generated method stub
    System.out.println("hello word");//实现我们的dao接口里的方法
  }
}

三.编写service类:

public interface IocDaoService {
  public void sayhello();//业务类接口,这里就跟dao一样
}

四.编写service的实现类:

public class IocDaoServiceImpl implements IocDaoService {
  private IocDao iocDao;  //创建一个接口.
  public IocDao getIocDao() {
    return iocDao;
  }
  //编写IocDao接口对应的set方法用于依赖注入
  //依赖注入的方式有三种:接口注入,构造方法注入,set注入;
  //此处为set注入
  public void setIocDao(IocDao iocDao) {
    this.iocDao = iocDao;
  }
  @Override
  public void sayhello() {
    // TODO Auto-generated method stub
    iocDao.sayhello();//调用接口方法
  }
}

五.编写bean.xml配置文件.(这里的i你可以看成是IocDaoImpl类,iocService看成IocDaoServiceImpl,iocDao这是IocDaoServiceImpl里的一个属性,这个属性传入的参数值为“i”);

<beans>
 <bean id="i" class="com.hck.dao.impl.IocDaoImpl"/> 
 <bean id="iocService" class="com.hck.service.impl.IocDaoServiceImpl">
   <property name="iocDao" ref="i"></property>
 </bean> 
</beans>

六.编写工厂接口.

//模拟ClassPathXmlApplicationContext实现的一个接口BeanFactory
public interface BeanFactory {
  public Object getBean(String beanName);
}

七.编写ClassPathXmlApplicationContext去读取配置文件,并且根据bean.xml里的配置对象去生成各种bean,完成其中的注入工作.(最重要的部分Ioc的实现原理),一字一句都有注释

//模拟ClassPathXmlApplicationContext去读取配置文件
public class ClassPathXmlApplicationContext implements BeanFactory {
  //定义map集合来存放bean.xml里的bean的id跟其对应的实例化对象
  //<bean id="i" class="com.hck.dao.impl.IocDaoImpl"/> 
  //那么类似的存放bean.put("i",new IocDaoImpl());这样子.
  Map<String, Object> beans=new HashMap<String,Object>();
  public ClassPathXmlApplicationContext(String xmlPath){
      try {
        //创建SAXBuilder对象解析文档
        SAXBuilder saxBuilder = new SAXBuilder();
        //解析build里的参数是一个文件路径.
        Document document = saxBuilder.build(xmlPath);
        //document.getRootElement().getChildren("bean")获取所有<bean>标签内容
        List elements = document.getRootElement().getChildren("bean");
        //遍历<bean>对象
        for (int i = 0; i < elements.size(); i++) {
          //获取第一个<bean>标签elements.get(0);
          Element element = (Element) elements.get(i);
          //获取<bean>标签里的<id>属性,
          //<bean id="i" class="com.hck.dao.impl.IocDaoImpl"/> 
          //即String beanName="i";
          String beanName = element.getAttributeValue("id");
          //同上String clazz="com.hck.dao.impl.IocDaoImpl";
          String clazz = element.getAttributeValue("class");
          //加载类对象并且实例化.Object object=new IocDaoImpl();
          Object object = Class.forName(clazz).newInstance();//object是IocDaoServiceImpl
          //将他们添加在map集合里,后面可以根据beanName直接获取到实例化对象.
          beans.put(beanName, object);
          //遍历<bean>标签下的<property>字标签.
          //第一个标签没有字标签所以直接跳过.已第二个为例子
          //<bean id="iocService" class="com.hck.service.impl.IocDaoServiceImpl">
          //<property name="iocDao" ref="i"></property></bean>
          List elements2 = element.getChildren("property");
          for (int j = 0; j < elements2.size(); j++) {
            //此处我们将获得<property name="iocDao" ref="i"></property></bean>
            Element element2 = (Element) elements2.get(j);
            //相当于String propertyName="iocDao";
            String propertyName = element2.getAttributeValue("name");
            //相当于String refBean="i";
            String refBean = element2.getAttributeValue("ref");
            //相当于String propertyName="IocDao";
            //目的是为了得到一个方法的名字setIocDao,用于反射调用
            propertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
            //这里的methodName="setIocDao";
            String methodName = "set" + propertyName;
            //获取Map集合里Key="i"的值;i对应的是IocDaoImpl的实例化对象
            //相当于 Object object2 =IocDaoImpl;
            Object object2 = beans.get(refBean);
            //获取IocDaoServiceImpl方法里的setIocDao方法.
            //第一个方法是方法名,第二个参数是方法的参数类型.
            Method method = object.getClass().getDeclaredMethod(methodName,
                object2.getClass().getInterfaces());
            //调用方法,并传入参数,完成依赖注入.
            method.invoke(object, object2);
          }
        }
        //      String beanName=document.getElementById(id).attributes().get("class");
        //      Object object=Class.forName(beanName).newInstance();
        //      return object;
      } catch (Exception e) {
        e.printStackTrace();
        // TODO: handle exception
      }
  }
/* (non-Javadoc)
 * @see com.hck.ioc.BeanFactory#getBean()
 */
@Override
public Object getBean(String beanName) {
  // TODO Auto-generated method stub
  return beans.get(beanName);
}  
}

八.编写测试类

public class Ioc {
  public static void main(String[] args) {
  ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("src/bean.xml");
  IocDaoService ids=(IocDaoService)applicationContext.getBean("iocService");
  ids.sayhello();
}
}

九.显示结果:

hello word

总结

以上就是本文关于Spring的Ioc模拟实现详细介绍的全部内容,希望对大家有所帮助。欢迎参阅:spring的IoC和DI详解、spring配置扫描多个包问题解析、SpringJDBC批量处理数据代码示例等,有什么问题可以随时留言,欢迎大家交流讨论。

 类似资料:
  • 本文向大家介绍Android 模拟器的使用详细介绍,包括了Android 模拟器的使用详细介绍的使用技巧和注意事项,需要的朋友参考一下 让我们一起学习一下模拟器的使用。 本文内容如下: 模拟器和真机的比较 创建Android模拟器(emulator) 运行Android模拟器 设置简体中文语言界面 模拟手机拨打电话和发送短信的功能 源代码:本讲没有代码 1.模拟器和真机的比较 android模拟器

  • 本文向大家介绍Go 实现热重启的详细介绍,包括了Go 实现热重启的详细介绍的使用技巧和注意事项,需要的朋友参考一下 最近在优化公司框架 trpc 时发现了一个热重启相关的问题,优化之余也总结沉淀下,对 go 如何实现热重启这方面的内容做一个简单的梳理。 1.什么是热重启? 热重启(Hot Restart),是一项保证服务可用性的手段。它允许服务重启期间,不中断已经建立的连接,老服务进程不再接受新连

  • 本文向大家介绍php  单例模式详细介绍及实现源码,包括了php  单例模式详细介绍及实现源码的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了PHP中数据库单例模式的实现代码分享,本文先是讲解了单例模式的一些知识,然后给出了数据库单例模式实现代码。 什么是单例模式 单例模式顾名思义,就是只有一个实例。 作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提

  • 本文向大家介绍Python 模块EasyGui详细介绍,包括了Python 模块EasyGui详细介绍的使用技巧和注意事项,需要的朋友参考一下 Python 模块EasyGui详细介绍 前言: 在Windows想用Python开发一些简单的界面,所以找到了很容易上手的EasyGui库。下面就分享一下简单的使用吧。 参考的链接:官网Tutorial 接下来,我将从简单,到复杂一点点的演示如何使用这个

  • 本文向大家介绍PHP单例模式详细介绍,包括了PHP单例模式详细介绍的使用技巧和注意事项,需要的朋友参考一下 单例模式的概念 单例模式是指整个应用中某个类只有一个对象实例的设计模式。具体来说,作为对象的创建方式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局的提供这个实例。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。 单例模式的特点 单例模式的主要特点是“三私一公

  • 本文向大家介绍C语言 栈的表示和实现详细介绍,包括了C语言 栈的表示和实现详细介绍的使用技巧和注意事项,需要的朋友参考一下 C语言 栈的表示和实现详细介绍 定义:栈是限定仅在表尾进行插入和删除操作的线性表。 栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第

  • 1. 前言 通过几个章节的学习,大家对于 Spring 已经有了初步的认知,我们通过案例练习,或者源码追踪,可以粗略的看到 Spring 框架初始化 bean 对象的过程,那么这个章节,我们模拟 Spring 框架的思路,来写一个类似 Spring 加载对象的案例,加深大家的印象。 2. 案例实现思路 2.1 步骤介绍 思路分析: 我们通过写过的案例可以知道: Spring 框架的容器 是一个接口

  • 本文向大家介绍Jenkins 关闭和重启详细介绍及实现,包括了Jenkins 关闭和重启详细介绍及实现的使用技巧和注意事项,需要的朋友参考一下 Jenkins 关闭和重启 我们用jar -jar jenkins.war来启动jenkins服务器,那么我们如何关闭或者重启jenkins服务器呢?经过搜索找到了相应的方法. 关闭jenkins服务 只需要在访问jenkins服务器的网址url地址后加上