主流框架二:Spring(2)Spring的IOC、依赖注入
一、IOC的概念和Spring中基于XML的IOC
(1)IOC概念
IOC:控制反转(把创建对象的权利交给框架)
原来:
我们在获取对象时,都是采用 new 的方式。是主动的。
现在:
我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。
这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。
明确ioc 的作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。
(2)使用Spring的IOC来解决程序的耦合
使用 spring 解决依赖关系,并不是真正的要做增删改查操作,所以此时我们没必要写实体类。
一、业务层的接口和实现类
/**
* 账户的业务层接口
*/
public interface IAccountService {
/**
* 保存账户(此处只是模拟,并不是真的要保存)
*/
void saveAccount();
}
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService {
//业务service层调用dao的方法,必须先有dao对象,此处有依赖关系
private IAccountDao accountDao = new AccountDaoImpl();// 此处的依赖关系有待解决
@Override
public void saveAccount() {
accountDao.saveAccount();
}
}
二、持久层接口和实现类
/**
* 账户的持久层接口
*/
public interface IAccountDao {
/**
* 保存账户
*/
void saveAccount();
}
/**
* 账户的持久层实现类
*/
public class AccountDaoImpl implements IAccountDao {
@Override
public void saveAccount() {
System.out.println("保存了账户");
}
}
1.基于xml开发的配置
(1)导包后在resources配置目录下创建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>
(2)让spring管理资源,在配置文件中配置 service 和 dao
<!-- 把对象的创建交给spring来管理-->
<!-- bean 标签:用于配置让 spring 创建对象,并且存入 ioc 容器之中
id 属性:对象的唯一标识。
class 属性:指定要创建对象的全限定类名
-->
<bean id="accountService" class="com.swpuembedded.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.swpuembedded.dao.impl.AccountDaoImpl"></bean>
(3)模拟一个表现层(web)来测试配置是否成功
/**
* 模拟一个表现层
*/
public class Client {
/**
* 使用 main 方法获取容器测试执行
*/
public static void main(String[] args) {
//1.使用 ApplicationContext 接口,就是在获取 spring 容器
//通过classpath来获取配置文件中bean内容的容器
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据 bean 的 id 从spring容器(applicationContext)获取对象
IAccountService aService = (IAccountService) ac.getBean("accountService");
IAccountDao aDao = (IAccountDao) ac.getBean("accountDao");
System.out.println(aService);
System.out.println(aDao);
}
}
2.ApplicationContext
ApplicationContext实际是就是spring中的一个工厂,通过工厂来获取bean对象。
(1)Spring的工厂类结构图
BeanFactory 才是 Spring 容器中的顶层接口。 ApplicationContext 是它的子接口。
核心容器的两个接口引发出的问题
* ApplicationContext: 单例对象 (智能) 更多采取此接口
* 它在构建核心容器时,创建对象采取的思想策略是采用立即加载的方式,也就是说只要一读取完配置文件就创建配置文件中配置的对象。
* BeanFactory: 多例对象
* 它在构建核心容器时,创建对象采取的思想策略是采用延迟加载的方式,也就是说什么时候根据id获取对象了,什么时候才真正的创建对象。
(2)ApplicationContext的三个常用的实现类
获取Spring的IOC核心容器,并根据ID获取对象
* (1)ClassPathXmlApplicationContext
* 可以加载类路径下的配置文件(配置文件在类的路径下,不在无法加载)
* (2)FileSystemXmlApplicationContext
* 可以加载磁盘任意路径,毕竟File,(必须有访问的权限)
* (3)AnnotationConfigApplicationContext
* 可以用于读取注解来创建容器,之后用的现在是xml配置
二、IOC中 bean标签 和 管理对象细节
(1)bean标签
在我们的bean.xml配置文件中,我们需要关注一些细节。
bean标签:用于配置对象让 spring 来创建的。默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。
id:给对象在容器中提供一个唯一标识。用于获取对象。
class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
scope:指定对象的作用范围。
* singleton :默认值,单例的.
* prototype :多例的.
<!-- 把对象的创建交给spring来管理-->
<bean id="accountService" class="com.swpuembedded.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.swpuembedded.dao.impl.AccountDaoImpl"></bean>
(2)Spring对bean的管理细节
1.创建bean的三种方式
第一种方式:使用默认构造函数创建
在spring的配置文件中使用bean标签,配以id和class且没有其他属性和标签时,采用的就是默认构造函数bean对象,此时如果类中没有默认构造函数,则实例化对象(AccountServiceImpl)无法创建
<bean id="accountService" class="com.swpuembedded.service.impl.AccountServiceImpl"></bean>
/**
* @author Mango
* 账户的业务层接口的实现类
*/
public class AccountServiceImpl implements IAccountService {
public AccountServiceImpl() {
System.out.println("AccountServiceImpl对象创建了");
}
public void saveAccount() {
System.out.println("service中的saveAccount方法执行了");
}
}
第二种方式:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象并存入spring容器)
该类存在jar包,InstanceFactory只是模拟而已
<bean id="instanceFactory" class="com.swpuembedded.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
/**
1. @author Mango
2. 模拟一个工厂类,该类存在jar包,无法通过修改源码方式来提供默认构造函数
*/
public class InstanceFactory {
public IAccountService getAccountService() {
return new AccountServiceImpl();
}
}
第三种方式:使用静态工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
<bean id="accountService" class="com.swpuembedded.factory.StaticFactory" factory-method="getAccountService"></bean>
与第二种方法的区别只是方法成为了静态方法,便可在class后加factory-method
/**
3. @author Mango
4. 使用静态工厂中的静态方法创建对象
*/
public class StaticFactory {
public static IAccountService getAccountService() {
return new AccountServiceImpl();
}
}
2.bean对象的作用范围
bean标签中的scope属性
(1)作用:用于指定bean的作用范围
一个应用只有一个对象的实例。或者是,每次访问对象时,都会重新创建对象实例。
(2)取值:常用的就是单例和多例的
singleton:单例的(默认值)
prototype:多例的
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
3.bean对象的生命周期
(1)单例对象
创建:当容器创建时,对象变创建
存在:只要容器存在,对象一直存在
销毁:只要容器销毁,便销毁了
注:单例对象与容器的生命周期相同
(2)多例对象
创建:当我们使用对象时,spring框架才为我们创建
存在:对象只要是在使用过程中就一直存在
销毁:当对象长时间的不使用时,且没有别的对象引用时,由Java的垃圾回收器回收
三、依赖注入(Dependency Injection)
概念:在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明依赖关系的维护,就成为依赖注入
(1)注入数据类型
依赖注入(使用bean标签):能注入的数据,有三类
(1)基本类型和String
(2)其他的bean类型(在配置文件中或者注解配置过得bean)
<bean id="accountService2" class="com.swpuembedded.service.impl.AccountServiceImpl2">
<property name="name" value="泰斯特"></property>
<property name="age" value="18"></property>
<property name="birthday" ref="now"></property>
</bean>
<!-- 通过字节码文件新建一个birthday对象存入容器,用id获取-->
<bean id="now" class="java.util.Date"></bean>
(3)复杂类型/集合类型
(2)注入方式
注入的方式,有三种:
第一种:使用构造函数来提供
顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 spring 框架来为我们注入。
使用的标签是:constructor-arg
标签出现的位置是:bean标签的内部
标签中的属性
type:用于指定要注入的数据的数据类型,数据类型也是构造函数中的某个或者某些参数的类型
index:用于指定要注入的数据构造函数中指定索引位置的参数赋值,索引的位置为0开始
name:用于指定给构造函数中指定名称的参数赋值 (常用的)
//用于指定给构造函数中哪个参数赋值
value:用于给基本类型(string)的数据
ref:用于指定其他的bean类型,它指的就是在spring的IOC核心容器中出现过的bean对象
优势:在获取bean对象时,注入数据时必须的操作,否则对象无法创建成功
弊端:改变了bean对象的实例化方式,使我们在创建对象时如果用不到也必须提供。
<bean id="accountService" class="com.swpuembedded.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="test"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<!--ref引用关联的bean对象-->
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
第二种:使用set方法提供
涉及的标签:property 出现的位置:bean标签的内部
标签的属性:
name:name:用于指定注入时所调用的set方法名称,不管类中的属性名称
value:用于给基本类型(string)的数据
ref:用于指定其他的bean类型,它指的就是在spring的IOC核心容器中出现过的bean对象
优势:创建对象时没有明确的限制。可以直接使用默认构造函数
弊端:如果某个成员必须有值,则获取对象时有可能set方法没有执行
<bean id="accountService2" class="com.swpuembedded.service.impl.AccountServiceImpl2">
<property name="name" value="泰斯特"></property>
<property name="age" value="18"></property>
<property name="birthday" ref="now"></property>
</bean>
第三种:使用注解来提供(之后具体写)
(3)注入集合属性—多个值
顾名思义就是给类中的集合成员传值。
它用的也是set方法注入的方式,只不过变量的数据类型都是集合。我们这里介绍注入数组List,Set,Map,Properties。
在需要引入依赖的类中:
/***/
public class AccountServiceImpl implements IAccountService {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
@Override
public void saveAccount() {
System.out.println(Arrays.toString(myStrs));
System.out.println(myList);
System.out.println(mySet);
System.out.println(myMap);
System.out.println(myProps);
}
}
<!-- 复杂类型的注入/集合类型注入
用于给list结构集合注入的标签-
list array set
用于Map结构集合注入的标签
map props
结构相同,标签可以替换
-->
<bean id="accountService3" class="com.swpuembedded.service.impl.AccountServiceImp3">
<!-- myStrs-->
<property name="myStrs">
<array>
<value>AAA</value>
<value>BBB</value>
</array>
</property>
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
</list>
</property>
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="testA" value="AAA"></entry>
<entry key="testB" value="BBB"></entry>
</map>
</property>
<property name="myProperties">
<props>
<prop key="testC">CCC</prop>
<prop key="testD">DDD</prop>
</props>
</property>
</bean>