@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的工作原理
在Spring
中TransactionInterceptor
和PlatformTransactionManager
这两个类是整个事务模块的核心,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、检查下自己的数据库是否支持事务,如
mysql
的mylsam
- 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