二、Spring之IoC
1. IoC与DI
(1) IoC
控制反转( IoC, Inversion of Control) , 是一个概念, 是一种思想。 控制反转就是对对象控制权的转移, 从程序代码本身反转到了外部容器。 把对象的创建、 初始化、销毁等工作交给spring容器来做。 由spring容器控制对象的生命周期。
DI
依赖注入: Dependency Injection。 依赖注入DI是指程序运行过程中, 若需要调用另
一个对象协助时, 无须在代码中创建被调用者, 而是依赖于外部容器, 由外部容器创
建后传递给程序。
依赖注入是目 前最优秀的解耦方式。 依赖注入让Spring的Bean之间以配置文件的方式
组织在一起, 而不是以硬编码的方式耦合在一起的。
IoC与DI的关系
IoC是一个概念, 是一种思想, 其实现方式多种多样。 当前比较流行的实现方式之一
是DI。
2. 第一个IoC程序
(1) 导入jar包( 基本4个)
spring-beans-4.3.16.RELEASE.jar
spring-context-4.3.16.RELEASE.jar
spring-core-4.3.16.RELEASE.jar
spring-expression-4.3.16.RELEASE.jar
再放入两个日志包:
com.springsource.org.apache.commons.logging-1.1.1.jar
com.springsource.org.apache.log4j-1.2.15.jar
(2) 创建spring配置文件
1) 创建applicationContext.xml
2) spring-framework-4.3.16.RELEASE/docs/spring-framework-reference/html/
xsd-configuration.html 中找到配置文件头信息(the beans schema)
<?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">
<!-- bean的定义:以下配置相当于SomeService service = new SomeServiceImpl(); -->
<bean id="someServiceImpl" class="com.mypack.service.impl.SomeServiceImpl"></bean>
</beans>
3) 复制本地文件 D:\eclipse_mars2\xsd\spring-beans-4.3.xsd 到 eclipse目录
复制http://www.springframework.org/schema/beans/
spring-beans.xsd 到XML catalog中 key
(3) Bean的定义与注册
<bean id="someServiceImpl" class="com.mypack.service.impl.
SomeServiceImpl"></bean>
(4) 从spring容器中获取Bean
ApplicationContext ac = new ClassPathXmlApplicationContext
("applicationContext.xml"); //通过ClassPathXmlApplicationContext这个实现类创建容器对象
SomeService service = ac.getBean("someServiceImpl", SomeService.class);
service.doSome();
(5) 另一种方法:通过BeanFactory创建
BeanFactory bf = new XmlBeanFactory(new ClassPathResource
("applicationContext.xml"));
SomeService service = bf.getBean("someServiceImpl", SomeService.class);
service.doSome();
3. ApplicationContext容器与BeanFactory容器的区别
(1) ApplicationContext
容器初始化时,所有的容器中的bean创建完毕,效率高但占用内存较多。
(2) BeanFactory当调用getBean获取相应对象时,才创建对象。
一开始不创建对象,不占用内存,但效率低。
4. Bean的装配
Bean的装配, 即Bean对象的创建。
(1) 默认装配方式(构造方式)
通过无参构造器创建对象,如上所示。
(2) 动态工厂创建Bean对象
先实例工厂方式创建bean对象
public SomeService getSomeService(){
SomeService someServiceImpl = new SomeServiceImpl();
return someServiceImpl;
}
然后在xml中注册工厂,从工厂中获取someServiceImpl的bean对象
<bean id="serviceFactory" class="com.mypack.factory.ServiceFactory"></bean>
<bean id="someServiceImpl" factory-bean="serviceFactory" factory-method="getSomeService"></bean>
(3) 静态工厂创建Bean对象
在ServiceFactory方法中创建静态方法
public class ServiceFactory {
public static SomeService getSomeService(){
SomeService someServiceImpl = new SomeServiceImpl();
return someServiceImpl;
}
}
xml文件中获取someServiceImpl的bean对象,直接通过类名.的方式调用
<bean id="someServiceImpl" class="com.mypack.factory.ServiceFactory" factory-method="getSomeService"></bean>
5. Bean的作用域
(1) 单态模式
SomeService service1 = ac.getBean("someServiceImpl", SomeService.class);
SomeService service2 = ac.getBean("someServiceImpl", SomeService.class);
System.out.println(service1==service2);
结果为true,容器初始化的时候,所有bean对象已经创建完毕。我们从容器中获取的对象拿到的都是相同对象。
(2) 原型模式
xml文件中,scope属性改为prototype,可变为原型模式(默认是单态模式singleto)
<bean id="someServiceImpl" class=
"com.mypack.service.impl.SomeServiceImpl" scope="prototype"></bean>
6. 基于XML的DI
所谓注入,可理解为对象的属性赋值
(1) 设值注入
1) 简单数据类型和引用数据类型注入
通过property标签进行赋值,简单数据类型(基本类型和字符串)使用value属性进行注入,引用类型用ref进行注入。
<bean id="star" class="com.mypack.pojo.Star">
<property name="name" value="贝克汉姆"></property>
<property name="age" value="39"></property>
<property name="partner" ref="partner"></property>
</bean>
<bean id="partner" class="com.mypack.pojo.Partner">
<property name="name" value="维多利亚"></property>
</bean>
2) 集合属性注入(array、set、list、map、properties)
<bean id="partner1" class="com.mypack.pojo.Partner">
<property name="name" value="维多利亚"></property>
</bean>
<bean id="partner2" class="com.mypack.pojo.Partner">
<property name="name" value="布兰妮"></property>
</bean>
<bean id="someService" class="com.mypack.service.SomeService">
<property name="myArray">
<!--字符串数组通过array子标签,用value属性注入-->
<array>
<value>北京</value>
<value>上海</value>
</array>
</property>
<property name="mySet">
<!--set集合通过set子标签,泛型是partner,用ref属性注入-->
<set>
<ref bean="partner1"/>
<ref bean="partner2"/>
</set>
</property>
<property name="myList">
<!--list集合通过list子标签,泛型是字符串,用value属性注入-->
<list>
<value>男</value>
<value>女</value>
</list>
</property>
<!--map集合通过map子标签,用entry属性为键值对赋值-->
<property name="myMap">
<map>
<entry key="qq" value="123456"></entry>
<entry key="mobile" value="13300000"></entry>
</map>
</property>
<!--property属性集通过props子标签,用key属性,值放在标签体里面-->
<property name="myProps">
<props>
<prop key="兴趣">足球</prop>
<prop key="爱好">下棋</prop>
</props>
</property>
</bean>
3) 域属性自动注入
① byName
byName方式域属性自动注入:要求注入的bean的id必须和被注入的bean象的属性名一致。使用autowire属性,设为byName。
<bean id="star" class="com.mypack.pojo.Star" autowire="byName">
<property name="name" value="贝克汉姆"></property>
<property name="age" value="39"></property>
</bean>
id="partner"和star类中相应的属性名要一致
<bean id="partner" class="com.mypack.pojo.Partner">
<property name="name" value="维多利亚"></property>
</bean>
② byType方式域属性自动注入:spring配置文件中查询与属性类型一致的bean并进行注入,autowire属性,设为byType。
<bean id="star" class="com.mypack.pojo.Star" autowire="byType">
<property name="name" value="贝克汉姆"></property>
<property name="age" value="39"></property>
</bean>
4) 局部配置方式和全局配置方式
① 局部配置方式:在bean便签当中,使用autowir属性进行注入。
代码如上所示。
②全局配置方式:配置文件中所有的bean都使用该种配置方式。
在beans标签下加入default-autowire="byType"(或byName),把每个bean标签中的autowire属性去掉。
5) 空字符串或null的注入
去掉原来的数值,加入<value>标签,可注入空字符串。
<property name="name"><value/></property>
加入<null/>标签,可注入空字符串。
<property name="name"><null/></property>
(2) 构造注入
1) 使用<constructor-arg>标签的name属性注入
<bean id="star" class="com.mypack.pojo.Star">
<constructor-arg name="name" value="郭靖"></constructor-arg>
<constructor-arg name="age" value="25"></constructor-arg>
<constructor-arg name="partner" ref="partner"></constructor-arg>
</bean>
2) 使用<constructor-arg>标签的index属性(索引号)注入
<bean id="star" class="com.mypack.pojo.Star">
<constructor-arg index="0" value="郭靖"></constructor-arg>
<constructor-arg index="1" value="25"></constructor-arg>
<constructor-arg index="2" ref="partner"></constructor-arg>
</bean>
3) 对构造器中对应的属性值进行注入
<bean id="star" class="com.mypack.pojo.Star">
<constructor-arg value="郭靖"></constructor-arg>
<constructor-arg value="25"></constructor-arg>
<constructor-arg ref="partner"></constructor-arg>
</bean>
7. 基于注解的DI
1) 环境搭建:导入aop包、添加context约束头信息(组件扫描器)
导入包spring-aop-4.3.16.RELEASE
到 spring-framework-4.3.16.RELEASE/docs/spring-framework-reference/htmlsingle/
index.html 中查找context schema,复制这一段到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"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- bean definitions here -->
</beans>
添加组件扫描器:
<context:component-scan base-package="com.mypack.pojo"></context:component-scan>
类定义前加上@Component,表示当前类交给Spring容器管理。为类成员注入值用@Value,对象属性用@Autowired注入。
@Component("myPartner")
public class Partner {
@Value("张三")
private String name;
@Override
public String toString() {
return "Partner [name=" + name + "]";
}
}
@Component
@Scope("prototype")
public class Star {
//简单数据类型通过@Value注入
@Value("李四")
private String name;
@Value("23")
private int age;
//引用数据类型通过@Autowired注入;默认情况下是byType方式注入;如果使用byName方式自动注入,
//需要与@Qualifier联合使用
/*
@Autowired
@Qualifier("myPartner")
*/
//引用数据类型通过@Resource也可以实现注入;默认情况下是byName方式注入,
//只有找不到与名称匹配的bean的时候才会按照类型来进行注入
@Resource
private Partner partner;
@Override
public String toString() {
return "Star [name=" + name + ", age=" + age + ", partner=" + partner + "]";
}
}
2) 常用注解:
@Component:当前类作为一个组件,交给容器进行管理
与@Component具有相同功能的还有另外3个注解
* @Repository:该注解添加在Dao实现类上
* @Service:该注解添加在Service实现类上
* @Controller:该注解添加在Controller类上
@Scope:bean作用域相关注解,指定对象的生命周期,singleto(默认),prototype,request,session,global session
@Value:对简单数据类型的依赖注入
@Resource:预属性注解(JDK),用于给引用注入容器的对象,默认byType
@Autowired:预属性注解(Spring),用于给引用注入容器的对象,默认byName