特性切换Java注释

特性切换Java注释,java,spring,jpa,annotations,featuretoggle,Java,Spring,Jpa,Annotations,Featuretoggle,如何切换java注释 简单功能切换:- 如果(启用切换)执行x Spring允许使用“profiles”来切换bean 我使用这些,它们很好,但我想在字段或类上切换注释。。我该怎么做 在用例中,我有一个具有jpa注释的类。我希望能够通过配置标记某些字段在某些环境中是@transient。我认为在字段级别上不可能。您可能要做的是将整个类从JPA考虑的Being中排除(通过持久化单元配置)。我相信这应该可以根据个人资料来完成。不,你不能这样做 注释只是一段元数据。编译源代码后,它会附加到字节码(好吧

如何切换java注释

简单功能切换:- 如果(启用切换)执行x

Spring允许使用“profiles”来切换bean

我使用这些,它们很好,但我想在字段或类上切换注释。。我该怎么做


在用例中,我有一个具有jpa注释的类。我希望能够通过配置标记某些字段在某些环境中是@transient。

我认为在字段级别上不可能。您可能要做的是将整个类从JPA考虑的Being中排除(通过持久化单元配置)。我相信这应该可以根据个人资料来完成。

不,你不能这样做

注释只是一段元数据。编译源代码后,它会附加到字节码(好吧,取决于保留)。因此,它总是存在的。您不能使用正常方式使其在运行时消失

然而,注释只是元数据。它自己什么也不做。应该有其他人检查注释,并相应地执行他们的工作。因此,您应该研究的是,您应该找到某种方法来告诉检查注释的“某人”,并告诉其解释注释的正确方法(例如忽略某些注释等)

没有通用的方法来执行此操作,因为它完全取决于检查注释的人



如果您坚持采取这种麻烦的方式,我相信您可以在运行时更改类。这将是一项乏味的工作。我记得,Javassist之类的工具允许您“重写”classloader加载的类并将其保存回去。然而,您将面临许多问题,例如,您的类替换过程应该在任何其他代码运行之前进行,如果没有,例如,Hibernate将已经检查了未修改的类并完成了其设置,甚至您后来从类中删除了注释,它不会做任何事情。

一个可能的选择是使用它的能力和spring的能力

我假设注释不能有条件地声明,但是您可以始终在一个单独的jar中编译它们,该jar可以根据特定的环境放入类路径中,以便加载时weaver能够找到它


更新

虽然这里有很多有用的答案,但我发现使用aspectj禁用/启用注释非常有趣,因此下面是示例

aspectj的最新版本支持删除注释,但目前此功能仅适用于字段注释,因此非常有用的方法是根本不声明注释,如果必须启用注释,则将jar与预编译的方面一起放入类路径中,这将使注释成为可能,正如我前面提到的


样本


第一个罐子

主课

package org.foo.bar;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("/applicationContext.xml");
        MyClass myObj = context.getBean("myObj", MyClass.class);

        System.out.println(myObj);
        System.out.println(myObj.getValue1());
        System.out.println(myObj.getValue2());
    }

}
我们将在其中声明注释的类

package org.foo.bar;

public class MyClass {

    @MyAnn("annotated-field-1")
    private String field1;
    private String field2;

    @MyAnn("annotated-method-1")
    public String getValue1() {
        String value = null;
        try {
            MyAnn ann = getClass().getDeclaredMethod("getValue1").getAnnotation(MyAnn.class);
            if(ann != null) {
                value = ann.value();
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        return value;
    }

    public String getValue2() {
        String value = null;
        try {
            MyAnn ann = getClass().getDeclaredMethod("getValue2").getAnnotation(MyAnn.class);
            if(ann != null) {
                value = ann.value();
            }
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        return value;
    }

    @Override
    public String toString() {
        String field1 = null;
        try {
            MyAnn ann = getClass().getDeclaredField("field1").getAnnotation(MyAnn.class);
            if(ann != null) {
                field1 = ann.value();
            }
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        String field2 = null;
        try {
            MyAnn ann = getClass().getDeclaredField("field2").getAnnotation(MyAnn.class);
            if(ann != null) {
                field2 = ann.value();
            }
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        StringBuilder sb = new StringBuilder();
        sb.append("MyClass");
        sb.append("{field1='").append(field1).append('\'');
        sb.append(", field2='").append(field2).append('\'');
        sb.append('}');
        return sb.toString();
    }
}
注释本身

package org.foo.bar;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface MyAnn {

    String value();

}
应用程序上下文

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <context:load-time-weaver />

    <bean id="myObj" class="org.foo.bar.MyClass" />

</beans>
META-INF/aop.xml

<?xml version="1.0"?>

<aspectj>
    <aspects>
        <aspect name="org.foo.bar.ToggleAnnotationAspect"/>
    </aspects>
</aspectj>
使用类路径中的第二个jar运行应用程序

java -javaagent:spring-instrument-3.1.3.RELEASE.jar \
     -classpath app1.jar;<rest_of_cp> org.foo.bar.Main
java -javaagent:spring-instrument-3.1.3.RELEASE.jar \
     -classpath app1.jar;app1-aspects.jar;<rest_of_cp> org.foo.bar.Main
因此,根本没有对应用程序源进行任何修改。

您可以尝试(使用方面):

但不确定概要文件注释是否适用于方面类。
此外,这种方式还需要为每个要应用此行为的字段添加这些方面。很难让它比这更通用。

如前所述,尝试“禁用”注释,虽然可能,但并不是解决问题的最佳方法。

正如Adrian Shum所说,您应该更改框架处理注释的方式。在您的情况下,JPA实现(如Hibernate)下面应该有一些ORM提供程序。

大多数ORM都有一些方法来提供自定义功能,例如在Hibernate的情况下,您可以创建一个新的ORM,并通过将Hibernate.ejb.interceptor添加到JPA配置中的持久化单元来注册它,详细说明如下


这个拦截器应该做什么取决于您,但我建议使用不同的注释(例如@ConditionalTransiet),一种方法是通过反射遍历字段,检查他们是否有注释以及注释是否在错误的环境中,然后使用onLoad和onSave从对象中删除相关字段。

在Hibernate的开始,他们使用单独的映射xml文件将其设计为将配置与实际类分开。后来添加注释只是为了方便起见

在标准化的JPA中,仍然可以使用orm.xml配置覆盖注释。请参阅以获取参考

在您的情况下,如果使用
metadata complete=“true”
,则所有元数据都来自orm.xml文件,而不是注释。然后可以使用两种不同的orm.xml

<entity class="Administration" access="PROPERTY" metadata-complete="true">
    <attributes>
       <basic name="status"/>
       <basic name="optional">
     </attributes>



不要处理注释…只有在处理itI时元数据才是好的,否则不遵循。我希望批注存在。。但通过配置/功能切换来禁用它们,这与Spring无关。您需要更改ORM处理注释的方式。使用拦截器有点过分。使用简单的orm.xml文件也可以做到这一点(请参见下面的答案)。
java -javaagent:spring-instrument-3.1.3.RELEASE.jar \
     -classpath app1.jar;app1-aspects.jar;<rest_of_cp> org.foo.bar.Main
MyClass{field1='null', field2='annotated-field-2'}
annotated-method-1
annotated-method-2
@Profile("active")
privileged aspect AddField {
    private String MyClass.name;
}

@Profile("inactive")
privileged aspect AddFieldTransient {
    @Transient
    private String MyClass.name;
}
<entity class="Administration" access="PROPERTY" metadata-complete="true">
    <attributes>
       <basic name="status"/>
       <basic name="optional">
     </attributes>
<entity class="Administration" access="PROPERTY" metadata-complete="true">
    <attributes>
       <basic name="status"/>
       <!-- omitted optional property -->
     </attributes>