手寫Spring註解事務(利用AOP技術 + 註解 + Spring程式設計式事務)
阿新 • • 發佈:2018-12-12
1.參考下面的文章搭建一個無事務管理的SSM操作資料庫的框架
2.AOP技術參考
3.第一步首先實現Spring程式設計式事務
1) 建立事務管理類工具,即手動開啟事務,手動提交事務,手動回滾事務的方法
package com.roger.core.utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Component; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; @Component @Scope("prototype")//設定成原型狀態,避免執行緒安全問題 public class TransactionUtil { private TransactionStatus transactionStatus = null; @Autowired private DataSourceTransactionManager transactionManager; public void begin(){ System.out.println("方法上有事務註解,使用手動的方式開啟事務..."); transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition()); } public void commit(){ if(transactionStatus != null){ System.out.println("提交事務..."); transactionManager.commit(transactionStatus); } } public void rollback(){ if(transactionStatus != null) { System.out.println("回滾事務..."); transactionManager.rollback(transactionStatus); } } }
2) 建立程式設計式事務管理的切面類
package com.roger.core.aspect; import com.roger.core.utils.TransactionUtil; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; //一定要把切面交給Spring管理 //在測試宣告式事務的時候,需要從Spring的容器中收回程式設計式事務的切面 //@Component @Aspect public class ProgramTransactionAspect { @Autowired(required = false) private TransactionUtil transactionUtil; @Pointcut("execution(* com.roger.biz.service.impl..*.*(..))") public void addTransaction(){ } //異常通知:給新增事務的方法回滾事務,當方法丟擲異常時 @AfterThrowing("addTransaction()") public void rollbackTransaction(){ //獲取當前事務,然後回滾 transactionUtil.rollback(); } //環繞通知:給需要新增事務的方法,手動開啟事務和提交事務 @Around("addTransaction()") public void around(ProceedingJoinPoint joinPoint) throws Throwable{ transactionUtil.begin(); joinPoint.proceed(); transactionUtil.commit(); } }
3).建立測試介面以及測試介面實現類
package com.roger.biz.service;
import com.roger.core.model.User;
public interface UserService {
void save(User user);
}
package com.roger.biz.service.impl; import com.roger.biz.dao.UserDao; import com.roger.biz.service.UserService; import com.roger.core.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { @Autowired(required = false) private UserDao userDao; @Override public void save(User user) { userDao.insert(user); //利用最簡單的構造異常來驗證事務的回滾操作 //通過回滾操作來和事務提交作對比 // int i = 1 / 0 ; } }
4.建立junit4測試類 --測試基類
package com.roger;
import com.roger.core.config.DataSourceConfig;
import com.roger.core.config.SpringConfig;
import com.roger.core.config.mybatis.SqlSessionConfig;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DataSourceConfig.class, SqlSessionConfig.class,SpringConfig.class})
public class SpringBaseTestSuit {
}
5.具體測試介面的junit4測試類
package com.roger.biz.service.impl;
import com.roger.SpringBaseTestSuit;
import com.roger.biz.service.UserService;
import com.roger.core.model.User;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.Assert.*;
public class UserServiceImplTest extends SpringBaseTestSuit {
@Autowired(required = false)
private UserService userService;
@Test
public void save() {
User user = new User();
user.setUserName("Jackson");
user.setAge(38);
user.setPhone("15498756489");
userService.save(user);
}
}
6.通過觀察資料庫操作表的記錄來觀察結果,這裡沒有寫太多的測試方法,來直接用Junit驗證測試結果
4.自定義事務註解-非常簡單的,只使用於方法級別的
package com.roger.core.annotaion;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CustomTransactional {
String value() default "";
}
5.改造程式設計式事務的切面類-使其適應註解方式
package com.roger.core.aspect;
import com.roger.core.annotaion.CustomTransactional;
import com.roger.core.utils.TransactionUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
////在測試程式設計式事務的時候,需要從Spring的容器中收回宣告式事務的切面
@Component//一定要把切面交給Spring管理
@Aspect
public class DeclareTransactionAspect {
@Autowired(required = false)
private TransactionUtil transactionUtil;
@Pointcut("execution(* com.roger.biz.service.impl..*.*(..))")
public void addTransaction() {
}
//異常通知:給新增事務的方法回滾事務,當方法丟擲異常時
@AfterThrowing("addTransaction()")
public void rollbackTransaction() {
transactionUtil.rollback();
}
//環繞通知:給需要新增事務的方法,手動開啟事務和提交事務
@Around("addTransaction()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
Class<?> targetCls = joinPoint.getTarget().getClass();
//獲取即將要執行方法
Method method = getInvokedMethod(targetCls, joinPoint);
if (method == null) {
joinPoint.proceed();
return;
}
//判斷執行方法是否有事務註解
Annotation customTransAnno = method.getAnnotation(CustomTransactional.class);
if (customTransAnno == null) {
System.out.println("方法上沒有事務註解,直接開始執行方法...");
joinPoint.proceed();
return;
}
transactionUtil.begin();
joinPoint.proceed();
transactionUtil.commit();
}
private Method getInvokedMethod(Class targetCls, ProceedingJoinPoint pJoinPoint) {
List<Class<? extends Object>> clazzList = new ArrayList<>();
Object[] args = pJoinPoint.getArgs();
for (Object arg : args) {
clazzList.add(arg.getClass());
}
Class[] argsCls = (Class[]) clazzList.toArray(new Class[0]);
String methodName = pJoinPoint.getSignature().getName();
Method method = null;
try {
method = targetCls.getMethod(methodName, argsCls);
} catch (NoSuchMethodException e) {
//不做任何處理,這個切面只處理事務相關邏輯
//其他任何異常不在這個切面的考慮範圍
}
return method;
}
}