Spring基于XML的IOC细节
一、Spring中的工厂的类结构图
1. BeanFactory和ApplicationContext的区别
-
BeanFactory
是Spring容器的顶层接口。适用于多例模式- 需要使用时创建对象
-
ApplicationContext
是BeanFactory
的子接口,适用于单利模式【常用】- 只要一读取配置文件,默认情况下就会创建对象。
2. ApplicationContext接口的实现类
-
ClassPathXmlApplicationContext
- 【推荐!】从根路径下加载配置文件
-
FileSystemXmlApplicationContext
- 【忘记吧!】从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
-
AnnotationConfigApplicationContext
- 当使用注解配置容器对象时,需要使用此类来创建spring容器。它用来读取注解。
二、IOC中bean标签和管理对象细节
bean.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.xsd">
<!--
bean 标签:用于配置让spring 创建对象,并且存入ioc容器中
id 属性:对象的唯一标识
class 属性:指定要闯将对象的全限定类名
-->
<!-- 配置service -->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl" ></bean>
</beans>
如果有多个配置文件,可以在主配置文件中使用
<import>
标签导入
...
<import resource="classpath*:applicationContext1.xml"></import>
</beans>
1. bean标签的作用
- 用于配置对象让spring来创建。
- 【默认情况下】调用的是类中的无参构造函数【反射】。如果没有无参构造函数则不能创建成功。
2. bean标签的属性介绍
<bean
id="accountDao"
-- 给对象在容器中提供一个唯一标识。用于获取对象。
class="com.itheima.dao.impl.AccountDaoImpl"
-- 指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
scope="prototype"
-- 指定对象的作用范围:取值及其含义如下
-- singleton: 默认值,单例的【常用】
-- prototype: 多例的
-- request: web项目中,Spring创建一个Bean对象,将对象存入request域中,请求范围
-- session: web项目中,Spring创建一个Bean对象,将对象存入session域中,会话范围
-- golbal-session: web项目中,【应用在Protlet环境,集群环境】,如果没有Protlet
环境,那么golbalSession相当于session
init-method="init"
-- 指定类中的初始化方法的名称
lazy-init="true"
-- 延迟加载,默认false
destroy-method="destory"
-- 指定类中销毁方法的名称
></bean>
3. bean的作用范围和生命周期
1. 单例对象:属性scope="singleton"
一个应用只有一个对象的实例。作用范围就是整个引用
-
生命周期:
- 对象出生:当应用加载,创建容器时,对象就被创建了。
- 对象活着:容器在,对象在
- 对象拜拜:应用卸载、容器销毁时。
2. 多例对象:属性scope="prototype"
每次访问对象时,都会重新创建对象实例;销毁方法就不会被执行了!控制权交给GC了
-
生命周期:
- 对象出生:当使用对象时
- 对象活着:对象在被使用中
- 对象拜拜:长时间不使用,被java的垃圾回收机制GC回收了
3. 实例化Bean的三种方式
1. 使用默认无参构造函数
在默认情况下:会根据默认无参构造函数来创建类对象,如果没有默认无参构造函数,将会创建失败。
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"/>
2. spring管理【静态】工厂-使用静态工厂的方式创建对象【使用jar包中的类】
比如,如下:使用StaticFactory类中静态方法createAccountService创建对象,并存入spring容器
- 模拟一个静态工厂类,创建业务层实现类
public class StaticFactory {
public static AccountService createAccountService() {
return new AccountServiceImpl();
}
}
- 配置文件bean.xml
<bean
id="accountService"
-- 指定bean的id,用于从容器中获取
class="com.itheima.factory.StaticFactory"
-- 指定【静态工厂】的全限定类名
factory-method="createAccountService"
-- 指定生产对象的静态方法
></bean>
3. spring管理【实例】工厂-使用实力工厂的方法创建对象【使用jar包中的类】
先把工厂的创建交给spring来管理,然后再使用工厂的bean来调用里面的方法;
演示:模拟一个实例工厂,创建业务层实现类,此工厂创建对象,必须现让spring创建该工厂的实例对象,再调用方法
- 实例工厂类
public class InstanceFactory {
public AccountService createAccountService() {
return new AccountServiceImpl();
}
}
- bean.xml配置
<bean id="instancFactory" class="com.itheima.factory.InstanceFactory"></bean>
-- 实例工厂的bean配置
<bean id="accountService"
factory-bean="instancFactory"
-- 指定实例工厂bean的id
factory-method="createAccountService"
-- 指定实例工厂中创建对象的方法
></bean>
三、Spring的依赖注入
依赖注入:Dependency Injection,spring框架核心ioc的具体实现。程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。
1. 构造函数注入
顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做,而是通过配置的方式,让spring框架来为我们注入。
- 被注入的类案例:【类中需要提供一个对应参数列表的构造函数】
public class AccountServiceImpl implements AccountService {
private String name;
private Integer age;
private Date birthday;
public AccountServiceImpl(String name, Integer age, Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
...
- bean.xml对应的配置的标签
constructor-arg
介绍
<bean id="xxx" class="xxx.xxx.xxx">
<constructor-arg
index="1"
-- (一般不用)指定参数在构造函数参数列表的索引位置
type="Integer"
-- (一般不用)指定参数在构造函数中的数据类型
name="name"
-- 【常用】指定参数在构造函数中的名称,形参名称
-\-\ 上面三个是找给谁赋值,下面两个是赋什么值 /-/-
value="张三"
-- 它能赋的值是基本的 数据类型 和String类型
ref="now"
-- 能赋的值为其他的bean类型 id,在配置文件中得配置该bean标签,
如下
></constructor-arg>
</bean>
<bean id="now" class="java.util.Date"></bean>
- bean.xml配置案例
<bean id="now" class="java.util.Date"></bean>
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<constructor-arg name="name" value=" 张三 "></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
2. set方法注入【开发中的常客】
需要在类中提供set方法
- bean类文件案例
public class AccountServiceImpl implements IAccountService {
private String name;
private Integer age;
private Date birthday;
public void setName(String name) { this.name = name; }
public void setAge(Integer age) { this.age = age; }
public void setBirthday(Date birthday) { this.birthday = birthday; }
...
- 涉及的标签
property
<property
name="name"
-- 被注入类的对应set方法“set”之后的部分首字母小写!
value="test"
-- 给属性赋值为基本数据类型和string类型的
ref="now"
-- 给属性赋值是其他bean类型的
></property>
<bean id="now" class="java.util.Date"></bean>
- bean.xml配置案例
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<property name="name" value="test"></property>
<property name="age" value="21"></property>
<property name="birthday" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>
3. 使用p名称空间注入数据(本质还是调用set方法)【了解】
此种方式通过xml中导入p名称空间,使用
p:propertyName
来注入数据,本质还是调用set方法实现注入功能
java代码【->2. set方法注入【开发中的常客】->1. bean类文件案例】
配置文件案例
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
-- 引入此命名空间约束
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 id="accountService"
class="com.itheima.service.impl.AccountServiceImpl4"
p:name="test" p:age="21" p:birthday-ref="now"/>
-- p:propertyName的形式
</beans>
4. 注入集合属性
给类中的集合成员传值,用的也是注入的方式,只不过变量的数据类型都是集合。一般注入的集合类型为List、Set、Map、Properties
- 被注入的JavaBean
public class AccountServiceImpl implements AccountService {
private String[] myStrs;
private List<Object> myList;
private Set<Object> mySet;
private Map<String, Object> myMap;
private Properties myProps;
public String[] getMyStrs() { return myStrs; }
public void setMyStrs(String[] myStrs) { this.myStrs = myStrs; }
@Override
public List<Object> getMyList() { return myList; }
public void setMyList(List<Object> myList) { this.myList = myList; }
public Set<Object> getMySet() { return mySet; }
public void setMySet(Set<Object> mySet) { this.mySet = mySet; }
public Map<String, Object> getMyMap() { return myMap; }
public void setMyMap(Map<String, Object> myMap) { this.myMap = myMap; }
public Properties getMyProps() { return myProps; }
public void setMyProps(Properties myProps) { this.myProps = myProps; }
...
- bean.xml中对应的配置
- 注入集合数据时,只要结构相同,标签可以互换
- 注入集合时,根据类型选择标签,同种集合类型通用
- List结构:
<array></array>
<list></list>
<set></set>
- Map结构的
<map></map>
<entry></entry>
<prop></prop>
<props></props>
- List结构:
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl" ></bean>
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" lazy-init="true">
<!-- 给数组注入数据 -->
<property name="myStrs">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<!-- 注入list集合数据 -->
<property name="myList">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
<ref bean="accountDao"></ref>
</list>
</property>
<!-- 注入set集合数据 -->
<property name="mySet">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
<ref bean="accountDao"></ref>
</set>
</property>
<!-- 注入Map数据 -->
<property name="myMap">
<props>
<prop key="A">AAA</prop>
<prop key="B">BBB</prop>
</props>
</property>
<!-- 注入properties数据 -->
<property name="myProps">
<map>
<entry key="A" value="AAA"></entry>
<entry key="D" value-ref="accountDao"></entry>
<entry key="B">
<value>BBB</value>
</entry>
<entry key="C">
<ref bean="accountDao"></ref>
</entry>
</map>
</property>
</bean>
</beans>