目录
1.Spring框架的主要内容
1.1Spring的发展版本
1.2Spring系统架构
(1)核心层
(2)AOP层
(3)数据层
(4)Web层
(5)Test层
1.3Spring核心概念
1.3.1IOC(Inversion of control)控制反转
1.3.2DI(Dependency Injection)依赖注入
1.3.3核心概念小结
1.4入门案例代码实现
步骤1:创建Maven项目
步骤2:添加Spring的依赖jar包
步骤3:添加案例中需要的类
步骤4:添加spring配置文件(先导入Spring的jar包)
步骤5:在配置文件中完成bean的配置
步骤6:获取IOC容器
步骤7:从容器中获取对象进行方法调用
步骤8:运行程序
总结
1.5DI入门案例
步骤1: 去除代码中的new
步骤2:为属性提供setter方法
步骤3:修改配置完成注入
步骤4:运行程序
1.6bean基础配置
1.6.1bean的name属性
*1.6.2bean作用范围scope配置
1.6.3scope思考
1.6.4小结
1.7bean实例化
1.7.1构造方法实例化bean
1.7.2静态工程实例化
1.7.3实例工厂与FactoryBean
1.7.4FactoryBean的使用
1.8bean的生命周期
1.8.2注册钩子关闭容器
注意(关于afterPropertiesSet与setBookDao的执行顺序):
1.8.3bean生命周期小结
1.9setter注入和构造器注入
1.9.1setter注入引用类型
1.9.2setter注入简单数据类型
1.9.3构造器注入引用数据类型
1.9.4构造器注入多个简单数据类型(解耦的两种方式)
1.9.5参数注入方式的选择
1.10自动配置
1.10.1依赖自动装配
1.10.2自动装配的方式(按类型byType和按名称byName)
1.10.3集合注入
1.Spring框架的主要内容
简化开发
: Spring
框架中提供了两个大的核心技术,分别是
:
IOC和
AOP
事务处理
1.Spring
的简化操作都是基于这两块内容
,
所以这也是
Spring
学习中最为重要的两个知识点。
2.
事务处理属于
Spring
中
AOP
的具体应用,可以简化项目中的事务管理,也是
Spring
技术中的一
大亮点。
框架整合
: Spring
在框架整合这块已经做到了极致,它可以整合市面上几乎所有主流框架,比
如
:
MyBatis
MyBatis-plus
Struts
Struts2
Hibernate
综上所述,对于
Spring
的学习,主要学习四块内容
:
(1)IOC,(2)
整合
Mybatis(IOC
的具体应用
)
,
(3)AOP,(4)
声明式事务
(AOP
的具体应用
)
1.1Spring的发展版本
Spring1.0
是纯配置文件开发
Spring2.0
为了简化开发引入了注解开发,此时是配置文件加注解的开发方式
Spring3.0
已经可以进行纯注解开发,使开发效率大幅提升,我们的课程会以注解开发为主
Spring4.0
根据
JDK
的版本升级对个别
API
进行了调整
Spring5.0
已经全面支持
JDK8
,现在
Spring
最新的是
5
系列所以建议大家把
JDK
安装成
1.8
版
1.2Spring系统架构
Spring Framework
的
5
版本目前没有最新的架构图,而最新的是
4
版本,所以接下来主要研究的
是
4
的架构图:
(1)核心层
Core Container:
核心容器,这个模块是
Spring
最核心的模块,其他的都需要依赖该模块
(2)AOP层
AOP:
面向切面编程,它依赖核心层容器,目的是
在不改变原有代码的前提下对其进行功能增强
Aspects:AOP
是思想
,Aspects
是对
AOP
思想的具体实现
(3)数据层
Data Access:
数据访问,
Spring
全家桶中有对数据访问的具体实现技术
Data Integration:
数据集
成,
Spring
支持整合其他的数据层解决方案,比如
Mybatis
Transactions:
事务,
Spring
中事务管理是
Spring AOP
的一个具体实现,也是后期学习的
重点内容
(4)Web层
这一层的内容将在
SpringMVC
框架具体学习
(5)Test层
Spring
主要整合了
Junit
来完成单元测试和集成测试
1.3Spring核心概念
在
Spring
核心概念这部分内容中主要包含
IOC/DI、IOC容器和
Bean
,
那么问题就来了,这些都是什
么呢
?
为了解决
现在代码在编写的过程中存在的问题是:
耦合度偏高
使用对象时,在程序中不要主动使用
new
产生对象,转换为由
外部
提供对象,这种实现思想
就是
Spring
的一个核心概念 。
1.3.1IOC(Inversion of control)控制反转
(1)什么是控制反转呢?
使用对象时,由主动
new
产生对象转换为由
外部
提供对象,此过程中对象创建控制权由程序转移到
外部,此思想称为控制反转。
(2)Spring和IOC之间的关系是什么呢?
Spring
技术对
IOC
思想进行了实现
Spring
提供了一个容器,称为
IOC
容器
,用来充当
IOC
思想中的
"
外部
"
IOC
思想中的
别人
[
外部
]
指的就是
Spring
的
IOC
容器
(3)IOC容器的作用以及内部存放的是什么?
IOC
容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象
被创建或被管理的对象在
IOC
容器中统称为
Bean
IOC
容器中放的就是一个个的
Bean
对象
(4)当IOC容器中创建好service和dao对象后,程序能正确执行么?
不行,因为
service
运行需要依赖
dao
对象
IOC
容器中虽然有
service
和
dao
对象
但是
service
对象和
dao
对象没有任何关系
需要把
dao
对象交给
service,
也就是说要绑定
service
和
dao
对象之间的关系
像这种在容器中建立对象与对象之间的绑定关系就要用到
DI(依赖注入)
:
1.3.2DI(Dependency Injection)依赖注入
(1)什么是依赖注入呢?
在容器中建立
bean
与
bean
之间的依赖关系的整个过程,称为依赖注入
业务层要用数据层的类对象,以前是自己
new
的,
现在自己不
new
了,靠
别人
[
外部其实指的就是
IOC
容器
]
来给注入进来,
这种思想就是依赖注入
(2)IOC容器中哪些bean之间要建立依赖关系呢?
这个需要程序员根据业务需求提前建立好关系,如
业务层需要依赖数据层,
service
就要和
dao
建
立依赖关系
Spring
的
IOC
和DI两个概念的最终目标就是
:
充分解耦
使用
IOC
容器管理
bean
(
IOC)
在
IOC
容器内将有依赖关系的
bean
进行关系绑定(
DI
)
最终结果为
:
使用对象时不仅可以直接从
IOC
容器中获取,并且获取到的
bean
已经绑定了所有的依
赖关系
1.3.3核心概念小结
(1)什么IOC/DI思想?
IOC:
控制反转,控制反转的是对象的创建权
DI:
依赖注入,绑定对象与对象之间的依赖关系
(2)什么是IOC容器?
Spring
创建了一个容器用来存放所创建的对象,这个容器就叫
IOC
容器
(3)什么是Bean?
容器中所存放的一个个对象就叫
Bean
或
Bean
对象
1.4入门案例代码实现
需求分析
:
将
BookServiceImpl
和
BookDaoImpl
交给
Spring
管理,并从容器中获取对应的
bean
对象进行方法调用。
1.
创建
Maven
的
java
项目
2.pom.xml
添加
Spring
的依赖
jar
包
3.
创建
BookService,BookServiceImpl
,
BookDao
和
BookDaoImpl
四个类
4.resources
下添加
spring
配置文件,并完成
bean
的配置
5.
使用
Spring
提供的接口完成
IOC
容器的创建
6.
从容器中获取对象进行方法调用
步骤1:创建Maven项目
步骤2:添加Spring的依赖jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>spring_01_quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
步骤3:添加案例中需要的类
创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类
public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
public interface BookService {
public void save();
}
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
步骤4:添加spring配置文件(先导入Spring的jar包)
resources
下添加
spring
配置文件
applicationContext.xml
,并完成
bean
的配置
步骤5:在配置文件中完成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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" name="service service2 bookEbi" class="com.itheima.service.impl.BookServiceImpl"/>
</beans>
注意事项:
bean
定义时
id
属性在同一个上下文中
(
配置文件
)
不能重复
步骤6:获取IOC容器
package com.itheima.service;
import com.itheima.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App2 {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}
步骤7:从容器中获取对象进行方法调用
package com.itheima.service;
import com.itheima.dao.BookDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App2 {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// BookDao bookDao = (BookDao) ctx.getBean("bookDao");
//
// bookDao.save();
BookService bookService = (BookService) ctx.getBean("service");
bookService.save();
}
}
步骤8:运行程序
测试结果为:
但是在
BookServiceImpl
的类中依然存在
BookDaoImpl
对象的new
操作,它们之间的耦合度还是比
较高(在BookServiceImpl实体类中还是存在new BookDaoImpl,故耦合度还是较高),这块该如何
解决,就需要用到下面的
DI:
依赖注入
。
总结
首先获取IOC容器,使用ClassPathXmlApplicationContext()方法对配置文件applicationContext.xml
进行加载,new出一个IOC容器对象调用getBean()方法通过id(bookService)对bean标签进行加
载,即可得到bookService对象,在配置文件中,id="bookService"的标签通过全类名定位该实体
类,最后调用其方法。
1.5DI入门案例
思路分析:
(1)
要想实现依赖注入,必须要基于
IOC
管理
Bean
DI
的入门案例要依赖于前面
IOC
的入门案例
(2)Service
中使用
new
形式创建的
Dao
对象是否保留
?
需要删除掉,最终要使用
IOC
容器中的
bean
对象
(3)Service
中需要的
Dao
对象如何进入到
Service
中
?
在
Service
中提供方法,让
Spring
的
IOC
容器可以通过该方法传入
bean
对象
(4)Service
与
Dao
间的关系如何描述
?
使用配置文件
需求
:
基于
IOC
入门案例,在
BookServiceImpl
类中删除
new
对象的方式,使用
Spring
的
DI
完成
Dao
层的注入
1.
删除业务层中使用
new
的方式创建的
dao
对象
2.
在业务层提供
BookDao
的
setter
方法
3.
在配置文件中添加依赖注入的配置
4.
运行程序调用方法
步骤1: 去除代码中的new
在BookServiceImpl类中,删除业务层中使用new的方式创建的dao对象
package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}
步骤2:为属性提供setter方法
在
BookServiceImpl
类中
,
为
BookDao
提供
setter
方法
package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
public class BookServiceImpl implements BookService {
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
步骤3:修改配置完成注入
在配置文件中添加依赖注入的配置
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--bean标签标示配置bean id属性标示给bean起名字 class属性表示给bean定义类型 -->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--配置server与dao的关系-->
<!--property标签表示配置当前bean的属性 name属性表示配置哪一个具体的属性 ref属性表示参照哪一个bean -->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
注意
:
配置中的两个
bookDao
的含义是不一样的
name="bookDao"
中
bookDao
的作用是让
Spring
的
IOC
容器在获取到名称后,将首字母大写,前
面加
set
找对应的
setBookDao()
方法进行对象注入
ref="bookDao"
中
bookDao
的作用是让
Spring
能在
IOC
容器中找到
id
为
bookDao
的
Bean
对象给
bookService
进行注入
综上所述,对应关系如下
:
步骤4:运行程序
运行,测试结果为:
1.6bean基础配置
class属性能不能写接口如BookDao的类全名呢?
答案肯定是不行,因为接口是没办法创建对象的。
1.6.1bean的name属性
步骤
1
:配置别名
打开
spring
的配置文件
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
<bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
<!--scope:为bean设置作用范围,可选值为单例singloton,非单例prototype-->
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
</beans>
说明
:Ebi
全称
Enterprise Business Interface
,翻译为企业业务接口
步骤
2:
根据名称容器中获取
bean
对象
package com.itheima;
import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppForName {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookService bookService = (BookService) ctx.getBean("service4");
bookService.save();
}
}
步骤
3:
运行程序
测试结果为:
注意事项
:
bean
依赖注入的
ref
属性指定
bean
,必须在容器中存在
如果不存在
,
则会报错,如下
:
这个错误大家需要特别关注下
:
获取
bean
无论是通过
id
还是
name
获取,如果无法获取到,将抛出异常
NoSuchBeanDefinitionException
*1.6.2bean作用范围scope配置
singleton(单例):
只有一个共享的实例存在,所有对这个bean的请求都会返回这个唯一的实例。
prototype(多例):
对这个bean的每次请求都会创建一个新的bean实例,类似于new。
1.6.3scope思考
为什么bean默认为单例?
1.bean
为单例的意思是在
Spring
的
IOC
容器中只会有该类的一个对象
2.bean
对象只有一个就避免了对象的频繁创建与销毁,达到了
bean
对象的复用,性能高
bean在容器中是单例的,会不会产生线程安全问题?
1.如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,
所有请求线程共用一个
bean
对象,所以会存在线程安全问题。
2.如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,
方法中的局部变量在方法
调用完成后会被销毁,所以不会存在线程安全问题。
哪些bean对象适合交给容器进行管理?
1.表现层对象
2.业务层对象
3.数据层对象
4.工具对象
哪些bean对象不适合交给容器进行管理?
封装实例的域对象,因为会引发线程安全问题,所以不适合。
1.6.4小结
1.7bean实例化
bean
是如何创建的
实例化
bean
的三种方式,
构造方法,静态工厂和实例工厂
1.7.1构造方法实例化bean
Spring内部走的依然是构造函数
,
能访问到类中的私有构造方法
,
显而易见
Spring
底层用的是反射,
Spring
底层使用的是类的无参构造方法。
1.7.2静态工程实例化
配置文件:
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory"
factory- method="getOrderDao"/>
class:
工厂类的类全名
factory-mehod:
具体工厂类中创建对象的方法名
通过IOC容器获取配置文件id,通过全类名获取工厂类,通过factory-method属性获取工厂类中的方法将对象返回到IOC容器中。
1.7.3实例工厂与FactoryBean
配置文件:
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
实例化工厂运行的顺序是
:
1.创建实例化工厂对象
,
对应的是第一行配置
2.调用对象中的方法来创建
bean
,对应的是第二行配置
3.factory-bean:
工厂的实例对象
factory-method:
工厂对象中的具体创建对象的方法名
,
对应关系如下
:
首先我们想要从IOC容器中调用到UserDao对象,并且是通过实例工厂的方法,在配置文件中,第一行配置IOC容器已经得到UserDaoFactory对象,我们通过factory-bean="userFactory"得到该对象,再通过属性factory-method="getUserDao"得到该对象的方法,从而返回UserDaoImpl实体类对象。
1.7.4FactoryBean的使用
具体的使用步骤为
:
(1)创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法
package com.itheima.factory;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import org.springframework.beans.factory.FactoryBean;
//FactoryBean创建对象
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
public Class<?> getObjectType() {
return UserDao.class;
}
}
(2)在Spring的配置文件中进行配置
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>
(3)AppForInstanceUser运行类不用做任何修改,直接运行
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
查看源码会发现,
FactoryBean
接口其实会有三个方法,分别是
:
方法一
:getObject()
,被重写后,在方法中进行对象的创建并返回
方法二
:getObjectType(),
被重写后,主要返回的是被创建类的
Class
对象
方法三
:
没有被重写,因为它已经给了默认值,从方法名中可以看出其作用是设置对象是否为单
例,默
认
true。
1.7.5bean小结
(1)bean是如何创建的呢?
构造方法
(2)Spring的IOC实例化对象的三种方式分别是:
构造方法
(
常用
)
静态工厂
(
了解
)
实例工厂
(
了解
)
FactoryBean(
实用
)
这些方式中,重点掌握
构造方法
和
FactoryBean
即可。
需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在
使
用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下。
1.8bean的生命周期
关于
bean
的相关知识还有最后一个是
bean
的生命周期
,
对于生命周期,我们主要围绕着
bean
生命周
期控
制
来讲解
:
首先理解下什么是生命周期?
从创建到消亡的完整过程
,
例如人从出生到死亡的整个过程就是一个生命周期。
bean生命周期是什么?
bean
对象从创建到销毁的整体过程。
构造方法bean生命周期控制是什么?
在
bean
创建后到销毁前做一些事情。
步骤1:添加初始化和销毁方法
针对这两个阶段,我们在
BooDaoImpl
类中分别添加两个方法,
方法名任意
package com.itheima.dao.impl;
import com.itheima.dao.BookDao;
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}
}
步骤
2:
配置生命周期
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
步骤
3:
运行程序
运行
AppForLifeCycle
打印结果为:
从结果中可以看出,
init
方法执行了,但是
destroy
方法却未执行,这是为什么呢
?
Spring
的
IOC
容器是运行在
JVM
中
运行
main
方法后
,JVM
启动
,Spring
加载配置文件生成
IOC
容器
,
从容器获取
bean
对象,然后调方
法执行
main
方法执行完后,
JVM
退出,这个时候
IOC
容器中的
bean
还没有来得及销毁就已经结束了
所以没有调用对应的
destroy
方法
1.8.1close关闭容器
ApplicationContext
中没有
close
方法
需要将
ApplicationContext
更换成
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
调用ctx的close()方法
ctx.close();
运行程序,就能执行
destroy
方法的内容
1.8.2注册钩子关闭容器
在容器未关闭之前,提前设置好回调函数,让
JVM
在退出之前回调此函数来关闭容器
调用
ctx
的
registerShutdownHook()
方法
ctx.registerShutdownHook();
注意
:
registerShutdownHook
在
ApplicationContext
中也没有,在其子类
运行后,查询打印结果
两种方式介绍完后,close和registerShutdownHook选哪个?
相同点
:
这两种都能用来关闭容器
不同点
:close()
是在调用的时候关闭,
registerShutdownHook()
是在
JVM
退出前调用关闭。
分析上面的实现过程,会发现添加初始化和销毁方法,即需要编码也需要配置,实现起来步骤比较
多同时
也比较乱。
简化操作bean的生命周期操作:
Spring
提供了两个接口来完成生命周期的控制,好处是可以不用再进行配置
init
-
method
和
destroy
-
method
接下来在
BookServiceImpl
完成这两个接口的使用
:
修改
BookServiceImpl
类,添加两个接口
InitializingBean
,
DisposableBean
并实现接口中的
两个方法
afterPropertiesSet
和
destroy
package com.itheima.service.impl;
import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
System.out.println("set .....");
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
public void destroy() throws Exception {
System.out.println("service destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}
注意(关于afterPropertiesSet与setBookDao的执行顺序):
对于
InitializingBean
接口中的
afterPropertiesSet
方法,翻译过来为
属性设置之后
。
对于
BookServiceImpl
来说,
bookDao
是它的一个属性
setBookDao
方法是
Spring
的
IOC
容器为其注入属性的方法
思考:afterPropertiesSet和setBookDao谁先执行?
setBookDao
方法先执行,初始化方法会在类中属性设置之后执行,因为bookDao是一个属性,而
afterPropertiesSet方法是在属性设置之后执行
1.8.3bean生命周期小结
(1)关于Spring中对bean生命周期控制提供了两种方式:
在配置文件中的
bean
标签中添加
init-method
和
destroy-method
属性
类实现
InitializingBean
与
DisposableBean
接口,这种方式了解下即可。
(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
初始化容器
1.
创建对象
(
内存分配
)
2.
执行构造方法
3.
执行属性注入
(set
操作
)
4.
执行
bean
初始化方法
使用
bean
1.
执行业务操作
关闭
/
销毁容器
1.
执行
bean
销毁方法
(3)关闭容器的两种方式:
ConfigurableApplicationContext
是
ApplicationContext
的子类
close()
方法
registerShutdownHook()方法
1.9setter注入和构造器注入
1.9.1setter注入引用类型
1.在实体类
中定义引用类型属性,并提供可访问的
set
方法
2.
配置中使用
property
标签
ref
属性注入引用类型对象
例如:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<!--注入引用类型-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
1.9.2setter注入简单数据类型
1.在实体类
中定义引用类型属性,并提供可访问的
set
方
2.
配置中使用
property
标签
value
属性注入简单数据类型的值
例如:
<!--注入简单类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
</bean>
说明:value:后面跟的是简单数据类型,对于参数类型,Spring在注入的时候会自动转换,但无法
将字母转换为数字
1.9.3构造器注入引用数据类型
1.删除
setter方法并且提供
构造方法
2.配置文件中进行配置
构造方式注入
例如:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>
说明
:
在标签中
name
属性对应的值为构造函数中方法形参的参数名,必须要保持一致。
ref
属性指向的是
spring
的
IOC
容器中其他
bean
对象。
此外,构造器注入多个引用数据类型,只需在构造器中添加形参,而在配置文件中添加
<constructor-arg name=? ref=?/>标签,该标签在多个的情况下,无先后顺序。
1.9.4构造器注入多个简单数据类型(解耦的两种方式)
1.添加多个
简单属性并提供
构造方法
2.配置完成
多个属性构造器注入
例如:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
根据构造方法参数名称注入
<constructor-arg name="connectionNum" value="10"/>
<constructor-arg name="databaseName" value="mysql"/>
</bean>
由于根据属性name来进行数据注入存在较高耦合,我们使用以下两种方式来降低耦合
方式一(将name属性替换为type属性,添加相应的类型):
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
根据构造方法参数类型注入
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>
方式二(将name属性替换为index属性,添加对应的参数位置(0开始)):
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--根据构造方法参数位置注入-->
<constructor-arg index="0" value="mysql"/>
<constructor-arg index="1" value="100"/>
</bean>
1.9.5参数注入方式的选择
1.
强制依赖使用构造器进行,使用
setter
注入有概率不进行注入导致
null
对象出现
强制依赖指对象在创建的过程中必须要注入指定的参数
2.
可选依赖使用
setter
注入进行,灵活性强
可选依赖指对象在创建过程中注入的参数可有可无
3. Spring
框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相
对严谨
4.
如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用
setter
注入完成可选
依赖的注入
5.
实际开发过程中还要根据实际情况分析,如果受控对象没有提供
setter
方法就必须使用构造器注
入
6.
自己开发的模块推荐使用
setter
注入
1.10自动配置
1.10.1依赖自动装配
IoC
容器根据
bean
所依赖的资源在容器中自动查找并注入到
bean
中的过程称为自动装配
1.10.2自动装配的方式(按类型byType和按名称byName)
按类型(常用)
按名称
按构造方法
不启用自动装配
在配置文件中,将<property>标签删除,在<bean>标签中添加autowire属性
例如:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>
</beans>
注意事项
:
需要注入属性的类中对应属性的
setter
方法不能省略
被注入的对象必须要被
Spring
的
IOC
容器管理
按照类型在
Spring
的
IOC
容器中如果找到多个对象,会报
NoUniqueBeanDefinitionException
一个类型在
IOC
中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>
</beans>
注意事项
:
按照名称注入中的名称指的是什么?
0bookDao
是
private
修饰的,外部类无法直接方法,
外
部类只能通过属性的
set
方法进行访问
对外部类来说,
setBookDao
方法名,去掉
set
后首字母小写是其属性名
为什么是去掉set首字母小写?
这个规则是
set
方法生成的默认规则,
set
方法的生成是把属性名首字母大写前面加
set
形成
的方法名,
所以按照名称注入,其实是和对应的
set
方法有关,但是如果按照标准起名称,属性名
和
set
对
应的名是一致的,
如果按照名称去找对应的
bean
对象,找不到则注入
Null,
当某一个类型
在
IOC
容器中有多个对象,按照名称注入只找其指定名称对应的
bean
对象,不会报错
两种方式介绍完后,以后用的更多的是按照类型注入。
最后对于依赖注入,需要注意一些其他的配置特征
:
1.
自动装配用于引用类型依赖注入,不能对简单类型进行操作
2.
使用按类型装配时(
byType
)必须保障容器中相同类型的
bean
唯一,推荐使用
3.
使用按名称装配时(
byName
)必须保障容器中具有指定名称的
bean
,因变量名与配置耦
合,不推
荐使用
4.
自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
1.10.3集合注入
常见的集合类型有哪些
?
数组
List
Set
Map
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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--数组注入-->
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<!--list集合注入-->
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>
<!--set集合注入-->
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>
<!--map集合注入-->
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>
<!--Properties注入-->
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>
</bean>
</beans>