特点绷Java注解(Feature Toggling Java Annotations)

2019-09-21 10:47发布

我怎样才能拥有拨动Java注解?

简单的功能切换: - 如果(切换启用)做X

Spring允许使用“配置文件”切换豆。

我使用这些,他们都很好,但我想切换现场或类注释..我该怎么办呢?

使用的情况下,我有了JPA注解的类。 我希望能够通过某些字段在某些环境中,当@Transient配置标记。

Answer 1:

其中一个可能的选项是使用AspectJ的符合其能力申报注释和春天的能力加载时间方面编织 。

我想,注释无法有条件申报,但你总是可以编译它们在一个单独的罐子可以投入取决于特定环境中的类路径,以便加载时织就能够找到它。


UPDATE

虽然这里有很多有用的答案,我发现禁用/启用注释挺有意思的演奏和AspectJ,所以样品低于。

的AspectJ的支持删除注释,但现在此功能的最新版本仅适用于该领域的注解,所以非常有用的方法是完全不声明注解,如果他们已启用 - 把罐子预编译方面,其将使注释到类路径正如我前面提到。


样品


第一个罐子

主类

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>

第二个罐子

纵横

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>

运行没有在classpath第二罐中的应用

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

运行在类路径中的第二个罐子中的应用

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

因此,无需修改应用程序源代码是人工做的。



Answer 2:

正如前面提到的那样,试图以“禁用”的注释,同时有可能,是不是真的接近问题的最佳途径。

就像阿德里安岑说,你应该改变的框架如何处理注释。 你的情况应该有你的JPA实现(比如Hibernate)下面一些ORM提供商。

大多数奥姆斯有一些方式来提供自定义功能,例如在休眠的情况下,你可以创建一个拦截器 ,并通过详见你的JPA配置中添加hibernate.ejb.interceptor到持久性单位注册它在这里 。

什么这个拦截器应该做的是你的,但我会建议使用不同的注解(如@ConditionalTransiet)的一种方法是去在通过反射的领域,检查他们是否有注释,如果它在错误的enviornment然后使用的onLoad和的onSave擦去对象相关的字段。



Answer 3:

我不认为这是可能的现场级。 什么你可能做的是从beeing认为通过JPA(通过持久化单元配置)排除整个班级。 这应该是可能的每个配置文件,我相信许多工作要做。



Answer 4:

不,你不能这样做。

注释是一个简单的片元数据。 它连接到字节码(当然,这取决于保留)后,您编译源代码。 因此,它是永远存在的。 你不能让它在运行时使用正常的方式消失。

然而,注释是简单的元数据。 它由它自己什么都没有。 应该有别人检查注释,并相应地做他们的工作。 因此,你应该看看到什么是,你应该找到某种方式来告诉大家,“某人”谁检查注释,并告诉它什么是解释注释的正确方法(如忽略某些注释等)

还有就是要执行这个动作,因为这一切都取决于谁正在检查注释中的一个不一般的方式。


如果你坚持要不厌其烦的方式,我相信你可以改变在运行的类。 这将是一个繁琐的工作。 我记得,如javassist工具允许您“重写”的类加载器加载的类并保存它回来。 但是你要面对很多问题,例如,你的类交替过程中应发生的任何其他代码运行之前,如果没有,例如Hibernate会已经检查未修改的班,做了设置,甚至你删除从班之后注解,它不会做任何事情。



Answer 5:

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

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

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

如果配置文件注释上的切面类的工作虽然不知道。 此外,这种方法需要你添加这些方面为要应用这种行为的每一个领域。 它很难说比这更通用。



Answer 6:

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



文章来源: Feature Toggling Java Annotations