spring(2)--------Ioc入门

IoC:Inverse of Control(控制反转)

  • 读作 “反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
  • 正控:若要使用某个对象,需要自己去负责对象的创建
  • 反控:若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架

简单理解:我吃了一个苹果。

我种下一棵苹果树,来年干了各种工作,几年后,苹果树长大了,我自己去苹果树上摘下一个苹果,吃到肚子里了。

Ioc方式:我去外面的超市,买了一箱苹果,吃了一个苹果,吃到肚子里去了。我没有去种苹果树,也没有干农活,但是我吃了一个苹果,达到了我吃了一个苹果的目的了,这里的超市就相当于Sprig。

 

Spring IoC 阐述

这就是一种控制反转的理念,上述的例子已经很好的说明了问题,我们再来描述一下控制反转的概念:控制反转是一种通过描述(在 Java 中可以是 XML 或者注解)并通过第三方(Spring)去产生或获取特定对象的方式。

  • 好处:
    降低对象之间的耦合
    我们不需要理解一个类的具体实现,只需要知道它有什么用就好了(直接向 IoC 容器拿)

主动创建的模式中,责任归于开发者,而在被动的模式下,责任归于 IoC 容器,基于这样的被动形式,我们就说对象被控制反转了。(也可以说是反转了控制)

 

Spring IoC 容器

Spring 会提供 IoC 容器来管理和容纳我们所开发的各种各样的 Bean,并且我们可以从中获取各种发布在 Spring IoC 容器里的 Bean,并且通过描述可以得到它。

Spring IoC 容器的设计

Spring IoC 容器的设计主要是基于以下两个接口:

  • BeanFactory
  • ApplicationContext

其中 ApplicationContext 是 BeanFactory 的子接口之一,换句话说:BeanFactory 是 Spring IoC 容器所定义的最底层接口,而 ApplicationContext 是其最高级接口之一,并对 BeanFactory 功能做了许多的扩展,所以在绝大部分的工作场景下,都会使用 ApplicationContext 作为 Spring IoC 容器。

 

org.springframework.beans-3.1.0.M2.jar包内的BeanFactory 官方的源码
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;

public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";

    Object getBean(String var1) throws BeansException;

    <T> T getBean(String var1, Class<T> var2) throws BeansException;

    <T> T getBean(Class<T> var1) throws BeansException;

    Object getBean(String var1, Object... var2) throws BeansException;

    boolean containsBean(String var1);

    boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;

    boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;

    boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;

    Class<?> getType(String var1) throws NoSuchBeanDefinitionException;

    String[] getAliases(String var1);
}

【getBean】 对应了多个方法来获取配置给 Spring IoC 容器的 Bean。
① 按照类型拿 bean:
bean = (Bean) factory.getBean(Bean.class);
注意: 要求在 Spring 中只配置了一个这种类型的实例,否则报错。(如果有多个那 Spring 就懵了,不知道该获取哪一个)
② 按照 bean 的名字拿 bean:
bean = (Bean) factory.getBean("beanName");
注意: 这种方法不太安全,IDE 不会检查其安全性(关联性)
③ 按照名字和类型拿 bean:(推荐)
bean = (Bean) factory.getBean("beanName", Bean.class);

  • 【isSingleton】 用于判断是否单例,如果判断为真,其意思是该 Bean 在容器中是作为一个唯一单例存在的。而【isPrototype】则相反,如果判断为真,意思是当你从容器中获取 Bean,容器就为你生成一个新的实例。
    注意: 在默认情况下,【isSingleton】为 ture,而【isPrototype】为 false
  • 关于 type 的匹配,这是一个按 Java 类型匹配的方式
  • 【getAliases】方法是获取别名的方法

这就是 Spring IoC 最底层的设计,所有关于 Spring IoC 的容器将会遵守它所定义的方法。

ApplicationContext

根据 ApplicationContext 的类继承关系图,可以看到 ApplicationContext 接口扩展了许许多多的接口,因此它的功能十分强大,所以在实际应用中常常会使用到的是 ApplicationContext 接口,因为 BeanFactory 的方法和功能较少,而 ApplicationContext 的方法和功能较多。

1.新建Source类

package pojo;

/**新建Source类
 * @author lenovo
 */
public class Source {
    private String fruit;   // 类型
    private String sugar;   // 糖分描述
    private String size;    // 大小杯
    /* setter and getter */

    public String getFruit() {
        return fruit;
    }

    public void setFruit(String fruit) {
        this.fruit = fruit;
    }

    public String getSugar() {
        return sugar;
    }

    public void setSugar(String sugar) {
        this.sugar = sugar;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }
}

2.src下创建bean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 通过 xml 方式装配 bean -->
    <bean name="source" class="pojo.Source">
        <property name="fruit" value="橙子"/>
        <property name="sugar" value="多糖"/>
        <property name="size" value="超大杯"/>
    </bean>
</beans>

3.创建测试类:

package test;


import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
        import pojo.Source;

/**
 * 定义了一个 bean ,这样 Spring IoC 容器在初始化的时候就能找到它们,然后使用
 * ClassPathXmlApplicationContext 容器就可以将其初始化
 * @author lenovo
 */
public class SpringTest {
    @Test
    public void test(){

        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Source source = (Source) context.getBean("source", Source.class);

        System.out.println(source.getFruit());
        System.out.println(source.getSugar());
        System.out.println(source.getSize());
    }
}

4.运行测试类

结果:

橙子
多糖
超大杯

这样就会使用 Application 的实现类 ClassPathXmlApplicationContext 去初始化 Spring IoC 容器,然后开发者就可以通过 IoC 容器来获取资源,我们就是使用 Application 的实现类 ClassPathXmlApplicationContext 去初始化 Spring IoC 容器的。

ApplicationContext 常见实现类:

1.ClassPathXmlApplicationContext:
读取classpath中的资源
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

2:FileSystemXmlApplicationContext:读取指定路径的资源
ApplicationContext applicationcontext = new FileSystemXmlApplicationContext("c:/applicationContext.xml");

3.XmlWebApplicationContext:需要在Web的环境下才可以运行
XmlWebApplicationContext cationcontext = new XmlWebApplicationContext(); // 这时并没有初始化容器
cationcontext.setServletContext(servletContext); // 需要指定ServletContext对象
cationcontext.setConfigLocation("/WEB-INF/applicationContext.xml"); // 指定配置文件路径,开头的斜线表示Web应用的根目录
cationcontext.refresh(); // 初始化容器

BeanFactory 和 ApplicationContext 的区别:

  • BeanFactory: 是Spring中最底层的接口,只提供了最简单的IoC功能,负责配置,创建和管理bean。
    在应用中,一般不使用 BeanFactory,而推荐使用ApplicationContext(应用上下文),原因如下。
  • ApplicationContext:
    1.继承了 BeanFactory,拥有了基本的 IoC 功能;
    2.除此之外,ApplicationContext 还提供了以下功能:
    ① 支持国际化;
    ② 支持消息机制;
    ③ 支持统一的资源加载;
    ④ 支持AOP功能;

Spring IoC 的容器的初始化和依赖注入

虽然 Spring IoC 容器的生成十分的复杂,但是大体了解一下 Spring IoC 初始化的过程还是必要的。这对于理解 Spring 的一系列行为是很有帮助的。

注意: Bean 的定义和初始化在 Spring IoC 容器是两大步骤,它是先定义,然后初始化和依赖注入的。

  • Bean 的定义分为 3 步:
    1.Resource 定位
    Spring IoC 容器先根据开发者的配置,进行资源的定位,在 Spring 的开发中,通过 XML 或者注解都是十分常见的方式,定位的内容是由开发者提供的。
    2.BeanDefinition 的载入
    这个时候只是将 Resource 定位到的信息,保存到 Bean 定义(BeanDefinition)中,此时并不会创建 Bean 的实例
    3.BeanDefinition 的注册
    这个过程就是将 BeanDefinition 的信息发布到 Spring IoC 容器中
    注意:此时仍然没有对应的 Bean 的实例。

做完了以上 3 步,Bean 就在 Spring IoC 容器中被定义了,而没有被初始化,更没有完成依赖注入,也就是没有注入其配置的资源给 Bean,那么它还不能完全使用。

对于初始化和依赖注入,Spring Bean 还有一个配置选项——【lazy-init】,其含义就是是否初始化 Spring Bean。在没有任何配置的情况下,它的默认值为 default,实际值为 false,也就是 Spring IoC 默认会自动初始化 Bean。如果将其设置为 true,那么只有当我们使用 Spring IoC 容器的 getBean 方法获取它时,它才会进行 Bean 的初始化,完成依赖注入。

 

IoC 是如何实现的

最后我们简单说说IoC是如何实现的。想象一下如果我们自己来实现这个依赖注入的功能,我们怎么来做? 无外乎:

  1. 读取标注或者配置文件,看看JuiceMaker依赖的是哪个Source,拿到类名
  2. 使用反射的API,基于类名实例化对应的对象实例
  3. 将对象实例,通过构造函数或者 setter,传递给 JuiceMaker

我们发现其实自己来实现也不是很难,Spring实际也就是这么做的。这么看的话其实IoC就是一个工厂模式的升级版!当然要做一个成熟的IoC框架,还是非常多细致的工作要做,Spring不仅提供了一个已经成为业界标准的Java IoC框架,还提供了更多强大的功能,希望了解IoC更多实现细节就去通过学习Spring的源码来加深理解!看源码是最好的学编程方式。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值