1、自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtTransactional {
}
2、配置类
//使用Spring的事务要开启此注解,才可以使用@Transactional注解
@EnableTransactionManagement
//自定义事务必须开启此注解,表示开启AOP
@EnableAspectJAutoProxy
@ComponentScan("tx")
@Configuration
public class Txconfig {
@Bean
public DataSource dataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("a123456");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
@Bean
public JdbcTemplate jdbcTemplate() throws PropertyVetoException {
//Spring对Configuration会做特殊处理,给容器中加组件的方法,多次调用都只是从容器中找组件
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
@Bean
public PlatformTransactionManager transactionManager() throws PropertyVetoException {
return new DataSourceTransactionManager(dataSource());
}
}
3、定义切面类
@Component
@Aspect
public class Aop {
@Autowired
TransactionUtils transactionUtils;
@AfterThrowing("execution(* tx.UserService.*(..))")
public void afterThrowing(){
//将当前事务进行回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
@Around("execution(* tx.UserService.*(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//1、判断当前代理对象的方法时候开启了ExtTransactional注解
ExtTransactional extTransactional = getExtTransactional(proceedingJoinPoint);
TransactionStatus transactionStatus = null;
//2、开启事务
transactionStatus = begin(transactionStatus, extTransactional);
//3、执行目标方法
proceedingJoinPoint.proceed();
//4、提交事务
if (transactionStatus != null) {
transactionUtils.commit(transactionStatus);
}
System.out.println("我是环绕通知-后");
}
/*
* 判断方法是否开启ExtTransactional注解
*/
public ExtTransactional getExtTransactional(ProceedingJoinPoint proceedingJoinPoint) throws NoSuchMethodException {
//获取代理对象的方法名称
String methodName = proceedingJoinPoint.getSignature().getName();
//获取目标对象类型
Class<?> classTarget = proceedingJoinPoint.getTarget().getClass();
//获取参数类型
Class[] parameterTypes = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterTypes();
//获取目标对象方法
Method method = classTarget.getMethod(methodName, parameterTypes);
ExtTransactional declaredAnnotation = method.getDeclaredAnnotation(ExtTransactional.class);
return declaredAnnotation;
}
public TransactionStatus begin(TransactionStatus transactionStatus, ExtTransactional extTransactional) {
if (extTransactional == null) {
return null;
}
return transactionUtils.begin();
}
}
4、事务工具类
@Component
public class TransactionUtils {
// 事物管理器
@Autowired
private PlatformTransactionManager dataSourceTransactionManager;
public TransactionStatus begin() {
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());
return transactionStatus;
}
public void commit(TransactionStatus transaction) {
dataSourceTransactionManager.commit(transaction);
}
public void rollback(TransactionStatus transaction) {
dataSourceTransactionManager.rollback(transaction);
}
}
5、Service层
@Component
public class UserService {
@Autowired
private UserDao userDao;
@ExtTransactional
public void add() {
userDao.insertUser();
int i = 1 / 0;
}
}
6、Dao层
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insertUser(){
String username = UUID.randomUUID().toString().substring(5);
String sql = "INSERT INTO user(username,age)VALUES(?,?)";
jdbcTemplate.update(sql,username,19);
}
}
7 测试类
public class TxTest {
@Test
public void test1() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Txconfig.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.add();
}
}
8 思考与问题
在切面类中,提交与回滚均需要传入TransactionStatus变量,可否将TransactionStatus定义为成员变量作为提交和回滚方法的共享变量,如果可以,是否存在线程安全问题?
因为在Spring的AOP中,是在实例bean创建的时候,将AOP中的切面方法织入到目标方法中的,形成执行链,并生产代理对象,最终由代理对象链式调用通知方法和目标方法实现AOP,那么在AOP中定义成员变量是否存在线程安全问题?
请大神留言解答疑惑。。。。