Spring IOC
一、Spring框架
Spring框架是一个开源的 JavaEE 的应用程序、其主要核心是 IOC(控制反转/依赖注入)与AOP(面向切面)两大技术,实现项目在开发过程中的轻松解耦,提高项目的开发效率。
Spring IOC(控制反转/依赖注入)
Spring AOP
Spring JDBC + 事务
1.Spring作用
Dao层:JDBC操作 对应的框架:Mybatis
Service层:Spring框架不是针对service层的业务逻辑的 service没有适合框架
Controller层:Servlet(接收请求 响应数据 地址配置 页面转发) 对应框架:Spring MVC、
Spring基于分布式的应用程序、基于轻量级的框架
配置管理
Bean对象的实例化-IOC
集成第三方的框架:Mybatis、Hibernate框架(持久层框架)、Spring MVC、Spring Security权限
Quartz时钟框架(定时任务处理):
1.自带服务
2.Mail邮件发送
3.定时任务处理-定时调度(定时短信、定时任务)
4.消息处理(异步处理)
2.Spring模块划分
Spring IOC模块:Bean对象的实例化 Bean的创建
Spring AOP模块:动态代理 面向切面编程
Spring JDBC+事务模块
Spring Web模块
3.配置环境
1.修改单元测试 JUnit 版本
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
2.添加 Spring 框架的依赖坐标
Maven仓库:https://mvnrepository.com/
<!-- 添加Spring框架的核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
3.在 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遵守的规范 官方指定
-->
</beans>
4.在 spring.xml 中配置 Bean 对象
<!--
id:bean对象的id,唯一标识。一般是Bean对象的名称的首字母小写
class:bean对象的类路径
-->
<bean id="userService"
class="com.xxxx.service.UserService"></bean>
5.加载配置文件,获取实例化对象
// 获取Spring上下文环境 (加载配置文件)
ApplicationContext ac = new
ClassPathXmlApplicationContext("spring.xml");
// 通过getBean方法得到Spring容器中实例化好的Bean对象 (实例化Bean对象)
// userService代表的是配置文件中bean标签的id属性值
UserService userService = (UserService)ac.getBean("userService");
// 调用方法 (使用实例化对象)
userService.test();
二、Spring IOC 配置文件加载
1.Spring 配置文件加载
根据相对路径加载资源
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
根据绝对路径加载资源(了解)
ApplicationContext ac = newFileSystemXmlApplicationContext("C:/IdeaWorkspace/spring01/src/main/resources/spring.xml");
2.Spring 多配置文件加载
Spring 框架启动时可以加载多个配置文件到环境中。对于比较复杂的项目,可能对应的配置文件有多个,项目在启动部署时会将多个配置文件同时加载进来。
1.可变参数,传入多个文件名
// 同时加载多个资源文件
ApplicationContext ac = newClassPathXmlApplicationContext("spring.xml","dao.xml");
2.通过总的配置文件import其他配置文件
<?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");
三、 Spring IOC 容器 Bean 对象实例化
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.xxxx.service.UserService"></bean> </beans>
-
获取实例化对象
ApplicationContext ac = newClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService)ac.getBean("userService");
userService.test();
2.静态工厂实例化(了解)
-
定义工厂类及对应的静态方法
-
配置bean对象对应的工厂类及静态方法
id:需要被实例化的bean对象的id
class:静态工厂类的路径
factory-method:静态工厂类中实例化bean对象的静态方法
<bean id="typeService" class="com.xxxx.factory.StaticFactory" factory-method="createService"></bean>
/** * 定义静态工厂类 */ public class StaticFactory { /** * 定义对应的静态方法 * @return */ public static TypeService createService() { return new TypeService(); } }
TypeService typeService = (TypeService) factory.getBean("typeService"); typeService.test();
3.实例化工厂实例化(了解)
实例化工厂实例化
1. 定义工厂类及对应的方法
2. 配置工厂对象
3. 配置bean对象对应的工厂对象及工厂方法
factory-bean:工厂对象对应的id属性值
factory-method:工厂类中的方法
<!-- 工厂对象-->
<bean id="instanceFactory" class="com.xxxx.factory.InstanceFactory"></bean>
<!-- bean对象 -->
<bean id="typeController" factory-bean="instanceFactory" factory-method="createTypeController"></bean>
/**
* 定义实例化工厂
*/
public class InstanceFactory {
/**
* 定义方法
* @return
*/
public TypeController createTypeController(){
return new TypeController();
}
}
TypeController typeController = (TypeController) factory.getBean("typeController");
typeController.test();
4.Spring三种实例化Bean的方式比较
方式一:通过bean的缺省构造函数创建,当各个bean的业务逻辑相互比较独立的时候或者和外界关联较少的时候可以使用。
方式二:利用静态factory方法创建,可以统一管理各个bean的创建,如各个bean在创建之前需要相同的初始化处理,则可用这个factory方法险进行统一的处理等等。
方式三:利用实例化factory方法创建,即将factory方法也作为了业务bean来控制,1可用于集成其他框架的bean创建管理方法,2能够使bean和factory的角色互换。
四、 Spring IOC 注入
图二中对于 UserDao 对象的创建并没有像图一那样主动的去实例化,而是通过带参方法形式将UserDao 传入过来,从而实现 UserService 对UserDao类 的依赖。
而实际创建对象的幕后对象即是交给了外部来创建。
1.Spring IOC 手动装配(注入)
set方法注入
1.属性字段提供set方法
2.在配置文件中通过property属性指定属性字段
四种方式,推荐使用set方法注入
1.业务对象 JavaBean
属性字段提供set方法
public class UserService {
// 业务对象UserDao set注入(提供set方法)
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
配置文件的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>
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;
}
}
<!--
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>
集合类型和属性对象
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(Propertiesproperties) {
this.properties = properties;
}
}
<!--
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>
测试
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();
}
}
<!--
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>
</property>
</bean>
构造器注入
提供带参构造器
单个Bean对象作为参数
public class UserService {
private UserDao userDao; // JavaBean 对象
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void test(){
System.out.println("UserService
Test...");
userDao.test();
}
}
<!--
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>
多个Bean对象作为参数
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();
}
}
<!--
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>
Bean对象和常用对象作为参数
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);
}
}
<!--
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>
循环依赖问题
循环问题产生的原因:
Bean通过构造器注入,之间彼此相互依赖对方导致bean无法实例化。
如何解决:将构造器注入改为set方法注入
静态工厂注入
定义静态工厂类
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标签,指定工厂对象并设置对应的方法
<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>
实例化工厂注入
定义工厂类
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标签,声明bean对象,指明工厂对象和工厂方法
<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>
重点掌握set注入和构造器注入,工厂方式了解即可。实际开发中基本使用set方式注入bean。
2.p名称空间的使用
spring2.5以后,为了简化setter方法属性注入,引用p名称空间的概念,可以将 子元素,简化为元素属性配置。
1.属性字段提供 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;
}
}
2.在配置文件 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.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>
3.Spring IOC 自动装配(注入)
注解方式注入 Bean
对于 bean 的注入,除了使用 xml 配置以外,可以使用注解配置。注解的配置,可以简化配置文件,提高开发的速度,使程序看上去更简洁。对于注解的解释,Spring对于注解有专门的解释器,对定义的注解进行解析,实现对应bean对象的注入。通过反射技术实现。
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对象添加注解
2.@Resource注解
@Resource注解实现自动注入 (反射)
- 注解默认通过属性字段名称查找对应的bean对象(属性字段名称与bean标签的id属性值一致)
- 如果属性字段名称不一样,则会通过类型(Class)类型
- 属性字段可以提供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();
}
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>
/**
* @Resource注解实现自动注入(反射)
* 当注入接口时,如果接口只有一个实现则正常实例化;
如果接口存在多个实现,则需要使用name属性指定需要被实例
化的bean对象
*/
public class UserService {
@Resource(name = "userDao01") // name属性值与其中一个实现类的bean标签的id属性值一致
private IUserDao iUserDao; // 注入接口(接口存在多个实现)
public void test() {
iUserDao.test();
}
}
3.@Autowired注解
@Autowired注解实现自动化注入
- 注解默认使用类型(Class类型)查找bean对象,与属性字段名称没有关系
- 属性字段可以提供set方法 也可以不提供
- 注解可以声明在属性级别 或 set方法级别
- 如果想要通过指定名称查找bean对象,需要结合@Qualifier使用(通过设置value属性值查找,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的耦合。
五、Spring IOC 扫描器
实际的开发中,bean的数量非常多,采用手动配置bean的方式已无法满足生产需要,Spring这时候同样提供了扫描的方式,对扫描到的bean对象统一进行管理,简化开发配置,提高开发效率。
作用:bean对象统一进行管理,简化开发配置,提高开发效率
1.Spring IOC 扫描器的配置
设置自动化扫描的范围:如果bean对象未在指定包范围,即使声明了注解,也无法实例化
使用指定的注解(声明在类级别):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>
2.使用特定的注解
@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...");
}
}
六、Bean的作用域与生命周期
1.Bean的作用域
默认情况下,我们从Spring容器中拿到的对象均是单例的,对于bean的作用域类型如下:
注意: lazy-init是懒加载, 如果等于true时作用是指Spring容器启动的时候不会去实例化这个bean, 而是在程序调用时才去实例化. 默认是false即Spring容器启动时实例化.
默认情况下,被管理的bean只会IOC容器中存在一个实例,对于所有获取该Bean的操作Spring容器将只返回同一个Bean。
singleton作用域(单例作用域)
Spring IOC容器在启动时,会将所有在singleton作用域中的bean对象实例化,并设置到单例缓存池中
lazy-init属性(懒加载)
如果设置为true,表示懒加载,容器在启动时,不会实例化bean对象,在程序调用时才会实例化
如果设置false(默认),表示不懒加载,容器启动则实例化bean对象
lazy-init属性为什么要设置为false?
1. 可以提前发现潜在的配置文件
2. Bean对象在启动时就会设置在单例缓存池中,使用时不需要再去实例化bean对象,提高程序运行效率
什么对象适合作为单例对象?(什么对象适合交给IOC容器实例化?)
无状态的对象(不存在改变当前对象状态的成员变量)
使用对象:controller层、service层、dao层
什么是无状态或状态不可改变的对象?
实际上对象状态的变化往往均是由于属性值得变化而引起的,比如user类 姓名属性会有变化,属性姓名的变化一般会引起user对象状态的变化。对于我们的程序来说,无状态对象没有实例变量的存在,保证了线程的安全性,service 层业务对象即是无状态对象。线程安全的。
<!-- singleton作用域 -->
<!-- 懒加载 -->
<bean id="roleService" class="com.xxxx.service.RoleService" lazy-init="true"></bean>
<!-- 默认是false,不懒加载 -->
<bean id="roleService" class="com.xxxx.service.RoleService" lazy-init="false"></bean>
prototype 作用域(原型作用域)
通过scope=“prototype” 设置bean的类型 ,每次向Spring容器请求获取Bean都返回一个全新的Bean,相对于"singleton"来说就是不缓存Bean,每次都是一个根据Bean定义创建的全新Bean。
<bean id="roleService" class="com.xxxx.service.RoleService" scope="prototype"></bean>
Web应用中的作用域
request作用域:表示每个请求需要容器创建一个全新Bean。
session作用域:表示每个会话需要容器创建一个全新Bean。
globalSession作用域
2.Bean的生命周期
对比已经学过的servlet 生命周期(容器启动装载并实例化servlet类,初始化servlet,调用service方法,销毁servlet)。
在Spring中,Bean的生命周期包括Bean的定义、初始化、使用和销毁4个阶段
Bean的定义
在配置文件中定义bean
通过bean标签定义对应bean对象
Bean的初始化
IOC容器启动时,自动实例化Bean对象
1. 在配置文档中通过指定 init-method 属性来完成。
2. 实现 org.springframework.beans.factory.InitializingBean 接口。
Bean的使用
1. 使用 BeanFactory对象
2. 使用 ApplicationContext对象
Bean的销毁
步骤一:通过 AbstractApplicationContext 对象,调用其close方法实现bean的销毁过程
步骤二:在配置文件中指定对应销毁的方法 destroy-method
<!-- Bean的初始化:在配置文档中通过指定 init-method 属性来完成-->
<bean id="roleService" class="com.xxxx.service.RoleService" init-method="init"></bean>
<!-- Bean的初始化:实现 org.springframework.beans.factory.InitializingBean 接口 -->
<bean id="roleService" class="com.xxxx.service.RoleService" ></bean>
<!-- Bean的销毁 -->
<bean id="roleService" class="com.xxxx.service.RoleService" destroy-method="destroy" ></bean>
// 通过 AbstractApplicationContext 对象,调用其close方法实现bean的销毁过程
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("spring03.xml");
System.out.println("销毁前:" + ctx.getBean("roleService"));
ctx.close();
System.out.println("销毁后:" + ctx.getBean("roleService"));
IOC/DI-控制反转和依赖注入 将对象实例化的创建过程转交给外部容器(IOC容器 充当工厂角色)去负责;属性赋值的操作;