第三章 使用 spring 的 IOC解决程序耦合
使用spring已将帮我们集成好的环境降低程序的耦合度:
3.1 准备 spring 的 的 开发包
- 官网:
- 下载地址:
- [http://repo.springsource.org/libs-release-local/org/springframework/spring](http://repo.springsource.org/libs-release-
3.2 Spring基于XML文件的IOC环境搭建和入门
(1)在xml文件中配置好
-
创建配置配置文件bean.xml
-
先导入Spring的约束(在官网上找)
<?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"> </beans>
-
在配置文件中创建好要加载的service和dao接口的实现类对象的配置
<?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"> <!-- 把对象的创建交给spring来管理--> <!-- id是唯一标准,在以后的方法中要想创建该对象可以使用该id--> <!-- class 是指向service或者dao的实现类 的全限定类名--> <bean id="accountService" class="com.zy.service.impl.AccountServiceImpl" /> <bean id="accountDao" class="com.zy.dao.Impl.AccountDaoImpl" /> </beans>
- 在标签中包含
- 每一个标签来指定一个series或者dao的实现类
- 标签有id和class两个属性
id属性
: 是唯一标准,在以后的方法中要想创建该对象可以使用该idclass属性
: 是指向service或者dao的实现类 的全限定类名
-
-
和2.1.3中配置bean.properties文件一样,只不过在配置该xml,spring会识别改xml文件,调用spring的容器方法会自动生成该对象
(2)怎么使用配置好的xml文件??
-
还是结合上面那个案例:
-
在series业务层中调用dao持久层的对象:
//账户的业务成实现类:业务成接口的实现类 public class AccountServiceImpl implements IAccountService { //1 获取核心容器对象(核心容器ApplicationContext有三个实现类) //在这里我们用的是ClassPathXmlApplicationContext实现类 //在构造方法中传入一个bean.xml配置文件的路径 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); //2 根据在bean.xml配置中的id获取bean对象,使用getBean()方法 IAccountDao iAccountDao = (IAccountDao)applicationContext.getBean("accountDao"); public void saveAccount(Account account) { iAccountDao.saveAccount(account); } }
-
-
在ui表现层调用业务层的对象:
//模拟一个表现层,用于调用业务层 public class Client { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); IAccountService iAccountService = (IAccountService) applicationContext.getBean("accountService"); System.out.println(iAccountService); iAccountService.saveAccount(new Account()); } }
3.3 ApplicationContext容器接口的三个实现类
ClassPathXmlApplicationContex
t实现类- 他可以加载类路径下的配置文件
- 要求配置文件必须在类路径下,不在类路径下加载不了。
- 在构造函数的形参中传入一个配置文件的全限定类名
FileSystemXmlApplicationContext
实现类 -------- 用的较少- 他可以加载磁盘任意路径下的配置文件(必须有访问权限)
- 在构造函数的形参中传入一个 路径
AnnotationConfigApplicationContext
实现类- 他是读取注解创建容器的,是后面的内容
3.4核心容器的两个接口引发出来的问题
- 两个核心容器接口:
ApplicationContext
接口BeanFactory
接口
ApplicationContext
接口 -------在开发中更多的是采用此接口定义容器对象- 他在构建核心容器的时,构建对象采取的策略是
立即加载的方式
立即加载方式:只要一读取完bean.xml配置文件,马上创建出配置文件中配置的对象
- 当执行完
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
的时候,配置文件中的对象都被创建出来放到容器中了 - 该接口的三个实现类在3.1.3已经说明了
单例对象适应
- 他在构建核心容器的时,构建对象采取的策略是
BeanFactory
接口- BeanFactory 才是 Spring 容器中的顶层接口,ApplicationContext 是它的子接口。
- 他在构建核心容器的时,构建对象采取的策略是
延迟加载的方式
延迟加载的方式:什么时候根据id获取对象了,什么时候才会真正的去创建对象
- 当执行完
beanFactor.getBean("accountService");
方法的时候,该对象才会被创建出来放到容器中 - 该接口也有很多的实现类,其中一个实现类是XmlBeanFactory(已经过时)构造方法中传入一个Resource对象(
new ClassPathResource("bean.xml")
) 多例对象适应
3.5 spring对Bean的管理细节
(1)创建Bean对象的三种方式
-
使用
默认的构造函数
创建对象,并存入spring容器中-
如何配置,使在spring的IOC创建该对象的时候,使用构造函数创建???
-
在配置文件中使用bean标签,配置id和class属性之后,其没有其他属性和标签的时(在后面使用其他标签或者属性来使用其他的构造函数)
默认调用class指向类的构造方法
-
如果创建的对象
没有默认构造函数,该对象无法创建
-
<!-- 方式1 使用默认构建函数 --> <!-- id是唯一标准,在以后的方法中要想创建该对象可以使用该id--> <!-- class 要使用什么类中的方法来创建该对象--> <!-- 调用class指向类的默认构造方法创建--> <bean id="accountService" class="com.zy.service.impl.AccountServiceImpl" />
-
-
-
通过
某个类的方法创建(普通工厂中的方法创建对象)
一个对象,并存入spring容器中-
如何配置,使在spring的IOC创建该对象的时候,使用
其他类的一个方法创建对象
???-
在bean标签中,id还是唯一标志,class不在需要指定,其次在添加 factory-bean属性指向实例化工厂类的id,根据该类的id找到该类在工厂中的对象,在通过factory-method 属性找到对象具体的方法,创建出class指定的类
-
factory-bean 属性:用于指定实例工厂 bean 的 id。
-
factory-method 属性:用于指定实例工厂中创建对象的方法。
-
<!-- 此种方式是: 调用InstanceFactory实现类中的createAccountService方法创建AccountService对象 先把工厂的创建交给 spring 来管理。 然后在使用工厂的 bean 来调用里面的方法 factory-bean 属性:用于指定实例工厂 bean 的 id。 factory-method 属性:用于指定实例工厂中创建对象的方法。 --> <!-- 在工厂中创建InstanceFactory实现类,通过instancFactory该id调用工厂中创建好的id--> <bean id="instancFactory" class="com.itheima.factory.InstanceFactory"></bean> <!-- 通过该id来调用容器中的方法的时候,先通过bean工厂对象factory-bean的id找到工厂中创建好的对象,在通过factory-method调用指定的方法--> <bean id="accountService" factory-bean="instancFactory" factory-method="createAccountService"></bean>
-
-
-
使用
工厂中的静态方法(某个类中的静态方法
)创建对象,,并存入spring容器中-
如何配置,使在spring的IOC创建该对象的时候,使用
其他类的一个静态方法创建对象
???-
在bean标签中,id还是唯一标志,其次在添加class属性指定为静态工厂的全限定类名,factory-method属性指定静态方法名
-
注意:
class属性此处指定的是 静态方法的类 ,class属性指向的是要调用哪个类的方法,而不是要创建什么实现类(应为在工厂中都是Object类型)
-
factory-method 属性:用于指定实例工厂中创建对象的静态方法。
-
<!-- 此种方式是: 使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器 id 属性:指定 bean 的 id,用于从容器中获取 class 属性:指定静态工厂的全限定类名 factory-method 属性:指定生产对象的静态方法 --> <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="createAccountService"></bean>
-
-
(2)bean标签的总结及bean对象的作用范围(scope属性):
-
bean标签的作用:
- 用于配置对象让 spring 来创建的
- 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功
-
bean标签的属性:
-
id属性
:给对象在容器中提供一个唯一标识。用于获取对象。 -
class属性
:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数,要调用该类中的方法 -
factory-bean 属性
:用于指定实例工厂 bean 的 id,调用该bean对象 -
factory-method 属性
:用于指定实例工厂中创建对象的方法。 -
init-method 属性
:创建该方法的时候,执行什么方法 -
destroy-method 属性
: 销毁该方法的时候,执行什么方法 -
scope属性 :指定对象的作用范围。
-
取值:
- singleton :默认值,单例的.
- prototype :多例的.
- request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.(作用在web应用的请求范围)
- session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.(作用在web应用的会话范围)
- global_session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于 session.(用作于集群环境的会话范围(全局会话范围),如果没有集群环境(只有一个服务器),他就是session)
- 当没有服务器集群(只有一个服务器 ),global_session就session
- 当有服务器集群的时候,各个服务器的session实现共享,就是global_session
-
-
(3) Bean对象的生命周期
- 单利对象和多例对象的生命周期不一样
单例对象: scope="singleton"
单例对象就是立即创建容器对象- 一个应用只有一个对象的实例。它的作用范围就是整个引用
- 生命周期:
- 出生时间:当应用加载(bean.xml文件被解析),创建容器时,对象就被创建了
- 存活时间:只要容器在,对象一直活着
- 死亡时间:当应用卸载,销毁容器时,对象就被销毁了
- 总结:单例对象的生命周期和容器的生命周期相同
多例对象:scope="prototype"
多例对象就是延迟创建容器对象- 每次访问对象时,都会重新创建对象实例。
- 生命周期:
- 出生时间:当使用对象时(applicationContext.getBean(“accountDao3”)),创建新的对象实例
- 存活时间:只要对象在使用中,就一直活着
- 死亡时间:当对象长时间不用时,被 java 的垃圾回收器回收了。
- 总结:和普通对象一样,长时间不用,被垃圾回收机制销毁
3.6 spring 的依赖注入
(1) 什么是依赖注入:
Dependency Injection。它是 spring 框架核心 ioc 的具体实现。IOC的作用是降低程序之间的依赖关系(在当前类中需要用到其他类的对象),各个类之间的依赖关系以后都交给spring来维护,我们只需要在配置文件中说明配置什么样的对象,告诉spring是在创建该对象的时候要传入的参数,就叫做依赖的注入
(调用本类的其他构造函数或者set方法构造本类的一个对象)
(2)依赖注入能注入的数据有三类:
- 基本类型和String类型
- 其他的Bean类型(在配置文件或者在注解配置过的Bean)
- 复杂类型 / 集合类型
(3) 注入的方式有三种:
- 第一种:使用构造函数提供
- 第二种:使用set方法提供
- 第三种:使用注解提供(在后面讲注解的时候,在讲)
- 第四种:使用p标签重命名
(4)使用构造函数注入:
-
需要使用的标签(在bean标签内部使用):
constructor-arg标签:
- 常用属性:
- type属性:指定参数在构造函数中的数据类型(不常用)
- index属性:指定参数在构造函数参数列表的索引位置(不常用)
name属性:指定参数在构造函数中的名称 (常用)
value属性:指定对于形参传入的值,它能赋的值是基本数据类型和 String 类型(常用)
ref 属性:指向其他Bean对象id,它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
- 常用属性:
-
优势:
- 可以在创建对象的时候,进行属性的注入,调用不同的构造方法
-
弊端:
- 根据传入的值来构建不同的构造方法,前体是要有这个构造方法
- 对于不同的传参,需要各自各样的构造方法
-
案例:
-
要Bean对象:User
-
public class User { //两个普通的类型 private String name; private int sex; //一个实体类类型 private Date date; public User() { } public void setName(String name) { this.name = name; } public void setSex(int sex) { this.sex = sex; } public void setDate(Date date) { this.date = date; } public String getName() { return name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", sex=" + sex + ", date=" + date + '}'; } }
-
-
容器配置文件:
-
<bean id="date" class="java.util.Date"/> <!--通过构造函数来创建user对象--> <bean id="user_0" class="com.zy.domain.User"/> <!-- 构造函数注入(传递三个参数的构造函数) --> <bean id="user_1" class="com.zy.domain.User"> <constructor-arg name="name" value="zhangsan"/> <constructor-arg name="sex" value="18"/> <constructor-arg name="date" ref="date"/> </bean> <!-- 在配置文件加载的时候,容器中会有两个User对象,一个是通过默认构造函数构造,一个是使用其他的构造函数构造-->
-
-
(5)使用set方法注入:
-
涉及到的标签:
property标签(在bean标签内部使用)
- 常见属性:
name属性:用于指定注入时所调用的set方法名称
value属性:指定对于形参传入的值,它能赋的值是基本数据类型和 String 类型(常用)
ref 属性:指向其他Bean对象id,它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
- 常见属性:
-
优点:不用各样的构造方法,使用set方法进行赋值
-
注意:在name属性的时候,set方法去掉set,在把第一个字母改为小写
-
案例:
-
<!--set方式注入--> <bean id="user_2" class="com.zy.domain.User"> <property name="name" value="张阳"/> <property name="date" ref="date"/> </bean>
-
(6) 注入集合数据:
-
采用set的方法给类的每一个属性进行赋值
-
使用到的标签:
<list> <array> <set> <map> <props>
用于给List结构的集合注入的标签: <list> <array> <set>
用于给Map结构的集合注入的标签:<map> <props>
- 总结:结构相同,标签可以互换
- 这些标签常见的属性:
在<list> <array> <set>,直接写多个<value>,value标签内部写值
- 在
<map>
使用<entry key="map的键" value="map的值"/>
或者<entry key="map的键"><value>map的值</value></entry>
- 在
<props>
标签中,使用<prop key="键">值</prop>
-
案例:
-
要容器中创建的Bean对象:
-
public class User2 { private String[] strings; private List<String> list; private Set<String> set; private Map<String, String> map; private Properties properties; public User2() { } public void setStrings(String[] strings) { this.strings = strings; } public void setList(List<String> list) { this.list = list; } public void setSet(Set<String> set) { this.set = set; } public void setMap(Map<String, String> map) { this.map = map; } public void setProperties(Properties properties) { this.properties = properties; } @Override public String toString() { return "User2{" + "strings=" + Arrays.toString(strings) + ", list=" + list + ", set=" + set + ", map=" + map + ", properties=" + properties + '}'; } }
-
-
配置文件:
-
<bean id="user_3" class="com.zy.domain.User2"> <property name="list"> <list> <value>aaa</value> <value>bbb</value> <value>ccc</value> </list> </property> <property name="strings"> <list> <value>123</value> <value>456</value> <value>789</value> </list> </property> <property name="set"> <set> <value>987</value> <value>876</value> <value>765</value> </set> </property> <property name="map"> <map> <entry key="abc" value="123"/> <entry key="bcd"><value>456</value></entry> </map> </property> <property name="properties"> <props> <prop key="zzz">111</prop> <prop key="FFF">222</prop> <prop key="ddd">333</prop> </props> </property> </bean>
-
-