10个方法: 想给10个方法都增加一种打印日志的功能,但是又不想(不能)改源码,此时可以给它使用AOP增强。
实际上,Spring的AOP,底层是通过动态代理实现的。在运行期间,通过代理技术动态生成代理对象,代理对象方法执行时进行功能的增强介入,再去调用目标方法,从而完成功能增强。
常用的动态代理技术有:
Spring的AOP采用了哪种代理方式?
目标对象(Target):要代理的/要增强的目标对象。
代理对象(Proxy):目标对象被AOP织入增强后,就得到一个代理对象
连接点(JoinPoint):能够被拦截到的点,在Spring里指的是方法
目标类里,所有能够进行增强的方法,都是连接点
切入点(PointCut):要对哪些连接点进行拦截的定义
已经增强的连接点,叫切入点
通知/增强(Advice):拦截到连接点之后要做的事情
对目标对象的方法,进行功能增强的代码
切面(Aspect):是切入点和通知的结合
织入(Weaving):把增强/通知 应用到 目标对象来创建代理对象的过程。Spring采用动态代理技术织入,而AspectJ采用编译期织入和装载期织入
UserServiceImpl
,有通知类或增强类MyAdvice
UserServiceImpl
的方法进行增强<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
com.mingye.aop.UserServiceImpl
package com.mingye.service;
public interface UserService {
void add();
void update();
}
package com.mingye.service.impl;
import com.mingye.service.UserService;
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("调用了UserServiceImpl的add方法~!");
//int a = 1 / 0 ;
}
public void update() {
System.out.println("调用了UserServiceImpl的update方法~!");
}
}
com.mingye.aop.MyAdvice
package com.mingye.advice;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
public void print(){
System.out.println("打印日志~!");
}
}
<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">
<bean id="us" class="com.mingye.service.impl.UserServiceImpl"/>
<bean id="myAdvice" class="com.mingye.advice.MyAdvice"/>
<aop:config>
<aop:aspect ref="myAdvice">
<aop:before method="print" pointcut="execution(* com.mingye.service.impl.UserServiceImpl.add())"/>
aop:aspect>
aop:config>
beans>
注意:在xml中增加aop的名称空间
/*
AOP 入门:
1. 导入依赖
2. 定义接口和实现类
3. 定义扩展的功能类 MyAdvice,提供一个方法即可
4. 配置AOP
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUserServiceImpl {
@Autowired
private UserService us;
@Test
public void testAdd(){
us.add();
}
@Test
public void testUpdate(){
us.update();
}
}
spring-context, aspectjweaver
<aop:config>
<aop:aspect ref="通知对象">
<aop:before method="通知对象里的通知方法" pointcut="切入点表达式"/>
aop:aspect>
aop:config>
execution([权限修饰符] 返回值类型 包名.类名.方法名(参数列表))
String
(如果类型有歧义,就写全限定类名,比如:java.util.Date
)*
,表示任意字符。比如Str*
,或者*
.
:表示当前包下的类或者子包。比如com.mingye.service
..
:表示当前包里所有后代类、后代包。比如com..service
*
:表示任意字符。比如:com.it*
, com.*
UserServiceImpl
*
表示任意字符。比如:*ServiceImpl
,*
*
表示任意字符。比如:save*
,*
String,Integer
表示第一个参数是String,第二个参数是Integer类型*
表示任意字符。比如:
String, *
表示第一个参数是String,第二个参数是任意类型Str*, Integer
表示第一个参数类型Str开头,第二个参数是Integer类型..
表示任意个数、任意类型的参数execution(public void com.mingye.dao.impl.UserDao.save())
execution(void com.mingye.dao.impl.UserDao.*(..))
execution(* com.mingye.dao.impl.*.*(..))
execution(* com.mingye.dao..*.*(..))
execution(* *..*.*(..)) --不建议使用
<aop:config>
<aop:aspect ref="myAdvice">
<aop:before method="print" pointcut="execution(* com.mingye..*.*(..))"/>
aop:aspect>
aop:config>
<aop:通知类型 method="通知中的方法" pointcut="切点表达式">aop:通知类型>
名称 | 标签 | 说明 |
---|---|---|
前置通知 |
|
通知方法在切入点方法之前执行 |
后置通知 |
|
在切入点方法正常执行之后,执行通知方法 |
异常通知 |
|
在切入点方法抛出异常时,执行通知方法 |
最终通知 |
|
无论切入点方法是否有异常,最终都执行通知方法 |
环绕通知 |
|
通知方法可以在切入点方法之前、之后都执行 |
注意:通知方法的名称随意,我们这里是为了方便理解,才起名称为:before, after等等
前置通知
MyAdvice
的before
方法:public void before(){
System.out.println("前置通知");
}
<aop:before method="before"
pointcut="execution(* com.mingye.service..*.*())"/>
后置通知
public void afterReturning(){
System.out.println("后置通知");
}
<aop:after-returning method="afterReturning"
pointcut="execution(* com.mingye.service..*.*())"/>
环绕通知
/*
环绕增强比较特殊一些,它需要我们在增强的方法里面手动调用目标方法;其实起到了特殊作用:
1.掌控方法的执行,起到拦截的效果
2.给方法传参数
3.也可以拿到方法的返回值
*/
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
//System.out.println("环绕:打印日志~!");
before();
//调用目标方法
//joinPoint.proceed(); //目标方法没有参数的调用
joinPoint.proceed(joinPoint.getArgs()); //目标方法有参的方式调用
afterReturning();
}
<aop:around method="around"
pointcut="execution(* com.mingye.service..*.*())"/>
异常抛出通知
public void afterThrowing(){
System.out.println("抛出异常通知");
}
<aop:after-throwing method="afterThrowing"
pointcut="execution(* com.mingye.service..*.*())"/>
最终通知
public void after(){
System.out.println("最终通知");
}
<aop:config>
<aop:aspect ref="myAdvice">
<aop:pointcut id="pointCut01" expression="execution(* com.mingye..*.*(..))"/>
<aop:before method="before" pointcut-ref="pointCut01"/>
<aop:after-returning method="afterReturning" pointcut-ref="pointCut01"/>
aop:aspect>
aop:config>
<aop:config>
<aop:pointcut id="xxx" expression="切入点表达式"/>
<aop:aspect ref="通知对象">
<aop:before method="通知对象里的通知方法" pointcut-ref="xxx"/>
<aop:after-returning method="通知对象里的通知方法" pointcut-ref="xxx"/>
<aop:after-throwing method="通知对象里的通知方法" pointcut-ref="xxx"/>
<aop:after method="通知对象里的通知方法" pointcut-ref="xxx"/>
<aop:around method="通知对象里的通知方法" pointcut-ref="xxx"/>
aop:aspect>
aop:config>
public Object aroundMethod(ProceedingJoinPoint pjp){
Object reuslt = null;
try{
//写前置通知代码
//调用目标对象的方法
result = pjp.proceed(pjp.getArgs());
//写后置通知代码
}catch(Throwable t){
//写异常通知代码
}finally{
//写最终通知代码
}
}
UserServiceImpl
,有通知类MyAdvice
UserServiceImpl
的方法进行增强@Component
标注两个类,配置成为bean对象aspectjweaver
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
使用注解标注两个类,配置成为bean对象
@Repository
, @Service
, @Controller
注解,按照分层进行配置在通知类中,使用注解配置织入关系
com.mingye.aop.Target
package com.mingye.service;
public interface UserService {
void add();
void update();
}
package com.mingye.service.impl;
import com.mingye.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("调用了UserServiceImpl的add方法~!~");
//int a = 1 / 0 ;
}
public void update() {
System.out.println("调用了UserServiceImpl的update方法~!~");
}
}
com.mingye.aop.MyAdvice
/*
MyAdvice是增强类,它需要做:
1. 把自己交给spring管理 , 打上注解@Component
2. 表示这个类是切面增强类,专门用来做增强的,打注解 @Aspect
3. 把什么方法用来做什么增强(增强谁),就在这个方法上面打注解
前置增强 === @Before
后置增强 === @AfterReturning
*/
@Component
@Aspect
public class MyAdvice {
@Before("execution(* com.mingye..*.*(..))")
public void print(){
System.out.println("打印日志~");
}
...
}
applicationContext.xml
中
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 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">
<context:component-scan base-package="com.mingye"/>
<aop:aspectj-autoproxy/>
beans>
package com.mingye.test;
import com.mingye.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestUserServiceImpl {
@Autowired
private UserService us;
@Test
public void testAdd(){
us.add();
}
}
创建功能类UserServiceImpl
创建增强类MyAdvice
给他们都打上注解
UserServiceImpl : @Service
2. MyAdvice : @Component @Aspect
1. 方法上面打上前置或者后置的注解
在applicationContext.xml中打开开关
<context:component-scan base-package="com.mingye"/>
<aop:aspectj-autoproxy/>
@通知注解("切入点表达式")
名称 | 注解 | 说明 |
---|---|---|
前置通知 | @Before |
通知方法在切入点方法之前执行 |
后置通知 | @AfterRuturning |
通知方法在切入点方法之后执行 |
异常通知 | @AfterThrowing |
通知方法在抛出异常时执行 |
最终通知 | @After |
通知方法无论是否有异常,最终都执行 |
环绕通知 | @Around |
通知方法在切入点方法之前、之后都执行 |
前置->最终->后置/异常
@Aspect
标的类)上增加一个额外的方法,在方法上使用@Pointcut
注解定义切入点表达式,package com.mingye.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/*
MyAdvice是增强类,它需要做:
1. 把自己交给spring管理 , 打上注解@Component
2. 表示这个类是切面增强类,专门用来做增强的,打注解 @Aspect
3. 把什么方法用来做什么增强(增强谁),就在这个方法上面打注解
前置增强 === @Before
后置增强 === @AfterReturning
*/
@Aspect
@Component
public class MyAdvice {
//@Before("execution(* com.mingye..*.*(..))")
public void print(){
System.out.println("打印日志~!");
}
//===============================================================
//这个abc方法的作用就是为了抽取切点表达式! ,并且这个abc方法不会被调用!
@Pointcut("execution(* com.mingye..*.*(..))")
public void abc(){
System.out.println("调用abc方法了~!");
}
//@Before("execution(* com.mingye..*.*(..))")
@Before("abc()")
public void before(){
System.out.println("前置:打印日志~!");
}
//@AfterReturning("execution(* com.mingye..*.*(..))")
@AfterReturning("abc()")
public void afterReturning(){
System.out.println("后置:打印日志~!");
}
//@AfterThrowing("execution(* com.mingye..*.*(..))")
public void afterThrowing(){
System.out.println("异常:打印日志~!");
}
//@After("execution(* com.mingye..*.*(..))")
public void after(){
System.out.println("最终:打印日志~!");
}
/*
环绕增强比较特殊一些,它需要我们在增强的方法里面手动调用目标方法。
*/
//@Around("execution(* com.mingye..*.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
//System.out.println("环绕:打印日志~!");
before();
//调用目标方法
//joinPoint.proceed(); //目标方法没有参数的调用
joinPoint.proceed(joinPoint.getArgs()); //目标方法有参的方式调用
afterReturning();
}
}
@Aspect
,声明成一个切面@Before/@AfterReturning/@AfterThrowing/@After/@Around
,配置切入点表达式
使用 @EnableAspectJAutoProxy 来允许AOP的自动配置
package com.mingye.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan("com.mingye")
@EnableAspectJAutoProxy
public class AppConfig {
}
package com.mingye.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class MyAdvice {
@Before("execution(* com.mingye..*.*(..))")
public void print(){
System.out.println("打印日志~");
}
}
package com.mingye.service;
public interface UserService {
void add();
void update();
}
package com.mingye.service.impl;
import com.mingye.service.UserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("调用了UserServiceImpl的add方法~!~");
}
public void update() {
System.out.println("调用了UserServiceImpl的update方法~!~");
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestUserServiceImpl02 {
@Autowired
private UserService us;
@Test
public void testAdd(){
us.add();
}
}
能不能描述一下环绕通知里面的实现步骤?
需求:任意业务层接口执行均可显示其执行效率(执行时长)
分析:
①:业务功能:业务层接口执行前后分别记录时间,求差值得到执行效率
②:通知类型选择前后均可以增强的类型——环绕通知
Spring整合mybatis对spring_db数据库中的Account进行CRUD操作
Spring整合Junit测试CRUD是否OK。
在pom.xml中添加aspectjweaver切入点表达式依赖
… …
@Component
@Aspect
public class MyAdvice02 {
//=======================使用环绕增强测试目标方法调用一万次耗费时间===============================
@Around("execution( * com.mingye..*.*(..))")
public void runSpeed(ProceedingJoinPoint joinPoint) throws Throwable {
//0. 通过方法签名得到方法的信息
Signature signature = joinPoint.getSignature();
//得到接口名称
String typeName = signature.getDeclaringTypeName();
//得到方法名字
String methodName = signature.getName();
//1. 记录时间
long start = System.currentTimeMillis();
//2. 调用目标方法
for (int i = 0; i < 10000; i++) {
joinPoint.proceed(joinPoint.getArgs());
}
//3. 记录时间
long end = System.currentTimeMillis();
//4. 计算差值
System.out.println("调用 "+typeName+" 的 " + methodName+ " 一万次,耗费时间:" + (end - start));
}
}
@Configuration
@ComponentScan("com.mingye")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableAspectJAutoProxy //开启AOP注解功能
public class SpringConfig {
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTestCase {
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
Account account = accountService.findById(2);
}
@Test
public void testFindAll(){
List<Account> list = accountService.findAll();
}
}
TransactionDefinition
表示PlatformTransactionManager
来完成TransactionStatus
用于表示一个运行着的事务的状态编程式事务管理:通过编写代码的方式实现事务管理
编程式事务管理,因事务管理与业务功能耦合性太强,不方便维护,目前已经基本不用
spring 2.0 就已经提供了 xml配置的声明式事务管理的支持
如果想要了解Spring的编程式事务,可参考《资料/spring02_transaction_program》
以下API仅做介绍了解,用于了解Spring事务相关的API,并回顾事务相关的概念
PlatformTransactionManager
PlatformTransactionManager
是接口类型,不同的dao层技术有不同的实现,例如:
DataSourceTransactionManager
HibernateTransactionManager
方法 | 返回值 | 说明 |
---|---|---|
getTransaction(TransactionDefinition td) |
TransactionStatus |
开启事务,并得到事务状态 |
commit(TransactionStatus status) |
提交事务 | |
rollback(TransactionStatus status) |
回滚事务 |
TransactionDefinition
方法 | 参数 | 返回值 | 说明 |
---|---|---|---|
getIsolationLevel() |
int |
获取事务的隔离级别 | |
getPropogationBehavior() |
int |
获取事务的传播行为 | |
getTimeout() |
int |
获取超时时间 | |
isReadOnly() |
boolean |
是否只读的事务 |
ISOLATION_DEFAULT
:默认事务隔离级别
repeatable read
read committed
ISOLATION_READ_UNCOMMITTED
:读未提交–存在脏读、不可重复读、幻读ISOLATION_READ_COMMITTED
:读已提交–存在不可重复读、幻读ISOLATION_REPEATABLE_READ
:重复读–存在幻读ISOLATION_SERIALIZABLE
:串行化–没有并发问题用于解决业务方法调用业务方法时,事务的统一性问题的
比如: A方法开启事务了之后,就调用了B方法,那么B方法是否也会被纳入事务管理的范畴呢?
以下三个,是要当前事务的
PROPAGATION_REQUIRED
:需要有事务。默认
PROPAGATION_SUPPORTS
:支持事务
PROPAGATION_MANDATORY
:强制的
以下三个,是不要当前事务的
PROPAGATION_REQUIRES_NEW
:新建的
PROPAGATION_NOT_SUPPORTED
:不支持的
PROPAGATION_NEVER
:非事务的
最后一个,是特殊的
PROPAGATION_NESTED
:嵌套的
REQUIRED
的操作超时后事务自动回滚
TransactionStatus
方法 | 返回值 | 说明 |
---|---|---|
hasSavePoint() |
boolean |
事务是否有回滚点 |
isCompleted() |
boolean |
事务是否已经完成 |
isNewTransaction() |
boolean |
是否是新事务 |
isRollbackOnly() |
boolean |
事务是否是 要回滚的状态 |
ISOLATION_DEFAULT
PROPAGATION_REQUIRED
JdbcTemplate
,不能使用dbutils
**<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>c3p0groupId>
<artifactId>c3p0artifactId>
<version>0.9.1.2version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.18version>
dependency>
dependencies>
AccountDao
和AccountDaoImpl
:package com.mingye.dao;
public interface AccountDao {
void kouqian(String from , int moeny);
void jiaqian(String to , int moeny);
}
package com.mingye.dao.impl;
import com.mingye.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
/*
1. 把自己交给spring管理
2. 让spring注入进来jdbctemplate
*/
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate template;
public void kouqian(String from, int money) {
String sql="update t_account set money = money - ? where name = ? ";
template.update(sql , money , from);
}
public void jiaqian(String to, int money) {
String sql="update t_account set money = money + ? where name = ? ";
template.update(sql , money , to);
}
}
AccountService
和AccountServiceImpl
package com.mingye.service;
public interface AccountService {
void transfer(String from ,String to , int money);
}
package com.mingye.service.impl;
import com.mingye.dao.AccountDao;
import com.mingye.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/*
1. 把这个类交给spring管理
2. 注入进来dao的对象!
*/
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao dao;
/**
* 转账
* @param from
* @param to
* @param money
*/
public void transfer(String from, String to, int money) {
//扣钱
dao.kouqian(from ,money);
//加钱
dao.jiaqian(to , money);
}
}
applicationContext.xml
<context:component-scan base-package="com.mingye"/>
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.mingye.dao"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" isolation="DEFAULT" timeout="-1" read-only="false" propagation="REQUIRED"/>
<tx:method name="add*" isolation="DEFAULT" timeout="-1" read-only="false" propagation="REQUIRED"/>
<tx:method name="update*" isolation="DEFAULT" timeout="-1" read-only="false" propagation="REQUIRED"/>
<tx:method name="delete*" isolation="DEFAULT" timeout="-1" read-only="false" propagation="REQUIRED"/>
<tx:method name="find*" isolation="DEFAULT" timeout="-1" read-only="true" propagation="REQUIRED"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution( * com.mingye..*.*(..))"/>
aop:config>
beans>
package com.mingye.test;
import com.mingye.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestAccountServiceImpl {
@Autowired
private AccountService as;
@Test
public void testTransfer(){
as.transfer("zs", "ls" , 100);
}
}
applicationContext.xml
即可:
<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:tx="http://www.springframework.org/schema/tx"
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/context http://www.springframework.org/schema/context/spring-context.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">
<context:component-scan base-package="com.mingye"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"/>
bean>
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"/>
<property name="jdbcUrl" value="${jdbcUrl}"/>
<property name="user" value="${user}"/>
<property name="password" value="${password}"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="edit*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="query*" isolation="DEFAULT" propagation="REQUIRED" read-only="true"/>
<tx:method name="find*" isolation="DEFAULT" propagation="REQUIRED" read-only="true"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.mingye..*.*(..))"/>
aop:config>
beans>
aop:config
:切面配置这个标签的配置,就是为了找到方法,然后给这些方法应用上事务。
<aop:config>
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* com.mingye.service.impl..*.*(..))"/>
aop:config>
aop:config
:aop提供的用于配置切面的标签aop:advisor
:Spring提供的专门用于配置事务的,作用类似于aop:aspect
advice-ref
:要引入的通知配置,必须要引用
所配置的事务通知pointcut
:切入点表达式tx:advice
:事务通知配置tx:advice
:
id
属性:唯一标识
transaction-manager
属性:配置一个事务管理器,即PlatformTransactionManager
的实现类对象
类似于我们的自己编写的事务管理器,里边提供了事务管理的方法,例如:提交、回滚事务的方法等等
tx:attributes
:在标签内部设置事务的属性信息(事务定义信息,TransactionDefinition
)
tx:method
:要进行事务控制的方法配置,表示 要对哪些方法,进行什么样的事务控制
name
属性:要进行事务控制方法名称,可以使用通配符*
isolation
属性:事务的隔离级别设置propagation
属性:事务传播特性read-only
属性:是否只读timeout
属性:超时时间。默认-1表示不限制,如果设置的话,单位是秒<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
<tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="edit*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
<tx:method name="query*" isolation="DEFAULT" propagation="REQUIRED" read-only="true"/>
<tx:method name="find*" isolation="DEFAULT" propagation="REQUIRED" read-only="true"/>
tx:attributes>
tx:advice>
<bean id="txManager" class="DataSourceTransactionManager全限定类名">
<property name="dataSource" ref="连接池"/>
bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="切入点表达式"/>
aop:config>
@Transactional
@Transactional //类里面的所有方法都有事务
@Service
public class AccountServiceImpl implements AccountService {
}
applicationContext.xml
中修改配置
<tx:annotation-driven transaction-manager="tm"/>
AccountServiceImpl
package com.mingye.service.impl;
import com.mingye.dao.AccountDao;
import com.mingye.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/*
注解事务的配置:
1. 在类上或者方法上打注解 @Transactional
1.1 在类身上打,即表示该类中的所有方法都会应用上事务
1.2 在方法身上打,即表示只有这个方法会应用上事务。
2. 在xml里面打开注解的开关
*/
//@Transactional //1. 类上打注解
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao dao;
/**
* 转账
* @param from
* @param to
* @param money
*/
@Transactional(isolation = Isolation.DEFAULT , propagation = Propagation.REQUIRED , readOnly = false , timeout = -1)
// @Transactional
public void transfer(String from, String to, int money) {
//扣钱
dao.kouqian(from ,money);
//int a = 1 / 0 ;
//加钱
dao.jiaqian(to , money);
}
}
applicationContext.xml
<context:component-scan base-package="com.mingye"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg name="dataSource" ref="dataSource"/>
bean>
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${driverClass}"/>
<property name="jdbcUrl" value="${jdbcUrl}"/>
<property name="user" value="${user}"/>
<property name="password" value="${password}"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven />
beans>
@Transactional
tx:advice
和事务切面的aop:config
isolation
属性:设置事务的隔离级别,从枚举Isolation
中取值propagation
属性:设置事务的传播特性,从枚举Propagation
中取值readOnly
属性:设置是否是只读的timeout
属性:设置超时时间,单位秒。-1表示不限制XML方式
applicationContext.xml
中开启 事务的注解驱动,否则无效
<tx:annotation-driven transaction-manager="txManager"/>
<tx:annotation-driver/>
纯注解方式
如果是纯注解,开启事务的注解驱动,需要在核心配置类上增加注解:@EnableTransactionManagement
配置示例
@Configuration
@ComponentScan("com.mingye")
@PropertySource("db.properties")
@EnableTransactionManagement
public class AppConfig {
@Value("${driverClass}")
private String driverClass;
@Value("${jdbcUrl}")
private String jdbcUrl;
@Value("${user}")
private String user;
@Value("${password}")
private String password;
//创建jdbctemplate
@Bean
public JdbcTemplate template(DataSource dataSource){
return new JdbcTemplate(dataSource);
}
@Bean
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driverClass);
ds.setJdbcUrl(jdbcUrl);
ds.setUser(user);
ds.setPassword(password);
return ds;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager tm = new DataSourceTransactionManager();
tm.setDataSource(dataSource);
return tm;
}
}
package com.mingye.test;
import com.mingye.config.AppConfig;
import com.mingye.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestAccountServiceImpl02 {
@Autowired
private AccountService as;
@Test
public void testTransfer(){
as.transfer("zs", "ls" , 100);
}
}
<bean id="txManager" class="DataSourceTransactionManager全限定类名">
<property name="dataSource" ref="连接池"/>
bean>
<tx:annotation-driven transaction-manager="txManager"/>
<context:component-scan base-package="com.mingye"/>
@Transactional