运行时注入Seata RM
背景需求
seata tcc 在对service类生成代理的时候会取@TwoPhaseBusinessAction值的会跟seata server 注册 TccResource, 在后面=注册branch的时候通过class.formName() 获取注解的值。 现在要求根绝不同的参数,需要注册不同的resource。 根据在不更改Seate TCC源码的, 现在考虑动态加载class类 从而为每个参数生成TccResource 然后生成beanDefinition, 手动注册到spring 容器中。
需要的类
- EnableTxmTcc 用于在springcontext 准备好后注册配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TccApplicationRunner.class)
public @interface EnableTxmTcc {
}
- 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());
}
}
- 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);
}
}
}
}
- TccResourceSelecter 根据参数返回不同的resource
@Service
@Slf4j
public class TccResourceSelecter {
public StockService getStockService(String name){
return RmResourceInit.TCC_RESOURCES.get(name);
}
}
- 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);
}
}
- 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;
}
}