spring中某方法@Transactional 设置了propogation后,能在方法中修改方法的事务传播属性吗?

发布于 2022-09-11 18:28:41 字数 220 浏览 14 评论 0

譬如

 @Transactional(propagation = Propagation.REQUIRES_NEW))
 void doSomething(){}
 
 @Transactional
 void caller(){
     //修改doSomething的事务传播属性为Propagation.REQUIRED
     doSomething();
 }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

凉宸 2022-09-18 18:28:41

不能...如果只是普通的修改一个方法的注解值的话可以通过反射办到
但是Spring实际上加载使用的是你原来类后生成的代理类 也就是说你得直接改他的代理类才行

但也不排除什么黑科技操作...不过以我的姿势水平来说是不能的

音盲 2022-09-18 18:28:41

题主你好,这个问题的回答,我觉得理论上可以,为啥说是理论呢
因为你的最终目标可能应该是可以在运行时动态改变注解中的属性值
所以这里涉及两个问题

  1. Java本身语法层面上是否支持可以修改
  2. 修改值之后,新值是否能生效

第一个问题是纯语法问题,先不提,这里先要说明哈第二个问题,因为Transactional注解是Spring的提供的配置注解,所以就算修改了Transactional中的属性值,运行时应该不是采用实时Transactional注解的新值,应该这类配置都是在Spring初始化时,就另外对象包装存储起来的,类似缓存起来,所以这点我没有深究,但是理论上改完Spring缓存值应该最终能达到目的。除非完全保护起来不允许修改,但是这个保护机制是Spring提供,并不涉及第一个问题

再来说说第一个问题,起初看到这个问题,我是这么想的,注解本身就是一个接口,而注解存在的地方其实就是这个接口的一个实现类,一个实例对象,所以换句话说其实就是改变实例对象的属性值,这个再怎么也应该可以做到,只是这些注解实例应该有一个地方存储,这个地方我之前没有了解过,但我觉得应该存在,最后我查找了一些资料了解了以下处理方式,仅供参考

假设我们一个类注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ClassAnnotation {
    String value() default "";
}

我们创建一个测试类TestClass,然后给它加上这个注解ClassAnnotation

@ClassAnnotation("class test")
public class TestClass{
}

平常我们在获取注解ClassAnnotation时,其实一般都是这么做的

ClassAnnotation classAnnotation = TestClass.class.getAnnotation(ClassAnnotation.class);

但是我们获取出来classAnnotation对象其实是一个代理对象

clipboard.png

这里我们可以拿到这个代理类的InvocationHandler类,然后InvocationHandler中一个属性memberValues,而这个memberValues属性其实是一个map,而这个map就是存储的这个注解中定义的各种属性信息,包括属性名和值,key是属性名,value是属性值,因此只用修改对应的属性名的属性值即可

所以就可以写出这样一个工具方法changeAnnotationValue

/**
     * 
     * @param annotation 当前注解代理对象
     * @param key 需要修改的注解的属性名
     * @param newValue 需要修改的注解的属性新值
     * @return
     */
    public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){
        Object handler = Proxy.getInvocationHandler(annotation);
        Field f;
        try {
            f = handler.getClass().getDeclaredField("memberValues");
        } catch (NoSuchFieldException | SecurityException e) {
            throw new IllegalStateException(e);
        }
        f.setAccessible(true);
        Map<String, Object> memberValues;
        try {
            memberValues = (Map<String, Object>) f.get(handler);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
        Object oldValue = memberValues.get(key);
        if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
            throw new IllegalArgumentException();
        }
        memberValues.put(key,newValue);
        return oldValue;
    }

然后用这个方法,我们可以做一个测试

ClassAnnotation classAnnotation = TestClass.class.getAnnotation(ClassAnnotation.class);
System.out.println("old ClassAnnotation = " + classAnnotation.value());
changeAnnotationValue(classAnnotation, "value", "another class annotation value");
System.out.println("modified ClassAnnotation = " + classAnnotation.value());

运行结果

clipboard.png

当然方法类注解或者字段类注解同理

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodAnnotation {
    String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FieldAnnotation {
    String value() default "";
}
@ClassAnnotation("class test")
public class TestClass{
    @FieldAnnotation("field test")
    public Object field;
    @MethodAnnotation("method test")
    public void method(){ }
    
    public static void main(String[] args) throws Exception {
        // 类注解
        ClassAnnotation classAnnotation = TestClass.class.getAnnotation(ClassAnnotation.class);
        System.out.println("old ClassAnnotation = " + classAnnotation.value());
        changeAnnotationValue(classAnnotation, "value", "another class annotation value");
        System.out.println("modified ClassAnnotation = " + classAnnotation.value());
        
        // 字段注解
        Field field = TestClass.class.getField("field");
        FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
        System.out.println("old FieldAnnotation = " + fieldAnnotation.value());
        changeAnnotationValue(fieldAnnotation, "value", "another field annotation value");
        System.out.println("modified FieldAnnotation = " + fieldAnnotation.value());
        
        // 方法注解
        Method method = TestClass.class.getMethod("method");
        MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
        System.out.println("old MethodAnnotation = " + methodAnnotation.value());
        changeAnnotationValue(methodAnnotation, "value", "another method annotation value");
        System.out.println("modified MethodAnnotation = " + methodAnnotation.value());
    }
}

以上就是语法层面的修改,Spring层面上的修改,我没有细追,不过我觉得是可以的,我只是简单测试过RequestMapping注解的method属性,之前只支持GET,后面改为只支持POST,并不会生效,所以我觉得Transactional注解应该也是类似,毕竟Spring肯定需要统一管理这些注解配置,而不会去原值读取

如果题主最终找出解决方案,麻烦更新分享哈,我后面有空了也会去追哈源码看看,谢谢啦

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文