changmingxie / tcc-transaction

tcc-transaction是TCC型事务java实现
Apache License 2.0
5.78k stars 2.79k forks source link

框架调用从业务补偿方法时spring注入类丢失问题 #412

Open BianChangcai opened 1 year ago

BianChangcai commented 1 year ago

public final class Terminator { private Terminator() { }

public static Object invoke(TransactionContext transactionContext, Invocation invocation, Class<? extends TransactionContextEditor> transactionContextEditorClass) {
    if (StringUtils.isNotEmpty(invocation.getMethodName())) {
        Object target = FactoryBuilder.factoryOf(invocation.getTargetClass()).getInstance();
        Method method = null;

        try {
            method = target.getClass().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
        } catch (NoSuchMethodException var12) {
            throw new SystemException(var12);
        }

        ((TransactionContextEditor)FactoryBuilder.factoryOf(transactionContextEditorClass).getInstance()).set(transactionContext, target, method, invocation.getArgs());

        Object var5;
        try {
            var5 = method.invoke(target, invocation.getArgs());
        } catch (InvocationTargetException | IllegalAccessException var10) {
            throw new SystemException(var10);
        } finally {
            ((TransactionContextEditor)FactoryBuilder.factoryOf(transactionContextEditorClass).getInstance()).clear(transactionContext, target, method, invocation.getArgs());
        }

        return var5;
    } else {
        return null;
    }
}

}

Springboot 版本:2.3.5.RELEASE TCC 版本:2.0.1 框架通过 Terminator 调用以下反射方法执行补偿逻辑时,因为 target 类中spring注入的Bean都是null,所以执行业务方法时报空指针异常,这个问题如何解决? var5 = method.invoke(target, invocation.getArgs());

再往上看,应该是框架创建代理类时注入依赖没有问题,框架的问题望高手指点? Object target = FactoryBuilder.factoryOf(invocation.getTargetClass()).getInstance();

BianChangcai commented 1 year ago

我从业务通过Feign调用的,从业务获取 TransactionContext 上下文都没问题的,就是Bean注入类都变成null了

BianChangcai commented 1 year ago

问题指正:应该是框架创建代理类时注入依赖有问题,不是没问题 再往上看,应该是框架创建代理类时注入依赖没有问题,框架的问题望高手指点? Object target = FactoryBuilder.factoryOf(invocation.getTargetClass()).getInstance();

BianChangcai commented 1 year ago

临时解决方法: 针对框架获取 Spring Bean 时,对 Bean 本身的依赖没有注入进去问题,在框架补偿方法中,自己编写一个从 Spring 容器中获取Bean 的方法来对依赖进行赋值。 具体方法: 1、先定义获取 Spirng Bean 的工具类 2、在补偿方法中通过工具类获取依赖 Bean,然后对被依赖类行依赖注入

1、工具类: @Component public class BeanUtils implements ApplicationContextAware { protected static ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
    if (applicationContext == null) {
        applicationContext = arg0;
    }

}

public static Object getBean(String name) {
    // name表示其他要注入的注解name名
    return applicationContext.getBean(name);
}

/**
 * 拿到ApplicationContext对象实例后就可以手动获取Bean的注入实例对象
 */
public static <T> T getBean(Class<T> clazz) {
    return applicationContext.getBean(clazz);
}

}

2、在补偿方法中使用方式: public void cancelCreditToPointAccountTcc(String param1, String param2) {

pointAccountService = BeanUtils.getBean(RpPointAccountService.class);

    pointAccountService.cancelCreditToPointAccountTcc(param1, param2);
}

pointAccountService 原先是通过 @Autowired 注解来注入的,引入框架后在补偿方法中失效了,为 null。

BianChangcai commented 1 year ago

tcc-transaction github 仓:"created_at": "2015-12-05T04:27:53Z", 2016年当时国内最好的 TCC 框架。

Seata github 仓:"created_at": "2018-12-28T08:37:22Z", 之后 Seata 等后来者追上。

BianChangcai commented 1 year ago

tcc-transaction github 仓:"created_at": "2015-12-05T04:27:53Z", 2016年当时国内最好的 TCC 框架。

Seata github 仓:"created_at": "2018-12-28T08:37:22Z", 之后 Seata 等后来者追上。

nervose commented 1 year ago

问题指正:应该是框架创建代理类时注入依赖有问题,不是没问题 再往上看,应该是框架创建代理类时注入依赖没有问题,框架的问题望高手指点? Object target = FactoryBuilder.factoryOf(invocation.getTargetClass()).getInstance();

TCC补偿时target对象的获取是通过类名,从spring容器中拿的。所以需要把使用了@Compensable注解的方法所在的类注册到spring容器中。如果是基于feign来传递上下文的话,可以参考tcc-transaction-http-sample中的实现。如果还有问题,建议提供下复现方式。