Spring之IOC实现原理

前段时间写了篇关于Spring的AOP框架实现原理的文章,现在浅谈下Spring的另一大核心概念IOC的原理。

IOC:即控制反转。官方解释是让调用类对某一接口实现类的依赖关系由第三方注入,以转移调用类对某一接口实现类的依赖。

下面将举个现实例子加深理解:

“剧本”要选定“角色”的“饰演者”,我们可以创建第三方“导演“来控制”剧本”,引入“导演”,“剧本”和“饰演者”将完全接偶。”导演“将拥有对”饰演者“的控制权,反转权则是指由”剧本“转移到了导演身上。

我们重新来看IOC的定义:

IOC:即控制反转。官方解释是让调用类(”剧本“)对某一接口实现类(”饰演者“)的依赖关系由第三方(”导演“)注入,以转移调用类(”剧本“)对某一接口实现类(”饰演者“)的依赖。

从注入方法上看,划分为三种类型

1.构造方法注入

2.属性注入

3.接口注入

下面我们通过(剧本-饰演者问题)来看注入的具体方式

构造方法注入:通过调用类的构造方法,将接口实现类通过构造方法变量传入

调用类(剧本)

Public class JuBen {
  private JueSe Js;
  //1:注入角色的具体扮演者
  public JuBen(JueSe Js) {
     this.Js=Js;
  }
    public void DuiBai() {
       Js.declare(“我想做一个好人!”);
    }

第三方类(导演)

Public class Director {
  public void direct() {
     //2.指定角色的扮演者
     JueSe  Js = new LiuDeHua();
     //3.注入具体扮演者到剧本中
     JuBen Jb = new JuBen(Js);
     Jb.DuiBai();
   }
}

属性注入:通过Setter方法完成调用类所需依赖的注入,更加灵活方便

调用类(剧本)

Public class JuBen {
  private JueSe Js;
  //1.属性注入方法
  public void setJueSe(JueSe Js) {
     this.Js=Js;
  }
  public void DuiBai(){
     Js.declare(“我想做一个好人!”);
    }
}

第三方类(导演)

Public class Director {
  public void direct() {
     JueSe Js=new LiuDeHua();
     JuBen Jb=new JuBen();
     //2.调用属性Setter方法注入
     Jb.setJueSe(Js);
     Jb.DuiBai();
}

接口注入:将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法。

1.调用类所依赖的注入方法提取到接口中

Public interface ActorArrangable {
  void injectJs(JueSe Js);
}

2.调用类实现接口

Public class JuBen implements ActorArrangable {
  private JueSe Js;
  //1.实现接口方法
  public void injectJs(JueSe Js)
    {this.Js = Js; }
  public void DuiBai() {
     Js.declare(“我想做一个好人!”)
    }
}

Spring作为一个容器,通过配置文件或者注解描述类和类之间的依赖关系,自动完成类的初始化和依赖注入的工作,下面是对以上实例进行配置的配置文件片段:

   //1.实现类实例化
   <bean id=“Js" class=“LiuDeHua" />
   //2.通过ljm-ref建立依赖关系
   <bean id=“Juben"   class=“JuBen"  p:ljm-ref=“Js”/>
</beans>


前面写过关于Java反射机制的文章,那么Java反射与IOC的关系是…?

在Spring中,通过IOC可以将实现类、参数信息等配置在其对应的配置文件中,那么当需要更改实现类或参数信息时,只需要修改配置文件即可, 我们还可以对某对象所需要的其它对象进行注入,这种注入都是在配置文件中做的。

Spring的IOC的实现原理利用的就是Java的反射机制,Spring的工厂类会帮我们完成配置文件的读取、利用反射机制注入对象等工作,我们可以通过bean的名称获取对应的对象。

下面我简单实现IOC如何利用反射从配置文件中读取信息

1.需要从配置文件(xml)中读取配置信息

package com.reflect;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

public class BeanFactory {

    private Map<String, Object> beanMap = new HashMap<String, Object>();

    /**
     * bean工厂的初始化.
     *
     * @param xml xml配置文件
     */
    public void init(String xml) {
        try {
            //1.创建读取配置文件的reader对象
            SAXReader reader = new SAXReader();

            //2.获取当前线程中的类装载器对象
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

            //3.从class目录下获取指定的xml文件
            InputStream ins = classLoader.getResourceAsStream(xml);
            Document doc = reader.read(ins);
            Element root = doc.getRootElement();
            Element foo;

            //4.遍历xml文件当中的Bean实例
            for (Iterator i = root.elementIterator("bean"); i.hasNext();) {
                foo = (Element) i.next();

                //5.针对每个一个Bean实例,获取bean的属性id和class
                Attribute id = foo.attribute("id");
                Attribute cls = foo.attribute("class");

                //6.利用Java反射机制,通过class的名称获取Class对象
                Class bean = Class.forName(cls.getText());
                //7.获取对应class的信息
                java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
                //8.获取其属性描述
                java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();

                //9.创建一个对象,并在接下来的代码中为对象的属性赋值
                Object obj = bean.newInstance();

                //10.遍历该bean的property属性
                for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {
                    Element foo2 = (Element) ite.next();

                    //11.获取该property的name属性
                    Attribute name = foo2.attribute("name");
                    String value = null;

                    //12.获取该property的子元素value的值
                    for (Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();)
                    {
                        Element node = (Element) ite1.next();
                        value = node.getText();
                        break;
                    }

                    //13.利用Java的反射机制调用对象的某个set方法,并将值设置进去
                    for (int k = 0; k < pd.length; k++) {
                        if (pd[k].getName().equalsIgnoreCase(name.getText()))
                        {
                            Method mSet = null;
                            mSet = pd[k].getWriteMethod();
                            mSet.invoke(obj, value);
                        }
                    }
                }

                //14.将对象放入beanMap中,其中key为id值,value为对象
                beanMap.put(id.getText(), obj);
            }
        } catch (Exception e) {
            System.out.println(e.toString());
        }
    }

    /**
     * 通过bean的id获取bean的对象.
     *
     * @param beanName
     *            bean的id
     * @return 返回对应对象
     */
    public Object getBean(String beanName) {
        Object obj = beanMap.get(beanName);
        return obj;
    }

    /**
     * 测试方法.
     *
     * @param args
     */
    public static void main(String[] args) {
        BeanFactory factory = new BeanFactory();
        factory.init("conf/config.xml");
        JavaBean javaBean = (JavaBean) factory.getBean("javaBean");
        System.out.println("userName=" + javaBean.getUserName());
        System.out.println("password=" + javaBean.getPassword());
    }
}

2.配置文件

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

<beans>
    <bean id="javaBean" class="com.jike.spring.chapter03.reflect.JavaBean">
       <property name="userName">
           <value>Jonney</value>
       </property>
       <property name="password">
           <value>1234567890</value>
       </property>
    </bean>
</beans>

3.对应的JavaBean

package com.reflect;

public class JavaBean {

     private String userName;
     private String password;

  public String getPassword() {
            return password;
     }

     public String getUserName() {
            return userName;
     }

     public void setUserName(String userName) {
            this.userName = userName;
     }

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


资源访问工具类

JDK所提供的访问资源的类并不能很好的满足各种底层资源的访问需求,因此,Spring设计了一个Resource接口,它为应用提供了更强大的访问底层资源的能力

主要方法:

1. boolean exists()

2. boolean isOpen()

3. URL getURL()

4. File getFile()

5. InputStream getInputStream()

具体实现类:

1. ByteArrayResource

2. ClassPathResource

3. FileSystemResource

4. InputStreamResource

5. ServletContextResource

6. UrlResource

为了访问不同类型的资源,必须使用相应的Resource实现类,这是比较麻烦的,Spring提供了一个强大的加载资源的机制,能够自动识别不同的资源类型:

资源类型地址前缀:

1. classpath classpath:com/xxx/bean.xml

2. File file:/com/xxx/bean.xml

3. http:// http://www.xxx.com/bean.xml

4. ftp ftp://www.xxx.com/bean.xml

5. 无前缀 com/jike/bean.xml

Ant风格的匹配符:

1. ?:匹配文件名中的一个字符

2. *:匹配文件名中的任意字符

3. **:匹配多层路径

Ant风格的资源路径示例:

1. Classpath:com/t*st.xml

2. File:D:/conf/*.xml

3. Classpath:com/**/test.xml

4. Classpath:org/springframework/*/.xml

Spring定义了一套资源加载的接口,并提供了实现类,如下:



BeanFactory和ApplicationContext

BeanFactory是Spring框架最核心的接口,它提供了高级IoC的配置机制。ApplicationContext建立在BeanFactory基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用一般成BeanFactory为IoC容器,而称ApplicationContext为应用上下文

BeanFactory

BeanFactory是一个类工厂,可以创建并管理各种类的对象,Spring称这些创建和管理的Java对象为Bean。在Spring中,Java对象的范围更加宽泛。接下来我们对BeanFactory的类体系结构以及装载初始化顺序进行说明:

类体系结构:

1. XmlBeanFactory

2. ListableBeanFactory

3. HierarhicalBeanFactory

4. ConfigurableBeanFactory

5. AutowireCapableBeanFactory

6. SingletonBeanFactory

7. BeanDefinitionRegistry

初始化顺序:

1. 创建配置文件

2. 装载配置文件

3. 启动IoC容器

4. 获取Bean实例

ApplicationContext

ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要以编程的方式方式实现,而在ApplicationContext中则可以通过配置的方式实现。

具体实现类:

1.ClassPathXmlApplicationContext

2.FileSystemXmlApplicationContext

3.ConfigurableApplicationContext

扩展接口:

1.ApplicationEventPublisher

2.MessageSource

3.ReaourcePatternResolver

4.LifeCycle

和BeanFactory初始化相似,ApplicationContext的初始化也很简单,根据配置文件路径不同可以选择不同的实现类加载:

ClassPathXmlApplicationContext

FileSystemXmlApplicationContext

Bean的实例化问题

使用BeanFactory从xml配置文件加载bean:

import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.FileSystemResource;   

public class XmlConfigWithBeanFactory {
  public static void main(String[] args) {
        XmlBeanFactory factory = new XmlBeanFactory(newFileSystemResource(                                                                                  "build/beans.xml"));
     } }

使用ApplicationConText从xml配置文件加载bean:

public class XmlConfigWithApplication{
 public static void main(String[] args){
  ApplicationContext application = new   ClassPathXmlApplicationContext(beans.xml"));             application.getBean("BeanName");
  } }

BeanFactory在初始化容器时并没有实例化Bean,直到第一次访问Bean时才实例化具体的Bean。

ApplicationContext则在初始化上下文时则实例单实例的Bean


时间: 2024-10-05 13:55:33

Spring之IOC实现原理的相关文章

简单解析Spring核心IOC容器原理

将大体流程解析了一边,具体可以看源代码一个方法一个方法的跟下 XmlBeanFactory的功能是建立在DefaultListableBeanFactory这个基本容器的基础上的,并在这个基本容器的基础上实行了其他诸如XML读取的附加功能. DefaultListableBeanFactory是BeanFactory的一个默认实现类. public class XmlBeanFactory extends DefaultListableBeanFactory { private final Xm

Spring IoC底层原理

-------------------siwuxie095 Spring IoC 底层原理 1.IoC 即 Inversion of Control,控制反转,把对象的创建 交给 Spring 进行管理 2.IoC 容器管理 Bean 的方式: (1)基于配置文件的方式 (2)基于注解的方式 3.IoC 底层原理所使用的技术: (1)XML 配置文件 (2)dom4j 解析 XML (3)工厂模式 (5)反射 4.原始方案,耦合度太高 public class UserService{ publ

spring ioc aop 原理

spring ioc aop 原理 spring ioc aop 的原理 spring的IoC容器是spring的核心,spring AOP是spring框架的重要组成部分. 在传统的程序设计中,当调用者需要被调用者的协助时,通常由调用者来创建被调用者的实例.但在spring里创建被调用者的工作不再由调用者来完成,因此控制反转(IoC):创建被调用者实例的工作通常由spring容器来完成,然后注入调用者,因此也被称为依赖注入(DI),依赖注入和控制反转是同一个概念. 面向方面编程(AOP)是以另

Spring?IOC设计原理解析:本文乃学习整理参考而来

Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. IoC容器的初始化 1. XmlBeanFactory(屌丝IOC)的整个流程 2. FileSystemXmlApplicationContext 的IOC容器流程 1.高富帅IOC解剖 2. 设置资源加载器和资源定位 3.AbstractApplicationContext的refresh函数载入

java框架篇---spring IOC 实现原理

IOC(DI):其实这个Spring架构核心的概念没有这么复杂,更不像有些书上描述的那样晦涩.java程序员都知道:java程序中的每个业务逻辑至少需要两个或以上的对象来协作完成,通常,每个对象在使用他的合作对象时,自己均要使用像new object() 这样的语法来完成合作对象的申请工作.你会发现:对象间的耦合度高了.而IOC的思想是:Spring容器来实现这些相互依赖对象的创建.协调工作.对象只需要关系业务逻辑本身就可以了.从这方面来说,对象如何得到他的协作对象的责任被反转了(IOC.DI)

Spring中ioc的实现原理

学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家分享网上的一些技术大牛们对Spring框架的IOC的理解以及谈谈我对Spring Ioc的理解. 一.分享Iteye的开涛对Ioc的精彩讲解 首先要分享的是Iteye的开涛这位技术牛人对Spring框架的IOC的理解,写得非常通俗易懂,以下内容全部来自原文,原文地址:http://jinniansh

Spring之IOC容器注入

上一篇做了IOC和AOP的原理介绍,这一篇主要讲解IOC的注入.不过我依然困惑一个问题: 一 : 依赖注入(DI)中有三种注入方式,那Spring中到底实现了几种方式?也是三种? IOC在很多框架中都有实现,并不是Spring特有的,之前说过IOC主要包含DL(Dependency Lookup)和DI(Dependency Injection),也就是说实现IOC的技术有很多,但是主要包含DI和DL,但是相对而言,DI应用范围比较广泛,我想这也是为什么Martin Fowler将控制反转用依赖

一点一点学架构(三)——Spring.NET IOC

IOC背景介绍 传统的实现: 由程序内部代码来控制类与类之间的关系(如:在一个具体的类中,调用另一个类的方法). 使用new关键字来实现两个类之间关系的组合. 这种实现方式会造成类之间耦合. IOC的实现: 它将类间关系从程序内部提到外部容器,也就是说由容器在运行期将类间的某种依赖关系动态注入类中. 对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A. IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的

深入理解Spring的IOC容器

IOC概述 IOC是Spring容器的内核,AOP.声明式事务等功能都依赖于此功能,它涉及代码解耦.设计模式.代码优化等问题的考量,我们将通过以下三个方面来深入理解IOC: IoC的初步理解 IoC的注入类型 构造器注入:通过调用类的构造函数,将接口实现的类通过构造函数变量传入. 属性注入:通过setter方法完成调用类所需依赖的注入,更加灵活方便. 接口注入:将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口提供相应的注入方法. IoC的注入方式 Spring最为一个容器,通过配