Spring IOC 解释与实现
阅读目录
一 什么是IOC
Inversin Of Control 控制反转, 解决了对象之间的解耦问题
程序创建对象的方式由之前new 的方式变成了由 框架 来创建和注入,这样做可以降低对象之间的耦合度。IOC的主要作用是管理程序的组件,创建对象和维护组件之间的关系
1、什么是Spring 容器?
- 在Spring 容器中,任何的Java类都被当成Bean 组件,通过容器管理和使用(任何的Java类 都可以由Spring 对象 创建对象 并进行管理 以及为维护组件关系)
- Spring 容器中实现了IOC(控制反转) 和AOP(面向切面编程)机制,
- Spring 容器有 ApplicationContext (应用程序上下文)和 BeanFactory (工厂模式)两种类型
2、如何使用Spring 容器
-
先建立一个项目, 准备好相应的 IOC的jar包点击下载建立Spring 项目相关jar包
-
配置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" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.1.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd"><!-- schema格式限制,一下代码必须符合*.xsd中限定的规范 --> <!-- 利用反射机制创建对象 --> <!-- <bean id="bean组件标识" class="包名.类名"></bean> --> <bean id="date" class="java.util.Date"></bean> </beans>
-
创建Spring 容器对象 然后获取容器中创建的对象的组件对象
import java.util.Date; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class ApplicationContextTest1 { public static void main(String[] args) { // 创建Spring 容器对象 ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml"); // 获取容器中对应的对象 Date date = (Date)appContext.getBean("date"); System.out.println(date); // 经典写法 Date date2 = appContext.getBean("date", Date.class); System.out.println(date2); } }
3、Spring 容器创建对象对的三种方式
- 构造器的方式 实例化(默认调用无参构造器)
配置文件:
代码实现:<bean id="对象标识符" class="对象包名.类目名"></bean>
// 创建Spring 容器对象 ApplicationContext appContext = new ClassPathXmlApplicationContext("配置文件相对路径"); // 获取容器中对应的对象 Date date2 = appContext.getBean("对象标识符", 对象包名.类目名);
- 静态工厂的方法 实例化
配置文件:
代码实现:<bean id="对象标识符" class="工厂包名.工厂类名" factory-method="静态方法名"></bean>
// 创建Spring 容器对象 ApplicationContext appContext = new ClassPathXmlApplicationContext("配置文件相对路径"); // 获取容器中对应的对象 Date date2 = appContext.getBean("对象标识符", 对象包名.类目名);
- 实例化工厂方法 实例化(一般是用于 使用一个已经存在的对象 来创建另一个类型的对象)
配置文件:
代码实现:<bean id="对象标识符" class="工厂包名.工厂类名" factory-method="实例化方法名" factory-bean="工厂对象id"></bean>
// 创建Spring 容器对象 ApplicationContext appContext = new ClassPathXmlApplicationContext("配置文件相对路径"); // 获取容器中对应的对象 Date date2 = appContext.getBean("对象标识符", 对象包名.类目名);
4、Spring 中 bean 对象的作用域
使用bean 标签中的 Scope 属性来指定, 值有:
- singleton 单例(默认的), 在每个Spring IOC 容器中,一个 bean,定义一个对象实例
- prototype 非单例 ,一个bean 对应多个对象实例
- request 在一个HTTP 请求中,一个bean 定义一个实例(仅限于Web环境)
- session 在一个HTTP Session 请求中,一个bean 定义一个实例(仅限于Web环境)
- global Session 在一个全局的 HTTP Session 中,一个bean 定义一个实例(仅在基于portlet的Web环境中)
5、bean对象的初始化
一个对象调用构造方法之后 可以进行初始化初始化的手段有两种:
- 第一在beans标签中 加 default-init-method=“初始化方法名” (由于这种方式影响范围比较大,则类中没有定义这个方法,也不会报错)
- 第二种方式是在bean标签中加 init-method=“初始化方法名” (由于这种方式影响范围比较小,则类中没有定义这个方法,会报错)
6、bean对象的销毁
指定Spring容器销毁Bean对象的销毁方法也是两种:
- 第一在beans标签中 加 default-destroy-method=“销毁方法名” (由于这种方式影响范围比较大,则类中没有定义这个方法,也不会报错)
- 第二种方式是在bean标签中加 destroy-method=“销毁方法名” (由于这种方式影响范围比较小,则类中没有定义这个方法,会报错,需要注意的是,必须在单例的模式下)
7、bean对象的延迟实例化
如果 实例的对象是单例的,延迟实例化的两种方式:
- 第一在beans标签中 加 default-lazy-method=“销毁方法名” (默认是false)
- 第二种方式是在bean标签中加 lazy-method=“销毁方法名” (默认是false)
二 什么是DI
Dependence Injection 依赖注入 ,他是对IOC的一种具体实现,他解决的问题是 组件之间的关联关系(装配)问题
三 DI的实现方式
1、setter 注入
看set方法后的标识,去了set,把后面的内容首字母小写
<bean id="对象标识符" class="包名.类名">
<property name="属性名(去了set,把后面的内容首字母小写)" value="注入的值"></property>
<!-- 如果注入的值是 引用类型 -->
<property name="属性名(去了set,把后面的内容首字母小写)" ref="对象标识符"></property>
</bean>
2、构造器 注入
<bean id="对象标识符" class="包名.类名">
<constructor-arg index="属性的下标" value="注入的值"></constructor-arg>
<!-- 如果注入的值是 引用类型 -->
<constructor-arg index="属性的下标" ref="对象标识符"></constructor-arg>
</bean>
3、自动化 注入
在bean 标签上 指点一个属性 autowire 可以根据指定的值 进行 自动化 注入
- byName 检查Spring 中有没有和属性名一致的 对象 id 如果找不到 不会报错
- constructor 可以使用<constructor-arg 占参数位置 然后 剩下的参数 可以采用自动化 优先使用名字进行匹配 如果名字不符合会启用类型查找
- byType 根据Spring 中属性类型进行查找,如果找不到或者多个类型相同的,会报错
<!-- autowire="constructor" --> <bean id="对象标识符" class="包名.类名" autowire="constructor"> <constructor-arg index="属性的下标" value="注入的值"></constructor-arg> <!-- 如果注入的值是 引用类型 --> <constructor-arg index="属性的下标" ref="对象标识符"></constructor-arg> </bean> <!-- autowire="byName" --> <bean id="对象标识符" class="包名.类名" autowire="byName"> <property name="属性名" value="值"></property> </bean> <!-- autowire="byType" --> <bean id="对象标识符" class="包名.类名" autowire="byType"> <property name="属性名" value="值"></property> </bean>
4、bean 参数的注入
-
简单值得注入 主要以setter 注入为主, 构造器注入为辅
<bean id="对象标识符" class="包名.类名"> <property name="属性名" value="值"></property> </bean>
-
集合类型的注入List ,Set(Set的值会排重),Map, Properties等
<bean id="对象标识符" class="包名.类名"> <property name="属性名"> <list> <value>值</value> <value>值</value> </list> </property> <property name="属性名"> <set> <value>值</value> <value>值</value> </set> </property> <property name="属性名"> <map> <entry key="键" value="值"></entry> <entry key="键" value="值"/> </map> </property> <property name="属性名"> <props> <prop key="键">值</prop> <prop key="键">值</prop> </props> </property> </bean>
-
自定义List,Set,Map, Properties等集合
<util:list id="list自定义对象标识符"> <value>值</value> </util:list> <util:set id="set自定义对象标识符"> <value>值</value> </util:set> <util:map id="map自定义对象标识符"> <entry key="键" value="值" ></entry > </util:map> <util:properties id="properties自定义对象标识符"> <prop key="建">值</prop> </util:properties> <bean id="对象标识符" class="包名.类名"> <property name="属性名" ref="list自定义对象标识符"></property> <property name="属性名" ref="set自定义对象标识符"></property> <property name="属性名" ref="map自定义对象标识符"></property> <property name="属性名" ref="properties自定义对象标识符"></property> </bean>
-
文件引入及表达式的引用
表达式值得引用:#{key}
表达式对象的引用:#{自定义对象标识符.key}
引用其他配置文件:<util:properties id="自定义对象标识符" location="classpath:需要引入配置文件的相对路径"/>
数据库连接池的例子:
1、表达式的使用:<!-- 连接数据库信息 单独封装成一个集合 --> <util:properties id="db"> <prop key="driverClassName">oracle.jdbc.OracleDriver</prop> <prop key="url">jdbc:oracle:thin:@127.0.0.1:1521:xe</prop> <prop key="username">system</prop> <prop key="password">123456</prop> </util:properties> <!-- 配置一个数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="#{db.driverClassName}"/> <property name="url" value="#{db.url}"/> <property name="username" value="#{db.username}"/> <property name="password" value="#{db.password}"/> </bean>
2、引入其他配置文件 及 表达式的使用
<!-- 引入其他配置文件 --> <util:properties id="db" location="classpath:db.properties"></util:properties> <!-- 配置一个数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="#{db.driverClassName}"/> <property name="url" value="#{db.url}"/> <property name="username" value="#{db.username}"/> <property name="password" value="#{db.password}"/> </bean>
四 组件扫面
为了简化之前的配置文件
Spring提供了一套基于注解(标注)配置文件的使用方法,使用该方法可以大大简化XML配置信息。
1、 和组件创建相关的注解
注解 | 描述 |
---|---|
@Component | 通用层组件注解 |
@Repository | 持久化层组件注解 |
@Service | 业务逻辑层组件注解 |
@Controller | 控制层组件注解 |
2、 组件创建及注解的使用
base-package:
指定组件扫面的开始位置,最扫描指定包及其子包下的类
配置文件:
<?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-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 开启组件扫描 -->
<context:component-scan base-package="包名"/>
</beans>
添加注解的类:
package com.test.bean;
import org.springframework.stereotype.Component;
// @Component() // 获取的值是类名首字母小写
// @Component(value="player") // 获取的值的义名字
@Component("player") // 获取的值的义名字
public class Player {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTeam() {
return team;
}
public void setTeam(String team) {
this.team = team;
}
public Player(String name, String team) {
super();
this.name = name;
this.team = team;
}
public Player() {
super();
name="no name";
team="no team";
}
private String name;
private String team;
}
创建对象及调用:
package com.test.test;
import com.test.bean.Player;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ComponentScanTest {
@Test
public void test() {
ApplicationContext app =
new ClassPathXmlApplicationContext("applicationContext.xml");
Player player = app.getBean("player", Player.class);
System.out.println("player.getName(): "+player.getName());
System.out.println("player.getTeam(): "+player.getTeam());
}
}
结果打印:
3、组件扫描相关的其他注解(标注)
- @Scrop : 控制bean的作用域,与之前 bean 标签中scope 属性完全相同
- @PostConstructor :初始化方法, 和之前的init-method 属性相同
- @PreDestroy :对象销毁之前调用的方法, 和之前的destroy-method 属性相同
4、和组件装配(配置)相关的注解(标注)
- @Value :注入简单值,复杂值需要结合 #{key} 表达式
- @AutoWired :可以加在 成员变量、 set方法、带参构造方法上 优先采用类型注入 找不到则启用名字查找 使用**@Qualifier(“名字”) **来指定名字查找
- @Resource :可以加载成员变量、set方法上,并且 这个标注 是java 语言库中的,优先使用名字查找 找不到启用类型查找,还可以使用name属性来指定名字