One of the possible options is to use aspectj with its ability to declare annotations and spring's ability of load-time aspect weaving.
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.
UPDATE
While there are a lot of useful answers here, I found disabling/enabling annotations quite interesting playing with aspectj, so the sample is below.
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.
SAMPLE
The first jar
The main class
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();
}
}
The annotation itself
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();
}
Application context
<?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>
The second jar
The aspect
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>
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
will print
MyClass{field1='annotated-field-1', field2='null'}
annotated-method-1
null
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
will print
MyClass{field1='null', field2='annotated-field-2'}
annotated-method-1
annotated-method-2
So no modification to the application source were made at all.