功能切换Java注释

时间:2012-08-22 02:18:00

标签: java spring jpa annotations featuretoggle

如何切换java注释?

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

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

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

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

6 个答案:

答案 0 :(得分:4)

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

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

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

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

答案 1 :(得分:3)

其中一个可能的选择是使用aspectj能力declare annotations和春天能力load-time aspect weaving

我认为注释不能有条件地声明,但是你总是可以在一个单独的jar中编译它们,这个jar可以根据特定的环境放入类路径中,这样加载时weaver就能找到它。


<强>更新

虽然这里有很多有用的答案,但我发现使用aspectj进行禁用/启用注释非常有趣,所以示例如下。

最新版本的aspectj支持删除注释,但是现在这个功能仅适用于字段注释,所以非常有用的方法是不要声明注释,如果必须启用它们 - 将jar放入如前所述,预编译的方面将使注释能够进入类路径。


<强>样品


第一个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>

第二个jar

方面

package org.foo.bar;

public aspect ToggleAnnotationAspect {

    declare @field : private String org.foo.bar.MyClass.field1 : -@MyAnn;
    declare @field : private String org.foo.bar.MyClass.field2 : @MyAnn("annotated-field-2");

    declare @method : public String org.foo.bar.MyClass.getValue2() : @MyAnn("annotated-method-2");

}

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

将打印

MyClass{field1='annotated-field-1', field2='null'}
annotated-method-1
null

使用类路径中的第二个jar运行应用程序

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

因此,根本没有对应用程序源进行任何修改。

答案 2 :(得分:1)

我认为这不可能在现场级别。你可能做的是将整个类排除在JPA考虑的范围之外(通过持久性单元配置)。我相信,这应该可以根据个人资料完成。

答案 3 :(得分:1)

不,你不能这样做。

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

然而,注释只是元数据。它本身没有任何作用。应该有其他人检查注释,并相应地完成他们的工作。因此,你应该研究的是,你应该找到一些方法告诉那个检查注释的“某人”,并告诉它解释注释的正确方法是什么(例如忽略一些注释等)

没有通用的方法来执行此操作,因为这一切都取决于正在检查注释的人。


如果你坚持采取麻烦的方式,我相信你可能会改变运行时的类。这将是一项繁琐的工作。我记得像Javassist这样的工具允许你“重写”由类加载器加载的类并将其保存回来。然而,你将面临很多问题,例如,你的类交替过程应该在任何其他代码运行之前进行,如果没有,例如,Hibernate已经检查过未修改的类并完成了它的设置,甚至你删除了之后来自班级的注释,它什么都不做。

答案 4 :(得分:1)

你可以尝试这个(使用方面):

@Profile("active")
privileged aspect AddField {
    private String MyClass.name;
}

@Profile("inactive")
privileged aspect AddFieldTransient {
    @Transient
    private String MyClass.name;
}

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

答案 5 :(得分:1)

在Hibernate的开头,他们设计了使用单独的映射xml文件将配置与实际类分开。注释仅在以后添加为方便的折衷方案。

在标准化的JPA中仍然可以使用orm.xml配置来覆盖注释。请参阅http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/xml-overriding.html以获取参考。

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

<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>