seata tcc

97 阅读2分钟

运行时注入Seata RM

背景需求

seata tcc 在对service类生成代理的时候会取@TwoPhaseBusinessAction值的会跟seata server 注册 TccResource, 在后面=注册branch的时候通过class.formName() 获取注解的值。 现在要求根绝不同的参数,需要注册不同的resource。 根据在不更改Seate TCC源码的, 现在考虑动态加载class类 从而为每个参数生成TccResource 然后生成beanDefinition, 手动注册到spring 容器中。

需要的类

  1. EnableTxmTcc 用于在springcontext 准备好后注册配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TccApplicationRunner.class)
public @interface EnableTxmTcc {
}
  1. TccApplicationRunner 初始化配置
@RequiredArgsConstructor
@Slf4j
public class TccApplicationRunner implements CommandLineRunner {

    final ApplicationContext ap;

    @Override
    public void run(String... args) throws Exception {

        StopWatch stopWatch = new StopWatch("init tcc resource");
        stopWatch.start();
        RmResourceInit.init(ap);
        stopWatch.stop();
        log.info("TccApplicationRunner init  success cost[{}]ms", stopWatch.getTotalTimeMillis());
    }
}
  1. RmResourceInit 注册RM

@Slf4j
public class RmResourceInit {

    public static final ConcurrentHashMap<String, StockService>  TCC_RESOURCES = new ConcurrentHashMap<>();

    public static void init(ApplicationContext ap) {
        for (int i = 0; i < 5 ; i++) {
            String name = "102" + i;
            try {
                Class<StockServiceImpl> stockServiceClass = TccServiceModify.modifyResource(name, StockServiceImpl.class, Map.of("name", name));
                SpringProxyUtils.beanRegister(ap, name, stockServiceClass);
                TCC_RESOURCES.put(name, SpringProxyUtils.getBean(ap, name));
                log.info("add name[{}] success!",  name);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

    }
}
  1. TccResourceSelecter 根据参数返回不同的resource
@Service
@Slf4j
public class TccResourceSelecter {

    public StockService getStockService(String name){
        return RmResourceInit.TCC_RESOURCES.get(name);

    }
}

  1. SpringProxyUtils 手动注册bean 到spring 容器中
public class SpringProxyUtils {


    public static<T> void beanRegister(ApplicationContext applicationContext, String beanName, Class<T> clazz){
        if (!StringUtils.hasText(beanName)){
            beanName = clazz.getName();
        }
        ConfigurableApplicationContext  configurableAp = (ConfigurableApplicationContext) applicationContext;

        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableAp.getBeanFactory();

        if (defaultListableBeanFactory.containsBeanDefinition(beanName)){
            defaultListableBeanFactory.removeBeanDefinition(beanName);
        }

        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);

        defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());


    }

    public static <T> T getBean(ApplicationContext applicationContext, String beanName){
        return (T) ((ConfigurableApplicationContext)applicationContext).getBeanFactory().getBean(beanName);
    }

}
  1. TccServiceModify 为每个参数生成不同的L临时Class
@Slf4j
public class TccServiceModify {

    private static final String ANNOTATION_VALUE_NAME = "memberValues";
    private static Class TCC_ANNOTATION = TwoPhaseBusinessAction.class;
    private static final String TCC_ANNOTATION_NAME = TwoPhaseBusinessAction.class.getName();
    public static <T> Class<T> modify() throws Exception {

        Class aClass = Class.forName("com.xiaoxin.sandbox_svc.service.impl.StockServiceImpl");


        Class[] interfaces = aClass.getInterfaces();
        for (Class it : interfaces) {
            if (it != StockService.class){
                continue;
            }
            log.info("have stock service");
            Method[] declaredMethods = it.getMethods();
            for (Method m : declaredMethods) {
                if (m.getName().equals("tryMock")) {
                    TwoPhaseBusinessAction twoPhaseBusines = m.getAnnotation(TwoPhaseBusinessAction.class);
                    //获取注解的处理器
                    InvocationHandler invocationHandler = Proxy.getInvocationHandler(twoPhaseBusines);
                    Field mfField = invocationHandler.getClass().getDeclaredField("memberValues");
                    mfField.setAccessible(true);
                    Map<String, Object> values = (Map<String, Object>) mfField.get(invocationHandler);
                    String oldName = (String) values.get("name");
                    values.put("name", "1020");
                    log.info("modify resource success! old:[{}], new[{}]", oldName, (String)values.get("name"));

                    //Field[] declaredFields = invocationHandler.getClass().getDeclaredFields();
                }
            }
        }


        return  aClass;

    }

    public static <T> Class<T> modifyResource(String resourceId, Class<T> aClass, Map<String, Object> params) throws Exception {
        Assert.hasText(resourceId, "modifyResource error: resourceId is empty!");

/*
        ClassPool classPool = ClassPool.getDefault();

        CtClass ctClass = classPool.get(aClass.getName());
        //CtClass baseSer = classPool.get("com.xiaoxin.sandbox_svc.service.StockService");

        CtClass stockIf = classPool.getAndRename("com.xiaoxin.sandbox_svc.service.StockService", "com.xiaoxin.sandbox_svc.service.StockService"+resourceId);

        CtClass stockClass = classPool.makeClass("com.xiaoxin.sandbox_svc.service.impl.StockServiceImpl" + resourceId, ctClass);


        for (CtMethod method : stockIf.getMethods()) {
            if (method.hasAnnotation(TCC_ANNOTATION)){//() -> new AnnotationsAttribute(method.getMethodInfo().getConstPool(), AnnotationsAttribute.visibleTag)
                AnnotationsAttribute annotationsAttribute = (AnnotationsAttribute) method.getMethodInfo().getAttributes().stream().filter(attributeInfo -> attributeInfo instanceof AnnotationsAttribute).findFirst().orElseGet(() -> new AnnotationsAttribute(method.getMethodInfo().getConstPool(), AnnotationsAttribute.visibleTag));
                Annotation annotation = annotationsAttribute.getAnnotation(TCC_ANNOTATION_NAME)!=null? annotationsAttribute.getAnnotation(TCC_ANNOTATION_NAME):new Annotation(TCC_ANNOTATION_NAME, method.getMethodInfo().getConstPool());


                annotation.addMemberValue("name", new StringMemberValue(resourceId, method.getMethodInfo().getConstPool()));
                annotationAddParam(annotation, params, method.getMethodInfo().getConstPool());

                annotationsAttribute.addAnnotation(annotation);
                
                method.getMethodInfo().addAttribute(annotationsAttribute);

                method.getMethodInfo().getAttributes();

            }
        }
        //stockIf.writeFile("E:\\workspace\\seata-test\\service\\sandbox_svc\\target\\classes");
        stockIf.toClass();
        stockClass.addInterface(stockIf);
        //stockClass.writeFile("E:\\workspace\\seata-test\\service\\sandbox_svc\\target\\classes");
       // return classPool.toClass(stockClass);*/
        return aClass;

    }

    public static <T> Class<T> modifyAnnotationAResource(Map<String, Object> updateValueMap, Class<T> resourceClass, Annotation annotation, Class<T> targetTClass){

       /* Class[] interfaces = aClass.getInterfaces();


        for (Class it : interfaces) {

            log.info("have stock service");
            Method[] methods = it.getMethods();
            for (Method m : methods) {
                TwoPhaseBusinessAction twoPhaseBusines = m.getAnnotation(TwoPhaseBusinessAction.class);
                if (twoPhaseBusines != null) {

                    //获取注解的处理器
                    InvocationHandler invocationHandler = Proxy.getInvocationHandler(twoPhaseBusines);
                    Field mfField = invocationHandler.getClass().getDeclaredField(ANNOTATION_VALUE_NAME);

                    mfField.setAccessible(true);
                    Map<String, Object> values = (Map<String, Object>) mfField.get(invocationHandler);
                    String oldName = (String) values.get("name");
                    values.put("name", resourceId);
                    log.info("modify resource success! old:[{}], new[{}]", oldName, (String)values.get("name"));
                    //Field[] declaredFields = invocationHandler.getClass().getDeclaredFields();
                }
            }
        }
        return  aClass;*/

        return null;
    }



    private static void annotationAddParam(Annotation annotation, Map<String, Object> params, ConstPool constPool){
        params.forEach((name, value) ->{
            annotation.addMemberValue(name, parseMemberValue(value, constPool));

        });

    }

    private static MemberValue parseMemberValue(Object obj, ConstPool constPool){
        if (obj instanceof String) {
            return new StringMemberValue((String) obj, constPool);
        }else if (obj instanceof Boolean) {
            return new BooleanMemberValue((Boolean) obj, constPool);
        }else if (obj instanceof Integer){
            return new IntegerMemberValue((Integer) obj, constPool);
        }else if (obj instanceof Class){
            return new ClassMemberValue(((Class)obj).getName(), constPool);
        }else if (obj.getClass().isArray()){
            ArrayMemberValue arrayMemberValue = new ArrayMemberValue(constPool);

            MemberValue[] memberValues =null;
            Object[] objects = (Object[]) obj;

            Object object = objects[0];
            if (object instanceof Class) {
                memberValues = new ClassMemberValue[objects.length];
            }else if (object instanceof String) {
                return new StringMemberValue((String) obj, constPool);
            }else if (object instanceof Boolean) {
                return new BooleanMemberValue((Boolean) obj, constPool);
            }else if (object instanceof Integer){
                return new IntegerMemberValue((Integer) obj, constPool);
            }
           for (int i = 0; i < objects.length; i++) {
               memberValues[i] = parseMemberValue(objects[i], constPool);
           }

            arrayMemberValue.setValue(memberValues);
            return arrayMemberValue;
        }
        return null;


    }





}