@Transaction注解哪些情况不生效?

编程

看一个最简单的CGLIB的例子,感受一下AOP是如何做到的?

/**

* Created with vernon-test

* Description:

* User: chenyuan

* Date: 16/4/25

* Time: 上午9:25

*/

public class Target {

public String execute() {

String message = "----------test()----------";

System.out.println(message);

return message;

}

}

/**

* Created with vernon-test

* Description:

* User: chenyuan

* Date: 16/4/25

* Time: 上午9:25

*/

public class MyMethodInterceptor implements MethodInterceptor {

@Override

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

System.out.println(">>>MethodInterceptor start...");

Object result = proxy.invokeSuper(obj, args);

System.out.println(">>>MethodInterceptor ending...");

return "haha";

}

}

/**

* Created with vernon-test

* Description:

* User: chenyuan

* Date: 16/4/25

* Time: 上午9:28

*/

public class CglibProxyTest {

public Object createProxy(Class targetClass) {

// 第一步

Enhancer enhancer = new Enhancer();

// 第二步

enhancer.setSuperclass(targetClass);

// 第三步

enhancer.setCallback(new MyMethodInterceptor());

// 第四步

return enhancer.create();

}

public static void main(String rags[]) {

CglibProxyTest cglibProxyTest = new CglibProxyTest();

Target proxyTarget = (Target) cglibProxyTest.createProxy(Target.class);

String res = proxyTarget.execute();

System.out.println(res);

}

}

执行后的结果显示

Connected to the target VM, address: "127.0.0.1:55868", transport: "socket"

>>>MethodInterceptor start...

----------test()----------

>>>MethodInterceptor ending...

haha

Disconnected from the target VM, address: "127.0.0.1:55868", transport: "socket"

实际上在执行execute()的前后就各自做了自己想要的操作。其实这个就是Spring AOP对简单的一个原型。

@Transaction的工作原理

SpringTransactionInterceptorPlatformTransactionManager这两个类是整个事务模块的核心,TransactionInterceptor负责拦截方法执行,进行判断是否需要提交或者回滚事务。PlatformTransactionManager是Spring 中的事务管理接口,真正定义了事务如何回滚和提交。我们重点研究下这两个类的源码。

TransactionInterceptor类中的代码有很多,我简化一下逻辑,方便说明:

//以下代码省略部分内容

public Object invoke(MethodInvocation invocation) throws Throwable {

//获取事务调用的目标方法

Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

//执行带事务调用

return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);

}

其实,这里也就是因为用到了动态代理。在事务回滚这个动作的前前后后可以做自己想要的东西。这是一个非常重要的设计思想。如果我们自己要写框架,这个模式可以作为你的第一参考。

基于注解的实现机制

  • 调用注解方法
  • 生成代理对象 - CglibAopProxy 调用内部类的方法DynamicAdvisedInterceptor.intercept()
  • TransactionInterceptor.invoke()拦截器拦截,在目标方法执行之前创建并加入事务
  • AbstractPlatformTransactionManager抽象事务管理器操作数据源DataSource提交或回滚事务

我们看了解一下它是如何仿照最上面的code来写的?

仿照上面Demo的第一步:

public Object getProxy(@Nullable ClassLoader classLoader) {

if (logger.isTraceEnabled()) {

logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());

}

try {

Class<?> rootClass = this.advised.getTargetClass();

Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

Class<?> proxySuperClass = rootClass;

if (ClassUtils.isCglibProxyClass(rootClass)) {

proxySuperClass = rootClass.getSuperclass();

Class<?>[] additionalInterfaces = rootClass.getInterfaces();

for (Class<?> additionalInterface : additionalInterfaces) {

this.advised.addInterface(additionalInterface);

}

}

// Validate the class, writing log messages as necessary.

validateClassIfNecessary(proxySuperClass, classLoader);

// 第一点:Configure CGLIB Enhancer...

Enhancer enhancer = createEnhancer();

if (classLoader != null) {

enhancer.setClassLoader(classLoader);

if (classLoader instanceof SmartClassLoader &&

((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {

enhancer.setUseCache(false);

}

}

// 第二点:setSuperclass

enhancer.setSuperclass(proxySuperClass);

enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));

enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);

enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));

// 第三点:获取Callback

Callback[] callbacks = getCallbacks(rootClass);

Class<?>[] types = new Class<?>[callbacks.length];

for (int x = 0; x < types.length; x++) {

types[x] = callbacks[x].getClass();

}

// fixedInterceptorMap only populated at this point, after getCallbacks call above

enhancer.setCallbackFilter(new ProxyCallbackFilter(

this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));

enhancer.setCallbackTypes(types);

// Generate the proxy class and create a proxy instance.

return createProxyClassAndInstance(enhancer, callbacks);

}

catch (CodeGenerationException | IllegalArgumentException ex) {

throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +

": Common causes of this problem include using a final class or a non-visible class",

ex);

}

catch (Throwable ex) {

// TargetSource.getTarget() failed

throw new AopConfigException("Unexpected AOP exception", ex);

}

}

仿照上面Demo的第三步:

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {

// Parameters used for optimization choices...

boolean exposeProxy = this.advised.isExposeProxy();

boolean isFrozen = this.advised.isFrozen();

boolean isStatic = this.advised.getTargetSource().isStatic();

// Choose an "aop" interceptor (used for AOP calls).

Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

// Choose a "straight to target" interceptor. (used for calls that are

// unadvised but can return this). May be required to expose the proxy.

Callback targetInterceptor;

if (exposeProxy) {

targetInterceptor = (isStatic ?

new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :

new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));

}

else {

targetInterceptor = (isStatic ?

new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :

new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));

}

// Choose a "direct to target" dispatcher (used for

// unadvised calls to static targets that cannot return this).

Callback targetDispatcher = (isStatic ?

new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());

Callback[] mainCallbacks = new Callback[] {

aopInterceptor, // for normal advice

targetInterceptor, // invoke target without considering advice, if optimized

new SerializableNoOp(), // no override for methods mapped to this

targetDispatcher, this.advisedDispatcher,

new EqualsInterceptor(this.advised),

new HashCodeInterceptor(this.advised)

};

Callback[] callbacks;

// If the target is a static one and the advice chain is frozen,

// then we can make some optimizations by sending the AOP calls

// direct to the target using the fixed chain for that method.

if (isStatic && isFrozen) {

Method[] methods = rootClass.getMethods();

Callback[] fixedCallbacks = new Callback[methods.length];

this.fixedInterceptorMap = new HashMap<>(methods.length);

// TODO: small memory optimization here (can skip creation for methods with no advice)

for (int x = 0; x < methods.length; x++) {

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);

fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(

chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());

this.fixedInterceptorMap.put(methods[x].toString(), x);

}

// Now copy both the callbacks from mainCallbacks

// and fixedCallbacks into the callbacks array.

callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];

System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);

System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);

this.fixedInterceptorOffset = mainCallbacks.length;

}

else {

callbacks = mainCallbacks;

}

return callbacks;

}

仿照上面Demo的第四步:

protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {

enhancer.setInterceptDuringConstruction(false);

enhancer.setCallbacks(callbacks);

return (this.constructorArgs != null && this.constructorArgTypes != null ?

enhancer.create(this.constructorArgTypes, this.constructorArgs) :

enhancer.create());

}

上面的几步就完成了一个动态代理的流程,就只需要真的发生调用的时候去执行动态代理类了。

哪些场景事物会失效?

  • 1、只对public修饰方法才起作用
  • 2、@Transaction默认检测异常为RuntimeException及其子类 如果有其他异常需要回滚事务的需要自己手动配置,例如:@Transactional(rollbackFor = Exception.class)
  • 3、确保异常没有被try-catch{}catch以后也不会回滚
  • 4、检查下自己的数据库是否支持事务,如mysqlmylsam
  • 5、SpringBoot项目默认已经支持事务,不用配置;其他类型项目需要在xml中配置是否开启事务
  • 6、如果在同一个类中,一个非@Transaction的方法调用有@Transaction的方法不会生效,因为代理问题

这里说下在同一个类中,一个非@Transaction的方法调用有@Transaction的方法不会生效。如果是在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用,必须将方法放入另外一个类中,并且该类通过Spring注入。

Spring 采用动态代理(AOP)实现对Bean的管理和切片,它为我们的每个class生成一个代理对象,只有在代理对象之间进行调用时,可以触发切面逻辑。

而在同一个类中,方法B调用A,调用的事元对象的方法,而不是通过代理对象,所以Spring无法切到这次调用,也就是无法通过注解保证事务性。

参考地址

  • https://blog.csdn.net/joker8023joker/article/details/103277571
  • https://blog.csdn.net/Dragon_1999/article/details/93059495

如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员简栈文化-小助手(lastpass4u),他会拉你们进群。

以上是 @Transaction注解哪些情况不生效? 的全部内容, 来源链接: utcz.com/z/517895.html

回到顶部