aop即Aspect-Oriented Programming ,面向切面编程。
可以使用xml或者注解的方式在项目中使用aop,以注解为例,一般使用可以引用 AspectJ,自己创建一个类,在类上标注好注解 @Aspect
@Aspect
public class LogAspect {}
复制代码
在xml中开启扫描即可找到这个注解
<aop:aspectj-autoproxy />
复制代码
在代码中建立好对应的Point Cut
@Pointcut("execution(* paxi.maokitty.verify.spring.aop.service.ExecuteService.*(..))")
public void allClassPointCut(){}
复制代码
这里PointCut表达式指定类paxi.maokitty.verify.spring.aop.service.ExecuteService所有方法都是目标对象
建立自己需要执行的方法(advice
)
@Before("allClassPointCut()")
public void beforeAspectExecuteService(JoinPoint joinPoint){
LOG.info("beforeAspectExecuteService execute method:{}",new Object[]{joinPoint.getStaticPart().toShortString()});
}
复制代码
即可达到对应的目标,而且这种方式做到了对原有代码的无入侵,体验很好。完整的可运行实例请戳这里
此处不讨论分布式事务
事务是数据库执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。当事务被提交给了数据库,数据库需要确保该事务中的所有操作都成功完成并且结果被永远保存在数据库中。如果事务中有的操作没有成功的完成,则事务中的所有操作都需要回滚,回到事务执行前的状态,同时,该事务对数据库的其他事务执行没有影响。数据库事务一般拥有以下四个特性
java中操作数据库操作的关键类是 Connection
,它代表了对数据库的一个连接,通过对应的方法
connection.commit()
:执行事务的提交语义con.rollback();
:执行事务的回滚语义 可以控制事务操作spring中最简单的实现只需要直接在要使用事务的类上添加注解 @Transactional
,并在xml中添加注解的扫描<tx:annotation-driven transaction-manager="txManagerTest"/>
基本就可以利用spring的事务了
spring对事务的实现则是通过aop来实现的。spring在扫描tx标签的时候,碰到transactional标注的类或者方法,会创建对应的AOP代理,在调用的时候则是AOP代理去执行,先按照AOP的方式执行相应的逻辑,再执行用户定义的方法,如果有问题则执行对应的事务
@Trace(
index = 13,
originClassName = "org.springframework.transaction.interceptor.TransactionAspectSupport",
function = "protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation) throws Throwable"
)
public void invokeWithinTransaction(){
//...
Code.SLICE.source("final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);")
.interpretation("查到对应方法的事务配置");
Code.SLICE.source("final PlatformTransactionManager tm = determineTransactionManager(txAttr);")
.interpretation("拿到transactionManager,比如用户在xml中配置的 org.springframework.jdbc.datasource.DataSourceTransactionManager");
Code.SLICE.source("final String joinpointIdentification = methodIdentification(method, targetClass);")
.interpretation("获取transaction标注的方法");
//...
Code.SLICE.source("TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);\n" +
" Object retVal = null;\n" +
" try {\n" +
" retVal = invocation.proceedWithInvocation();\n" +
" }\n" +
" catch (Throwable ex) {\n" +
" // target invocation exception\n" +
" completeTransactionAfterThrowing(txInfo, ex);\n" +
" throw ex;\n" +
" }\n" +
" finally {\n" +
" cleanupTransactionInfo(txInfo);\n" +
" }\n" +
" commitTransactionAfterReturning(txInfo);\n" +
" return retVal;")
.interpretation("这里就是标准的事务处理流程 1:获取事务;2:执行用户自己的方法;3:如果执行过程中抛出了异常执行异常抛出后的事务处理逻辑 4:清除事务信息 5:提交事务");
//...
}
复制代码
这里就注意到 所谓 物理事务 和 逻辑事务的区别
事务的隔离机制与传播机制源码注解解释各自含义,实际就是Connection的定义
对不用的隔离机制,也就产生了 脏读、不可重复读、幻读的场景
Y表示会存在,N表示不存在
实质上就是在不同隔离机制下,多个事务读取数据的影响
spring自定义的传播机制,实际上就是代码的处理逻辑,在不同的场景下做出的限制
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
复制代码
它底层去提交事务或是回滚事务,本质上还是java的Connection来最终执行操作,另外对于对于一次访问的多个数据库的事务操作,spring自己将连接与线程建立了关联关系,即每个线程都持有的是同一个连接,来保证期望相同的数据库操作在同一个事务里面。源码的详细追踪实例可以戳这里