一、IOC的概念
Inversion of control 控制反转;
以前: 对象的协作关系A类自己来负责;
A do() ---> B f1()
---> C f2()
do(){
new B().f1();
new C().f2();
}
对象A负责建立与其协作对象的关系;
问题:
1)对象协作关系的建立与业务本身的逻辑纠缠在一起;
2)协作关系也是不容易维护的;(B, C发生改变)
现在: 对象的协作关系容器来负责;
A do() ---> IB <---B f1()
---> IC <---C f2()
setIB(B)
setIC(C)
对象不再负责建立与协作对象的关系, 由容器来负责对象的协作关系;
控制: 指对象的协作关系由谁来建立;
反转: 原来由对象自己调用的变成了容器调用;
好处: 代码更好维护, 协作关系不需要考虑了;
对象依赖于接口, 协作对象发生改变以后, 不需要改变;
控制反转又叫依赖注入: 将对象的协作关系由容器来注入;
二、 基本的容器与IOC的使用
1, spring-framework-2.0.6-with-dependencies
MyEclipse 自带的有jar包,但是会有一些问题;
我们再创建自己的user库: spring2_lib:
需要三个地方的包:
spring framework/dist/spring.jar
lib/jakarta-commons/*.jar
lib/cglib/*.jar
新建工程springdev 右击, myeclipse-- add spring capabilities--- add user libriay--spring2_lib
接口: Greeter
package ioc1;
public interface Greeter {
public String sayHello();
}
实现类: GreeterImpl
package ioc1;
public class GreeterImpl implements Greeter{
private String user;
//提供一个set方法, 可以注入
public void setUser(String user) {
this.user = user;
}
public String sayHello() {
return "你好 " +user;
}
}
配置文件: 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-2.0.xsd">
<bean id="greeter" class="ioc1.GreeterImpl">
<property name="user"><!-- 属性名字 -->
<value>zhangsan</value>
</property>
</bean>
</beans>
测试类:Test
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;
public class Test {
public static void main(String[] args) {
//配置文件的路径
Resource rs=new ClassPathResource("/ioc1/applicationContext.xml");
BeanFactory bf=new XmlBeanFactory(rs);
Greeter gt=(Greeter) bf.getBean("greeter");//配置文件中id的名字
System.out.println(gt.sayHello());
}
}
2, IOC的类型:
1) set 方式来注入:
<property name=””></property>
其中,name属性的取值依setter方法名而定
对于基本类型,spring容器会自动做类型转换,以便赋值;
2)构造器的方式来注入;
<constructor-arg index=””></ constructor-arg>
其中,index表示构造方法中的参数位置(第一个参数索引为0)
3)实现特定的接口来注入,
这种方式已经不采用了,因为业务对象会依赖框架的特定接口;
" 侵入式 "方案;
注入的类型:
1)八种基本类型+String ;
2)对象
3)集合: list, set , map , properties
4)null
3, Spring 容器 :
用于实例化对象, 然后赋值或者装配, 生命周期管理的一种类;
容器的种类:
BeanFactory: 1)所有容器的父接口, 组件的生成和组装;
2)提供了基本的IOC的功能;
3)一个实现: XmlBeanFactory ,
需要提供一个Resource的实例给XmlFactory, Resource对象来包装配置文件;
4)getBean(String)方法,返回一个装配好的对象;
5)在默认情况下返回的对象都采用的单例模式;
6)直到调用getBean方法时, 才会实例化并且装配对象;
实例化 -- 构造对象, 给属性赋值 -- 装配/注入 ;
ApplicationContext: Spring的上下文, 也是一个容器, 是BeanFactory的子接口;
增强了BeanFactory, 提供了事件处理, 国际化,复杂的生命周期管理;
一般情况下用ApplicationContext, 而不用BeanFactory;
实现类:
ClassPathXmlApplicationContext: 依据classpath查找配置文件;
容器初始化的时候会预先实例化单例的对象; 可以配置, 有的采用单例有的不用; 三、set 方式注入:
<1>. 基本类型的注入:
注入简单类型: 八种基本类型+String
方式: id是bean的id号, 唯一的; class是bean类的全路径;
<bean id="" ,class="">
<property name=""> <!--属性名, set方法去掉set并将首字母小写后的名字 -->
<value>属性值</value>
</property>
</bean>
对于基本类型, 会自动转换数据类型; 跟序列化的意思差不多;
用ApplicationContext 容器写测试类:
ApplicationContext ctx=new ClassPathXmlApplicationContext("/ioc1/applicationContext.xml");
Greeter gt2=(Greeter) ctx.getBean("greeter");
Greeter gt3=(Greeter) ctx.getBean("greeter");
System.out.println(gt2.sayHello());
//测试单例模式;true
System.out.println(gt2==gt3);
<2>. 对象类型的注入:
1) <ref local> 要注入的对象在同一个配置文件中;
2) <ref bean> 要注入的对象可以分散在其他的配置文件中;
3) 将要注入的bean类的配置信息直接嵌入到bean类中;(有点象内部类)
不让客户端直接用getBean方式获得,该对象只被注入的对象使用;
例子1:
实现类:
public class OderServiceImpl implements OrderService{
private SomeBean sb; //最好用接口
}
============================第一种:<ref local>=================================
配置文件1: <ref local>
使用local则定义的两个类必须出现在同一个配置文件中
<!-- 第一个bean -->
<bean id="otherBean" class="IOC.pm.set.Class.OtherBean">
<property name="str"><!-- 属性名字 -->
<value>someinfo</value>
</property>
</bean>
<!-- 第二个bean -->
<bean id="someBean" class="IOC.pm.set.Class.SomeBean">
<property name="ob">
<ref local="otherBean"/> //另外一个bean的id;
</property>
</bean>
===========================第二种:嵌套==================================
或者2: 嵌到一起: 被嵌套的bean就不能够使用getBean()来返回了;
使用嵌套定义的两个类必须出现在同一个配置文件中
<bean id="someBean" class="IOC.pm.set.Class.SomeBean">
<property name="ob">
<!--这样的话ohterBean只能被someBean调用 -->
<bean id="otherBean" class="IOC.pm.set.Class.OtherBean">
<property name="str1"><!-- 属性名字 -->
<value>tarena</value>
</property>
</bean>
</property>
</bean>
============================第三种:<ref bean>==================================
或者3: <ref bean>: 功能最强大的搜索多个配置文件;
加一个配置文件: otherContext.xml 放someBean的配置;
applicationContext.xml中 <ref bean="otherBean"/>
测试类中: 写成一个字符串数组;
ApplicationContext ctx=new ClassPathXmlApplicationContext(
new String[]{"/ioc2/applicationContext.xml","/ioc2/other-config.xml.xml"});
<3>. 集合的注入: (看老师的示例; )
List: 可重复的,有序
可以存放字符串,对象, 以及集合;
<property name="list">
<list>
<value>String1</value>
<value>String2</value>
</list>
</property>
Set: 不重复, 无序
可以存放字符串,对象, 以及集合;
<property name="set">
<set>
<value>String1</value>
<value>String1</value>
</set>
</property>
Map:Key(只能用String), value 键值对;
<property name="map">
<map>
<entry key="key1"><!-- 用key来存放key的值,用value来存放value值 -->
<value>value1</value>
</entry>
<entry key="key2">
<value>value2</value>
</entry>
</map>
</property>
Properties: key(String),value(String) 键值对
<property name="properties">
<props>
<prop key="key11">value11</prop>
<prop key="key22">value22</prop>
</props>
</property>
四、构造器的方式注入:
<1>. <constructor-arg>
<value>基本类型</value>
</constructor-arg>
<2>. <constructor-arg>
<ref bean=""></ref>
</constructor-arg>
<bean id="someBean" class="ioc4.SomeBean">
<constructor-arg index="1"><!--参数的顺序可以改变,下标从0开始 -->
<value>String1</value>
</constructor-arg>
<constructor-arg index="0">
<value>String2</value>
</constructor-arg>
</bean>
构造器注入和set方式注入比较:
用构造器注入:如果要注入的对象,属性不是特别多,并且属性值一定要被注入后才能来使用;
其他情况用set方式注入;
五、IOC的特例 ;
1, 自动装配, 让容器依据某种规则,自动的对组件实施装配;
在bean中:
autowire=""
<1>. byName: 匹配属性的名字与bean的id号进行匹配; 找set方法;
someBean中有一个ob的属性:
<bean id="ob" class="ioc6.OtherBean">
<property name="str1">
<value>String1</value>
</property>
</bean>
<bean id="someBean" class="ioc6.SomeBean" autowire="byName">
<property name="str1">
<value>String1</value>
</property>
</bean>
<2>. byType: 寻找配置文件, 匹配的属性类型与bean一致; 找set方法;
上面例子改成byType 也是正确的, 并且ob可以改为别的名字;
如果在配置文件中找到符合条件的bean的个数超过一个,会报错;
<3>. constructor: 匹配构造器,看构造器的参数类型和配置文件bean的类型是否一致,
一致就成功,否则报错;
匹配个数超过一个,也报错;
提供一个构造器;
<4>. autodetect: 自动检测, 先构造器, 再set方法(也就是可以set方法和构造方法可以同时使用);
自动从配置文件中寻找对应的bean的名字;
注意:手动装配会覆盖自动装配的策略;
问题:
1)自动装配用的很少, 配置文件中,bean的关系不清晰, 易出错;
在建立原型的时候, 快速搭建, 真正开发的时候, 一般手动配置;
2)自动装配和手动装配配合在一起使用; 手动可以覆盖自动的;
3)自动装配搭配一个装配检查; 属性先检查, 出错后就不再进行了;
dependency-check=""
simple :只检查基本类型属性是否装配成功;
objects:只检查Object类型是否装配成功;
all:全检查;
既可以和自动装配一起使用, 也可以单独用来检查是否装配成功;
自动装配的使用情况:
1,构建系统原形;
2,与dependency-check=""配合使用;
3,手动装配会和自动装配要配合使用;
2, 用工厂的方式创建对象: 静态工厂, 实例工厂;
原因是: 很多对象不方便采用配置文件去装配;
如: 获得数据库连接, 可以写一个ConnectionFactory, getConnection方法
静态工厂类, 静态方法, getBean("connection")就可以得到连接;
public static Car createCar(){// 静态工厂,静态方法
return new Car();
}
<bean id="car" class="ioc5.CarFactory" factory-method="createCar" />
Car car=(Car) ctx.getBean("car");
System.out.println(car);
实例工厂: 指工厂实例化后才能使用,
public Car createCar(){// 必须是实例方法
return new Car();
}
<bean id="carFactory" class="ioc5.CarFactory" />
<bean id="car" factory-bean="carFactory" factory-method="createCar" />
3,继承关系:
<bean id="abstractBean" class="day02.ioc7.SomeBean" abstract="true">
<property name="str1">
<value>string1</value>
</property>
<property name="num">
<value>20</value>
</property>
</bean>
<!-- 继承关系 -->
<bean id="someBean" parent="abstractBean">
<property name="num">
<value>30</value>
</property>
</bean>
得到的bean不想用单例时, 在bean标签上加 Scope=singleton/prototype
装配中的其它情况:
单例:scope=""
singleton,prototype
初始化与销毁: init-method,destroy-method