Spring事务增强
来源:互联网 发布:淘宝认证照片拍摄技巧 编辑:程序博客网 时间:2024/06/15 02:03
一:问题描述
1.这是具体的事务配置
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
<tx:advice id="transactionAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/> <tx:method name="*" propagation="SUPPORTED" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="transactionPointcut" expression="execution(* com.ph3636.*.service.*.*(..))"/> <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice"/> </aop:config>
2.示例代码
包路径com.ph3636.test.service.Test1Service
@Servicepublic class Test1Service{
@Autowired private Test2Service test2Service;
public void test(){
test2Service.add();①add();
}
public void add(){
test2Service.add();②
int i = 1 / 0;//这里可以替换为一个更隐秘的获取数据库连接失败错误
test3Service.add();③
}}3.执行test方法后会成功执行①②,令人匪夷所思的是②为什么没有跟随这个错误一块回滚,而是自己单独提交成功?二:事务解析
1.xml文件里面的那些方法正则表达式(add*)配置会随着Spring的IOC容器启动并且在Bean进行初始化的时候放出对应的类NameMatchTransactionAttributeSource的属性Map<String, TransactionAttribute> nameMap中,每个配置的方法的名字,隔离级别,事务传播属性都在TransactionAttribute中。
2.事务的aop处理会读取切点配置的类路径expression="execution(* com.ph3636.*.service.*.*(..))",以及需要的通知transactionAdvice也就是上面的方法处理,然后把这些处理组成一个事务拦截器TransactionInterceptor,最后形成一个代理。
3.因为这些Service类没有具体的实现接口,所以spring默认会用CglibAopProxy生成对应的代理类,然后等到执行目标方法时会经过回调Callback进而调用intercept方法开始执行具体的操作
4.创建代理的入口为bean初始化时遇到FactoryBean这个类型的类时会调用他ProxyFactoryBean的getObject方法
private synchronized Object getSingletonInstance() {if (this.singletonInstance == null) {this.targetSource = freshTargetSource();if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {// Rely on AOP infrastructure to tell us what interfaces to proxy.Class<?> targetClass = getTargetClass();if (targetClass == null) {throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");}setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));}// Initialize the shared singleton instance.super.setFrozen(this.freezeProxy);this.singletonInstance = getProxy(createAopProxy());}return this.singletonInstance;}然后用默认的DefaultAopProxyFactory代理工厂创建代理类public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface()) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}这里会判断适用那种方法创建代理类,Cglib还是JDK自带的,最后返回AopProxy。5.根据这个aop代理CglibAopProxy创建具体的代理对象getProxy(createAopProxy());public Object getProxy(ClassLoader classLoader) {....Callback[] callbacks = getCallbacks(rootClass);...return createProxyClassAndInstance(enhancer, callbacks);}private Callback[] getCallbacks(Class<?> rootClass) throws Exception {// Choose an "aop" interceptor (used for AOP calls).Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);主要是这个.....Callback[] mainCallbacks = new Callback[]{aopInterceptor, // for normal advicetargetInterceptor, // invoke target without considering advice, if optimizednew SerializableNoOp(), // no override for methods mapped to thistargetDispatcher, this.advisedDispatcher,new EqualsInterceptor(this.advised),new HashCodeInterceptor(this.advised)}; ......Callback[] callbacks; ...callbacks = mainCallbacks;}return callbacks;}这个就是具体的service类的代理类了。6.开始最初的方法调用,会进入DynamicAdvisedInterceptor的intercept方法
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Class<?> targetClass = null;Object target = null;try {if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// May be null Get as late as possible to minimize the time we// "own" the target, in case it comes from a pool.target = getTarget();if (target != null) {targetClass = target.getClass();}List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;// Check whether we only have one InvokerInterceptor: that is,// no real advice, but just reflective invocation of the target.if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {// We can skip creating a MethodInvocation: just invoke the target directly.// Note that the final invoker must be an InvokerInterceptor, so we know// it does nothing but a reflective operation on the target, and no hot// swapping or fancy proxying.retVal = methodProxy.invoke(target, args);}else {// We need to create a method invocation...retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null) {releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}this.advised.exposeProxy这个属性是本文的重点,是否暴露代理,默认是false。这个方法会获取具体的操作对象,以及getInterceptorsAndDynamicInterceptionAdvice家在这个切点的所有拦截器,最后组装成一个CglibMethodInvocation开始进行处理,retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();然后释放资源,恢复以前的的面貌,返回结果。7.开始拦截器的调用proceed,先判断是否执行完全部的拦截器,是的话就执行目标方法
public Object proceed() throws Throwable {//We start with an index of -1 and increment early.if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {............................}else {// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}8.这里主要介绍TransactionInterceptor@Overridepublic Object invoke(final MethodInvocation invocation) throws Throwable {Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {@Overridepublic Object proceedWithInvocation() throws Throwable {return invocation.proceed();}});}protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)throws Throwable {// If the transaction attribute is null, the method is non-transactional.final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);final PlatformTransactionManager tm = determineTransactionManager(txAttr);final String joinpointIdentification = methodIdentification(method, targetClass);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exceptioncompleteTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}commitTransactionAfterReturning(txInfo);return retVal;}.......}getTransactionAttributeSource().getTransactionAttribute(method, targetClass)从这里取出方法对应的事务配置信息for (String mappedName : this.nameMap.keySet()) {if (isMatch(methodName, mappedName) &&(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {attr = this.nameMap.get(mappedName);bestNameMatch = mappedName;}}这里根据最少匹配规则选出xml对应的配置信息。determineTransactionManager(txAttr)选择具体的事务管理器,也就是解析你使用的是Jta还是DataSource。9.创建事务信息createTransactionIfNecessary,完成之后就会把当前事务信息TransactionInfo绑定到这个线程里(ThreadLocal)以供下次可能使用,protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {TransactionStatus status = null;if (txAttr != null) {if (tm != null) {status = tm.getTransaction(txAttr);}}return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);}获取事务状态信息public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {Object transaction = doGetTransaction();if (definition == null) {// Use defaults if no transaction definition given.definition = new DefaultTransactionDefinition();}if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.return handleExistingTransaction(definition, transaction, debugEnabled);}else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {SuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}catch (RuntimeException ex) {resume(null, suspendedResources);throw ex;}catch (Error err) {resume(null, suspendedResources);throw err;}}else {// Create "empty" transaction: no actual transaction, but potentially synchronization.boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}}首先从doGetTransaction线程私有的ThreadLocal获取已经存在的事务对象DataSourceTransactionObject,然后判断有没有具体配置的方法事务配置DefaultTransactionDefinition,没有的话就是用默认的,然后判断isExistingTransaction这个事务对象中是否存在已有的事务链接信息,10.下来就是事务传播级别起作用的时候了,
int PROPAGATION_REQUIRED = 0;支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。int PROPAGATION_SUPPORTS = 1;支持当前事务,如果当前没有事务,就以非事务方式执行。int PROPAGATION_MANDATORY = 2;支持当前事务,如果当前没有事务,就抛出异常。int PROPAGATION_REQUIRES_NEW = 3;新建事务,如果当前存在事务,把当前事务挂起。外层事务失败回滚的话里面新建的那个事务不会回滚int PROPAGATION_NOT_SUPPORTED = 4;以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。int PROPAGATION_NEVER = 5;以非事务方式执行,如果当前存在事务,则抛出异常。int PROPAGATION_NESTED = 6;支持当前事务,如果当前事务存在,则执行一个嵌套事务(外层事务失败回滚的话里面新建的那个事务也会回滚,因为他设置了保存点Savepoint),如果当前没有事务,就新建一个事务。根据这7个级别分别挂起使用事务。然后retVal = invocation.proceedWithInvocation();进行下一个拦截器的调用。DefaultTransactionStatus事
if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}
务状态有个特别重要的属性private final boolean newTransaction;它关系到是否提交现在这个事务,当方法具体执行完毕之后就会开始回滚或者提交事务信息,提交事务发生在AbstractPlatformTransactionManager的processCommit,这个值的具体设置在每个传播级别里面,所以就会影响到具体事务的提交。else if (status.isNewTransaction()) {doCommit(status);}三:原因解析因为上面这些事务拦截器处理是针对一个具体代理的,但是最初的案例从别的地方开始执行Test1Service.test()时,他其实执行的是Test1Service的代理对象,所以就会执行上面的整个流程,但是刚开始的时候没有事务包裹,因为是PROPAGATION_SUPPORTS ,①执行的时候会自己建立一个新事务,完事之后就会提交,后面发生任何异常都不会影响它。等到执行到this.add()时也就会把他当做一个内部的普通方法调用,而不会把他当成一个具体的对象需要代理,但是如果执行到test2Service.add();②时候,就会执行test2Service的代理也就会创建一个完整的新事务,因为他已经显式表明了我就是test2Service代理对象,最后事务也就会提交成功,到达这个时候int i = 1 / 0;虽然会抛出一个错误,但是他的外层方法并没有包裹事务,而前面的②自己有一个单独的事务,所以他失败也不会影响到②。
四:解决办法
这种代码本身就是错误的,但是不排除有这种特殊的需求,如果是这样的话,spring事务处理设置了一个开关进行代理暴露 <aop:config expose-proxy="true"/>这个配置对应到
if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}这里就会把正在执行的代理设置到与线程相关的ThreadLocal中,然后在代码里获取线程绑定的这个代理进行显式调用即可,((TestService) AopContext.currentProxy()).add();
这个时候当代吗执行到这里时,就不会把他当做内部的普通方法调用,而是把它作为一个单独完整的代理进行处理,这个时候他们也就会被包裹到一个事务,荣辱与共了。
- Spring事务增强
- spring事务增强,事务回滚如何判断?希望在前端上有个提示。
- Spring增强
- Spring-------- 增强
- 增强事务复制性能
- 增强事务复制性能
- spring学习笔记(23)基于tx/aop配置切面增强事务
- Intellij IDEA中Spring配置aop增强事务 出现BeanCreationException异常处理方法
- 事务:spring事务支持
- 事务:spring事务支持
- spring 事务,事务特性
- Spring事务
- Spring 事务
- Spring事务
- spring事务
- Spring事务
- spring 事务
- spring 事务
- JVM调优--计算对象占用的空间
- C#学习笔记:在控件上绘制文字
- Oracle数据库常用的sql'语句
- Kubernetes初探:原理及实践应用
- git 常用命令流程
- Spring事务增强
- 默认的markdown编辑器
- HTML5的特性与发展趋势
- JAVA实现HTTPS接口(POST方式)
- 【备忘】最新python数据分析升级版视频教程
- 音频处理工具 GoldWave / Cool Edit Pro
- peerjs填坑
- ThreadLocal线程本地变量的超详细解析
- 数据特征分析---Python数据挖掘与分析