第16节:SpringAOP机制详解
16.1 AOP 概述
16.1.1 什么是 AOP
AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
AOP 动态代理
动态代理: 反射
反射体现了java语言的动态性。
16.1.2 AOP编程思想
OOA OOP OOT OOSM
AOP 面向切面编程是一种编程思想,是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
16.1.3 Spring中AOP的常用术语
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。 真正被增强了的方法。 transfer方法。
Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。 transfer方法,事务相关
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。【重点】
Target(目标对象):代理的目标对象: accountService 对象。
Proxy (代理):一个类被 AOP 织入增强后,就产生一个结果代理类 Proxy
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入
Aspect(切面):是切入点和通知(引介)的结合
具体的描述了: 通知应用在具体的某个切点上~ 。
16.1.4 AOP 的作用及优势
作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
优势:减少重复代码,提高开发效率,并且便于维护
16.2 Spring基于XML的AOP配置
1 、搭建maven工程,沿用上一章节转账的业务场景
2 、准备好通知类TransactionManager
3 、讲解基于XML形式的AOP配置,详细介绍每个标签,每个标签当中属性的作用
4 、测试使用AOP进行事务配置
5 、常用通知类型的总结
6 、详解切入点表达式的语法,常用案例的列举
16.2.1 构建maven工程添加依赖
< properties>
< spring.version> 5.2.5.RELEASE</ spring.version>
</ properties>
< dependencies>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-context</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> org.aspectj</ groupId>
< artifactId> aspectjweaver</ artifactId>
< version> 1.8.7</ version>
</ dependency>
</ dependencies>
16.2.2 沿用转账业务的代码
准备实体类,业务层和持久层代码。我们沿用上一章节中的代码即可。
16.2.3 创建 Spring 的配置文件并导入约束
<?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: aop= " http://www.springframework.org/schema/aop"
xsi: schemaLocation= " http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd" >
</ beans>
16.2.4 配置 Spring 的 IOC
< bean id = " dataSource" class = " com.mchange.v2.c3p0.ComboPooledDataSource" >
< property name = " driverClass" value = " com.mysql.jdbc.Driver" > </ property>
< property name = " jdbcUrl" value = " jdbc:mysql://localhost:3306/test" > </ property>
< property name = " user" value = " root" > </ property>
< property name = " password" value = " root" > </ property>
</ bean>
< bean id = " connectionUtils" class = " com.offcn.utils.ConnectionUtils" >
< property name = " dataSource" ref = " dataSource" > </ property>
</ bean>
< bean id = " queryRunner" class = " org.apache.commons.dbutils.QueryRunner" > </ bean>
< bean id = " accountDao" class = " com.offcn.dao.impl.AccountDaoImpl" >
< property name = " queryRunner" ref = " queryRunner" > </ property>
< property name = " connectionUtils" ref = " connectionUtils" > </ property>
</ bean>
< bean id = " accountService" class = " com.offcn.service.impl.AccountServiceImpl" >
< property name = " accountDao" ref = " accountDao" > </ property>
</ bean>
16.2.5 抽取公共代码制作成通知(advice)
public class TransactionManager {
private ConnectionUtils connectionUtils;
public void setConnectionUtils ( ConnectionUtils connectionUtils) {
this . connectionUtils = connectionUtils;
}
public void beginTransaction ( ) {
try {
connectionUtils. getThreadConnection ( ) . setAutoCommit ( false ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
public void commit ( ) {
try {
connectionUtils. getThreadConnection ( ) . commit ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
public void rollback ( ) {
try {
connectionUtils. getThreadConnection ( ) . rollback ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
public void release ( ) {
try {
connectionUtils. removeConnection ( ) ;
connectionUtils. getThreadConnection ( ) . close ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
}
16.2.6 通知类用 bean 标签配置
< bean id = " txManager" class = " com.offcn.utils.TransactionManager" >
< property name = " connectionUtils" ref = " connectionUtils" > </ property>
</ bean>
16.2.7 使用 aop:config 声明 AOP 配置
aop:config:
作用: 开始声明aop配置
< aop: config>
</ aop: config>
16.2.8 使用 aop:aspect 配置切面
aop:aspect
作用: 用于配置切面
属性:
id :给切面提供一个唯一标识。
ref:引用配置好的通知类 bean 的 id。
< aop: aspect id = " tdAdvice" ref = " txManager" >
</ aop: aspect>
16.2.9 使用 aop:pointcut 配置切入点表达式
aop:pointcut
作用: 用于配置切入点表达式。就是指定对哪些类的哪些方法进行增强。
属性: expression:用于定义切入点表达式。
id:用于给切入点表达式提供一个唯一标识
< aop: pointcut id = " point1"
expression = " execution( public void com.offcn.service.impl.AccountServiceImpl.transfer(java.lang.String,java.lang.String,java.lang.Double))" />
16.2.10 使用 aop:xxx 配置对应的通知类型
aop:before
作用:用于配置前置通知。指定增强的方法在切入点方法之前执行
属性:
method:用于指定通知类中的增强方法名称
ponitcut-ref:用于指定切入点的表达式的引用
poinitcut:用于指定切入点表达式
执行时间点:
切入点方法执行之前执行
< aop: before method = " beginTransaction" pointcut-ref = " point1" > </ aop: before>
aop:after-returning
作用:
用于配置后置通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
执行时间点:
切入点方法正常执行之后。它和异常通知只能有一个执行
< aop: after-returning method = " commit" pointcut-ref = " point1" />
aop:after-throwing
作用:
用于配置异常通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
执行时间点:
切入点方法执行产生异常后执行。它和后置通知只能执行一个
< aop: after-throwing method = " rollback" pointcut-ref = " point1" />
aop:after
作用:
用于配置最终通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
执行时间点:
无论切入点方法执行时是否有异常,它都会在其后面执行。
< aop: after method = " release" pointcut-ref = " point1" />
16.3 切入点表达式说明
16.3.1 切点表达式的语法
execution ( [ 修饰符] 返回值类型 包名. 类名. 方法名( 参数) )
案例:
execution ( public void com. offcn. service. impl. AccountServiceImpl. transfer ( String , String , Double ) )
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用星号* 代表任意
- 包名与类名之间一个点 . 代表当前包下的类,两个点 . . 表示当前包及其子包下的类
- 参数列表可以使用两个点 . . 表示任意个数,任意类型的参数列表
例如:
全匹配方式
public void
com. ujiuye. service. impl. AccountServiceImpl. saveAccount ( com. ujiuye. domain. Account)
访问修饰符可以省略
void com. ujiuye. service. impl. AccountServiceImpl. saveAccount ( com. ujiuye. domain. Account)
返回值可以使用* 号,表示任意返回值
* com. ujiuye. service. impl. AccountServiceImpl. saveAccount ( com. ujiuye. domain. Account)
包名可以使用 * 号,表示任意包,但是有几级包,需要写几个 *
* * . *. *. *. AccountServiceImpl. saveAccount ( com. ujiuye. domain. Account)
使用. . 来表示当前包,及其子包
* com. . AccountServiceImpl. saveAccount ( com. ujiuye. domain. Account)
类名可以使用* 号,表示任意类
* com. . *. saveAccount ( com. ujiuye. domain. Account)
方法名可以使用* 号,表示任意方法
* com. . *. *( com. ujiuye. domain. Account)
参数列表可以使用* ,表示参数可以是任意数据类型,但是必须有参数
* com. . *. *( * )
参数列表可以使用. . 表示有无参数均可,有参数可以是任意类型
* com. . *. *( . . )
全通配方式:
* * . . *. *( . . )
注意: 通常情况下,我们都是对业务层的方法进行增强,所以切入点表达式都是切到业务层实现类。
execution ( * com. ujiuye. service. impl. *. *( . . ) )
16.3.2 环绕通知配置事务管理
在TransactionManager 类当中添加方法
public Object transactionAround ( ProceedingJoinPoint pjp) {
Object returnValue = null ;
try {
Object [ ] args = pjp. getArgs ( ) ;
beginTransaction ( ) ;
returnValue = pjp. proceed ( args) ;
commit ( ) ;
} catch ( Throwable e) {
rollback ( ) ;
e. printStackTrace ( ) ;
} finally {
release ( ) ;
}
return returnValue;
}
aop: around:
作用:
用于配置环绕通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut- ref:指定切入点表达式的引用
说明:
它是 spring 框架为我们提供的一种可以在代码中手动控制增强代码什么时候执行的方式。
注意:通常情况下,环绕通知都是独立使用的
< aop: config>
< aop: aspect id= "tdAdvice" ref= "txManager" >
< aop: pointcut id= "point1" expression= "execution(* com.offcn.service.impl.*.*(..))" / >
< ! -- 配置环绕通知 -- >
< aop: around method= "transactionAround" pointcut- ref= "point1" / >
< / aop: aspect>
< / aop: config>
16.4 Spring基于注解的AOP配置
AOP注解方式和XML方式完成的功能是一样的,只是采用了两种开发方式而已。将原有的XML方式使用注解逐一替代。
16.4.1 构建maven工程添加AOP注解的相关依赖
< properties>
< spring. version> 5.2 .5 . RELEASE< / spring. version>
< / properties>
< dependencies>
< dependency>
< groupId> org. springframework< / groupId>
< artifactId> spring- context< / artifactId>
< version> ${ spring. version} < / version>
< / dependency>
< dependency>
< groupId> org. aspectj< / groupId>
< artifactId> aspectjweaver< / artifactId>
< version> 1.8 .7 < / version>
< / dependency>
< / dependencies>
16.4.2 在配置文件中导入 context 的名称空间且配置
<?xml version="1.0" encoding="UTF-8"?>
< beans xmlns = " http://www.springframework.org/schema/beans"
xmlns: aop= " http://www.springframework.org/schema/aop"
xmlns: context= " http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
</ beans>
16.4.3 资源使用注解配置
< bean id = " dataSource" class = " com.mchange.v2.c3p0.ComboPooledDataSource" >
< property name = " driverClass" value = " com.mysql.jdbc.Driver" > </ property>
< property name = " jdbcUrl" value = " jdbc:mysql://localhost:3306/test" > </ property>
< property name = " user" value = " root" > </ property>
< property name = " password" value = " root" > </ property>
</ bean>
< bean id = " queryRunner" class = " org.apache.commons.dbutils.QueryRunner" >
</ bean>
16.4.4 在配置文件中指定 spring 要扫描的包
< context: component-scan base-package = " com.offcn" > </ context: component-scan>
16.4.5 通知类使用注解配置
@Component ( "txManager" )
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
}
16.4.6 在通知类上使用@Aspect 注解声明为切面[重点]
@Component ( "txManager" )
@Aspect
public class TransactionManager {
@Autowired
private ConnectionUtils connectionUtils;
}
16.4.7 在增强的方法上使用注解配置通知
@Before
作用:
把当前方法看成是前置通知
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@Before ( "execution(* com.offcn.service.impl.*.*(..)))" )
public void beginTransaction ( ) {
try {
System . out. println ( "before.........................." ) ;
connectionUtils. getThreadConnection ( ) . setAutoCommit ( false ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
@AfterReturning
作用:
把当前方法看成是后置通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用
@AfterReturning ( "execution(* com.offcn.service.impl.*.*(..)))" )
public void commit ( ) {
try {
connectionUtils. getThreadConnection ( ) . commit ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
@AfterThrowing
作用:
把当前方法看成是异常通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用
@AfterThrowing ( "execution(* com.offcn.service.impl.*.*(..)))" )
public void rollback ( ) {
try {
connectionUtils. getThreadConnection ( ) . rollback ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
@After
作用:
把当前方法看成是最终通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用
@After ( "execution(* com.offcn.service.impl.*.*(..)))" )
public void release ( ) {
try {
connectionUtils. removeConnection ( ) ;
connectionUtils. getThreadConnection ( ) . close ( ) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
}
16.4.8 在 spring 配置文件中开启 spring 对注解 AOP 的支持
< aop: aspectj-autoproxy/>
16.4.9 环绕通知注解配置
@Around
作用:
把当前方法看成是环绕通知。
属性:
value:用于指定切入点表达式,还可以指定切入点表达式的引用。
@Around ( "execution(* com.offcn.service.impl.*.*(..)))" )
public Object transactionAround ( ProceedingJoinPoint pjp) {
Object returnValue = null ;
try {
Object [ ] args = pjp. getArgs ( ) ;
beginTransaction ( ) ;
returnValue = pjp. proceed ( args) ;
commit ( ) ;
} catch ( Throwable e) {
rollback ( ) ;
e. printStackTrace ( ) ;
} finally {
release ( ) ;
}
return returnValue;
}
16.4.10 切入点表达式注解
@Pointcut ( "execution(* com.offcn.service.impl.*.*(..))" )
private void point1 ( ) { }
@Around ( "point1()" )
public Object transactionAround ( ProceedingJoinPoint pjp) {
Object returnValue = null ;
try {
Object [ ] args = pjp. getArgs ( ) ;
beginTransaction ( ) ;
returnValue = pjp. proceed ( args) ;
commit ( ) ;
} catch ( Throwable e) {
rollback ( ) ;
e. printStackTrace ( ) ;
} finally {
release ( ) ;
}
return returnValue;
}
总结:
(1 )applicationContext. xml当中配置的对象, 开启了包扫描器, 注解模式创建。
(2 )TransactionManager : 事务管理器的类: @Aspect 注解。 注解在核心配置文件当中。开启一个开关~
16.5 Spring事务详解【掌握】
1、介绍Spring当中进行事务控制的常用对象以及作用
2、事务的隔离级别,不同隔离级别产生的错误数据
3、扩展事务的传播行为,事务传播行为的对应几种情况,面试问题
4、完成基于XML形式事务配置
16.5.1 Spring中事务的API详解
1. PlatformTransactionManager 作用(掌握)
PlatformTransactionManager 接口是 Spring 的事务管理器,它里面提供了我们常用的操作事务的方法。
注意:
PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如:Dao 层技术是jdbc 或 mybatis, JdbcTemplate 时:org. springframework. jdbc. datasource. DataSourceTransactionManager .
Dao 层技术是hibernate时:org. springframework. orm. hibernate5. HibernateTransactionManager
等价于: TransactionManager 常用的对象: . DataSourceTransactionManager
2. TransactionDefinition 作用
TransactionDefinition 是事务的定义信息对象,里面有如下方法
2.1 事务隔离级别
设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。
事务隔离级别 说明 ISOLATION_DEFAULT 默认级别,归属下列某一种 ISOLATION_READ_UNCOMMITTED 未提交读,可以读取未提交数据 ISOLATION_READ_COMMITTED 已提交读,只能读取已提交数据,解决脏读问题(Oracle默认级别) ISOLATION_REPEATABLE_READ 可重复读,解决不可重复度问题(MySQL默认级别) ISOLATION_SERIALIZABLE 串行化,节约幻读(虚读)问题
2.2 事务传播行为
事务传播行为 说明 REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值) SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务) MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常 REQUERS_NEW 新建事务,如果当前在事务中,把当前事务挂起。 NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 NEVER 以非事务方式运行,如果当前存在事务,抛出异常 NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作
2.3 事务超时时间
默认值是- 1 ,没有超时限制。如果有,以秒为单位进行设置。
2.4 是否是只读事务
建议查询时设置为只读。
事务只读: 查询操作~
写事务: 数据库的写操作。
4. TransactionStatus 作用
TransactionStatus 接口提供的是事务具体的运行状态,方法介绍如下。
5. 三个对象之间的关系:
PlatformTransactionManager 会根据 TransactionDefinition 对象当中定义的信息,进行事务的管理操作。
TransactionStatus 记录了事务的状态信息。
掌握: PlatformTransactionManager
16.5.2 Spring基于XML的事务配置
讲解思路:
1、准备转账的业务场景,演示没有事务控制时,数据一致性受损
2、在XML配置文件当中,使用Spring提供的声明式事务进行控制,详解事务控制的每个步骤,每个步骤当中涉及的每个标签,讲解的过程当中复习巩固AOP当中的相关概念,加深学员对概念的理解
3、测试基于XML声明式事务是否生效
(1)添加相关技术依赖
< properties>
< spring.version> 5.2.5.RELEASE</ spring.version>
</ properties>
< dependencies>
< dependency>
< groupId> junit</ groupId>
< artifactId> junit</ artifactId>
< version> 4.12</ version>
< scope> test</ scope>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-context</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-test</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-jdbc</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< version> 5.1.47</ version>
</ dependency>
< dependency>
< groupId> com.mchange</ groupId>
< artifactId> c3p0</ artifactId>
< version> 0.9.5.2</ version>
</ dependency>
< dependency>
< groupId> org.aspectj</ groupId>
< artifactId> aspectjweaver</ artifactId>
< version> 1.8.7</ version>
</ dependency>
</ dependencies>
(2)创建配置文件并导入约束
< 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"
xmlns: aop= " http://www.springframework.org/schema/aop"
xmlns: tx= " http://www.springframework.org/schema/tx"
xsi: schemaLocation= "
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
< context: component-scan base-package = " com.offcn" > </ context: component-scan>
< bean id = " dataSource" class = " com.mchange.v2.c3p0.ComboPooledDataSource" >
< property name = " driverClass" value = " com.mysql.jdbc.Driver" > </ property>
< property name = " jdbcUrl" value = " jdbc:mysql://localhost:3306/test" > </ property>
< property name = " user" value = " root" > </ property>
< property name = " password" value = " root" > </ property>
</ bean>
< bean id = " jdbcTemplate" class = " org.springframework.jdbc.core.JdbcTemplate" >
< property name = " dataSource" ref = " dataSource" > </ property>
</ bean>
</ beans>
(3)沿用转账业务的代码
@Repository ( "accountDao" )
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public Account findByName ( String name) {
String sql = "select * from account where name =? " ;
Account account = this . jdbcTemplate. queryForObject ( sql, new BeanPropertyRowMapper < > ( Account . class ) , name) ;
return account;
}
@Override
public void update ( Account account) {
String sql = "update account set money =? where name =? " ;
this . jdbcTemplate. update ( sql, account. getMoney ( ) , account. getName ( ) ) ;
}
}
(4)配置事务管理器
< bean id = " transactionManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name = " dataSource" ref = " dataSource" > </ property>
</ bean>
(5)配置事务的通知引用事务管理器
< tx: advice id = " txAdvice" transaction-manager = " transactionManager" >
</ tx: advice>
(6)配置事务的属性
< tx: attributes>
< tx: method name = " *" />
</ tx: attributes>
(7)配置 AOP 切入点表达式
< aop: config>
< aop: pointcut id = " myPointcut" expression = " execution(* com.offcn.service.impl.*.*(..))" />
</ aop: config>
(8) 配置切入点表达式和事务通知的对应关系
< aop: advisor advice-ref = " txAdvice" pointcut-ref = " myPointcut" > </ aop: advisor>
第17节:Spring事务详解-注解
17.1 基于注解的事务配置
17.1.1 添加相关技术依赖
< properties>
< spring.version> 5.2.5.RELEASE</ spring.version>
</ properties>
< dependencies>
< dependency>
< groupId> junit</ groupId>
< artifactId> junit</ artifactId>
< version> 4.12</ version>
< scope> test</ scope>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-context</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-test</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-jdbc</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< version> 5.1.47</ version>
</ dependency>
< dependency>
< groupId> com.mchange</ groupId>
< artifactId> c3p0</ artifactId>
< version> 0.9.5.2</ version>
</ dependency>
< dependency>
< groupId> org.aspectj</ groupId>
< artifactId> aspectjweaver</ artifactId>
< version> 1.8.7</ version>
</ dependency>
17.1.2 创建配置文件导入约束
< 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"
xmlns: aop= " http://www.springframework.org/schema/aop"
xmlns: tx= " http://www.springframework.org/schema/tx"
xsi: schemaLocation= "
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
< context: component-scan base-package = " com.offcn" > </ context: component-scan>
< bean id = " dataSource" class = " com.mchange.v2.c3p0.ComboPooledDataSource" >
< property name = " driverClass" value = " com.mysql.jdbc.Driver" > </ property>
< property name = " jdbcUrl" value = " jdbc:mysql://localhost:3306/test" > </ property>
< property name = " user" value = " root" > </ property>
< property name = " password" value = " root" > </ property>
</ bean>
< bean id = " jdbcTemplate" class = " org.springframework.jdbc.core.JdbcTemplate" >
< property name = " dataSource" ref = " dataSource" > </ property>
</ bean>
</ beans>
17.1.3 沿用转账业务的代码
17.2 事务管理配置步骤(重点)
17.2.1 配置事务管理器并注入数据源
< bean id = " transactionManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name = " dataSource" ref = " dataSource" > </ property>
</ bean>
17.2.2 在业务层使用注解
@Service ( "accountService" )
@Transactional ( readOnly = true , propagation = Propagation . SUPPORTS)
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao ;
@Transactional ( readOnly = false , propagation = Propagation . REQUIRED)
@Override
public void transfer ( String sourceAccountName, String targetAccountName, Double money) {
Account sAccount = accountDao. findByName ( sourceAccountName) ;
Account tAccount = accountDao. findByName ( targetAccountName) ;
sAccount. setMoney ( sAccount. getMoney ( ) - money) ;
tAccount. setMoney ( tAccount. getMoney ( ) + money) ;
accountDao. update ( sAccount) ;
accountDao. update ( tAccount) ;
}
}
17.2.3 在配置文件中开启注解事务
< tx: annotation-driven transaction-manager = " transactionManager" />
17.3 Spring事务源码分析(了解)
17.3.1 DataSourceUtils源码分析
看名字就能知道这个类是对DataSource 的一个封装,这个类提供了一系列操作数据库连接的工具方法。这个类在Spring 事务中非常重要,最主要的作用就是提供了能够从当前线程获取开启事务时绑定的连接。其中Spring Jdbc 里的JdbcTemplate 类就是采用DataSourceUtils . getConnection ( ) 方法获取连接的。
public static Connection getConnection ( DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection ( dataSource) ;
} catch ( SQLException var2) {
throw new CannotGetJdbcConnectionException ( "Failed to obtain JDBC Connection" , var2) ;
} catch ( IllegalStateException var3) {
throw new CannotGetJdbcConnectionException ( "Failed to obtain JDBC Connection: " + var3. getMessage ( ) ) ;
}
}
public static Connection doGetConnection ( DataSource dataSource) throws SQLException {
Assert . notNull ( dataSource, "No DataSource specified" ) ;
ConnectionHolder conHolder = ( ConnectionHolder ) TransactionSynchronizationManager . getResource ( dataSource) ;
if ( conHolder == null || ! conHolder. hasConnection ( ) && ! conHolder. isSynchronizedWithTransaction ( ) ) {
logger. debug ( "Fetching JDBC Connection from DataSource" ) ;
Connection con = fetchConnection ( dataSource) ;
if ( TransactionSynchronizationManager . isSynchronizationActive ( ) ) {
try {
ConnectionHolder holderToUse = conHolder;
if ( conHolder == null ) {
holderToUse = new ConnectionHolder ( con) ;
} else {
conHolder. setConnection ( con) ;
}
holderToUse. requested ( ) ;
TransactionSynchronizationManager . registerSynchronization ( new DataSourceUtils. ConnectionSynchronization ( holderToUse, dataSource) ) ;
holderToUse. setSynchronizedWithTransaction ( true ) ;
if ( holderToUse != conHolder) {
TransactionSynchronizationManager . bindResource ( dataSource, holderToUse) ;
}
} catch ( RuntimeException var4) {
releaseConnection ( con, dataSource) ;
throw var4;
}
}
return con;
} else {
conHolder. requested ( ) ;
if ( ! conHolder. hasConnection ( ) ) {
logger. debug ( "Fetching resumed JDBC Connection from DataSource" ) ;
conHolder. setConnection ( fetchConnection ( dataSource) ) ;
}
return conHolder. getConnection ( ) ;
}
}
17.3.2 TransactionSynchronizationManager源码分析
这个类的作用就是绑定资源到当前线程、注册TransactionSynchronization 接口、绑定事务的各个属性。
public abstract class TransactionSynchronizationManager {
private static final ThreadLocal < Set < TransactionSynchronization > > synchronizations = new NamedThreadLocal ( "Transaction synchronizations" ) ;
public static void initSynchronization ( ) throws IllegalStateException {
if ( isSynchronizationActive ( ) ) {
throw new IllegalStateException ( "Cannot activate transaction synchronization - already active" ) ;
} else {
logger. trace ( "Initializing transaction synchronization" ) ;
synchronizations. set ( new LinkedHashSet ( ) ) ;
}
}
}
第18节:Spring整合Mybatis(重点)
18.1 整合思路分析
Mybatis 框架是一个持久层ORM框架,而Spring 则是一个综合性一站式框架。所以整合是Mybatis 往Spring 上整合。就是让Spring 框架接管Mybatis 的组件。
Mybatis 单独运行时,数据源的管理,事务的管理, SqlSessionFactory 以及接口的实现类都是Mybatis 管理的,整合后以上组件交给Spring 管理。
18.2 添加技术依赖
< properties>
< spring.version> 5.2.5.RELEASE</ spring.version>
</ properties>
< dependencies>
< dependency>
< groupId> junit</ groupId>
< artifactId> junit</ artifactId>
< version> 4.12</ version>
< scope> test</ scope>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-context</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-test</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-jdbc</ artifactId>
< version> ${spring.version}</ version>
</ dependency>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< version> 5.1.47</ version>
</ dependency>
< dependency>
< groupId> com.mchange</ groupId>
< artifactId> c3p0</ artifactId>
< version> 0.9.5.2</ version>
</ dependency>
< dependency>
< groupId> org.aspectj</ groupId>
< artifactId> aspectjweaver</ artifactId>
< version> 1.8.7</ version>
</ dependency>
< dependency>
< groupId> org.mybatis</ groupId>
< artifactId> mybatis-spring</ artifactId>
< version> 2.0.0</ version>
</ dependency>
< dependency>
< groupId> org.mybatis</ groupId>
< artifactId> mybatis</ artifactId>
< version> 3.4.6</ version>
</ dependency>
</ dependencies>
18.3 构建数据库表并创建实体User
create table User (
id int primary key auto_increment ,
name varchar ( 32 ) not null ,
address varchar ( 32 ) not null ,
birthday date
) ;
public class User implements Serializable {
private Integer id;
private String name;
private String address;
private Date birthday;
public Integer getId ( ) {
return id;
}
public void setId ( Integer id) {
this . id = id;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public String getAddress ( ) {
return address;
}
public void setAddress ( String address) {
this . address = address;
}
public Date getBirthday ( ) {
return birthday;
}
public void setBirthday ( Date birthday) {
this . birthday = birthday;
}
@Override
public String toString ( ) {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", address='" + address + '\'' +
", birthday=" + birthday +
'}' ;
}
}
18.4 编写dao层的接口UserMapper
public interface UserMapper {
int insert ( User user) ;
int update ( User user) ;
int delete ( Integer id) ;
User findById ( Integer id) ;
List < User > findAll ( ) ;
}
18.5 构建mapper接口对应的sql配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<! DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
< mapper namespace = " com.offcn.mapper.UserMapper" >
< resultMap id = " BaseResultMap" type = " com.offcn.pojo.User" >
< id column = " id" jdbcType = " INTEGER" property = " id" />
< result column = " name" jdbcType = " VARCHAR" property = " name" />
< result column = " address" jdbcType = " VARCHAR" property = " address" />
< result column = " birthday" jdbcType = " DATE" property = " birthday" />
</ resultMap>
< insert id = " insert" parameterType = " com.offcn.pojo.User" >
insert into user (name, birthday, address)
values (#{name}, #{birthday},#{address})
</ insert>
< update id = " update" >
update user
set
name= #{name},
birthday=#{birthday},
address = #{address}
where id=#{id}
</ update>
< delete id = " delete" >
delete from user
where id =#{id}
</ delete>
< select id = " findById" resultMap = " BaseResultMap" >
select * from user where id=#{id}
</ select>
< select id = " findAll" resultMap = " BaseResultMap" >
select * from user
</ select>
</ mapper>
18.6 构建服务层接口UserService
public interface UserService {
int insert ( User user) ;
int update ( User user) ;
int delete ( Integer id) ;
User findById ( Integer id) ;
List < User > findAll ( ) ;
}
18.7 构建服务层实现类UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public int insert ( User user) {
int num = userMapper. insert ( user) ;
return num;
}
@Override
public int update ( User user) {
int num = userMapper. update ( user) ;
return num;
}
@Override
public int delete ( Integer id) {
int num = userMapper. delete ( id) ;
return num;
}
@Override
public User findById ( Integer id) {
User user = userMapper. findById ( id) ;
return user;
}
@Override
public List < User > findAll ( ) {
List < User > userList = userMapper. findAll ( ) ;
return userList;
}
}
18.8 构建Spring框架的配置文件配置IOC管理的对象【重点】
< beans xmlns = " http://www.springframework.org/schema/beans"
xmlns: xsi= " http://www.w3.org/2001/XMLSchema-instance"
xmlns: aop= " http://www.springframework.org/schema/aop"
xmlns: tx= " http://www.springframework.org/schema/tx"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
< context: component-scan base-package = " com.offcn" > </ context: component-scan>
< bean id = " dataSource" class = " com.mchange.v2.c3p0.ComboPooledDataSource" >
< property name = " driverClass" value = " com.mysql.jdbc.Driver" > </ property>
< property name = " jdbcUrl" value = " jdbc:mysql://localhost:3306/test" > </ property>
< property name = " user" value = " root" > </ property>
< property name = " password" value = " root" > </ property>
</ bean>
< bean id = " sqlSessionFactory" class = " org.mybatis.spring.SqlSessionFactoryBean" >
< property name = " dataSource" ref = " dataSource" />
< property name = " mapperLocations" value = " classpath:com/offcn/mapper/*Mapper.xml" > </ property>
< property name = " configLocation" value = " classpath:SqlMapConfig.xml" />
< property name = " typeAliasesPackage" value = " com/offcn/pojo" />
< property name = " plugins" >
< array>
< bean class = " com.github.pagehelper.PageInterceptor" >
< property name = " properties" >
< value>
helperDialect=MySQL
reasonable=true
supportMethodsArguments=true
params=count=countSql
autoRuntimeDialect=true
</ value>
</ property>
</ bean>
</ array>
</ property>
</ bean>
< bean id = " mapperScannerConfigurer" class = " org.mybatis.spring.mapper.MapperScannerConfigurer" >
< property name = " basePackage" value = " com.offcn.mapper" > </ property>
</ bean>
< bean id = " transactionManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name = " dataSource" ref = " dataSource" />
</ bean>
< tx: annotation-driven transaction-manager = " transactionManager" />
</ beans>
18.9 测试代码
@RunWith ( SpringJUnit4ClassRunner . class )
@ContextConfiguration ( locations = "classpath:applicationContext.xml" )
public class TestAccountTransfer {
@Autowired
private UserService userService;
@Test
public void testInsert ( ) {
User user = new User ( ) ;
user. setName ( "admin" ) ;
user. setAddress ( "china" ) ;
user. setBirthday ( new Date ( ) ) ;
int num = userService. insert ( user) ;
System . out. println ( "num:" + num) ;
}
@Test
public void testUpdate ( ) {
User user = new User ( ) ;
user. setName ( "marry" ) ;
user. setAddress ( "America" ) ;
user. setBirthday ( new Date ( ) ) ;
user. setId ( 1 ) ;
int num = userService. update ( user) ;
System . out. println ( "num:" + num) ;
}
@Test
public void testDelete ( ) {
int num = userService. delete ( 1 ) ;
System . out. println ( "num:" + num) ;
}
@Test
public void testFindById ( ) {
User user = userService. findById ( 2 ) ;
System . out. println ( "user:" + user) ;
}
@Test
public void testFindByAll ( ) {
List < User > userList = userService. findAll ( ) ;
System . out. println ( "userList:" + userList) ;
}
}