一篇Spring IOC笔记
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
个模块集合中。
1.
核⼼容器:
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
,但提供了额
外的功能,最出⾊的要数函数调⽤和简单字符串的模板函数。
2. Spring-AOP
:
Spring-aop
是
Spring
的另⼀个核⼼模块
,
在
Spring
中,他是以
JVM
的动态代理技术为基
础,然后设计出了⼀系列的
Aop
横切实现,⽐如前置通知、返回通知、异常通知等。通过其配置
管理特性,
Spring AOP
模块直接将⾯向切⾯的编程功能集成到了
Spring
框架中。所以,可以很容
易地使
Spring
框架管理的任何对象⽀持
AOP
。
3. Spring Data Access(
数据访问
)
:由
Spring-jdbc
、
Spring-tx
、
Spring-orm
、
Spring-jms
和
Spring-oxm 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
等。
4. Web
模块:由
Spring-web
、
Spring-webmvc
、
Spring-websocket
和
Spring-webmvc-portlet 4
个模块组
成,
Web
上下⽂模块建⽴在应⽤程序上下⽂模块之上,为基于
Web
的应⽤程序提供了上下⽂。
Web
模块还简化了处理多部分请求以及将请求参数绑定到域对象的⼯作。
5.
报⽂发送:即
Spring-messaging
模块。
Spring-messaging
是
Spring4
新加⼊的⼀个模块,主要职责是为
Spring
框架集成⼀些基础的报⽂传
送应⽤。
6.
单元测试:即
Spring-test
模块。
Spring-test
模块主要为测试提供⽀持
2.3. Spring
框架环境搭建
2.3.1.
环境要求
JDK
版本:
JDK 1.7
及以上版本
Spring
版本:
Spring 5.x
版本
2.3.2.
新建
Maven
项⽬
1.
创建
Maven
的普通
Java
项⽬
2.
设置项⽬的坐标
3.
设置项⽬的
Maven
环境
4.
设置项⽬的名称和存放的⼯作空间
2.3.3.
调整项⽬环境
1.
修改
JDK
版本
2.
修改单元测试
JUnit
版本
3. build
标签中的
pluginManagement
标签
2.3.4.
添加
Spring
框架的依赖坐标
Maven
仓库:
https://mvnrepository.com/
2.3.5.
编写
Bean
对象
2.3.6.
添加
Spring
配置⽂件
1.
在项⽬的
src
下创建⽂件夹
resources
(
Alt + insert
)
<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>
<dependency>
<groupId>
junit
</groupId>
<artifactId>
junit
</artifactId>
<version>
4.12
</version>
<scope>
test
</scope>
</dependency>
<!--
删除
build
标签中的
pluginManagement
标签
-->
<build>
</build>
<!--
添加
Spring
框架的核⼼依赖
-->
<dependency>
<groupId>
org.springframework
</groupId>
<artifactId>
spring-context
</artifactId>
<version>
5.2.4.RELEASE
</version>
</dependency>
package
com
.
xxxx
.
service
;
public class
UserService
{
public
void
test
(){
System
.
out
.
println
(
"Hello Spring!"
);
}
}
2.
将
resources
标记为资源⽬录
3.
在
src\main\resources
⽬录下新建
spring.xml
⽂件,并拷⻉官⽹⽂档提供的模板内容到
xml
中。
配置
bean
到
xml
中,把对应
bean
纳⼊到
Spring
容器来管理
spring.xml
4.
在
spring.xml
中配置
Bean
对象
<?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.xxxx.service.UserService"
></bean>
</beans>
2.3.7.
加载配置⽂件,获取实例化对象
3. Spring IOC
容器
Bean
对象实例化模拟
思路
:
1.
定义
Bean
⼯⼚接⼝,提供获取
bean
⽅法
2.
定义
Bean
⼯⼚接⼝实现类,解析配置⽂件,实例化
Bean
对象
3.
实现获取
Bean
⽅法
3.1.
定义
Bean
属性对象
<!--
id
:
bean
对象的
id
,唯⼀标识。⼀般是
Bean
对象的名称的⾸字⺟⼩写
class
:
bean
对象的类路径
-->
<bean
id
=
"userService"
class
=
"com.xxxx.service.UserService"
></bean>
package
com
.
xxxx
;
import
com
.
xxxx
.
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
();
}
}
package
com
.
xxxx
.
spring
;
/**
* bean
对象
*
⽤来接收配置⽂件中
bean
标签的
id
与
class
属性值
*/
public class
MyBean
{
private
String
id
;
// bean
对象的
id
属性值
3.2.
添加
dom4j
坐标依赖
3.3.
准备⾃定义配置⽂件
spring.xml
private
String
clazz
;
// bean
对象的类路径
public
MyBean
() {
}
public
MyBean
(
String
id
,
String
clazz
) {
this
.
id
=
id
;
this
.
clazz
=
clazz
;
}
public
String
getId
() {
return
id
;
}
public
void
setId
(
String
id
) {
this
.
id
=
id
;
}
public
String
getClazz
() {
return
clazz
;
}
public
void
setClazz
(
String
clazz
) {
this
.
clazz
=
clazz
;
}
}
<!-- dom4j -->
<dependency>
<groupId>
dom4j
</groupId>
<artifactId>
dom4j
</artifactId>
<version>
1.6.1
</version>
</dependency>
<!-- XPath -->
<dependency>
<groupId>
jaxen
</groupId>
<artifactId>
jaxen
</artifactId>
<version>
1.1.6
</version>
</dependency>
3.4.
定义
Bean
⼯⼚接⼝
3.5.
定义
Bean
接⼝的实现类
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<bean
id
=
"userService"
class
=
"com.xxxx.service.UserService"
></bean>
<bean
id
=
"accountService"
class
=
"com.xxxx.service.AccountService"
></bean>
</beans>
package
com
.
xxxx
.
spring
;
/**
* Bean
⼯⼚接⼝定义
*/
public interface
MyFactory
{
//
通过
id
值获取对象
public
Object
getBean
(
String
id
);
}
package
com
.
xxxx
.
spring
;
import
org
.
dom4j
.
Document
;
import
org
.
dom4j
.
DocumentException
;
import
org
.
dom4j
.
Element
;
import
org
.
dom4j
.
XPath
;
import
org
.
dom4j
.
io
.
SAXReader
;
import
java
.
net
.
URL
;
import
java
.
util
.
ArrayList
;
import
java
.
util
.
HashMap
;
import
java
.
util
.
List
;
import
java
.
util
.
Map
;
/**
*
模拟
Spring
的实现
* 1
、通过构造器得到相关配置⽂件
* 2
、通过
dom4j
解析
xml
⽂件,得到
List
存放
id
和
class
* 3
、通过反射实例化得到对象
Class.forName(
类的全路径
).newInstance();
通过
Map<id,Class>
存
储
* 4
、得到指定的实例化对象
*/
public class
MyClassPathXmlApplicationContext
implements
BeanFactory
{
private
Map beans
=
new
HashMap
();
//
实例化后的对象放⼊
map
private
List
<
MyBean
>
myBeans
;
//
存放已读取
bean
配置信息
/* 1
、通过构造器得到相关配置⽂件
*/
public
MyClassPathXmlApplicationContext
(
String
fileName
) {
/* 2
、通过
dom4j
解析
xml
⽂件,得到
List
(存放
id
和
class
)
*/
this
.
parseXml
(
fileName
);
/* 3
、通过反射实例化得到对象
Class.forName(
类路径
).newInstance();
通过
Map
存储
*/
this
.
instanceBean
();
}
/**
*
通过
dom4j
解析
xml
⽂件,得到
List
存放
id
和
class
* 1
、获取解析器
* 2
、得到配置⽂件的
URL
* 3
、通过解析器解析
xml
⽂件(
spring.xml
)
* 4
、通过
xpath
语法,获取
beans
标签下的所有
bean
标签
* 5
、通过指定语法解析⽂档对象,返回集合
* 6
、判断集合是否为空,遍历集合
* 7
、获取标签元素中的属性
* 8
、得到
Bean
对象,将
Bean
对象设置到集合中
* @param fileName
*/
private
void
parseXml
(
String
fileName
) {
// 1
、获取解析器
SAXReader reader
=
new
SAXReader
();
// 2
、得到配置⽂件的
URL
URL url
=
this
.
getClass
().
getClassLoader
().
getResource
(
fileName
);
try
{
// 3
、通过解析器解析
xml
⽂件(
spring.xml
)
Document document
=
reader
.
read
(
url
);
// 4
、通过
xpath
语法,获取
beans
标签下的所有
bean
标签
XPath xPath
=
document
.
createXPath
(
"beans/bean"
);
//
通过指定语法解析⽂档对象,返回集合
List
<
Element
>
list
=
xPath
.
selectNodes
(
document
);
//
判断集合是否为空,遍历集合
if
(
list
!=
null
&&
list
.
size
()
>
0
) {
myBeans
=
new
ArrayList
<>
();
for
(
Element el
:
list
) {
//
获取标签元素中的属性
String
id
=
el
.
attributeValue
(
"id"
);
// id
属性值
String
clazz
=
el
.
attributeValue
(
"class"
);
// class
属性值
System
.
out
.
println
(
el
.
attributeValue
(
"id"
));
System
.
out
.
println
(
el
.
attributeValue
(
"class"
));
//
得到
Bean
对象
MyBean bean
=
new
MyBean
(
id
,
clazz
);
//
将
Bean
对象设置到集合中
myBeans
.
add
(
bean
);
}
}
}
catch
(
DocumentException e
) {
e
.
printStackTrace
();
}
3.6.
测试⾃定义
IOC
容器
1.
创建与配置⽂件中对应的
Bean
对象
UserService.java
AccountService.java
}
/**
*
通过反射实例化得到对象
* Class.forName(
类的全路径
).newInstance();
*
通过
Map<id,Class>
存储
*/
private
void
instanceBean
() {
//
判断
bean
集合是否为空,不为空遍历得到对应
Bean
对象
if
(
myBeans
!=
null
&&
myBeans
.
size
()
>
0
) {
for
(
MyBean bean
:
myBeans
){
try
{
//
通过类的全路径实例化对象
Object
object
=
Class
.
forName
(
bean
.
getClazz
()).
newInstance
();
//
将
id
与实例化对象设置到
map
对象中
beans
.
put
(
bean
.
getId
(),
object
);
}
catch
(
Exception e
) {
e
.
printStackTrace
();
}
}
}
}
/**
*
通过
key
获取
map
中的指定
value
* @param id
* @return
*/
@Override
public
Object
getBean
(
String
id
) {
Object
object
=
beans
.
get
(
id
);
return
object
;
}
}
package
com
.
xxxx
.
service
;
public class
UserService
{
public
void
test
(){
System
.
out
.
println
(
"UserService Test..."
);
}
}
2.
测试是否可以获取实例化的
Bean
对象
Spring
容器在启动的时候 读取
xml
配置信息,并对配置的
bean
进⾏实例化(这⾥模拟的⽐较简
单,仅⽤于帮助⼤家理解),同时通过上下⽂对象提供的
getBean()
⽅法拿到我们配置的
bean
对
象,从⽽实现外部容器⾃动化维护并创建
bean
的效果。
4. Spring IOC
配置⽂件加载
4.1. Spring
配置⽂件加载
spring.xml
package
com
.
xxxx
.
service
;
public class
AccountService
{
public
void
test
(){
System
.
out
.
println
(
"AccountService Test..."
);
}
}
package
com
.
xxxx
;
import
com
.
xxxx
.
spring
.
MyFactory
;
import
com
.
xxxx
.
spring
.
MyClassPathXmlApplicationContext
;
import
com
.
xxxx
.
service
.
AccountService
;
import
com
.
xxxx
.
service
.
UserService
;
public class
App
{
public static
void
main
(
String
[]
args
) {
MyFactory factory
=
new
MyClassPathXmlApplicationContext
(
"spring.xml"
);
//
得到实例化对象
UserService userService
=
(
UserService
)
factory
.
getBean
(
"userService"
);
userService
.
test
();
UserService userService2
=
(
UserService
)
factory
.
getBean
(
"userService"
);
System
.
out
.
println
(
userService
+
"====="
+
userService2
);
AccountService accountService
=
(
AccountService
)
factory
.
getBean
(
"accountService"
);
accountService
.
test
();
}
}
4.1.1.
根据相对路径加载资源
4.1.2.
根据绝对路径加载资源(了解)
4.2. Spring
多配置⽂件加载
Spring
框架启动时可以加载多个配置⽂件到环境中。对于⽐较复杂的项⽬,可能对应的配置⽂件有多
个,项⽬在启动部署时会将多个配置⽂件同时加载进来。
service.xml
dao.xml
4.2.1.
可变参数,传⼊多个⽂件名
<?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>
ApplicationContext ac
=
new
ClassPathXmlApplicationContext
(
"spring.xml"
);
ApplicationContext ac
=
new
FileSystemXmlApplicationContext
(
"C:/IdeaWorkspace/spring01/src/main/resources/spring.x
ml"
);
<?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>
<?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.xxxx.dao.UserDao"
></bean>
</beans>
4.2.2.
通过总的配置⽂件
import
其他配置⽂件
spring.xml
加载时只需加载总的配置⽂件即可
5. Spring IOC
容器
Bean
对象实例化
5.1.
构造器实例化
注:
通过默认构造器创建 空构造⽅法必须存在 否则创建失败
1.
设置配置⽂件
spring.xml
2.
获取实例化对象
//
同时加载多个资源⽂件
ApplicationContext ac
=
new
ClassPathXmlApplicationContext
(
"spring.xml"
,
"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"
>
<!--
导⼊需要包含的资源⽂件
-->
<import
resource
=
"service.xml"
/>
<import
resource
=
"dao.xml"
/>
</beans>
//
加载总的资源⽂件
ApplicationContext ac
=
new
ClassPathXmlApplicationContext
(
"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>
ApplicationContext ac
=
new
ClassPathXmlApplicationContext
(
"spring.xml"
);
UserService userService
=
(
UserService
)
ac
.
getBean
(
"userService"
);
userService
.
test
();
5.2.
静态⼯⼚实例化(了解)
注:
要有该⼯⼚类及⼯⼚⽅法
⼯⼚⽅法为静态的
1.
定义静态⼯⼚类
2.
设置配置⽂件
spring.xml
3.
获取实例化对象
当我们指定
Spring
使⽤静态⼯⼚⽅法来创建
Bean
实例时,
Spring
将先解析配置⽂件,并根据配置
⽂件指定的信息,
通过反射调⽤静态⼯⼚类的静态⼯⼚⽅法,并将该静态⼯⼚⽅法的返回值作为
Bean
实例
,在这个过程中,
Spring
不再负责创建
Bean
实例,
Bean
实例是由⽤户提供的静态⼯⼚⽅
法提供
的。
package
com
.
xxxx
.
factory
;
import
com
.
xxxx
.
service
.
UserService
;
/**
*
定义静态⼯⼚类
*/
public class
StaticFactory
{
/**
*
定义对应的静态⽅法,返回实例化对象
* @return
*/
public static
UserService createUserService
() {
return new
UserService
();
}
}
<?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.factory.StaticFactory"
factory
method
=
"createUserService"
></bean>
</beans>
ApplicationContext ac
=
new
ClassPathXmlApplicationContext
(
"spring.xml"
);
UserService userService
=
(
UserService
)
ac
.
getBean
(
"userService"
);
userService
.
test
();
5.3.
实例化⼯⼚实例化(了解)
注:
⼯⼚⽅法为⾮静态⽅法
需要配置⼯⼚
bean
,并在业务
bean
中配置
factory-bean
,
factory-method
属性
1.
定义⼯⼚类
2.
设置配置⽂件
spring.xml
3.
获取实例化对象
package
com
.
xxxx
.
factory
;
import
com
.
xxxx
.
service
.
UserService
;
/**
*
定义⼯⼚类
*/
public class
InstanceFactory
{
/**
*
定义⽅法,返回实例化对象
* @return
*/
public
UserService createUserService
() {
return new
UserService
();
}
}
<?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.xxxx.factory.InstanceFactory"
></bean>
<bean
id
=
"userService"
factory-bean
=
"instanceFactory"
factory
method
=
"createUserService"
></bean>
</beans>
5.4. Spring
三种实例化
Bean
的⽅式⽐较
⽅式⼀:
通过
bean
的缺省构造函数创建
,当各个
bean
的业务逻辑相互⽐较独⽴的时候或者和外界
关联较少的时候可以使⽤。
⽅式⼆:利⽤静态
factory
⽅法创建,可以统⼀管理各个
bean
的创建,如各个
bean
在创建之前需要
相同的初始化处理,则可⽤这个
factory
⽅法险进⾏统⼀的处理等等。
⽅式三:利⽤实例化
factory
⽅法创建,即将
factory
⽅法也作为了业务
bean
来控制,
1
可⽤于集成其
他框架的
bean
创建管理⽅法,
2
能够使
bean
和
factory
的⻆⾊互换。
开发中项⽬⼀般使⽤⼀种⽅式实例化
bean
,项⽬开发基本采⽤第⼀种⽅式,交给
Spring
托管,使⽤时
直接拿来使⽤即可。另外两种了解
6. Spring IOC
注⼊
⼿动实例化与外部引⼊
图⼀:
图⼆:
ApplicationContext ac
=
new
ClassPathXmlApplicationContext
(
"spring.xml"
);
UserService userService
=
(
UserService
)
ac
.
getBean
(
"userService"
);
userService
.
test
();
对⽐发现:图⼆中对于
UserDao
对象的创建并没有像图⼀那样主动的去实例化,⽽是通过带参⽅法形
式将
UserDao
传⼊过来,从⽽实现
UserService
对
UserDao
类
的依赖。
⽽实际创建对象的幕后对象即是交给了外部来创建。
6.1. Spring IOC
⼿动装配(注⼊)
Spring
⽀持的注⼊⽅式共有四种:
set
注⼊、构造器注⼊、静态⼯⼚注⼊、实例化⼯⼚注⼊。
6.1.1. set
⽅法注⼊
注:
属性字段需要提供
set
⽅法
四种⽅式,推荐使⽤
set
⽅法注⼊
6.1.1.1.
业务对象
JavaBean
1.
属性字段提供
set
⽅法
2.
配置⽂件的
bean
标签设置
property
标签
public class
UserService
{
//
业务对象
UserDao set
注⼊(提供
set
⽅法)
private
UserDao userDao
;
public
void
setUserDao
(
UserDao userDao
) {
this
.
userDao
=
userDao
;
}
}
<?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"
>
6.1.1.2.
常⽤对象和基本类型
1.
属性字段提供
set
⽅法
2.
配置⽂件的
bean
标签设置
property
标签
<!--
IOC
通过
property
标签⼿动装配(注⼊):
Set
⽅法注⼊
name
:
bean
对象中属性字段的名称
ref
:指定
bean
标签的
id
属性值
-->
<bean
id
=
"userDao"
class
=
"com.xxxx.dao.UserDao"
></bean>
<bean
id
=
"userService"
class
=
"com.xxxx.service.UserService"
>
<!--
业务对象 注⼊
-->
<property
name
=
"userDao"
ref
=
"userDao"
/>
</bean>
</beans>
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
;
}
}
<?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.xxxx.service.UserService"
>
<!--
常⽤对象
String
注⼊
-->
<property
name
=
"host"
value
=
"127.0.0.1"
/>
<!--
基本类型注⼊
-->
<property
name
=
"port"
value
=
"8080"
/>
</bean>
6.1.1.3.
集合类型和属性对象
1.
属性字段提供
set
⽅法
2.
配置⽂件的
bean
标签设置
property
标签
</beans>
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
;
}
}
<?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.xxxx.service.UserService"
>
<!--List
集合 注⼊
-->
6.1.1.4.
测试代码
UserService.java
<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>
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
;
spring.xml
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
();
}
}
<?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.xxxx.dao.UserDao"
></bean>
<bean
id
=
"userService"
class
=
"com.xxxx.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>
6.1.2.
构造器注⼊
注:
提供带参构造器
6.1.2.1.
单个
Bean
对象作为参数
Java
代码
XML
配置
</property>
</bean>
</beans>
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 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
=
"userService"
class
=
"com.xxxx.service.UserService"
>
<constructor-arg
name
=
"userDao"
ref
=
"userDao"
></constructor-arg>
</bean>
6.1.2.2.
多个
Bean
对象作为参数
Java
代码
XML
配置
6.1.2.3. Bean
对象和常⽤对象作为参数
</beans>
public class
UserService
{
private
UserDao userDao
;
// JavaBean
对象
private
AccountDao accountDao
// JavaBean
对象
public
UserService
(
UserDao userDao
,
AccountDao accountDao
) {
this
.
userDao
=
userDao
;
this
.
accountDao
=
accountDao
;
}
public
void
test
(){
System
.
out
.
println
(
"UserService Test..."
);
userDao
.
test
();
accountDao
.
test
();
}
}
<?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>
Java
代码
XML
配置
6.1.2.4.
循环依赖问题
循环问题产⽣的原因:
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 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>
Bean
通过构造器注⼊,之间彼此相互依赖对⽅导致
bean
⽆法实例化。
问题展示:
1. Java
代码
2. XML
配置
如何解决:将构造器注⼊改为
set
⽅法注⼊
1. 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..."
);
}
}
<!--
如果多个
bean
对象中互相注⼊,则会出现循环依赖的问题
可以通过
set
⽅法注⼊解决
-->
<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>
2. XML
配置
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..."
);
}
}
<!--
<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"
/>
6.1.3.
静态⼯⼚注⼊
1.
定义静态⼯⼚类
2. Java
代码
3. XML
配置
在配置⽂件中设置
bean
标签,指定⼯⼚对象并设置对应的⽅法
6.1.4.
实例化⼯⼚注⼊
1.
定义⼯⼚类
</bean>
public class
StaticFactory
{
//
定义静态⽅法
public static
TypeDao createTypeDao
() {
return new
TypeDao
();
}
}
public class
TypeService
{
private
TypeDao typeDao
;
public
void
setTypeDao
(
TypeDao typeDao
) {
this
.
typeDao
=
typeDao
;
}
public
void
test
() {
System
.
out
.
println
(
"TypeService Test..."
);
}
}
<bean
id
=
"typeService"
class
=
"com.xxxx.service.TypeService"
>
<property
name
=
"typeDao"
ref
=
"typeDao"
/>
</bean>
<!--
静态⼯⼚注⼊:
静态⼯⼚注⼊也是借助
set
⽅法注⼊,只是被注⼊的
bean
对象的实例化是通过静态⼯⼚实例化的
-->
<bean
id
=
"typeDao"
class
=
"com.xxxx.factory.StaticFactory"
factory
method
=
"createTypeDao"
></bean>
2. Java
代码
3. XML
配置
声明⼯⼚
bean
标签,声明
bean
对象,指明⼯⼚对象和⼯⼚⽅法
重点掌握
set
注⼊和构造器注⼊,⼯⼚⽅式了解即可。实际开发中基本使⽤
set
⽅式注⼊
bean
。
6.1.5.
注⼊⽅式的选择
开发项⽬中
set
⽅式注⼊⾸选
使⽤构造注⼊可以在构建对象的同时⼀并完成依赖关系的建⽴,对象⼀建⽴则所有的⼀切也就准备好
了,但如果要建⽴的对象关系很多,使⽤构造器注⼊会在构建函数上留下⼀⻓串的参数
,
且不易记忆
,
这
时使⽤
Set
注⼊会是个不错的选择。
使⽤
Set
注⼊可以有明确的名称,可以了解注⼊的对象会是什么,像
setXXX()
这样的名称会⽐记忆
Constructor
上某个参数的位置代表某个对象更好。
p
名称空间的使⽤
public class
InstanceFactory
{
public
TypeDao createTypeDao
() {
return new
TypeDao
();
}
}
public class
TypeService
{
private
TypeDao typeDao
;
public
void
setTypeDao
(
TypeDao typeDao
) {
this
.
typeDao
=
typeDao
;
}
public
void
test
() {
System
.
out
.
println
(
"TypeService Test..."
);
}
}
<bean
id
=
"typeService"
class
=
"com.xxxx.service.TypeService"
>
<property
name
=
"typeDao"
ref
=
"typeDao"
/>
</bean>
<!--
实例化⼯⼚注⼊:
实例化⼯⼚注⼊也是借助
set
⽅法注⼊,只是被注⼊的
bean
对象的实例化是通过实例化⼯⼚实例化的
-->
<bean
id
=
"instanceFactory"
class
=
"com.xxxx.factory.InstanceFactory"
></bean>
<bean
id
=
"typeDao"
factory-bean
=
"instanceFactory"
factory-method
=
"createTypeDao"
>
</bean>
spring2.5
以后,为了简化
setter
⽅法属性注⼊,引⽤
p
名称空间的概念,可以将 ⼦元素,简化为元素属
性配置。
1.
属性字段提供
set
⽅法
2.
在配置⽂件
spring.xml
引⼊
p
名称空间
6.2. Spring IOC
⾃动装配(注⼊)
注解⽅式注⼊
Bean
对于
bean
的注⼊,除了使⽤
xml
配置以外,可以使⽤注解配置。注解的配置,可以简化配置⽂件,
提⾼开发的速度,使程序看上去更简洁。对于注解的解释,
Spring
对于注解有专⻔的解释器,对定义的
注解进⾏解析,实现对应
bean
对象的注⼊。通过
反射技术实现
。
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
;
}
}
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.xxxx.dao.UserDao"
></bean>
<!--
p:
属性名
:="xxx"
引⼊常量值
p:
属性名
-ref:="xxx"
引⼊其他
Bean
对象的
id
属性值
-->
<bean
id
=
"userService"
class
=
"com.xxxx.service.UserService"
p:userDao-ref
=
"userDao"
p:host
=
"127.0.0.1"
/>
</beans>
6.2.1.
准备环境
1.
修改配置⽂件
2.
开启⾃动化注⼊
3.
给注⼊的
bean
对象添加注解
6.2.2. @Resource
注解
@Resource
注解实现⾃动注⼊(反射)
默认根据属性字段名称查找对应的
bean
对象 (属性字段的名称与
bean
标签的
id
属性值相等)
如果属性字段名称未找到,则会通过类型(
Class
类型)查找
属性可以提供
set
⽅法,也可以不提供
set
⽅法
注解可以声明在属性级别 或
set
⽅法级别
可以设置
name
属性,
name
属性值必须与
bean
标签的
id
属性值⼀致;如果设置了
name
属性值,就只
会按照
name
属性值查找
bean
对象
当注⼊接⼝时,如果接⼝只有⼀个实现则正常实例化;如果接⼝存在多个实现,则需要使⽤
name
属性指定需要被实例化的
bean
对象
代码示例
1.
默认根据属性字段名称查找对应的
bean
对象 (属性字段的名称与
bean
标签的
id
属性值相等)
<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>
/**
* @Resource
注解实现⾃动注⼊(反射)
*
默认根据属性字段名称查找对应的
bean
对象 (属性字段的名称与
bean
标签的
id
属性值相等)
*/
public class
UserService
{
@Resource
private
UserDao userDao
;
//
属性字段的名称与
bean
标签的
id
属性值相等
public
void
setUserDao
(
UserDao userDao
) {
this
.
userDao
=
userDao
;
}
2.
如果属性字段名称未找到,则会通过类型(
Class
类型)查找
3.
属性可以提供
set
⽅法,也可以不提供
set
⽅法
4.
注解可以声明在属性级别 或
set
⽅法级别
public
void
test
() {
//
调⽤
UserDao
的⽅法
userDao
.
test
();
}
}
/**
* @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
();
}
}
/**
* @Resource
注解实现⾃动注⼊(反射)
*
属性可以提供
set
⽅法,也可以不提供
set
⽅法
*/
public class
UserService
{
@Resource
private
UserDao userDao
;
//
不提供
set
⽅法
public
void
test
() {
//
调⽤
UserDao
的⽅法
userDao
.
test
();
}
}
/**
* @Resource
注解实现⾃动注⼊(反射)
*
注解可以声明在属性级别 或
set
⽅法级别
*/
5.
可以设置
name
属性,
name
属性值必须与
bean
标签的
id
属性值⼀致;如果设置了
name
属性值,就
只会按照
name
属性值查找
bean
对象
6.
当注⼊接⼝时,如果接⼝只有⼀个实现则正常实例化;如果接⼝存在多个实现,则需要使⽤
name
属性指定需要被实例化的
bean
对象
定义接⼝类
IUserDao.java
定义接⼝实现类
UserDao01.java
public class
UserService
{
private
UserDao userDao
;
@Resource
//
注解也可设置在
set
⽅法上
public
void
setUserDao
(
UserDao userDao
) {
this
.
userDao
=
userDao
;
}
public
void
test
() {
//
调⽤
UserDao
的⽅法
userDao
.
test
();
}
}
/**
* @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
();
}
}
package
com
.
xxxx
.
dao
;
/**
*
定义接⼝类
*/
public interface
IUserDao
{
public
void
test
();
}
定义接⼝实现类
UserDao02.java
XML
配置⽂件
使⽤注解
UserService.java
package
com
.
xxxx
.
dao
;
/**
*
接⼝实现类
*/
public class
UserDao01
implements
IUserDao
{
@Override
public
void
test
(){
System
.
out
.
println
(
"UserDao01..."
);
}
}
package
com
.
xxxx
.
dao
;
/**
*
接⼝实现类
*/
public class
UserDao02
implements
IUserDao
{
@Override
public
void
test
(){
System
.
out
.
println
(
"UserDao02..."
);
}
}
<!--
开启⾃动化装配(注⼊)
-->
<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>
6.2.3. @Autowired
注解
@Autowired
注解实现⾃动化注⼊:
默认通过类型(
Class
类型)查找
bean
对象
与属性字段的名称⽆关
属性可以提供
set
⽅法,也可以不提供
set
⽅法
注解可以声明在属性级别 或
set
⽅法级别
可以添加
@Qualifier
结合使⽤,通过
value
属性值查找
bean
对象(
value
属性值必须要设置,且值要与
bean
标签的
id
属性值对应)
1.
默认通过类型(
Class
类型)查找
bean
对象
与属性字段的名称⽆关
2.
属性可以提供
set
⽅法,也可以不提供
set
⽅法
/**
* @Resource
注解实现⾃动注⼊(反射)
*
当注⼊接⼝时,如果接⼝只有⼀个实现则正常实例化;如果接⼝存在多个实现,则需要使⽤
name
属性指
定需要被实例化的
bean
对象
*/
public class
UserService
{
@Resource
(
name
=
"userDao01"
)
// name
属性值与其中⼀个实现类的
bean
标签的
id
属性值⼀致
private
IUserDao iUserDao
;
//
注⼊接⼝(接⼝存在多个实现)
public
void
test
() {
iUserDao
.
test
();
}
}
/**
* @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
();
}
}
/**
* @Autowired
注解实现⾃动化注⼊
3.
注解可以声明在属性级别 或
set
⽅法级别
4.
可以添加
@Qualifier
结合使⽤,通过
value
属性值查找
bean
对象(
value
属性值必须要设置,且值要
与
bean
标签的
id
属性值对应)
*
属性可以提供
set
⽅法,也可以不提供
set
⽅法
*/
public class
UserService
{
@Autowired
private
UserDao userDao
;
//
不提供
set
⽅法
public
void
test
() {
//
调⽤
UserDao
的⽅法
userDao
.
test
();
}
}
/**
* @Autowired
注解实现⾃动化注⼊
*
注解可以声明在属性级别 或
set
⽅法级别
*/
public class
UserService
{
private
UserDao userDao
;
@Autowired
//
注解可以声明在
set
⽅法级别
public
void
setUserDao
(
UserDao userDao
) {
this
.
userDao
=
userDao
;
}
public
void
test
() {
//
调⽤
UserDao
的⽅法
userDao
.
test
();
}
}
/**
* @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
扫描器的配置
1.
设置⾃动化扫描范围
2.
使⽤特定的注解
@Repository
(
Dao
层)
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>
@Service
(
Service
层
)
@Controller
(
Controller
层 )
@Component
(任意层)
7.2. Spring
模拟⽤户登录流程
7.2.1. Dao
层 (查询⽤户记录)
1.
定义
JavaBean User.java
@Repository
public class
ResourceDao
{
public
void
test
() {
System
.
out
.
println
(
"ResourceDao..."
);
}
}
@Service
public class
ResourceService
{
@Resource
private
ResourceDao resourceDao
;
// service
层注⼊
dao
层的
bean
对象
public
void
test
() {
System
.
out
.
println
(
"ResourceService..."
);
resourceDao
.
test
();
}
}
@Controller
public class
ResourceController
{
@Autowired
private
ResourceService resourceService
;
// Controller
层注⼊
service
层的
bean
对象
public
void
test
() {
System
.
out
.
println
(
"ResourceController..."
);
resourceService
.
test
();
}
}
@Component
public class
PropertyUtils
{
public
void
test
(){
System
.
out
.
println
(
"PropertyUtils..."
);
}
}
2.
编写
Dao
层
UserDao.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
;
}
}
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
;
}
7.2.2. Service
层 (业务逻辑处理)
1.
定义业务处理返回消息模型
MessageModel.java
2.
编写
Service
层
UserService.java
//
如果正确,将⽤户名称和密码设置到
user
对象中
user
=
new
User
();
user
.
setUserName
(
USERNAME
);
user
.
setUserPwd
(
USERPWD
);
return
user
;
}
}
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
;
}
}
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
;
7.2.3. Controller
层 (接收请求)
@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
;
}
}
1.
编写
Controller
层
UserController.java
7.2.4.
通过
JUnit
进⾏测试
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
;
}
}
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
应⽤中的作⽤域
1.
request
作⽤域
表示每个请求需要容器创建⼀个全新
Bean
。⽐如提交表单的数据必须是对每次请求新建⼀个
Bean
来保持这些表单数据,请求结束释放这些数据。
2.
session
作⽤域
表示每个会话需要容器创建⼀个全新
Bean
。⽐如对于每个⽤户⼀般会有⼀个会话,该⽤户的⽤户
信息需要存储到会话中,此时可以将该
Bean
作⽤域配置为
session
级别。
3.
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
属性来完成。
⽅式⼆:
实现
org.springframework.beans.factory.InitializingBean
接⼝。
public class
RoleService
{
//
定义初始化时需要被调⽤的⽅法
public
void
init
() {
System
.
out
.
println
(
"RoleService init..."
);
}
}
<!--
通过
init-method
属性指定⽅法
-->
<bean
id
=
"roleService"
class
=
"com.xxxx.service.RoleService"
init-method
=
"init"
></bean>
public class
RoleService
implements
InitializingBean
{
@Override
public
void
afterPropertiesSet
()
throws
Exception
{
System
.
out
.
println
(
"RoleService init..."
);
}
}
<bean
id
=
"roleService"
class
=
"com.xxxx.service.RoleService"
></bean>
Bean
对象实例化过程是在
Spring
容器初始化时被实例化的,但也不是不可改变的,可以通过
lazy
init="true"
属性延迟
bean
对象的初始化操作,此时再调⽤
getBean
⽅法时才会进⾏
bean
的初始化操作
8.2.3. Bean
的使⽤
⽅式⼀:
使⽤
BeanFactory
⽅式⼆:
使⽤
ApplicationContext
8.2.4. Bean
的销毁
实现销毁⽅式
(Spring
容器会维护
bean
对象的管理,可以指定
bean
对象的销毁所要执⾏的⽅法
)
。
步骤⼀:
实现销毁⽅式
(Spring
容器会维护
bean
对象的管理,可以指定
bean
对象的销毁所要执⾏的⽅
法
)
步骤⼆:
通过
AbstractApplicationContext
对象,调⽤其
close
⽅法实现
bean
的销毁过程
//
得到
Spring
的上下⽂环境
BeanFactory factory
=
new
ClassPathXmlApplicationContext
(
"spring.xml"
);
RoleService roleService
=
(
RoleService
)
factory
.
getBean
(
"roleService"
);
//
得到
Spring
的上下⽂环境
ApplicationContext ac
=
new
ClassPathXmlApplicationContext
(
"spring.xml"
);
RoleService roleService
=
(
RoleService
)
ac
.
getBean
(
"roleService"
);
<bean
id
=
"roleService"
class
=
"com.xxxx.service.RoleService"
destroy-method
=
"destroy"
>
</bean>
AbstractApplicationContext ctx
=
new
ClassPathXmlApplicationContext
(
"spring.xml"
);
ctx
.
close
();
IOC/DI-
控制反转和依赖注⼊
将对象实例化的创建过程转交给外部容器(
IOC
容器 充当⼯⼚⻆⾊)去负责;属性赋值的操作;