Spring IOC
1. 主要内容
2. Spring 框架
2.1. Spring 框架概念
Spring 是众多开源java项⽬中的⼀员,基于分层的javaEE应⽤⼀站式轻量级开源框架,主要核⼼是IOC(控制反转/依赖注⼊)与 AOP(⾯向切⾯)两⼤技术,实现项⽬在开发过程中的轻松解耦,提⾼项⽬的开发效率。
在项⽬中引⼊Spring⽴即可以带来下⾯的好处
- 降低组件之间的耦合度,实现软件各层之间的解耦。
- 可以使⽤容器提供的众多服务,如:事务管理服务、消息服务等等。
- 当我们使⽤容器管理事务时,开发⼈员就不再需要⼿⼯控制事务.也不需处理复杂的事务传播。
- 容器提供单例模式⽀持,开发⼈员不再需要⾃⼰编写实现代码。
- 容器提供了AOP技术,利⽤它很容易实现如权限拦截、运⾏期监控等功能。
2.2. Spring 源码架构
Spring 总共⼤约有20个模块,由1300多个不同的⽂件构成。⽽这些组件被分别整合在核⼼容器(Core Container)、Aop(Aspect Oriented Programming)和设备⽀持(Instrmentation)、数据访问及集成(Data Access/Integeration)、Web、报⽂发送(Messaging)、测试6个模块集合中。
-
核⼼容器:Spring-beans 和 Spring-core 模块是 Spring 框架的核⼼模块,包含控制反转(Inversion of Control, IoC)和依赖注⼊(Dependency Injection, DI),核⼼容器提供 Spring 框架的基本功能。核⼼容器的主要组件是 BeanFactory,⼯⼚模式的实现。BeanFactory 使⽤控制反转(IOC) 思想将应⽤程序的配置和依赖性规范与实际的应⽤程序代码分开。
Spring 上下⽂Spring Context:Spring 上下⽂是⼀个配置⽂件,向 Spring 框架提供上下⽂信息。Spring 上下⽂包括企业服务,例如 JNDI、EJB、电⼦邮件、国际化、校验和调度功能。
Spring-Expression 模块是统⼀表达式语⾔(unified EL)的扩展模块,可以查询、管理运⾏中的对象,同时也⽅便的可以调⽤对象⽅法、操作数组、集合等。它的语法类似于传统EL,但提供了额外的功能,最出⾊的要数函数调⽤和简单字符串的模板函数。
- Spring-AOP:Spring-aop是Spring的另⼀个核⼼模块, 在Spring中,他是以JVM的动态代理技术为基础,然后设计出了⼀系列的Aop横切实现,⽐如前置通知、返回通知、异常通知等。通过其配置管理特性,Spring AOP 模块直接将⾯向切⾯的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象⽀持 AOP。
-
Spring Data Access(数据访问):由Spring-jdbc、Spring-tx、Spring-orm、Spring-jms和Springoxm 5个模块组成 Spring-jdbc 模块是 Spring 提供的JDBC抽象框架的主要实现模块,⽤于简化Spring JDBC。
Spring-tx 模块是SpringJDBC事务控制实现模块。使⽤Spring框架,它对事务做了很好的封装,通过它的Aop配置,可以灵活的配置在任何⼀层。
Spring-Orm 模块是ORM框架⽀持模块,主要集成 hibernate, Java Persistence API (JPA) 和 Java Data Objects (JDO) ⽤于资源管理、数据访问对象(DAO)的实现和事务策略。
Spring-Jms 模块(Java Messaging Service)能够发送和接受信息。
Spring-Oxm 模块主要提供⼀个抽象层以⽀撑OXM(OXM 是 Object-to-XML-Mapping 的缩写,
它是⼀个O/M-mapper,将java对象映射成 XML 数据,或者将 XML 数据映射成 java 对象),例如:JAXB, Castor, XMLBeans, JiBX 和 XStream 等。
- Web 模块:由Spring-web、Spring-webmvc、Spring-websocket和Spring-webmvc-portlet 4个模块组成,Web 上下⽂模块建⽴在应⽤程序上下⽂模块之上,为基于 Web 的应⽤程序提供了上下⽂。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的⼯作。
-
报⽂发送:即Spring-messaging模块。
Spring-messaging是Spring4 新加⼊的⼀个模块,主要职责是为Spring 框架集成⼀些基础的报⽂传送应⽤。
- 单元测试:即Spring-test模块。Spring-test模块主要为测试提供⽀持
2.3. Spring 框架环境搭建
2.3.1. 环境要求
JDK 版本:
JDK 1.7 及以上版本
Spring版本:
Spring 5.x版本
2.3.2. 新建 Maven 项⽬
- 创建 Maven 的普通 Java 项⽬
- 设置项⽬的坐标
- 设置项⽬的 Maven 环境
- 设置项⽬的名称和存放的⼯作空间
2.3.3. 调整项⽬环境
- 修改 JDK 版本
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
- 修改单元测试 JUnit 版本
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
- build标签中的pluginManagement标签
<!--删除build标签中的pluginManagement标签-->
<build>
</build>
2.3.4. 添加 Spring 框架的依赖坐标
Maven仓库:https://mvnrepository.com/
<!-- 添加Spring框架的核⼼依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
2.3.5. 编写 Bean 对象
package com.suntao.service;
public class UserService {
public void test(){
System.out.println("Hello Spring!");
}
}
2.3.6. 添加Spring 配置⽂件
- 在项⽬的src下创建⽂件夹 resources(Alt+insert)
- 将 resources 标记为资源⽬录
-
在 src\main\resources ⽬录下新建 spring.xml ⽂件,并拷⻉官⽹⽂档提供的模板内容到 xml中。配置 bean 到 xml 中,把对应 bean 纳⼊到 Spring 容器来管理
spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
xmlns 即 xml namespace xml使⽤的命名空间
xmlns:xsi 即xml schema instance xml 遵守的具体规范
xsi:schemaLocation 本⽂档xml遵守的规范 官⽅指定
-->
<bean id="userService" class="com.suntao.service.UserService"></bean>
</beans>
- 在 spring.xml 中配置 Bean 对象
<!--
id:bean对象的id,唯⼀标识。⼀般是Bean对象的名称的⾸字⺟⼩写
class:bean对象的类路径
-->
<bean id="userService" class="com.suntao.service.UserService"></bean>
2.3.7. 加载配置⽂件,获取实例化对象
package com.suntao;
import com.suntao.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 获取Spring上下⽂环境 (加载配置⽂件)
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
// 通过getBean⽅法得到Spring容器中实例化好的Bean对象 (实例化Bean对象)
// userService代表的是配置⽂件中bean标签的id属性值
UserService userService = (UserService) ac.getBean("userService");
// 调⽤⽅法 (使⽤实例化对象)
userService.test();
}
}
3. Spring IOC 容器 Bean 对象实例化模拟
思路:
- 定义Bean ⼯⼚接⼝,提供获取bean⽅法
- 定义Bean⼯⼚接⼝实现类,解析配置⽂件,实例化Bean对象
- 实现获取Bean⽅法
4. Spring IOC 配置⽂件加载
4.1. Spring 配置⽂件加载
spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.xxxx.service.UserService"></bean>
</beans>
4.1.1. 根据相对路径加载资源
4.1.2. 根据绝对路径加载资源(了解)
ApplicationContext ac = new
FileSystemXmlApplicationContext("C:/IdeaWorkspace/spring01/src/main/resources/
spring.xml");
4.2. Spring 多配置⽂件加载
Spring 框架启动时可以加载多个配置⽂件到环境中。对于⽐较复杂的项⽬,可能对应的配置⽂件有多个,项⽬在启动部署时会将多个配置⽂件同时加载进来。
service.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.suntao.service.UserService"></bean>
</beans>
dao.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.suntao.dao.UserDao"></bean>
</beans>
4.2.1. 可变参数,传⼊多个⽂件名
// 同时加载多个资源⽂件
ApplicationContext ac = new
ClassPathXmlApplicationContext("spring.xml","dao.xml");
4.2.2. 通过总的配置⽂件import其他配置⽂件
spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--导⼊需要包含的资源⽂件-->
<import resource="service.xml"/>
<import resource="dao.xml"/>
</beans>
加载时只需加载总的配置⽂件即可
// 加载总的资源⽂件
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
5. Spring IOC 容器 Bean 对象实例化
5.1. 构造器实例化
注:通过默认构造器创建 ,空构造⽅法必须存在 否则创建失败
- 设置配置⽂件 spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.suntao.service.UserService"></bean>
</beans>
- 获取实例化对象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.test();
5.2. 静态⼯⼚实例化(了解)
注:
- 要有该⼯⼚类及⼯⼚⽅法
- ⼯⼚⽅法为静态的
- 定义静态⼯⼚类
package com.suntao.factory;
import com.suntao.service.UserService;
/**
* 定义静态⼯⼚类
*/
public class StaticFactory {
public static UserService createUserService() {
return new UserService();
}
}
- 设置配置⽂件 spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--静态⼯⼚-->
<bean id="userService" class="com.suntao.factory.StaticFactory" factorymethod="createUserService"></bean>
</beans>
- 获取实例化对象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.test();
当我们指定Spring使⽤静态⼯⼚⽅法来创建Bean实例时,Spring将先解析配置⽂件,并根据配置⽂件指定的信息,通过反射调⽤静态⼯⼚类的静态⼯⼚⽅法,并将该静态⼯⼚⽅法的返回值作为Bean实例,在这个过程中,Spring不再负责创建Bean实例,Bean实例是由⽤户提供的静态⼯⼚⽅法提供的。
5.3. 实例化⼯⼚实例化(了解)
注:
- ⼯⼚⽅法为⾮静态⽅法
- 需要配置⼯⼚bean,并在业务bean中配置factory-bean,factory-method属性
package com.suntao.factory;
import com.suntao.service.UserService;
public class InstanceFactory {
/**
* 定义⽅法,返回实例化对象
* @return
*/
public UserService createUserService() {
return new UserService();
}
}
- 设置配置⽂件 spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
实例化⼯⼚
1.定义实例化⼯⼚bean
2.引⽤⼯⼚bean 指定⼯⼚创建⽅法(⽅法为⾮静态)
-->
<bean id="instanceFactory" class="com.suntao.factory.InstanceFactory">
</bean>
<bean id="userService" factory-bean="instanceFactory" factorymethod="createUserService"></bean>
</beans>
- 获取实例化对象
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) ac.getBean("userService");
userService.test();
5.4. Spring三种实例化Bean的⽅式⽐较
- ⽅式⼀:通过bean的缺省构造函数创建,当各个bean的业务逻辑相互⽐较独⽴的时候或者和外界关联较少的时候可以使⽤。
- ⽅式⼆:利⽤静态factory⽅法创建,可以统⼀管理各个bean的创建,如各个bean在创建之前需要相同的初始化处理,则可⽤这个factory⽅法险进⾏统⼀的处理等等。
-
⽅式三:利⽤实例化factory⽅法创建,即将factory⽅法也作为了业务bean来控制,1可⽤于集成其他框架的bean创建管理⽅法,2能够使bean和factory的⻆⾊互换。
开发中项⽬⼀般使⽤⼀种⽅式实例化bean,项⽬开发基本采⽤第⼀种⽅式,交给Spring托管,使⽤时直接拿来使⽤即可。另外两种了解
6. Spring IOC 注⼊
⼿动实例化与外部引⼊
图⼀:
图⼆:
对⽐发现:图⼆中对于 UserDao 对象的创建并没有像图⼀那样主动的去实例化,⽽是通过带参⽅法形式将UserDao 传⼊过来,从⽽实现 UserService 对UserDao类 的依赖。
⽽实际创建对象的幕后对象即是交给了外部来创建。
6.1. Spring IOC ⼿动装配(注⼊)
Spring ⽀持的注⼊⽅式共有四种:set 注⼊、构造器注⼊、静态⼯⼚注⼊、实例化⼯⼚注⼊。
6.1.1. set⽅法注⼊
注:
- 属性字段需要提供set⽅法
- 四种⽅式,推荐使⽤set⽅法注⼊
6.1.1.1. 业务对象 JavaBean
- 属性字段提供set⽅法
public class UserService {
// 业务对象UserDao set注⼊(提供set⽅法)
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
- 配置⽂件的bean标签设置property标签
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通过property标签⼿动装配(注⼊):
Set⽅法注⼊
name:bean对象中属性字段的名称
ref:指定bean标签的id属性值
-->
<bean id="userDao" class="com.suntao.dao.UserDao"></bean>
<bean id="userService" class="com.suntao.service.UserService">
<!--业务对象注⼊-->
<property name="userDao" ref="userDao"/>
</bean>
</beans>
6.1.1.2. 常⽤对象和基本类型
- 属性字段提供set⽅法
public class UserService {
// 常⽤对象String set注⼊(提供set⽅法)
private String host;
public void setHost(String host) {
this.host = host;
}
// 基本类型Integer set注⼊(提供set⽅法)
private Integer port;
public void setPort(Integer port) {
this.port = port;
}
}
- 配置⽂件的bean标签设置property标签
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通过property标签⼿动装配(注⼊):
Set⽅法注⼊
name:bean对象中属性字段的名称
value:具体的值(基本类型 常⽤对象|⽇期 集合)
-->
<bean id="userService" class="com.suntao.service.UserService">
<!--常⽤对象String 注⼊-->
<property name="host" value="127.0.0.1"/>
<!--基本类型注⼊-->
<property name="port" value="8080"/>
</bean>
</beans>
6.1.1.3. 集合类型和属性对象
- 属性字段提供set⽅法
public class UserService {
// List集合 set注⼊(提供set⽅法)
public List<String> list;
public void setList(List<String> list) {
this.list = list;
}
// Set集合 set注⼊(提供set⽅法)
private Set<String> set;
public void setSet(Set<String> set) {
this.set = set;
}
// Map set注⼊(提供set⽅法)
private Map<String,Object> map;
public void setMap(Map<String, Object> map) {
this.map = map;
}
// Properties set注⼊(提供set⽅法)
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
}
- 配置⽂件的bean标签设置property标签
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通过property标签⼿动装配(注⼊):
Set⽅法注⼊
name:bean对象中属性字段的名称
value:具体的值(基本类型 常⽤对象|⽇期 集合)
-->
<!--List集合 注⼊-->
<property name="list">
<list>
<value>上海</value>
<value>北京</value>
<value>杭州</value>
</list>
</property>
<!--Set集合注⼊-->
<property name="set">
<set>
<value>上海SH</value>
<value>北京BJ</value>
<value>杭州HZ</value>
</set>
</property>
<!--Map注⼊-->
<property name="map">
<map>
<entry>
<key><value>周杰伦</value></key>
<value>我是如此相信</value>
</entry>
<entry>
<key><value>林俊杰</value></key>
<value>可惜没如果</value>
</entry>
<entry>
<key><value>陈奕迅</value></key>
<value>⼗年</value>
</entry>
</map>
</property>
<!--Properties注⼊-->
<property name="properties">
<props>
<prop key="上海">东⽅明珠</prop>
<prop key="北京">天安⻔</prop>
<prop key="杭州">⻄湖</prop>
</props>
</property>
</beans>
6.1.1.4. 测试代码
UserService.java
public class UserService {
// 业务对象UserDao set注⼊(提供set⽅法)
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 常⽤对象String set注⼊(提供set⽅法)
private String host;
public void setHost(String host) {
this.host = host;
}
// 基本类型Integer set注⼊(提供set⽅法)
private Integer port;
public void setPort(Integer port) {
this.port = port;
}
// List集合 set注⼊(提供set⽅法)
public List<String> list;
public void setList(List<String> list) {
this.list = list;
}
// List集合输出
public void printList() {
list.forEach(s -> System.out.println(s));
}
// Set集合 set注⼊(提供set⽅法)
private Set<String> set;
public void setSet(Set<String> set) {
this.set = set;
}
// Set集合输出
public void printSet() {
set.forEach(s -> System.out.println(s));
}
// Map set注⼊(提供set⽅法)
private Map<String,Object> map;
public void setMap(Map<String, Object> map) {
this.map = map;
}
// Map输出
public void printMap() {
map.forEach((k,v) -> System.out.println(k + "," + v));
}
// Properties set注⼊(提供set⽅法)
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
// Properties输出
public void printProperties(){
properties.forEach((k,v) -> System.out.println(k + ","+ v ));
}
public void test(){
System.out.println("UserService Test...");
userDao.test();
studentDao.test();
System.out.println("Host:" + host + ",port:" + port);
// List集合
printList();
// Set集合
printSet();
// Map
printMap();
// Properties
printProperties();
}
}
spring.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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通过property标签⼿动装配(注⼊):
Set⽅法注⼊
name:bean对象中属性字段的名称
ref:指定bean标签的id属性值
value:具体的值(基本类型 常⽤对象|⽇期 集合)
-->
<bean id="userDao" class="com.suntao.dao.UserDao"></bean>
<bean id="userService" class="com.suntao.service.UserService">
<!--业务对象 注⼊-->
<property name="userDao" ref="userDao"/>
<property name="studentDao" ref="studentDao"/>
<!--常⽤对象String 注⼊-->
<property name="host" value="192.168.1.109"/>
<!--基本类型注⼊-->
<property name="port" value="8080"/>
<!--List集合 注⼊-->
<property name="list">
<list>
<value>上海</value>
<value>北京</value>
<value>杭州</value>
</list>
</property>
<!--Set集合注⼊-->
<property name="set">
<set>
<value>上海SH</value>
<value>北京BJ</value>
<value>杭州HZ</value>
</set>
</property>
<!--Map注⼊-->
<property name="map">
<map>
<entry>
<key><value>周杰伦</value></key>
<value>我是如此相信</value>
</entry>
<entry>
<key><value>林俊杰</value></key>
<value>可惜没如果</value>
</entry>
<entry>
<key><value>陈奕迅</value></key>
<value>⼗年</value>
</entry>
</map>
</property>
<!--Properties注⼊-->
<property name="properties">
<props>
<prop key="上海">东⽅明珠</prop>
<prop key="北京">天安⻔</prop>
<prop key="杭州">⻄湖</prop>
</props>
</property>
</bean>
</beans>
6.1.2. 构造器注⼊
注:
- 提供带参构造器
6.1.2.1. 单个Bean对象作为参数
Java 代码
public class UserService {
private UserDao userDao; // JavaBean 对象
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void test(){
System.out.println("UserService Test...");
userDao.test();
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通过构造器注⼊:
通过constructor-arg标签进⾏注⼊
name:属性名称
ref:指定bean标签的id属性值
-->
<bean id="userDao" class="com.xxxx.dao.UserDao" ></bean>
<bean id="accountDao" class="com.xxxx.dao.AccountDao" ></bean>
<bean id="userService" class="com.xxxx.service.UserService">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
<constructor-arg name="accountDao" ref="accountDao"></constructor-arg>
</bean>
</beans>
6.1.2.3. Bean对象和常⽤对象作为参数
Java 代码
public class UserService {
private UserDao userDao; // JavaBean 对象
private AccountDao accountDao; // JavaBean 对象
private String uname; // 字符串类型
public UserService(UserDao userDao, AccountDao accountDao, String uname) {
this.userDao = userDao;
this.accountDao = accountDao;
this.uname = uname;
}
public void test(){
System.out.println("UserService Test...");
userDao.test();
accountDao.test();
System.out.println("uname:" + uname);
}
}
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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
IOC通过构造器注⼊:
通过constructor-arg标签进⾏注⼊
name:属性名称
ref:指定bean标签的id属性值
value:基本类型 常⽤对象的值
index:构造器中参数的下标,从0开始
-->
<bean id="userDao" class="com.xxxx.dao.UserDao" ></bean>
<bean id="accountDao" class="com.xxxx.dao.AccountDao" ></bean>
<bean id="userService" class="com.xxxx.service.UserService">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
<constructor-arg name="accountDao" ref="accountDao"></constructor-arg>
<constructor-arg name="uname" value="admin"></constructor-arg>
</bean>
</beans>
6.1.2.4. 循环依赖问题
循环问题产⽣的原因:
Bean通过构造器注⼊,之间彼此相互依赖对⽅导致bean⽆法实例化。
问题展示:
- Java 代码
public class AccountService {
private RoleService roleService;
public AccountService(RoleService roleService) {
this.roleService = roleService;
}
public void test() {
System.out.println("AccountService Test...");
}
}
public class RoleService {
private AccountService accountService;
public RoleService(AccountService accountService) {
this.accountService = accountService;
}
public void test() {
System.out.println("RoleService Test...");
}
}
- XML配置
<!--
如果多个bean对象中互相注⼊,则会出现循环依赖的问题
可以通过set⽅法注⼊解决
-->
<bean id="accountService" class="com.suntao.service.AccountService">
<constructor-arg name="roleService" ref="roleService"/>
</bean>
<bean id="roleService" class="com.suntao.service.RoleService">
<constructor-arg name="accountService" ref="accountService"/>
</bean>
如何解决:将构造器注⼊改为set⽅法注⼊
3. Java代码
public class AccountService {
private RoleService roleService;
/* public AccountService(RoleService roleService) {
this.roleService = roleService;
}*/
public void setRoleService(RoleService roleService) {
this.roleService = roleService;
}
public void test() {
System.out.println("AccountService Test...");
}
}
public class RoleService {
private AccountService accountService;
/* public RoleService(AccountService accountService) {
this.accountService = accountService;
}*/
public void setAccountService(AccountService accountService) {
this.accountService = accountService;
}
public void test() {
System.out.println("RoleService Test...");
}
}
- XML配置
<!--
<bean id="accountService" class="com.xxxx.service.AccountService">
<constructor-arg name="roleService" ref="roleService"/>
</bean>
<bean id="roleService" class="com.xxxx.service.RoleService">
<constructor-arg name="accountService" ref="accountService"/>
</bean>
-->
<!--修改为set⽅法注⼊-->
<bean id="accountService" class="com.xxxx.service.AccountService">
<property name="roleService" ref="roleService"/>
</bean>
<bean id="roleService" class="com.xxxx.service.RoleService">
<property name="accountService" ref="accountService"/>
</bean>
6.1.3. 静态⼯⼚注⼊
- 定义静态⼯⼚类
public class StaticFactory {
// 定义静态⽅法
public static TypeDao createTypeDao() {
return new TypeDao();
}
}
- Java代码
public class TypeService {
private TypeDao typeDao;
public void setTypeDao(TypeDao typeDao) {
this.typeDao = typeDao;
}
public void test() {
System.out.println("TypeService Test...");
}
}
-
XML配置
在配置⽂件中设置bean标签,指定⼯⼚对象并设置对应的⽅法
<bean id="typeService" class="com.suntao.service.TypeService">
<property name="typeDao" ref="typeDao"/>
</bean>
<!--
静态⼯⼚注⼊:
静态⼯⼚注⼊也是借助set⽅法注⼊,只是被注⼊的bean对象的实例化是通过静态⼯⼚实例化的
-->
<bean id="typeDao" class="com.suntao.factory.StaticFactory" factorymethod="createTypeDao"></bean
6.1.4. 实例化⼯⼚注⼊
- 定义⼯⼚类
public class InstanceFactory {
public TypeDao createTypeDao() {
return new TypeDao();
}
}
- Java代码
public class TypeService {
private TypeDao typeDao;
public void setTypeDao(TypeDao typeDao) {
this.typeDao = typeDao;
}
public void test() {
System.out.println("TypeService Test...");
}
}
-
XML配置
声明⼯⼚bean标签,声明bean对象,指明⼯⼚对象和⼯⼚⽅法
<bean id="typeService" class="com.suntao.service.TypeService">
<property name="typeDao" ref="typeDao"/>
</bean>
<!--
实例化⼯⼚注⼊:
实例化⼯⼚注⼊也是借助set⽅法注⼊,只是被注⼊的bean对象的实例化是通过实例化⼯⼚实例
化的
-->
<bean id="instanceFactory" class="com.suntao.factory.InstanceFactory">
</bean>
<bean id="typeDao" factory-bean="instanceFactory" factorymethod="createTypeDao"></bean>
重点掌握set注⼊和构造器注⼊,⼯⼚⽅式了解即可。实际开发中基本使⽤set⽅式注⼊bean。
6.1.5. 注⼊⽅式的选择
开发项⽬中set⽅式注⼊⾸选
使⽤构造注⼊可以在构建对象的同时⼀并完成依赖关系的建⽴,对象⼀建⽴则所有的⼀切也就准备好了,但如果要建⽴的对象关系很多,使⽤构造器注⼊会在构建函数上留下⼀⻓串的参数,且不易记忆,这时使⽤Set注⼊会是个不错的选择。
使⽤Set注⼊可以有明确的名称,可以了解注⼊的对象会是什么,像setXXX()这样的名称会⽐记忆Constructor上某个参数的位置代表某个对象更好。
p名称空间的使⽤
spring2.5以后,为了简化setter⽅法属性注⼊,引⽤p名称空间的概念,可以将 ⼦元素,简化为元素属性配置。
- 属性字段提供 set ⽅法
public class UserService {
// 业务对象UserDao set注⼊(提供set⽅法)
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 常⽤对象String set注⼊(提供set⽅法)
private String host;
public void setHost(String host) {
this.host = host;
}
}
- 在配置⽂件 spring.xml 引⼊ p 名称空间
xmlns:p="http://www.springframework.org/schema/p"
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.suntao.dao.UserDao"></bean>
<!--
p:属性名:="xxx" 引⼊常量值
p:属性名-ref:="xxx" 引⼊其他Bean对象的id属性值
-->
<bean id="userService" class="com.suntao.service.UserService"
p:userDao-ref="userDao"
p:host="127.0.0.1" />
</beans>
6.2. Spring IOC ⾃动装配(注⼊)
注解⽅式注⼊ Bean
对于 bean 的注⼊,除了使⽤ xml 配置以外,可以使⽤注解配置。注解的配置,可以简化配置⽂件,提⾼开发的速度,使程序看上去更简洁。对于注解的解释,Spring对于注解有专⻔的解释器,对定义的注解进⾏解析,实现对应bean对象的注⼊。通过反射技术实现。
6.2.1. 准备环境
- 修改配置⽂件
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
- 开启⾃动化注⼊
<!--开启⾃动化装配(注⼊)-->
<context:annotation-config/>
<bean id="userDao" class="com.xxxx.dao.UserDao"></bean>
<bean id="userService" class="com.xxxx.service.UserService"></bean>
- 给注⼊的bean对象添加注解
6.2.2. @Resource注解
@Resource注解实现⾃动注⼊(反射)
- 默认根据属性字段名称查找对应的bean对象 (属性字段的名称与bean标签的id属性值相等)
- 如果属性字段名称未找到,则会通过类型(Class类型)查找
- 属性可以提供set⽅法,也可以不提供set⽅法
- 注解可以声明在属性级别 或 set⽅法级别
- 可以设置name属性,name属性值必须与bean标签的id属性值⼀致;如果设置了name属性值,就只会按照name属性值查找bean对象
-
当注⼊接⼝时,如果接⼝只有⼀个实现则正常实例化;如果接⼝存在多个实现,则需要使⽤name属性指定需要被实例化的bean对象
代码示例
- 默认根据属性字段名称查找对应的bean对象 (属性字段的名称与bean标签的id属性值相等)
/**
* @Resource注解实现⾃动注⼊(反射)
* 默认根据属性字段名称查找对应的bean对象 (属性字段的名称与bean标签的id属性值相等)
*/
public class UserService {
@Resource
private UserDao userDao; // 属性字段的名称与bean标签的id属性值相等
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test() {
// 调⽤UserDao的⽅法
userDao.test();
}
}
- 如果属性字段名称未找到,则会通过类型(Class类型)查找
/**
* @Resource注解实现⾃动注⼊(反射)
* 如果属性字段名称未找到,则会通过类型(Class类型)查找
*/
public class UserService {
@Resource
private UserDao ud; // 当在配置⽂件中属性字段名(ud)未找到,则会查找对应的class(UserDao类型)
public void setUd(UserDao ud) {
this.ud = ud;
}
public void test() {
// 调⽤UserDao的⽅法
ud.test();
}
}
- 属性可以提供set⽅法,也可以不提供set⽅法
/**
* @Resource注解实现⾃动注⼊(反射)
* 属性可以提供set⽅法,也可以不提供set⽅法
*/
public class UserService {
@Resource
private UserDao userDao; // 不提供set⽅法
public void test() {
// 调⽤UserDao的⽅法
userDao.test();
}
}
- 注解可以声明在属性级别 或 set⽅法级别
/**
* @Resource注解实现⾃动注⼊(反射)
* 注解可以声明在属性级别 或 set⽅法级别
*/
public class UserService {
private UserDao userDao;
@Resource // 注解也可设置在set⽅法上
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test() {
// 调⽤UserDao的⽅法
userDao.test();
}
}
- 可以设置name属性,name属性值必须与bean标签的id属性值⼀致;如果设置了name属性值,就只会按照name属性值查找bean对象
/**
* @Resource注解实现⾃动注⼊(反射)
* 可以设置name属性,name属性值必须与bean的id属性值⼀致;
* 如果设置了name属性值,就只会按照name属性值查找bean对象
*/
public class UserService {
@Resource(name = "userDao") // name属性值与配置⽂件中bean标签的id属性值⼀致
private UserDao ud;
public void test() {
// 调⽤UserDao的⽅法
ud.test();
}
}
-
当注⼊接⼝时,如果接⼝只有⼀个实现则正常实例化;如果接⼝存在多个实现,则需要使⽤name属性指定需要被实例化的bean对象
定义接⼝类 IUserDao.java
package com.xxxx.dao;
/**
* 定义接⼝类
*/
public interface IUserDao {
public void test();
}
定义接⼝实现类 UserDao01.java
package com.xxxx.dao;
/**
* 接⼝实现类
*/
public class UserDao01 implements IUserDao {
@Override
public void test(){
System.out.println("UserDao01...");
}
}
定义接⼝实现类 UserDao02.java
package com.xxxx.dao;
/**
* 接⼝实现类
*/
public class UserDao02 implements IUserDao {
@Override
public void test(){
System.out.println("UserDao02...");
}
}
XML配置⽂件
<!--开启⾃动化装配(注⼊)-->
<context:annotation-config/>
<bean id="userService" class="com.xxxx.service.UserService"></bean>
<bean id="userDao01" class="com.xxxx.dao.UserDao01"></bean>
<bean id="userDao02" class="com.xxxx.dao.UserDao01"></bean>
使⽤注解 UserService.java
/**
* @Resource注解实现⾃动注⼊(反射)
* 当注⼊接⼝时,如果接⼝只有⼀个实现则正常实例化;如果接⼝存在多个实现,则需要使⽤
name属性指定需要被实例化的bean对象
*/
public class UserService {
@Resource(name = "userDao01") // name属性值与其中⼀个实现类的bean标签的id属性值⼀致
private IUserDao iUserDao; // 注⼊接⼝(接⼝存在多个实现)
public void test() {
iUserDao.test();
}
}
6.2.3. @Autowired注解
@Autowired注解实现⾃动化注⼊:
- 默认通过类型(Class类型)查找bean对象 与属性字段的名称⽆关
- 属性可以提供set⽅法,也可以不提供set⽅法
- 注解可以声明在属性级别 或 set⽅法级别
- 可以添加@Qualifier结合使⽤,通过value属性值查找bean对象(value属性值必须要设置,且值要与bean标签的id属性值对应)
- 默认通过类型(Class类型)查找bean对象 与属性字段的名称⽆关
/**
* @Autowired注解实现⾃动化注⼊
* 默认通过类型(Class类型)查找bean对象 与属性字段的名称⽆关
*/
public class UserService {
@Autowired
private UserDao userDao; // 默认通过类型(Class类型)查找bean对象 与属性字段
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test() {
// 调⽤UserDao的⽅法
userDao.test();
}
}
- 属性可以提供set⽅法,也可以不提供set⽅法
/**
* @Autowired注解实现⾃动化注⼊
* 属性可以提供set⽅法,也可以不提供set⽅法
*/
public class UserService {
@Autowired
private UserDao userDao; // 不提供set⽅法
public void test() {
// 调⽤UserDao的⽅法
userDao.test();
}
}
- 注解可以声明在属性级别 或 set⽅法级别
/**
* @Autowired注解实现⾃动化注⼊
* 注解可以声明在属性级别 或 set⽅法级别
*/
public class UserService {
private UserDao userDao;
@Autowired// 注解可以声明在set⽅法级别
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void test() {
// 调⽤UserDao的⽅法
userDao.test();
}
}
- 可以添加@Qualifier结合使⽤,通过value属性值查找bean对象(value属性值必须要设置,且值要与bean标签的id属性值对应
/**
* @Autowired注解实现⾃动化注⼊
* 可以添加@Qualifier结合使⽤,通过value属性值查找bean对象
value属性值必须要设置,且值要与bean标签的id属性值对应
*/
public class UserService {
@Autowired
@Qualifier(value="userDao") // value属性值必须要设置,且值要与bean标签的id属性值对应
private UserDao userDao;
public void test() {
userDao.test();
}
}
推荐使⽤@Resource 注解是属于J2EE的,减少了与Spring的耦合。
7. Spring IOC 扫描器
实际的开发中,bean的数量⾮常多,采⽤⼿动配置bean的⽅式已⽆法满⾜⽣产需要,Spring这时候同样提供了扫描的⽅式,对扫描到的bean对象统⼀进⾏管理,简化开发配置,提⾼开发效率。
7.1. Spring IOC 扫描器的配置
Spring IOC 扫描器
作⽤:bean对象统⼀进⾏管理,简化开发配置,提⾼开发效率
1、设置⾃动化扫描的范围
如果bean对象未在指定包范围,即使声明了注解,也⽆法实例化
2、使⽤指定的注解(声明在类级别) bean对象的id属性默认是 类的⾸字⺟⼩写
Dao层:
@Repository
Service层:
@Service
Controller层:
@Controller
任意类:
@Component
注:开发过程中建议按照指定规则声明注解
- 设置⾃动化扫描范围
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 设置⾃动化扫描的范围 -->
<context:component-scan base-package="com.xxxx"/>
</beans>
-
使⽤特定的注解
@Repository (Dao层)
@Repository
public class ResourceDao {
public void test() {
System.out.println("ResourceDao...");
}
}
@Service(Service层 )
@Service
public class ResourceService {
@Resource
private ResourceDao resourceDao; // service层注⼊dao层的bean对象
public void test() {
System.out.println("ResourceService...");
resourceDao.test();
}
}
@Controller (Controller 层 )
@Controller
public class ResourceController {
@Autowired
private ResourceService resourceService; // Controller层注⼊service层的bean对象
public void test() {
System.out.println("ResourceController...");
resourceService.test();
}
}
@Component (任意层)
@Component
public class PropertyUtils {
public void test(){
System.out.println("PropertyUtils...");
}
}
7.2. Spring 模拟⽤户登录流程
7.2.1. Dao层 (查询⽤户记录)
- 定义JavaBean User.java
package com.xxxx.po;
/**
* User ⽤户实体类
*/
public class User {
private String userName; // ⽤户名称
private String userPwd; // ⽤户密码
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
}
- 编写Dao层 UserDao.java
package com.xxxx.dao;
import com.xxxx.po.User;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
private final String USERNAME = "admin";
private final String USERPWD = "admin";
/**
* 通过⽤户名称查询⽤户对象
* @param userName
* @return
*/
public User queryUserByUserName(String userName){
User user = null;
// 判断⽤户名称是否正确
if(!USERNAME.equals(userName)){
// 如果不正确,返回null
return null;
}
// 如果正确,将⽤户名称和密码设置到user对象中
user = new User();
user.setUserName(USERNAME);
user.setUserPwd(USERPWD);
return user;
}
}
7.2.2. Service层 (业务逻辑处理)
- 定义业务处理返回消息模型 MessageModel.java
package com.xxxx.po.vo;
/**
* 定义业务处理返回消息模型
* 封装返回结果
*/
public class MessageModel {
private Integer resultCode = 1; // 结果状态码 1=成功,0=失败
private String resultMsg = "操作成功!"; // 结果提示信息
public Integer getResultCode() {
return resultCode;
}
public void setResultCode(Integer resultCode) {
this.resultCode = resultCode;
}
public String getResultMsg() {
return resultMsg;
}
public void setResultMsg(String resultMsg) {
this.resultMsg = resultMsg;
}
}
- 编写Service层 UserService.java
package com.xxxx.service;
import com.xxxx.dao.UserDao1;
import com.xxxx.po.User;
import com.xxxx.po.vo.MessageModel;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserService {
@Resource
private UserDao userDao;
/**
* 验证⽤户登录
* @param userName
* @param userPwd
* @return
*/
public MessageModel userLoginCheck(String userName, String userPwd){
// 定义业务处理返回消息模型
MessageModel messageModel = new MessageModel();
// 判断⽤户名称是否⾮空
if(null == userName || "".equals(userName.trim())){
messageModel.setResultCode(0);
messageModel.setResultMsg("⽤户名不能为空!");
return messageModel;
}
// 判断⽤户密码是否为空
if(null == userPwd || "".equals(userPwd.trim())){
messageModel.setResultCode(0);
messageModel.setResultMsg("密码不能为空!");
return messageModel;
}
// 通过⽤户名称查询⽤户对象
User user = userDao.queryUserByUserName(userName);
// 判断⽤户对象是否为空
if(null == user){
messageModel.setResultCode(0);
messageModel.setResultMsg("该⽤户不存在!");
return messageModel;
}
// 如果⽤户对象不为空,判断密码是否正确
if(!user.getUserPwd().equals(userPwd)){
messageModel.setResultCode(0);
messageModel.setResultMsg("⽤户密码不正确!");
return messageModel;
}
// 登录成功
messageModel.setResultMsg("登录成功!");
return messageModel;
}
}
7.2.3. Controller层 (接收请求)
- 编写Controller层 UserController.java
package com.xxxx.controller;
import com.xxxx.po.vo.MessageModel;
import com.xxxx.service.UserService1;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController {
@Resource
private UserService userService;
/**
* ⽤户登录
* @param userName
* @param userPwd
* @return
*/
public MessageModel login(String userName, String userPwd){
// 调⽤Dao层判断⽤户登录操作,返回结果
MessageModel messageModel = userService.userLoginCheck(userName,userPwd);
return messageModel;
}
}
7.2.4. 通过 JUnit 进⾏测试
package com.xxxx;
import com.xxxx.controller.UserController;
import com.xxxx.po.vo.MessageModel;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestLogin {
@Test
public void test() {
// 得到Spring容器上下⽂环境
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
// 得到UserController实例化对象
UserController userController = (UserController)ac.getBean("userController");
// 传⼊参数调⽤UserController的⽅法,返回封装类
MessageModel messageModel= userController.login("admin", "admin");
System.out.println("状态码:" + messageModel.getResultCode() + ",提示信息:" + messageModel.getResultMsg());
}
}
8. Bean的作⽤域与⽣命周期
8.1. Bean的作⽤域
默认情况下,我们从Spring容器中拿到的对象均是单例的,对于bean的作⽤域类型如下:
8.1.1. singleton 作⽤域
注意: lazy-init是懒加载, 如果等于true时作⽤是指Spring容器启动的时候不会去实例化这个bean,⽽是在程序调⽤时才去实例化. 默认是false即Spring容器启动时实例化.
默认情况下,被管理的bean只会IOC容器中存在⼀个实例,对于所有获取该Bean的操作Spring容器将只返回同⼀个Bean。
容器在启动的情况下就实例化所有singleton 的 bean对象,并缓存与容器中
lazy-init属性(懒加载)
如果为false,则在IOC容器启动时会实例化bean对象,默认false
如果为true,则IOC容器启动时不会实例化Bean对象,在使⽤bean对象时才会实例化
lazy-init设置为false有什么好处?
1)可以提前发现潜在的配置问题
2)Bean 对象存在于缓存中,使⽤时不⽤再去实例化bean,加快程序运⾏效率
什么对象适合作为单例对象?
⼀般来说对于⽆状态或状态不可改变的对象适合使⽤单例模式。(不存在会改变对象状态的成员变量)
⽐如:controller层、service层、dao层
什么是⽆状态或状态不可改变的对象?
实际上对象状态的变化往往均是由于属性值得变化⽽引起的,⽐如user类 姓名属性会有变化,属性姓名的变化⼀般会引起user对象状态的变化。对于我们的程序来说,⽆状态对象没有实例变量的存在,保证了线程的安全性,service 层业务对象即是⽆状态对象。线程安全的。
8.1.2. prototype 作⽤域
通过scope=“prototype” 设置bean的类型 ,每次向Spring容器请求获取Bean都返回⼀个全新的Bean,相对于"singleton"来说就是不缓存Bean,每次都是⼀个根据Bean定义创建的全新Bean。
8.1.3. Web应⽤中的作⽤域
-
request作⽤域
表示每个请求需要容器创建⼀个全新Bean。⽐如提交表单的数据必须是对每次请求新建⼀个Bean来保持这些表单数据,请求结束释放这些数据。
-
session作⽤域
表示每个会话需要容器创建⼀个全新Bean。⽐如对于每个⽤户⼀般会有⼀个会话,该⽤户的⽤户信息需要存储到会话中,此时可以将该Bean作⽤域配置为session级别。
-
globalSession作⽤域
类似于session作⽤域,其⽤于portlet(Portlet是基于Java的Web组件,由Portlet容器管理,并由容器处理请求,⽣产动态内容)环境的web应⽤。如果在⾮portlet环境将视为session作⽤域。
配置⽅式和基本的作⽤域相同,只是必须要有web环境⽀持,并配置相应的容器监听器或拦截器从⽽能应⽤这些作⽤域,⽬前先熟悉概念,后续集成web时讲解具体使⽤,⼤家只需要知道有这些作⽤域就可以了。
8.2. Bean的⽣命周期
对⽐已经学过的servlet ⽣命周期(容器启动装载并实例化servlet类,初始化servlet,调⽤service⽅法,销毁servlet)。
同样对于Spring容器管理的bean也存在⽣命周期的概念
在Spring中,Bean的⽣命周期包括Bean的定义、初始化、使⽤和销毁4个阶段
8.2.1. Bean的定义
在Spring中,通常是通过配置⽂档的⽅式来定义Bean的。
在⼀个配置⽂档中,可以定义多个Bean。
8.2.2. Bean 的初始化
默认在IOC容器加载时,实例化对象。
Spring bean 初始化有两种⽅式:
⽅式⼀:在配置⽂档中通过指定 init-method 属性来完成。
public class RoleService {
// 定义初始化时需要被调⽤的⽅法
public void init() {
System.out.println("RoleService init...");
}
}
<!-- 通过init-method属性指定⽅法 -->
<bean id="roleService" class="com.xxxx.service.RoleService" initmethod="init"></bean>
⽅式⼆: 实现 org.springframework.beans.factory.InitializingBean 接⼝。
public class RoleService implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("RoleService init...");
}
}
Bean对象实例化过程是在Spring容器初始化时被实例化的,但也不是不可改变的,可以通过 lazyinit=“true” 属性延迟bean对象的初始化操作,此时再调⽤getBean ⽅法时才会进⾏bean的初始化操作
8.2.3. Bean 的使⽤
⽅式⼀:使⽤ BeanFactory
// 得到Spring的上下⽂环境
BeanFactory factory = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) factory.getBean("roleService");
⽅式⼆:使⽤ ApplicationContext
// 得到Spring的上下⽂环境
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
RoleService roleService = (RoleService) ac.getBean("roleService");
8.2.4. Bean的销毁
实现销毁⽅式(Spring容器会维护bean对象的管理,可以指定bean对象的销毁所要执⾏的⽅法)。
步骤⼀:实现销毁⽅式(Spring容器会维护bean对象的管理,可以指定bean对象的销毁所要执⾏的⽅法)
步骤⼆:通过 AbstractApplicationContext 对象,调⽤其close⽅法实现bean的销毁过程
AbstractApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
ctx.close();
IOC/DI-控制反转和依赖注⼊
将对象实例化的创建过程转交给外部容器(IOC容器 充当⼯⼚⻆⾊)去负责;属性赋值的操作;