Spring深入浅出(一)IOC详解

IOC即inverse of control 控制反转

以前对象之间的引用是通过new来调用实现,有了Spring IOC,我们可以把对象之间的引用交给他来管理,这样就把控制权交给了Spring,所以就叫做控制反转。

Spring IOC的实现用到了设计模式:简单工厂,他也是从简单工厂进化而来的,下面我们看看Spring的IOC是如何进化来的。

简单工厂模式实现:
package org;
//抽象接口
interface Fruit{
    public void eat();
}
//实现类A
class Apple implements Fruit{
    public void eat(){
        System.out.println("吃苹果。");
    }
}
//实现类B
class Orange implements Fruit{
    public void eat(){
        System.out.println("吃橘子");
    }
}
//工厂类
class Factory{     
    public static Fruit getInstance(String className){
        Fruit f=null;
        if(className.equals("apple")){
            f=new Apple();
        }
        if(className.endsWith("orange")){
            f=new Orange();
        }
        return f;
    }
}
public class FactoryDemo02 {
    public static void main(String args[]){
    Fruit f=Factory.getInstance("apple");
    f.eat();
    }
}

反射+简单工厂

但是工厂类如果这样写的话,就有一个问题,如果增加了水果,比如香蕉,那么在工厂类里面也要进行相关的修改了,这样不合理,而java的反射机制可以解决这个问题
package org1;

interface Fruit {
	public void eat();
}

class Apple implements Fruit {
	public void eat() {
		System.out.println("吃苹果。");
	}
}

class Orange implements Fruit {
	public void eat() {
		System.out.println("吃橘子");
	}
}

class Factory {
	public static Fruit getInstance(String className) {
		Fruit f = null;
		try {
			f = (Fruit) Class.forName(className).newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return f;
	}
}

public class CopyOfFactoryDemo03 {
	public static void main(String args[]) {
		Fruit f = Factory.getInstance("org1.Apple");
		f.eat();
	}
}

反射+简单工厂+xml配置文件

利用java的反射机制,就能动态的实例化各种类了。 但是这个程序还是存在一个问题,就是主函数这里需要填入一个完整的类名称,不够方便,所以要增加配置文件来简化
package org3;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Properties;

interface Fruit {
    public void eat();
}

class Apple implements Fruit {
    public void eat() {
        System.out.println("吃苹果。");
    }
}

class Orange implements Fruit {
    public void eat() {
        System.out.println("吃橘子");
    }
}

class Factory {
    public static Fruit getInstance(String className) {
        Fruit f = null;
        try {
            f = (Fruit) Class.forName(className).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}

class PropertiesOperate{
    private Properties pro=null;
    private File file=new File("d:"+File.separator+"fruit.properties");
    
    public PropertiesOperate(){
        this.pro=new Properties();
        if(file.exists()){
            try {
                pro.loadFromXML(new FileInputStream(file));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else{
            this.save();
        }
    }
    private void save(){
        this.pro.setProperty("apple","org3.Apple");
        this.pro.setProperty("orange", "org3.Orange");
        try {
            this.pro.storeToXML(new FileOutputStream(this.file),"Fruit");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public Properties getProperties(){
        return this.pro;
    }
}

public class CopyOfFactoryDemo04 {
    public static void main(String args[]) {
        Properties pro=new PropertiesOperate().getProperties();
        Fruit f= Factory.getInstance(pro.getProperty("apple"));
        f.eat();
    }
}

终极版本Spring IOC

加入配置文件问题就解决了,以后如果要增加新的水果类,都要在这个配置文件里面登记。这时我们可以说配置文件可以控制程序的执行,现在看起来有点像spring的ioc了。下面我们来看看Spring IOC是如何实现的。
package test2;

public class Person {
	private String name;
	private int age;
	private Grade grade;
	public String getName() {
		return name;
	}
	
	public Grade getGrade() {
		return grade;
	}

	public void setGrade(Grade grade) {
		this.grade = grade;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getAge() {
		return age;
	}

	public int getTotleGrade() {
		return grade.getEnglish()+grade.getMath();
	}
}

package test2;

public class Grade {
	private int math;
	private int english;
	public int getMath() {
		return math;
	}
	public void setMath(int math) {
		this.math = math;
	}
	public int getEnglish() {
		return english;
	}
	public void setEnglish(int english) {
		this.english = english;
	}
}

Bean.xml配置文件(该文件只要放在test2包里面就好了)
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>//很多豆豆
	<bean id="Person" class="test2.Person">//第一个豆豆,是一个Person类,id名字随便取,还要写上类的全名
		<property name="name">//下面开始把这个类里面的所有属性列出来,并赋值,至于你说难道一定要赋值吗?我想可以,我刚学,不知道
			<value>小龙</value>//这里的名字是通过程序里面的set来赋值的,不信你去掉程序里面相关的set,就出错了
		</property>
		<property name="age">
			<value>23</value>
		</property>
		<property name="grade">//这里有点特别,这个grade变量是一个对象,和一般的变量要区别对待
			<ref local="Grade"/>//这里指向了本配置文件里面一个名字叫Grade(即id=Grade)的bean
		</property>
	</bean>
	<bean id="Grade" class="test2.Grade">//同上
		<property name="math">
			<value>99</value>
		</property>
		<property name="english">
			<value>59</value>
		</property>
	</bean>
</beans>

Test类
package test2;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import test.ExampleBean;

public class Test {
	public static void main(String args[]){
		Resource input = new ClassPathResource("test2/Bean.xml");//Bean.xml的路径

		System.out.println("resource is:" + input);

		BeanFactory factory = new XmlBeanFactory(input);//把input扔到工厂里面去,这个工厂就能为你提供实例了(我也不知道能不能这样说)

		Person person =(Person) factory.getBean("Person");//你要一个叫Person的东西,那好,工厂就去找“Person"给你
		Grade grade=(Grade)factory.getBean("Grade");
		System.out.println("姓名:"+person.getName());//person可以调用里面相关的方法,就相当于new了一个Person一样
		System.out.println("年龄:"+person.getAge());
		System.out.println("数学成绩:"+grade.getMath());
		System.out.println("英语成绩:"+grade.getEnglish());
		System.out.println("数学,英语总成绩:"+person.getTotleGrade());
	}
}

如此看来,你在对比一开始的那个水果的程序,你会发现,spring配置文件,还是一个工厂,只不过换种形式一样,他管理所有的类,新建的类要到工厂里面去登记,不然就不能被主程序用,这就是为什么说ioc就是工厂模式的升级版。至于配置文件的书写,就跟堆积木一样。
---------------------------------
顺便提下,关于Spring读取配置文件的方法:
applicationcontext---
FileSystemXmlApplicationContext---这个方法是从文件绝对路径加载配置文
ClassPathXmlApplicationContext---这个方法是从classpath下加载配置文件(适合于相对路径方式加载)
XmlWebApplicationContext----专为web工程定制的方法,推荐Web项目中使用。
beanfactory---
ClassPathResource --- 从系统的类路径中加载 
FileSystemResource --- 从文件系统加载,比如说自己指定配置文件的全路径 
InputStreamResource --- 从输入流中加载 
ServletContextResource --- 从Servlet 上下文环境中加载 
UrlResource --- 从指定的Url加载

---------------------------------------------
BeanFactory和ApplicationContext的区别
ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能: 
        ? MessageSource, 提供国际化的消息访问 
        ? 资源访问,如URL和文件 
        ? 事件传播 
        ? 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层 
最主要的就是BeanFactory延迟加载,当使用到getBean的时候才会抛异常,而ApplicationContext在刚开始启动加载的时候就会抛出异常,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用ApplicationContext。






Spring深入浅出(一)IOC详解

上一篇:十二 手游开发神器 cocos2d-x editor 之游戏暂停悬浮层


下一篇:高效编辑器vim之标签使用