且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

功能切换 Java 注释

更新时间:2022-03-26 04:08:45

可能的选择之一是使用 aspectj 能够声明注释和spring的能力加载时方面编织.

One of the possible options is to use aspectj with its ability to declare annotations and spring's ability of load-time aspect weaving.

我认为注解不能有条件地声明,但你总是可以在一个单独的 jar 中编译它们,该 jar 可以根据特定环境放入类路径,以便加载时编织器能够找到它.

I suppose that annotations cannot be declared conditionally, but you can always compile them in a separate jar that can be put into the classpath depending on the certain environment so that load-time weaver will be able to find it.

更新

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

While there are a lot of useful answers here, I found disabling/enabling annotations quite interesting playing with aspectj, so the sample is below.

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

The latest versions of aspectj support removing of annotations, but for now this feature is only available for the field annotations, so quite useful way is not to declare annotations at all and if they have to be enabled - to put the jar with precompiled aspects which will enable the annotations into the classpath as I mentioned earlier.

样品

第一个罐子

主类

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());
    }

}

我们将在其中声明注解的类

The class we will declare annotations in

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

META-INF/aop.xml

<?xml version="1.0"?>

<aspectj>
    <aspects>
        <aspect name="org.foo.bar.ToggleAnnotationAspect"/>
    </aspects>
</aspectj>

在类路径中没有第二个 jar 的情况下运行应用程序


Running the application without the second jar in the 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

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

Running the application with the second jar in the classpath

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

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

So no modification to the application source were made at all.