Spring IoC 初探

1 什么是IoC
IoC 这么高大上的名字,咋听起来挺不容易理解的,说白了就是以前是对象自己获取它的协作对象,现在交给了Spring容器,就是获取协作对象的方式反转了。
Martin Fowler起了另一个名字DI(依赖注入),就是由Spring容器将对象依赖的协作对象注入进去。如果还不理解也没关系,看完下面的代码就揭开IoC的神秘面纱了。
2 Spring之Hello world
任何语言都从hello world开始,这个也不例外。
先定义一个接口:
public interface HelloService {
    public void sayHello();
}

定义它的实现类:
public class HelloServiceImpl implements HelloService {
    private String greeting;
    public HelloServiceImpl(){}
    public HelloServiceImpl(String greeting){
        this.greeting = greeting;
    }
    public void sayHello(){
        System.out.println(greeting);
    }
    public void setGreeting(String greeting){
        this.greeting = greeting;
    }
}


hello.xml这个放到src根目录下:
<?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="hello" class=" HelloServiceImpl">
        <property name = "greeting">
            <value>hello frank1234,start spring now!</value>
        </property>
    </bean>
</beans>

测试类:
public class HelloMain {
    public static void main(String[] args) throws Exception{
        ApplicationContext factory = new ClassPathXmlApplicationContext("hello.xml");
        HelloService service = (HelloService)factory.getBean("hello");
        service.sayHello();

    }
}

输出:hello frank1234,start spring now!

测试类代码等同于:
HelloServiceImpl helloService = new HelloServiceImpl();
helloService.setGreeting("hello frank1234,start spring now!");
helloService.sayHello();



HelloServiceImpl类的属性greeting,就是通过Spring容器,根据xml配置文件注入到HelloServiceImpl的setGreeting方法的。当然Spring也可以将一个对象注入到另外一个对象中,下面看看代码示例,代码是程序猿们交流的语言。

3传统对象协作方式vs IoC
比如有个Person(人)类,Person 依赖Head(脑袋)类,通过Head来思考,代码如下:
Person:
public class Person {
    Head head = null;
    public Person(){
        head = new Head();
    }
    public void think(){
        head.think();
    }
}

Head:
public class Head {
    public void think(){
        System.out.println("我用脑袋思考");
    }
}


这样人和脑袋就耦合到一起了,kao,这人和脑袋一解耦,人就挂了啊,看来这个例子举的不咋地啊。

假如突然这人说不用脑袋思考了,改成用屁股思考了,那么就要重新修改类,改成如下:
Person:
public class Person {
    Ass ass = null;
    public Person(){
        ass = new Ass();
    }
    public void think(){
        ass.think();
    }
}


Ass(屁股):

public void think(){
    System.out.println("我用屁股思考");
}

由此可见,改动较大。如果用IoC那么可以怎么做呢。

添加一个思考者接口:
public interface Thinker {
    public void think();
}


Ass实现Thinker接口:
public class Ass implements Thinker{
    public void think(){
        System.out.println("我用屁股思考");
    }
}


Head实现Thinker接口:
public class Head implements Thinker{
    public void think(){
        System.out.println("我用脑袋思考");
    }
}

Person改为如下,Person不再创建协作对象,改由容器配制。
public class Person {
    Thinker thinker ;
    public Person(){

    }
    public void think(){
        thinker.think();
    }
    public void setThinker(Thinker thinker){
        this.thinker = thinker;
    }
}


Spring配制文件如下:
<?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="thinker" class="Head">
    </bean>
    <bean id="person" class="Person">
        <property name = "thinker">
            <ref bean="thinker"></ref>
        </property>
    </bean>
</beans>


有了这个就可以想用脑袋思考就用脑袋思考,想用屁股思考就用屁股思考,配制一下就可以了,解耦了。
4 BeanFactory vs ApplicationContext
容器是Spring框架的核心,Spring容器使用IoC管理所有组成应用系统的组件。
Spring有两个容器:BeanFactory是最简单的容器,提供了依赖注入的支持。ApplicationContext建立在Bean工厂基础之上,提供了系统架构服务。
4.1 BeanFactory
BeanFactory可以创建和分发各种类型的Bean,实例化对象的时候创建协作对象间的关联关系,Bean在需要的时候才会被实例化,而不是在加载Bean配置文件的时候实例化。
就是在调用getBean("myBean")的时候实例化的。

4.2 ApplicationContext
ApplicationConext建立在BeanFactory之上,所以凡是BeanFactory能干的事情,ApplicationContext都可以干。另外ApplicationContext还提供了更多的功能:
1)提供了文本信息解析工具,包括国际化的支持。
2)提供了载入文件资源的通用方法。
3)可以向监听的Bean发送事件。

ApplicationContext有3个实现:
ClassPathXmlApplicationContext:从类路径的xml中加载配置文件。
例如:ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
FileSystemXmlApplicationContext:从文件系统的xml中加载配置文件。
例如:ApplicaitonContext ctx = new FileSystemXmlApplicationContext("c:/bean.xml");
XmlWebApplicationContext:从web系统的xml中加载配置文件。

另外还有一点不同就是ApplicationConext会在上下文启动后预载入所有的单实例Bean。

5 实例化和销毁
1)通过Bean定义实现。
<bean id="connectionPool" class="com.xxx.MyConnectionPool" init-method="initialize" destroy-method="close"/>
这样MyConnectionPool实例化后立马会调用initialize()方法,Bean从容器删除之前,会调用close()方法。
2)通过接口实现。
InitializingBean 接口中的方法afterPropertiesSet() 同上面的init-method和DisposableBean接口中的方法destroy()同上面的destroy-method。
因为第二种方法会绑定Spring的接口,而Spring讲究松耦合,所以建议使用第1中方式。
6 手动装配
6.1 set注入
1)最简单Bean。
<bean id="foo" class="com.xxx.Foo">
<property name="name"> <value>Foo</value>
</property>
</bean>
给Foo类的name属性,注入值Foo

2)引用其他Bean
<bean id="foo" class="com.xxx.Foo">
<property name="bar"> <ref bean="bar"/>
</property>
</bean>
<bean id="bar" class="com.xxx.Bar"/>

将Bar的对象注入到Foo类的bar属性中。

3)内部Bean
<bean id="foo" class="com.xxx.Foo">
<property name="bar"> <bean class="com.xxx.Bar"/>
</property>
</bean>
同上,只是这个内部Bean无法被复用。

4)List
<property name="barList">
<list>
<value>bar1</value>
<ref bean="bar2"/>
</list>
</property>

List中放了一个String的bar1和Bar的对象bar2。

5)Set
<property name="barSet">
<set>
<value>bar1</value>
<ref bean="bar2"/>
</set>
</property>
同上。

6)Map
<property name="barMap">
<map>
<entry key="key1">
<value>bar1</value>
</entry>
<entry key="key1">
<ref bean="bar2"/>
</entry>
</map>
</property>

限制:key值只能是String类型的,但value可以是任意类型的。


6.2 构造方法注入
<bean id="foo" class="com.xxx.Foo">
<constructor-arg>
<value>40</value>
</constructor-arg>
</bean>

其中的value也可以是其他Bean 例如<ref bean="bar1"/>,同setter注入的格式相同。笔者认为做产品最重要的一点就是保持一致性,在同一个系统中解决同样的问题,都是用同一种方法,这样使用者能够触类旁通。否则这个地方是这样,那个地方是那样,时间长了连自己也不知道到底应该怎么样了。

为了解决不同构造参数的问题,可以使用index属性,index从0开始数数。
例如:
<bean id="foo" class="com.xxx.Foo">
<constructor-arg index="0">
<value>40</value>
</constructor-arg>
<constructor-arg index="1">
<ref bean="bar1"/>
</constructor-arg>
</bean>


6 其他
6.1 对Bean进行后处理
public interface BeanPostProcessor{
Object postProcessBeforeInitialization(Object bean,String name);
Object postProcessAfterInitialization(Object bean,String name);
}
postProcessBeforeInitialization在init-method之前被调用,postProcessAfterInitialization在init-method之后马上调用。

可以在这两个方法中进行后处理。

6.2独立配置文件
项目中有时需要将配置文件分开管理,这样就需要Spring具有读取独立配置文件的能力。
Spring的相关配置如下:
jdbc.properties:
database.url=xxx
database. driverClassName=xxx
database.username=xxx
database.password=xxx

<bean id="propertyConfigurer" clas="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>jdbc.properties</value>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url">
<value>${database.url}</value>
</property>
<property name="driverClassName">
<value>${database.driverClassName}</value>
</property>
<property name="username">
<value>${database.username}</value>
</property>
<property name="password">
<value>${database.password}</value>
</property>
</bean>





《Spring in action》

你可能感兴趣的:(spring ioc)