控制反转(IoC)

控制反转

将组件对象的控制权从代码本身转移到外部容器。

  • 组件化的思想:分离关注点,使用接口,不再关注实现。
    (目的:解耦合。实现每个组件时只关注组件内部的事情)
  • 依赖的注入:将组件的构建和使用分开。

       当某个角色(比如一个java实例,调用者)需要另一个角色(另一个java实例,被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但是在spring里,创建被调用者的工作不再由调用者来完成。因此被称为控制反转;创建被调用者实例的工作通常由spring容器来完成,然后注入调用者,因此也称为依赖注入。这样给程序带来很大的灵活性,这样也实现了我们的接口和实现的分离。
       简而言之也就是说我们要获得一个对象,不由我们开发者自己创建,而是由我们的容器来注入给我们的程序来使用。

使用Spring实现“控制反转”

在这里插入图片描述
Spring配置文件applicationContext.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-3.2.xsd">
	<!-- 通过bean元素声明需要Spring创建的实例。该实例的类型通过class属性指定,并通过id属性为该实例指定一个名称,以便在程序中使用 -->
	<bean id="helloSpring" class="cn.springdemo.HelloSpring">
	<!-- property元素用来为实例的属性赋值,此处实际是调用setWho()方法实现赋值操作-->
		<property name="who">
			<!-- 此处将字符串"Spring"赋值给who属性 -->
			<value>Spring</value>
		</property>
	</bean>
</beans>

类HelloSpring.java代码如下:

package cn.springdemo;
/**
 * 第一个Spring,输出"Hello,Spring!"。
 */
public class HelloSpring {
	// 定义who属性,该属性的值将通过Spring框架进行设置
	private String who = null;

	/**
	 * 定义打印方法,输出一句完整的问候。
	 */
	public void print() {
		System.out.println("Hello," + this.getWho() + "!");
	}

	public String getWho() {
		return who;
	}
	public void setWho(String who) {
		this.who = who;
	}
}

测试类HelloSpringTest.java代码如下:

package test;

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

import cn.springdemo.HelloSpring;

public class HelloSpringTest {

    @Test
    public void helloSpring() {
        // 通过ClassPathXmlApplicationContext实例化Spring的上下文
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        // 通过ApplicationContext的getBean()方法,根据id来获取bean的实例
        HelloSpring helloSpring = (HelloSpring) context.getBean("helloSpring");
        // 执行print()方法
        helloSpring.print();
    }
}

输出结果:Hello,Spring!

组装打印机

在这里插入图片描述
(1) 定义组件接口
墨盒接口代码:

/**
 * 墨盒接口。
 */
public interface Ink {
    /**
     * 定义打印采用的颜色的方法。
     * 
     * @param r 红色值
     * @param g 绿色值
     * @param b 蓝色值
     * @return 返回打印采用的颜色
     */
    public String getColor(int r, int g, int b);
}
纸张接口代码:
/**
 * 纸张接口。
 */
public interface Paper {
    public static final String newline = "\r\n";
    /**
     * 输出一个字符到纸张。
     */
    public void putInChar(char c);
    /**
     * 得到输出到纸张上的内容。
     */
    public String getContent();
}

(2) 使用接口开发打印机(面向接口编程)
墨盒接口的实现类——彩色墨盒,代码:

package cn.ink;
import java.awt.Color;
import cn.printer.Ink;
/**
 * 彩色墨盒。ColorInk实现Ink接口。
 */
public class ColorInk implements Ink {
	// 打印采用彩色
	public String getColor(int r, int g, int b) {
		Color color = new Color(r, g, b);
		return "#" + Integer.toHexString(color.getRGB()).substring(2);
	}
}

墨盒接口的实现类——灰色墨盒,代码:

import java.awt.Color;
import cn.printer.Ink;
/**
 * 灰色墨盒。GreyInk实现Ink接口。
 */
public class GreyInk implements Ink {
    // 打印采用灰色
    public String getColor(int r, int g, int b) {
        int c = (r + g + b) / 3;
        Color color = new Color(c, c, c);
        return "#" + Integer.toHexString(color.getRGB()).substring(2);
    }
}
纸张接口的实现类——文本纸张,代码:
package cn.paper;
import cn.printer.Paper;
/**
 * 文本打印纸张实现。TextPaper实现Paper接口。
 */
public class TextPaper implements Paper {
    // 每行字符数
    private int charPerLine = 16;
    // 每页行数
    private int linePerPage = 5;
    // 纸张中内容
    private String content = "";
    // 当前横向位置,从0到charPerLine-1
    private int posX = 0;
    // 当前行数,从0到linePerPage-1
    private int posY = 0;
    // 当前页数
    private int posP = 1;
    public String getContent() {
        String ret = this.content;
        // 补齐本页空行,并显示页码
        if (!(posX == 0 && posY == 0)) {
            int count = linePerPage - posY;
            for (int i = 0; i < count; ++i) {
                ret += Paper.newline;
            }
            ret += "== 第" + posP + "页 ==";
        }
        return ret;
    }
    public void putInChar(char c) {
        content += c;
        ++posX;
        // 判断是否换行
        if (posX == charPerLine) {
            content += Paper.newline;
            posX = 0;
            ++posY;
        }
        // 判断是否翻页
        if (posY == linePerPage) {
            content += "== 第" + posP + "页 ==";
            content += Paper.newline + Paper.newline;
            posY = 0;
            ++posP;
        }
    }
    // setter方法,用于属性注入
    public void setCharPerLine(int charPerLine) {
        this.charPerLine = charPerLine;
    }
    // setter方法,用于属性注入
    public void setLinePerPage(int linePerPage) {
        this.linePerPage = linePerPage;
    }
}

(3) 组装打印机
打印机程序类,代码:


package cn.printer;
/**
 * 打印机程序。
 */
public class Printer {
    // 面向接口编程,而不是具体的实现类
    private Ink ink = null;
    // 面向接口编程,而不是具体的实现类
    private Paper paper = null;
    /**
     * 设值注入所需的setter方法。
     * @param ink 传入墨盒参数
     */
    public void setInk(Ink ink) {
        this.ink = ink;
    }
    /**
     * 设值注入所需的setter方法。
     * @param paper 传入纸张参数
     */
    public void setPaper(Paper paper) {
        this.paper = paper;
    }
    /**
     * 打印机打印方法。
     * @param str 传入打印内容
     */
    public void print(String str) {
        // 输出颜色标记
        System.out.println("使用" + ink.getColor(255, 200, 0) + "颜色打印:\n");
        // 逐字符输出到纸张
        for (int i = 0; i < str.length(); ++i) {
            paper.putInChar(str.charAt(i));
        }
        // 将纸张的内容输出
        System.out.print(paper.getContent());
    }
}

(4) 注入组装打印机时所依赖的对象
相当于把创建和组装的需求通过配置文件告诉Spring,由Spring负责实施,而不是通过硬编码实现(需要提前为Printer类的ink和paper属性增加setter方法,Spring是通过调用setter方法实现的注入)。
编辑applicationContext.xml文件,使用Spring创建Ink、Paper和Printer的实例并进行组装。
Spring配置文件applicationContext.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-3.2.xsd">
    <!-- 定义彩色墨盒bean,该bean的id是colorInk,class指定该bean实例的实现类 -->
    <bean id="colorInk" class="cn.ink.ColorInk" />

    <!-- 定义灰色墨盒bean,该bean的id是greyInk,class指定该bean实例的实现类 -->
    <bean id="greyInk" class="cn.ink.GreyInk" />

    <!-- 定义A4纸张bean,该bean的id是a4Paper,class指定该bean实例的实现类 -->
    <bean id="a4Paper" class="cn.paper.TextPaper">
        <!-- property元素用来指定需要容器注入的属性,charPerLine需要容器注入, TextPaper类必须拥有setCharPerLine()方法。--> 
        <!--  注入每行字符数 --> 
        <property name="charPerLine" value="10" />
        <!-- property元素用来指定需要容器注入的属性,linePerPage需要容器注入,TextPaper类必须拥有setLinePerPage()方法。 --> 
        <!-- 注入每页行数 -->
        <property name="linePerPage" value="8" />
    </bean>

    <!-- 定义B5纸张bean,该bean的id是b5Paper,class指定该bean实例的实现类 -->
    <bean id="b5Paper" class="cn.paper.TextPaper">
        <!-- property元素用来指定需要容器注入的属性,charPerLine需要容器注入, TextPaper类必须拥有setCharPerLine()方法。注入每行字符数 -->
        <property name="charPerLine" value="6" />
        <!-- property元素用来指定需要容器注入的属性,linePerPage需要容器注入, TextPaper类必须拥有setLinePerPage()方法。注入每页行数 -->
        <property name="linePerPage" value="5" />
    </bean>

    <!-- 组装打印机。定义打印机bean,该bean的id是printer, class指定该bean实例的实现类 -->
    <bean id="printer" class="cn.printer.Printer">
        <!-- 通过ref属性注入已经定义好的bean -->
        <!-- 注入彩色墨盒 -->
        <property name="ink" ref="colorInk"></property>
        <!-- 注入B5打印纸张 -->
        <property name="paper" ref="b5Paper"></property>
    </bean>

</beans>

(5) 运行打印机
运行打印机的测试类代码:

package cn.test;

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

import cn.printer.Printer;

/**
 * 测试运行打印机。
 */
public class PrinterTest {
    @Test
    public void printerTest() {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        // 通过Printer bean的id来获取Printer实例
        Printer printer = (Printer) context.getBean("printer");
        String content = "几位轻量级容器的作者曾骄傲地对我说:这些容器非常有"
                + "用,因为它们实现了“控制反转”。这样的说辞让我深感迷惑:控"
                + "制反转是框架所共有的特征,如果仅仅因为使用了控制反转就认为"
                + "这些轻量级容器与众不同,就好像在说“我的轿车是与众不同的," + "因为它有4个轮子。”";
        printer.print(content);
    }
}

运行测试类中的printerTest()方法,根据applicationContext.xml的注入不同,输出结果不同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值