Spring Framework 系统架构
第一部分:Core Container:核心容器
核心概念(Ioc/DI)
代码书写现状
耦合度偏高
解决方案
使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象
IoC(Inversion of Control)控制反转
对象的撞见控制权由程序转移到外部,这种思想称为控制反转
概念:使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
Spring技术对IoC思想进行了实现
Spring提供了一个容器,称为IoC容器,用来充当IoC思想中的”外部“
IoC容器负责对象的创建、初始化等一系列工资,被创建或被管理的对象在IoC容器中统称为Bean
DI(Dependency Injection)依赖注入
在容器中建立bean与bean之间的依赖关系的整个过程叫做依赖注入
这么做的目的:就是为了充分解耦
如何实现的:1、使用IoC容器管理bean(IoC)
2、在IoC容器内将所有依赖关系的bean进行关系绑定(DI)
最终效果
使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系
IoC-------------------------------------------------------------------------------
IoC分析:
1、管理什么?(Service与Dao)
2、如何将被管理的对象告知IoC容器?(配置)
3、被管理的对象交给IoC容器,如何获取到IoC容器?(接口)
4、IoC容器得到后,如何从容器中获取bean?(接口方法)
5、使用Spring导入哪些坐标?(pom.xml)
IoC步骤:
1、导入Spring坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
2、定义Spring管理的类(接口)
public interface BookService {
void save();
}
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
public void save(){
System.out.println("book service save ...");
bookDao.save();
}
}
3、创建Spring配置文件,配置对应类作为Spring管理的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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!-- 2.配置bean-->
<!-- bean标签表示配置bean
id属性表示给bean起名字
class属性表示给bean定义类型
注意:bean定义时id属性在同一个上下文中不能重复
-->
<bean id="bookDao" class="com.spring.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.spring.service.impl.BookServiceImpl"/>
</beans>
4、初始化IoC容器(Spring核心容器/Spring容器),通过容器获取bean
public class App {
public static void main(String[] args) {
//3.获取IoC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//4.获取bean
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.save();
}
}
DI---------------------------------------
DI分析:
1.基于IoC管理bean
2.Service中使用new的形式创建的Dao对象是否保留?(否)
3.Service中需要的Dao对象如何进入到Service中?(提供方法)
4.Service与Dao之间的关系如何描述?(配置)
DI步骤:
1、删除使用new的形式创建对象的代码
public class BookServiceImpl implements BookService {
//5.删除业务层中使用new的方式创建的dao对象
private BookDao bookDao = new BookDaoImpl; ========>>>>>> private BookDao bookDao;
public void save(){
System.out.println("book service save ...");
bookDao.save();
}
}
2、提供依赖对象对应的setter方法
public class BookServiceImpl implements BookService {
//5.删除业务层中使用new的方式创建的dao对象
private BookDao bookDao ;
public void save(){
System.out.println("book service save ...");
bookDao.save();
}
//6.提供一个对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
3、配置service与dao之间的关系
<?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.spring.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.spring.service.impl.BookServiceImpl">
<!--7.配置server与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>
</beans>
bean配置
为什么bean默认为单例???
适合交给容器进行管理的bean
* 表现层对象
* 业务层对象
* 数据层对象
* 工具对象
不适合给容器进行管理的bean
* 封装实体的域对象
bean实例化
bean本质上就是对象,创建bean使用构造方法完成
实例化bean的三种方式----------构造方法(常用)
*提供可访问的构造方法
*配置
*无参构造方法如果不存在,将抛出异常BeanCreationException
实例化bean的三种方式---------- 静态工厂(了解)
*静态工厂
*配置
实例化bean的三种方式---------- 实例工厂(了解)
*实例工厂
*配置
实例化bean的三种方式---------- 改良第三种--FactoryBean(实用)
*FactorryBean
*配置
bean生命周期
*生命周期:从创建到消亡的完整过程
*bean生命周期:bean从创建到销毁的整体过程
*bean生命周期控制:在bean创建后到销毁前做一些事情
bean生命周期控制
*提供生命周期控制方法
*配置生命周期控制方法
bean生命周期控制---------接口控制(了解)
*实现InitializingBean,DisposableBean接口
bean生命周期经过哪些阶段
*初始化容器
1.创建对象(内存分配)
2.执行构造方法
3.执行属性注入(set操作)
4.执行bean初始化方法
*使用bean
1.执行业务操作
*关闭、销毁容器
1.执行bean的销毁方法
bean销毁时机
*容器关闭前触发bean的销毁
*关闭容器的方式
*手动关闭容器
ConfigurableApplicationContext接口close()操作
*注册关闭钩子,在虚拟机退出前先关闭容器再退出虚拟机
ConfigurableApplicationContext接口registerShutdownHook()操作
依赖注入方式
思考:向一个类中传递数据的方式有几种?
*普通方法(set方法)
*构造方法
思考:依赖注入描述了在容器中建立bean与bean之间的依赖关系的过程,如果bean运行需要的是数字或字符串呢?
*引用类型
*简单类型(基本数据类型与String)
依赖注入方式
*setter注入
*简单引用类型
*引用类型
*构造器注入
*简单类型
*引用类型
setter注入--------引用类型
*在bean中定义引用类型属性并提供可访问的set方法
*配置中使用property标签ref属性注入引用类型对象
setter注入--------简单类型
*在bean中定义引用类型属性并提供可访问的set方法
*配置中使用property标签value属性注入简单类型数据
构造器注入--------引用类型(了解)
*在bean中定义引用类型属性并提供可访问的构造方法
*配置中使用constructor-arg标签ref属性注入简单类型数据
构造器注入--------简单类型(了解)
*在bean中定义引用类型属性并提供可访问的set方法
*配置中使用constructor-arg标签value属性注入简单类型数据
构造器注入--------参数适配(了解)
*配置中使用constructor-arg标签type属性设置按形参类型注入
*配置中使用constructor-arg标签index属性设置按形参位置注入
依赖注入方式选择
1.强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
2.可选依赖使用setter注入进行,灵活性强
3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
4.如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
5.实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
6.自己开发的模块推荐使用setter注入
依赖自动装配
*IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
*自动装配的方式
*按类型(常用)
*按名称
*按构造方法
*不启用自动装配
配置中使用bean标签autowire属性设置自动装配的类型
依赖自动装配特征
*自动装配用于引用类型依赖注入,不能对简单类型进行操作
*使用按类型装配时( byType ) 必须保障容器中相同类型的bean唯一,推荐使用
*使用按名称装配时( byName )必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
*自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
注入集合对象
*注入数组对象
*注入List对象(重点)
*注入Set对象
*注入Map对象
*注入Properties对象
Spring如何管理第三方的资源(bean)
案例:数据源对象管理
*导入druid坐标
*配置数据源对象作为spring管理的bean
Springn加载properties文件
*开启context命名空间
*使用context命名空间,加载指定properties文件
*使用${}读取加载的属性值
注意点:
*不加载系统属性
*加载多个properties文件
*加载所有properties文件
*加载properties文件标准格式
*从类路径或jar包中搜索并加载properties文件
容器
创建容器
*方式一:类路径加载配置文件
*方式二:文件路径加载配置文件
*加载多个配置文件
获取bean
*方式一:使用bean名称获取
*方式二:使用bean名称获取并指定类型
*方式三:使用bean类型获取
容器类层次结构图:
BeanFactory是IoC容器的顶层接口
BeanFactory初始化
*类路径加载配置文件
*BeanFactory创建完毕后,所有的bean均为延迟加载
*ApplicationContext想要被延迟加载的话,在bean后边加上lazy-init="true"即可
bean相关
依赖注入相关
注解开发
注解开发定义bean
*使用@Component定义bean
*核心配置文件中通过组件扫描加载bean
Spring提供@Component注解的三个衍生注解
*@Controller:用于表现层bean定义
*@Service:用于业务层bean定义
*@Repository:用于数据层bean定义
纯注解开发
*Spring3.0升级了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道
*@Configuration注解用于设定当前类为配置类
*@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
*读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
Bean的管理
bean作用范围
*使用@Scope定义bean作用范围
bean生命周期
*使用@PostConstruct、@PreDestroy定义bean生命周期
依赖注入
自动装配
*使用@Autowired注解开启自动装配模式(按类型)
注意:自动装配基于反射设计创建对象并暴力反射对应属性为私有属性初始化数据,因此无需提供setter方法
注意:自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法
*使用@Qualifier注解开启指定名称装配bean
*注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用
使用@Value实现简单类型注入
加载properties文件
*使用@PropertySource注解加载properties文件
*注意:路径仅支持单一文件配置,多文件请使用数组格式配置,不允许使用通配符*
第三方bean管理
*使用@Bean配置第三方bean
因为不放在SpingConfig类中,所以新建另一个类
*使用独立的配置类管理第三方bean
*将独立的配置类加入核心配置
*方式一:导入式(推荐)
*使用@Import注解手动加入配置类到核心配置,此注解只能添加一次,多个数据请用数组格式
*方式二:扫描式
*使用@ComponentScan注解扫描配置类所在的包,加载对应的配置类信息
第三方bean依赖注入
*简单类型依赖注入
*引用类型依赖注入
*引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象
注解开发总结
XML配置比注解配置
Spring整合MyBatis
整合数据层技术MyBatis
第三部分:AOP
核心概念
AOP 面向切面编程,一种编程范式,知道开发这如何组织程序结构
OOP面向对象编程
作用:在不惊动原始设计的基础上为其进行功能增强
Spring理念:无入侵式/无侵入式
连接点( JoinPoint ):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等
在springAOP中,理解为方法的执行
切入点 ( Pointcut ) ︰匹配连接点的式子
在SpringAOP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
一个具体方法: com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法
匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
通知( Advice ):在切入点处执行的操作,也就是共性功能
在SpringAOP中,功能最终以方法的形式呈现
通知类∶定义通知的类
切面( Aspect )︰描述通知与切入点的对应关系
AOP入门案例
1.导入相关坐标
2.定义dao接口与实现类
3.定义通知类,制作通知
4.定义切入点
说明:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑
5.绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行位置
6.定义通知类受Spring容器管理,并定义当前类为切面类
7.开启Spring对AOP注解驱动支持
AOP工作流程
SpringAOP本质:代理模式
1. Spring容器启动
2.读取所有切面配置中的切入点
3.初始化bean,判定bean对应的类中的方法是否匹配到任意切入点
匹配失败,创建对象
匹配成功,创建原始对象(目标对象)的代理对象
4.获取bean执行方法
获取bean,调用方法并执行,完成操作
获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作
AOP核心概念
目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现
AOP切入点表达式
切入点:要进行增强的方法
切入点表达式:要进行增强方法的描述方式
切入点表达式标准格式:动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
动作关键字︰描述切入点的行为动作,例如execution表示执行到指定切入点
访问修饰符: public , private等,可以省略
返回值
包名
类/接口名
方法名
参数
异常名︰方法定义中抛出指定异常,可以省略
可以使用通配符描述切入点,快速描述
*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现
匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法
.. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法
+ :专用于匹配子类类型
书写技巧
所有代码按照标准规范开发,否则以下技巧全部失效
描述切入点通常描述接口,而不描述实现类
访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)
返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
包名书写尽量不使用..匹配,效率过低,常用*做单个包描述匹配,或精准匹配~
接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名
方法名书写以动词进行精准匹配,名词采用*匹配,例如getByld书写成getBy*,selectAll书写成selectAll
参数规则较为复杂,根据业务方法灵活调整
通常不使用异常作为匹配规则
AOP通知类型
AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行的代码时要将其加入到合理的位置
AOP通知共分为五种
*前置通知
*后置通知
*环绕通知(重点)
*返回后通知(了解)
*抛出异常后通知(了解)
名称:@Before
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行
范例:
相关属性:value(默认):切入点方法名,格式为类名.方法名()
名称:@After
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行
范例:
相关属性:value(默认):切入点方法名,格式为类名.方法名()
名称:@Around
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行
范例:
Around注意事项
1 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
2.通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
3.对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型
4.原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
5.由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
名称:@AfterReturning(了解)
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行
范例:
相关属性:value(默认):切入点方法名,格式为类名.方法名()
名称:@AfterThrowing(了解)
类型:方法注解
位置:通知方法定义上方
作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行
范例:
相关属性:value(默认):切入点方法名,格式为类名.方法名()
环绕通知测试执行效率
获取接口名,接口方法示例:
AOP通知获取数据
获取参数
获取切入点方法的参数
JoinPoint:适用于前置、后置、返回后、抛出异常后通知
JoinPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数
ProceedJointPoint:适用于环绕通知
ProceedJoinPoint是JoinPoint的子类
获取返回值
获取切入点方法返回值
返回后通知
抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
环绕通知
环绕通知中可以手工书写对原始方法的调用,得到的结果即为原始方法的返回值
获取异常(了解)
获取切入点方法运行异常信息
抛出异常后通知
抛出异常后通知可以获取切入点方法中出现的异常信息,使用形参可以接收对应的异常对象
环绕通知
抛出异常后通知可以获取切入点方法运行的异常信息,使用形参可以接收运行时抛出的异常对象
使用环绕通知处理用户名或密码多余空格解决示例:
AOP总结:
概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
作用︰在不惊动原始设计的基础上为方法进行功能增强
核心概念
代理(Proxy ) : SpringAOP的核心本质是采用代理模式实现的
连接点( JoinPoint ) :在SpringAOP中,理解为任意方法的执行
切入点( Pointcut ) :匹配连接点的式子,也是具有共性功能的方法描述
通知(Advice ):若干个方法的共性功能,在切入点处执行,最终体现为一个方法
切面(Aspect )∶描述通知与切入点的对应关系
目标对象( Target ) ︰被代理的原始对象成为目标对象
切入点表达式标准格式∶动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名)
execution( * com.itheima. service.*Service.*( ..))
切入点表达式描述通配符︰
作用:用于快速描述,范围描述
*︰匹配任意符号(常用)
.. :匹配多个连续的任意符号(常用)
+︰匹配子类类型
切入点表达式书写技巧
1.按标准规范开发
2.查询操作的返回值建议使用*匹配
3.减少使用..的形式描述包
4.对接口进行描述,使用*表示模块名,例如UserService的匹配描述为*Service
5.方法名书写保留动词,例如get,使用*表示名词,例如getById匹配描述为getBy*
6.参数根据实际情况灵活调整
通知类型
前置通知
后置通知
环绕通知(重点)
环绕通知依赖形参ProceedingJoinPoint才能实现对原始方法的调用环绕通知可以隔离原始方法的调用执行
环绕通知返回值设置为object类型
环绕通知中可以对原始方法调用过程中出现的异常进行处理
返回后通知
抛出异常后通知
获取切入点方法的参数
JoinPoint:适用于前置、后置、返回后、抛出异常后通知,设置为方法的第一个形参
ProceedJointPoint:适用于环绕通知
获取切入点方法返回值
返回后通知
环绕通知
获取切入点方法运行异常信息
抛出异常后通知
环绕通知
第四部分:事务
Spring事务
事务作用:在数据层保障一系列的数据库操作同成功同失败
Spring事务作用:在数据层或业务层保障一系列的数据库操作同成功同失败
纯注解使用事务(银行转账事务示例)
1.在业务层接口上添加Spring事务管理
注意事项:
Spring注解式事务通常提娜佳在业务层接口中而不会添加到业务层实现中,是为了降低耦合度
注解式事务可以添加到业务方法上表示当前方法开启事务,也可以添加到接口上表示当前接口中所有方法开启事务
2.设置事管理器
注意事项:
事务管理器要根据实现技术进行选择
MyBatis框架使用的是JDBC事务
3.开启注解式事务驱动
Spring事务角色
事务角色
事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以式业务层方法
事务相关配置
事务的传播行为
事务传播行为:事务协调员对事务管理员所携带事务的处理态度
案例(转账业务追加日志)
1.在业务层接口上添加Spring事务,设置事务传播行为REQUIRES_NEW(需要新事务)
上课笔记:
使用SqlSessionTemplate的特点:
1. 一定是关闭了的:否则无法多次使用
2. 关闭、提交、回滚这些操作都是全自动的
3. 整个Spring工厂只会有一个UserService对象,这个对象也只有一个SqlSessionTemplate/SqlSession
4. 原理:内部维护了一个factory,然后用户调用方法的时候会新开一个内部的sqlSession提供使用
配置AOP+TX事务
<tx:advice> 取代<aop:aspect>切面,内置了事务相关的切面方法
<tx:method> 对哪些方法应用切面:事务支持
name 表示方法名,可以使用通配符
propagation 事务的传播属性
REQUIRED 默认值,必须开启事务
SUPPORTS 支持事务,如果有就继续,没有也不需要开启
REQUIRES_NEW 即使上层有事务,这里也开启新事务
NEVER 无需事务,即使有也不需要
isolation 隔离级别
DEFAULT 表示默认(oracle-读已提交,mysql-可重复读)
READ_UNCOMMITTED 读未提交
READ_COMMITTED 都已提交,oracle默认
REPEATABLE_READ 可重复读,mysql默认
SERIALIZABLE 序列化
read-only 事务是否只读
false 默认值,事务不是只读,可以添加事务
true 只读[提高性能],不能任意添加修改事务特性
rollback-for 对于什么【异常】回滚,默认是任何异常都回滚
no-rollback-for 对于什么【异常】不回滚,优先级高于:rollback-for
第五部分:家族
SpringMVC
SpringMVC概述:
SpringMVC技术与Servlet技术功能相同,均属于web层开发技术
SpringBoot
SpringCloud
AOP:面向切面编程
Aspects:AOP思想实现
Data Access:数据访问
Web:Web开发
Test:单元测试与集成测试