自己实现一个简单的数据库事务
原理:通过Spring提供的BeanPostProcessor来对使用了事务注解的类进行动态代理,通过spring提供的获取数据库连接工具类DataSourceUtils来获取连接。
只有两个类 MyTransactional 和 TransactionBeanPostProcessor,MyTransactional 为自定义的事务方法注解,TransactionBeanPostProcessor生成动态代理。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface MyTransactional { }
@Component public class TransactionBeanPostProcessor implements BeanPostProcessor{ private static final Logger logger = LoggerFactory.getLogger(TransactionBeanPostProcessor.class); @Autowired private DataSource datasource; @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (!getNeedTransactionMethods(bean).isEmpty()) { //jdk原生动态代理只支持实现接口的类做代理. bean = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), bean.getClass().getInterfaces(), new TransactionInvocationHandler(bean)); logger.info("generate transaction proxy for "+bean.getClass()); } return bean; } private static List<Method> getNeedTransactionMethods(Object bean) { Method[] methods = bean.getClass().getMethods(); List<Method> transactionMethods = new ArrayList<>(); for(Method method : methods) { if (method.isAnnotationPresent(MyTransactional.class)) { transactionMethods.add(method); } } return transactionMethods; } /** * 使用动态代理来植入数据库事务逻辑 * Created by 01385234 on 2019年10月23日. */ public class TransactionInvocationHandler implements InvocationHandler{ private Object proxyObject; public TransactionInvocationHandler(Object proxyObject) { super(); this.proxyObject = proxyObject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Method objMethod = proxyObject.getClass().getMethod(method.getName(), method.getParameterTypes()); if (objMethod.isAnnotationPresent(MyTransactional.class)) { TransactionSynchronizationManager.initSynchronization(); //统一用spring提供的工具类DataSourceUtils来获取数据库连接,该connection放入threadlocal中, //mybatis中使用的connection也从该threadlocal中取,从而保证了同一个事务中使用同一个connection。 Connection connection = DataSourceUtils.doGetConnection(datasource); connection.setAutoCommit(false); try { method.invoke(proxyObject, args); connection.commit(); }catch(Exception e) { logger.error("事务方法:{},args:{}出现异常,方法回滚",method,args); connection.rollback(); throw e; } } return method.invoke(proxyObject, args); } } }