一. Spring 简介
1.1 Spring 简介
- Spring 是一个为简化企业级开发而生的开源框架
- Spring 是一个 IOC(DI)和AOP容器框架
- IOC全称:Inversion of Control【控制反转】
- 将对象【万物皆对象】控制权交给Spring
- DI全称:(Dependency Injection):依赖注入
- AOP 全称:Aspect-Oriented Programming,面向切面编程
1.2 搭建 Spring 框架步骤
- 导入jar包
- 编写核心配置文件
- 配置名称:applicationContext.xml【beans.xml 或 spring.xml】
- 使用核心类库
1.3 Spring 特性
- 非侵入式:基于 Spring 开发的应用中的对象可以不依赖于 Spring 的 API
- 容器:Spring 是一个容器,因为它包含并且管理应用对象的生命周期
- 组件化:Spring 实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和java注解组合这些对象。
- 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring自身也提供了表述层的SpringMVC和持久层的JDBCTemplate)
1.4 Spring 中 getBean() 三种方式
- getBean(String beanId):通过beanId获取对象
- 不足:需要强制类型转换,不灵活
- getBean(Class clazz):通过 Class 方式获取对象
- 不足:容器中有多个相同类型 bean 的时候,会报如下错误:
- expected single matching bean but found 2: stuZhengzhong,stuZhouxu
- 不足:容器中有多个相同类型 bean 的时候,会报如下错误:
- getBean(String beanId,Clazz clazz):通过 beanId 和 Class 获取对象
- 推荐使用(三种方式如下图)
通过 Spring 创建对象,都是调用的无参构造器
注意:框架默认都是通过无参构造器,帮助我们创建对象。
所以:如提供对象的构造器时,一定添加无参构造器
1.5 bean 标签详解
- 属性:
- id:bean 的唯一标识
- class:定义bean的类型【class 全类名】
- 子标签
- property:为对象中属性赋值【set 注入】
- name 属性:设置属性名称
- value属性:设置属性数值
- property:为对象中属性赋值【set 注入】
二.SpringIOC 底层实现
IOC:将对象的控制权反转给 Spring
2.1 BeanFactory 与 ApplicationContext
- BeanFactory:IOC 容器的基本实现,是Spring 内部的使用接口,是面向Spring本身的,不是提供给开发人员使用的。
- ApplicationContext:BeanFactory 的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。
2.2 图解 IOC 类的结构
结构:
- BeanFactory:Spring 底层 IOC 实现【面向 Spring 框架】
- ...
- ApplicationContext:面向程序员
- ConfigurableApplicationContext:提供关闭或刷新容器对象方法
- ...
- ClassPathXmlApplicationContext:基于类路径检索 xml 文件
- AnnotationConfigApplicationContext:基于注解创建容器对象
- FileSystemXmlApplicationContext:基于文件系统检索 xml 文件
- ...
- ConfigurableApplicationContext:提供关闭或刷新容器对象方法
- ApplicationContext:面向程序员
- ...
三.Spring 依赖注入数值问题【重点】
3.1 字面量数值
- 数据类型:基本数据类型及包装类,String
- 语法:value属性或value标签
3.2 CDATA区
- 语法:<![CDATA[]]>
- 作用:在 xml 中定义特殊字符时,使用 CDATA 区
如:
在 Mybatis 的 sql 语句中有特殊符号,也可以用
3.3 外部已声明bean及级联属性赋值
- 语法:ref
- 注意:级联属性更改数值会影响外部声明 bean【ref 赋值的是引用】
- 示例代码:
3.4 内部bean
- 概述
- 内部类:在一个类中完整定义另一个类,当前类称之为内部类
- 内部bean:在一个bean中完整定义另一个bean,当前bean称之为内部bean
- 注意:内部bean不会直接装配到IOC容器中
- 示例代码:
3.5 集合
- List
- Map
提取map
第四章 Spring 依赖注入方式【基于xml】
为属性赋值方式
- 通过 xxxset()方法
- 通过构造器
- 反射
4.1 set注入
- 语法:<property>
4.2 构造器注入
- 语法:<constructor-arg>
4.3 p名称空间注入
导入名称空间:xmlns:p="http://www.springframework.org/schema/p"
- 语法:<bean p:xxx>
第五章 Spring 管理第三方bean
5.1 Spring 管理 druid 步骤
- 导入 jar 包
- 编写db.properties配置文件
- 编写applicationContext.xml相关代码
第六章 Spring 中 FactoryBean
6.1 Spring中两种bean
- 一种是普通bean
- 另一种是工厂bean【FactoryBean】
- 作用:如果需要我们程序员参与到bean的创建时,使用FactoryBean
6.2 FactoryBean 使用步骤
- 实现FactoryBean接口
- 重写方法【三个】① getObject ② getObjectType ③ isSingleton
- 装配工厂bean
- 测试
package com.yzj.backend.factory;
public class MyFactoryBean implements FactoryBean<Dept> {
/**
* getObject():参与对象创建的方法
* @return
* @throws Exception
*/
@Override
public Dept getObject() throws Exception {
Dept dept = new Dept(101,"研发部门");
return dept;
}
/**
* 设置参与对象class
* @return
*/
@Override
public Class<?> getObjectType() {
return Dept.class;
}
/**
* 设置当前对象是否为单例
* @return
*/
@Override
public boolean isSingleton() {
return true;
}
}
第七章 Spring 中 bean 的作用域
7.1 语法
- 在bean标签中添加属性:scope属性即可
7.2 四个作用域
- singleton【默认值】:单例【在容器中只有一个对象】
- 对象创建时机:创建容器对象时,spring创建我们的对象【无参构造器】
- prototype:多例【在容器中有多个对象】
- 对象创建时机:getBean()方法被调用时,创建对象执行
- request:请求域
- 当前请求有效,离开请求域失效
- 当前请求:URL不变即为当前请求
- session:会话域
- 当前会话有效,离开当前会话失效
- 当前会话:当前浏览器不关闭不更换即为当前会话
第八章 Spring中bean的生命周期
8.1 bean的生命周期
① 通过构造器或工厂方法创建bean实例
② 为bean的属性设置值和对其他bean的引用
postProcessBeforeInitialization(Object,String):在bean的初始化之前执行
③ 调用bean的初始化方法
postProcessAfterInitialization(Object,String):在bean的初始化之后执行
④ bean可以使用了
⑤ 当容器关闭时,调用bean的销毁方法
8.2 bean的后置处理器
- 作用:在调用初始化方法前后对bean进行额外的处理。
- 实现:
- 实现BeanPostProcessor接口
- 重写方法
- postProcessBeforeInitialization(Object,String):在bean的初始化之前执行
- postProcessAfterInitialization(Object,String):在bean的初始化之后执行
- 在容器中装配后置处理器
- 注意:装配后置处理器会为 当前容器中每个bean 均装配,不能为局部bean装配后置处理器
第九章 Spring 中自动装配【基于XML】
9.1 Spring 中提供两种装配方式
- 手动装配
- 自动装配
9.2 Spring 自动装配语法及规则
- 在bean标签中添加属性:Autowire即可
- byName:对象中属性名称与容器中的beanId进行匹配,如果属性名与beanId数值一致,则自动装配成功
- byType:对象中属性类型与容器中class进行匹配,如果唯一匹配则自动装配成功
- 匹配 0 个:未装配
- 匹配多个,会报如下错
- expected single matching bean but found 2: deptDao,deptDao2
- 注意:基于XML方式的自动装配,只能装配非字面量数值
9.3 总结
- 基于XML自动装配,底层使用set注入
- 最终:不建议使用 byName,byType,建议使用注解方式自动装配
第十章 Spring 中注解【非常重要】
10.1 使用注解将对象装配到 IOC 容器中
约定:约束 > 配置【注解 > XML】> 代码
位置:在类上面标识
注意:
- Spring 本身不区分四个注解【四个注解本质是一样的@Component】,提供四个注解的目的只有一个:提高代码的可读性
- 使用注解装配对象,默认将类名首字母小写作为beanId
- 可以使用value属性,设置beanId;当注解中只使用一个value属性时,value关键字可省略
- 装配对象四个注解
- @Component:装配普通组件到IOC容器
- @Repository:装配持久化层组件到IOC容器
- @Service:装配业务逻辑层组件到IOC容器
- @Controller:装配控制层 | 表示层组件到IOC容器
- 使用注解步骤
- 导入相关jar包【已导入】
- 开启组件扫描
- 使用注解标识组件
10.2 使用注解装配对象中属性【自动装配】
- @Autowired注解
- 作用:自动装配对象中属性
- 装配原理:反射机制
- 装配方式
- 先按照byType进行匹配
- 匹配 1 个:匹配成功,正常使用
- 匹配 0 个:
- 默认【@Autowired(required=true)】报错
- @Autowired(required=false),不会报错
- 匹配多个
- 再按照byName进行唯一筛选
- 筛选成功【对象中属性名称 == beanId】,正常使用
- 筛选失败【对象中属性名称 != beanId】,报如下错误:
//expected single matching bean but found 2: deptDao,deptDao2
- 再按照byName进行唯一筛选
- 先按照byType进行匹配
- @Autowired中required属性
- true:表示被标识的属性必须装配数值,如未装配,会报错。
- false:表示被标识的属性不必须装配数值,如未装配,不会报错。
- @Qualifier注解
- 作用:配合@Autowired一起使用,将设置beanId名称装配到对象属性中
- 注意:不能单独使用,需要与@Autowired一起使用
- @Value
- 作用:装配对象中属性【字面量数值】
第十一章 Spring 中组件扫描
11.1 默认使用情况
11.2 包含扫描
- 注意:
- 使用包含扫描之前,必须设置 use-default-filters="false"【关闭当前包及其子包的扫描】
- type
- annotation:设置被扫描注解的全类名
- assignable:设置被扫描实现类的全类名
11.3 排除扫描
第十三章 Spring 完全注解开发【0配置】
13.1 完全注解开发步骤
1.创建配置类
2.在class上面添加注解
- @Configuration:标识当前类是一个配置类,作用:代替XML配置文件
- @ComponentScan:设置组件扫描当前包及其子包
3.使用AnnotationConfigApplicationContext容器对象
13.2 实例代码
第十四章 Spring 集成 Junit4
14.1 集成步骤
1.导入jar包
- spring-test-5.3.1.jar
2.指定Spring的配置文件的路径
- 【@ContextConfiguration】
3.指定Spring环境下运行Junit4的运行器
- @RunWith
第十五章 AOP前奏
15.1 代理模式
- 代理模式:我们需要做一件事情,又不期望自己亲力亲为,此时,可以找一个代理【中介】
- 我们【目标对象】与中介【代理对象】不能相互转换,因为是“兄弟”关系
15.2 为什么需要代理【程序中】
- 需求:实现【加减乘除】计算器类
- 在加减乘除方法中,添加日志功能【在计算之前,记录日志。在计算之后,显示结果。】
- 实现后发现问题如下
- 日志代码比较分散,可以提取日志类
- 日志代码比较混乱,日志代码【非核心业务代码】与加减乘除方法【核心业务代码】书写一处
- 总结:在核心业务代码中,需要添加日志功能,但不期望在核心业务代码中书写日志代码
- 此时:使用代码模块解决问题【先将日志代码横向提取到日志类中,再动态织入回到业务代码中】
15.3 手动实现动态代理环境搭建
- 实现方式
- 基于接口实现动态代理:JDK动态代理
- 基于继承实现动态代理:Cglib,Javassist动态代理
- 实现动态代理关键步骤
- 一个类:Proxy
- 概述:Proxy代理类的基类【类似Object】
- 作用:newProxyInstance():创建代理对象
- 一个接口:InvocationHandler
- 概述:实现【动态织入效果】关键接口
- 作用:invoke(),执行invoke()实现动态织入效果
- 一个类:Proxy
15.4 手动实现动态代理关键步骤
注意:代理对象与实现类【目标对象】是"兄弟"关系,不能相互转换
- 创建类【为了实现创建代理对象工具类】
- 提供属性【目标对象:实现类】
- 提供方法【创建代理对象】
- 提供有参构造器【避免目标对为空】
工具类
package com.yzj.backend.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyProxy {
/**
* 目标对象【目标客户】
*/
private Object target;
public MyProxy(Object target){
this.target = target;
}
/**
* 获取目标对象的,代理对象
* @return
*/
public Object getProxyObject(){
Object proxyObj = null;
/**
* 类加载器【ClassLoader loader】,目标对象类加载器
* 目标对象实现接口:Class<?>[] interfaces,目标对象实现所有接口
* InvocationHandler h
*/
ClassLoader classLoader = target.getClass().getClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
//创建代理对象
proxyObj = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
//执行invoke()实现动态织入效果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取方法名【目标对象】
String methodName = method.getName();
//执行目标方法之前,添加日志
MyLogging.beforeMethod(methodName, args);
//触发目标对象目标方法
Object rs = method.invoke(target, args);
//执行目标方法之后,添加日志
MyLogging.afterMethod(methodName, args);
return rs;
}
});
return proxyObj;
}
}
测试
第十六章 Spring中AOP
16.1 AspectJ框架【AOP框架】
- AspectJ是java社区里最完整最流行的AOP框架。
- 在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。
16.2 使用AspectJ步骤
1.添加jar包支持
2.配置文件
- 开启组件扫描
- 开启AspectJ注解支持
3.将MyLogging类上面添加注解
- @Component:将当前类标识为一个组件
- @Aspect:将当前类标识为切面类【非核心业务提取类】
4.将MyLogging中的方法中添加通知注解
- @Before
5.测试
16.3 Spring 中 AOP 概述
- AOP:Aspect-Oriented Programming,面向切面编程【面向对象的一种补充】
- 优势:
- 解决代码分散问题
- 解决代码混乱问题
- 优势:
- OOP:Object-Oriented Programming,面向对象编程
16.4 Spring中AOP相关术语
- 横切关注点:非核心业务代码【日志】,称之为横切关注点
- 切面(Aspect):将横切关注点提取到类中,这个类称之为切面类
- 通知(Advice):将横切关注点提取到类中之后,横切关注点改名为:通知
- 目标(Target):目标对象,指的是需要被代理的对象【实现类】
- 代理(Proxy):代理对象可以理解为:中介
- 连接点(Joinpoint):通知方法需要指定通知位置,这个位置称之为:连接点【通知之前】
- 切入点(pointcut):通知方法需要指定通知位置,这个位置称之为:切入点【通知之后】
第十七章 AspectJ详解
17.1 AspectJ中切入点表达式
- 语法:@Before(value="execution(权限修饰符 返回值类型 包名.类名.方法名(参数类型))")
- 通配符
- 【*】:
- 【*】:可以代表任意权限修饰符&返回值类型
- 【*】:可以代表任意包名,任意类名,任意方法名
- 【..】:
- 【..】:代表任意参数类型及参数个数
- 【*】:
- 重用切入点表达式
1.使用@PointCut注解,提取可重用的切入点表达式
2.使用方法名()引入切入点表达式
17.2 AspectJ中JoinPoint对象
- JoinPont【切入点对象】
- 作用:
- 获取方法名称
//获取方法签名【方法签名=方法名+参数列表】 joinPoint.getSignature(); //获取方法名称 String methodName = joinPoint.getSignature().getName();
- 获取参数
Object[] args = joinPoint.getArgs();
- 获取方法名称
17.3 AspectJ中通知
- 前置通知
- 语法:@Before
- 执行时机:指定方法执行之前执行【如目标方法中有异常,会执行】
- 指定方法:切入点表达式设置位置
- 示例代码
-
后置通知
-
语法:@After
-
执行时机:指定方法所有通知执行之后执行【如目标方法中有异常,会执行】
-
示例代码
-
-
返回通知
-
语法:@AfterReturnning
-
执行时机:指定方法返回结果时执行,【如目标方法中有异常,不执行】
-
注意事项:@AfterReturnning中returnning属性与入参中参数名一致
-
示例代码
-
-
异常通知
-
语法:@AfterThrowing
-
执行时机:指定方法出现异常时执行,【如目标方法中无异常,不执行】
-
注意事项:@AfterThrowing中的throwing属性值与入参参数名一致
-
示例代码:
-
总结
-
有异常:前置通知=》异常通知=》后置通知
-
无异常:前置通知=》返回通知=》后置通知
-
-
-
环绕通知【前四个通知整合】
-
语法:@Around
-
作用:整合前四个通知
-
注意:
-
参数中必须使用ProceedingJoinPoint
-
环绕通知必须将返回结果,作为返回值
-
-
示例代码:
-
17.4 定义切面优先级
- 语法:@Order(value=index)
- index是int类型,默认值是int可存储的最大值
- 数值越小,优先级越高【一般建议使用正整数】
- 示例代码
17.5 基于XML方式配置AOP(了解)
第十八章 Spring 中 JdbcTemplate
18.1 JdbcTemplate简介
- Spring提供的JdbcTemplate是一个小型的持久化层框架,简化Jdbc代码
- MyBatis是一个半自动化的ORM持久化层框架
18.2 JdbcTemplate基本使用
- 导入jar包
- 编写配置文件
- db.properties:设置连接数据库属性
- applicationContext.xml【spring配置文件】
- 加载外部属性文件
- 装配数据源【DataSources】
- 装配JdbcTemplate
- 示例代码:
- 使用核心类库【JdbcTemplate】
18.3 JdbcTemplate的常用API
- jdbcTemplate.update(String sql,Object... args):通用的增删改方法
- jdbcTemplate.batchUpdate(String sql,List<Object[]> args):通用的批处理增删改方法
- jdbcTemplate.queryForObject(String sql,Class clazz,Object... args):查询单个数值
- String sql = "select count(1) from tbl_xxx";
- jdBCTemplate.queryForObject(String sql,RowMapper<T> rm,Object... args):查询单个对象
- String sql = "select col1,col2... from tbl_xxx";
- jdbcTemplate.query(String sql,RowMapper<T> rm,Object... args):查询多个对象
18.4 使用JdbcTemplate搭建Service&Dao层
- Service层依赖Dao层
- Dao层依赖JdbcTemplate
- 示例代码
第十九章 Spring声明式事务管理
回顾事务
1.事务四大特征【ACID】
- 原子性
- 一致性
- 隔离性
- 持久性
2.事务三种行为
- 开启事务:connection.setAutoCommit(false)
- 提交事务:connection.commit()
- 回滚事务:connection.rollback()
19.1 Spring中支持事务管理
- 编程式事务管理【传统事务管理】
- 获取数据库连接Connection对象
- 取消事务的自动提交【开启事务】
- 执行操作
- 正常完成操作时手动提交事务
- 执行失败时回滚事务
- 关闭相关资源
- 不足:
- 事务管理代码【非核心业务】与核心业务代码相耦合
- 事务管理代码分散
- 事务管理代码混乱
- 事务管理代码【非核心业务】与核心业务代码相耦合
- 声明式事务管理【使用AOP管理事务】
- 先横向提取【事务管理代码】,再动态织入
19.2 使用声明式事务管理
不用事务管理代码,发现:同一个业务中,会出现局部成功及局部失败的现象【不正常】
- 添加支持【AspectJ的jar包】
- 编写配置文件
- 配置事务管理器
- 开启事务注解支持
- 在需要事务管理的业务方法上,添加注解@Transactional
- 总结:
- 添加声明式事务管理之后,获取的是代理对象,代理对象不能转化为目标对象【实现类】
19.3 Spring声明式事务管理属性
@Transactional注解属性
- 事务传播行为【Propagation】
- 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播
- 如:执行事务方法method()1【事务x】之后,调用事务方法method2()【事务y】,此时需要设置method()2方法的事务传播行为
- Spring的7中传播行为
- 图解事务传播行为
- REQUIRED
- REQUIRED_NEW
- 使用场景
- 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播
- 事务隔离级别【Isolation】
- 隔离级别概述:一个事务与其他事务之间的隔离等级【1,2,4,8】
- 隔离等级:
- 读未提交【1】:READ UNCOMMITTED
- 存在问题:脏读【读取到了未提交数据】
- 读已提交【2】:READ COMMITTED
- 存在问题:可能出现不可重复读
- 可重复读【4】:REPEATABLE READ
- 存在问题:可能出现幻读
- 串行化【8】:SERIALIZABLE
- 读未提交【1】:READ UNCOMMITTED
- 事务超时【timeout】
- 设置事务超时时间,到达指定时间后会强制事务回滚
- 类型:int,单位:秒
- 默认值:-1【未设置强制回滚时间】
- 事务只读【readonly】
- 一般事务方法中只有查询操作时,才将事务设置为只读
- 默认值:false
- 事务回滚【不回滚】
- rollbackFor:设置回滚的异常Class
- noRollbackFor:设置不回滚异常Class
- 各种数据库产品对事务隔离级别的支持程度