Spring AOP does not work with a small functions - java

I have a typical task to check proceed time of many functions. I made an annotation
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface ProceedTimeLogger {}
and an aspect for this annotation
#Aspect
#Component
public class ProceedTimeLoggerAspect {
public static final Logger LOGGER = LoggerFactory.getLogger(ProceedTimeLoggerAspect.class);
#Around(value = "(#within(com.security.annotation.ProceedTimeLogger) " +
"|| #annotation(com.security.annotation.ProceedTimeLogger))" +
"|| execution(* com.security.permissions.permissions.*+.*(..))")
public Object profile(ProceedingJoinPoint pjp) throws Throwable {
String methodName = pjp.getSignature().getName();
String methodId = getMethodId();
long start = System.currentTimeMillis();
LOGGER.info("method '{}' id={} started", methodName, methodId);
Object proceed = pjp.proceed();
long end = System.currentTimeMillis() - start;
LOGGER.info("method '{}' id={} proceed time = {} ms", methodName, methodId, end);
return proceed;
}
public String getMethodId() {
String randomUUID = UUID.randomUUID().toString();
return randomUUID.substring(randomUUID.lastIndexOf("-") + 1, randomUUID.length());
}
}
Now my annotation covers som about 40 classes, but from all of this classes it works only in one method:
#Component
#ProceedTimeLogger
public class Permission {
...some methods...
//this function covers!!
public PermissionDTO getDTO(String permissionVal, String productDictId) {
PermissionDTO result = new PermissionDTO();
result.setMenuView(isMenuView(productDictId));
result.setDataView(isDataView(productDictId));
result = initOptionalDtoFields(result, productDictId);
return result;
}
...another non-covered methods...
public String getProductType(String productDictId) {
if (productDictId == null) {
return null;
}
Product product = dataDispatcher.getDictionaryById(Product.class, Long.valueOf(productDictId));
return Optional.ofNullable(product.getType())
.map(BaseDict::getIdMDMP)
.map(String::valueOf)
.orElse(null);
}
#ProceedTimeLogger
public String getProductCategory(String productDictId) {
if (productDictId == null) {
return null;
}
Product product = dataDispatcher.getDictionaryById(Product.class, Long.valueOf(productDictId));
return Optional.ofNullable(product.getProductCategory())
.map(BaseDict::getIdMDMP)
.map(String::valueOf)
.orElse(null);
}
}
Wtat's different? Why does these methods are not being covered?

Related

Modify annotation value on Java 8 using reflection [duplicate]

Imagine there is a class:
#Something(someProperty = "some value")
public class Foobar {
//...
}
Which is already compiled (I cannot control the source), and is part of the classpath when the jvm starts up. I would like to be able to change "some value" to something else at runtime, such that any reflection thereafter would have my new value instead of the default "some value".
Is this possible? If so, how?
Warning: Not tested on OSX - see comment from #Marcel
Tested on OSX. Works fine.
Since I also had the need to change annotation values at runtime, I revisited this question.
Here is a modified version of #assylias approach (many thanks for the inspiration).
/**
* Changes the annotation value for the given key of the given annotation to newValue and returns
* the previous value.
*/
#SuppressWarnings("unchecked")
public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){
Object handler = Proxy.getInvocationHandler(annotation);
Field f;
try {
f = handler.getClass().getDeclaredField("memberValues");
} catch (NoSuchFieldException | SecurityException e) {
throw new IllegalStateException(e);
}
f.setAccessible(true);
Map<String, Object> memberValues;
try {
memberValues = (Map<String, Object>) f.get(handler);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
Object oldValue = memberValues.get(key);
if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
throw new IllegalArgumentException();
}
memberValues.put(key,newValue);
return oldValue;
}
Usage example:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface ClassAnnotation {
String value() default "";
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface FieldAnnotation {
String value() default "";
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface MethodAnnotation {
String value() default "";
}
#ClassAnnotation("class test")
public static class TestClass{
#FieldAnnotation("field test")
public Object field;
#MethodAnnotation("method test")
public void method(){
}
}
public static void main(String[] args) throws Exception {
final ClassAnnotation classAnnotation = TestClass.class.getAnnotation(ClassAnnotation.class);
System.out.println("old ClassAnnotation = " + classAnnotation.value());
changeAnnotationValue(classAnnotation, "value", "another class annotation value");
System.out.println("modified ClassAnnotation = " + classAnnotation.value());
Field field = TestClass.class.getField("field");
final FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
System.out.println("old FieldAnnotation = " + fieldAnnotation.value());
changeAnnotationValue(fieldAnnotation, "value", "another field annotation value");
System.out.println("modified FieldAnnotation = " + fieldAnnotation.value());
Method method = TestClass.class.getMethod("method");
final MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
System.out.println("old MethodAnnotation = " + methodAnnotation.value());
changeAnnotationValue(methodAnnotation, "value", "another method annotation value");
System.out.println("modified MethodAnnotation = " + methodAnnotation.value());
}
The advantage of this approach is, that one does not need to create a new annotation instance. Therefore one doesn't need to know the concrete annotation class in advance. Also the side effects should be minimal since the original annotation instance stays untouched.
Tested with Java 8.
This code does more or less what you ask for - it is a simple proof of concept:
a proper implementation needs to also deal with the declaredAnnotations
if the implementation of annotations in Class.java changes, the code will break (i.e. it can break at any time in the future)
I have no idea if there are side effects...
Output:
oldAnnotation = some value
modifiedAnnotation = another value
public static void main(String[] args) throws Exception {
final Something oldAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("oldAnnotation = " + oldAnnotation.someProperty());
Annotation newAnnotation = new Something() {
#Override
public String someProperty() {
return "another value";
}
#Override
public Class<? extends Annotation> annotationType() {
return oldAnnotation.annotationType();
}
};
Field field = Class.class.getDeclaredField("annotations");
field.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) field.get(Foobar.class);
annotations.put(Something.class, newAnnotation);
Something modifiedAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("modifiedAnnotation = " + modifiedAnnotation.someProperty());
}
#Something(someProperty = "some value")
public static class Foobar {
}
#Retention(RetentionPolicy.RUNTIME)
#interface Something {
String someProperty();
}
This one works on my machine with Java 8. It changes the value of ignoreUnknown in the annotation #JsonIgnoreProperties(ignoreUnknown = true) from true to false.
final List<Annotation> matchedAnnotation = Arrays.stream(SomeClass.class.getAnnotations()).filter(annotation -> annotation.annotationType().equals(JsonIgnoreProperties.class)).collect(Collectors.toList());
final Annotation modifiedAnnotation = new JsonIgnoreProperties() {
#Override public Class<? extends Annotation> annotationType() {
return matchedAnnotation.get(0).annotationType();
} #Override public String[] value() {
return new String[0];
} #Override public boolean ignoreUnknown() {
return false;
} #Override public boolean allowGetters() {
return false;
} #Override public boolean allowSetters() {
return false;
}
};
final Method method = Class.class.getDeclaredMethod("getDeclaredAnnotationMap", null);
method.setAccessible(true);
final Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) method.invoke(SomeClass.class, null);
annotations.put(JsonIgnoreProperties.class, modifiedAnnotation);
SPRING can do this job very easily , might be useful for spring developer .
follow these steps :-
First Solution :-
1)create a Bean returning a value for someProperty . Here I injected the somePropertyValue with #Value annotation from DB or property file :-
#Value("${config.somePropertyValue}")
private String somePropertyValue;
#Bean
public String somePropertyValue(){
return somePropertyValue;
}
2)After this , it is possible to inject the somePropertyValue into the #Something annotation like this :-
#Something(someProperty = "#{#somePropertyValue}")
public class Foobar {
//...
}
Second solution :-
1) create getter setter in bean :-
#Component
public class config{
#Value("${config.somePropertyValue}")
private String somePropertyValue;
public String getSomePropertyValue() {
return somePropertyValue;
}
public void setSomePropertyValue(String somePropertyValue) {
this.somePropertyValue = somePropertyValue;
}
}
2)After this , it is possible to inject the somePropertyValue into the #Something annotation like this :-
#Something(someProperty = "#{config.somePropertyValue}")
public class Foobar {
//...
}
Try this solution for Java 8
public static void main(String[] args) throws Exception {
final Something oldAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("oldAnnotation = " + oldAnnotation.someProperty());
Annotation newAnnotation = new Something() {
#Override
public String someProperty() {
return "another value";
}
#Override
public Class<? extends Annotation> annotationType() {
return oldAnnotation.annotationType();
}
};
Method method = Class.class.getDeclaredMethod("annotationData", null);
method.setAccessible(true);
Object annotationData = method.invoke(getClass(), null);
Field declaredAnnotations = annotationData.getClass().getDeclaredField("declaredAnnotations");
declaredAnnotations.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) declaredAnnotations.get(annotationData);
annotations.put(Something.class, newAnnotation);
Something modifiedAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("modifiedAnnotation = " + modifiedAnnotation.someProperty());
}
#Something(someProperty = "some value")
public static class Foobar {
}
#Retention(RetentionPolicy.RUNTIME)
#interface Something {
String someProperty();
}
i am able to access and modify annotaions in this way in jdk1.8,but not sure why has no effect,
try {
Field annotationDataField = myObject.getClass().getClass().getDeclaredField("annotationData");
annotationDataField.setAccessible(true);
Field annotationsField = annotationDataField.get(myObject.getClass()).getClass().getDeclaredField("annotations");
annotationsField.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) annotationsField.get(annotationDataField.get(myObject.getClass()));
annotations.put(Something.class, newSomethingValue);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
Annotation attribute values have to be constants - so unless you want to do some serious byte code manipulation it won't be possible. Is there a cleaner way, such as creating a wrapper class with the annotation you desire?

How to code spring custom annotation which works like ibatis Select annotation?

I am trying to make a custom annotation like ibatis #Select.
Anyway, In conclusion, the goal is
append some data into the parameter which the method has custom annotation
First take a look end point - ArtistNodeRepository.java
#Repository
public interface ArtistNodeRepository {
#CreateNode(tid = "artist")
public Node create(Map data) throws Exception;
}
What want to do with CreateNode annotation is put data.put("type", "artist") into parameter Map.
Here is the Annotation - CreateNode.java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#Documented
public #interface CreateNode {
String[] values() default "";
String tid();
}
To controller annotation, I prepared this BeanPostProcessor - NodeAnnotationProcessor.java
#Component
public class NodeAnnotationProcessor implements BeanPostProcessor {
private ConfigurableListableBeanFactory configurableListableBeanFactory;
#Autowired
public NodeAnnotationProcessor(ConfigurableListableBeanFactory configurableListableBeanFactory) {
super();
this.configurableListableBeanFactory = configurableListableBeanFactory;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
this.scanNodeAnnotation(bean, beanName);
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// this.scanNodeAnnotation(bean, beanName);
return bean;
}
protected void scanNodeAnnotation(Object bean, String beanName){
this.configureMethodAction(bean);
}
private void configureMethodAction(Object bean){
Class<?> managedBeanClass = bean.getClass();
ReflectionUtils.MethodCallback methodCallback = new NodeMethodCallback(configurableListableBeanFactory, bean);
ReflectionUtils.doWithMethods(managedBeanClass, methodCallback);
}
}
I am not clear where to put MethodCallback to postProcessBeforeInitialization or postProcessAfterInitialization. In my thought, it would be in after since I am trying to manipulate parameter of the method
Finally, this is the MethodCallback - NodeMethodCallback.java
public class NodeMethodCallback implements ReflectionUtils.MethodCallback {
private Logger logger = LoggerFactory.getLogger(NodeMethodCallback.class);
private ConfigurableListableBeanFactory beanFactory;
private Object bean;
private static int AUTOWIRE_MODE = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
public NodeMethodCallback(ConfigurableListableBeanFactory beanFactory, Object bean) {
this.beanFactory = beanFactory;
this.bean = bean;
}
#Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
logger.info("doWith method info :: " + String.valueOf(bean) + "." + bean.getClass().getName());
/*
What I expected is Printing ArtistNodeRepository Class with create Method
But It prints something like ...
SessionFlashMapManager
DefaultRequestToViewNameTranslator
...
*/
try {
logger.info("When I call you :: " + method.getName()); // I expect method which contains #CreateNode annotation, but it is not ...
Annotation[] methodAnnotations = method.getDeclaredAnnotations();
boolean isTarget = false;
String tid = "";
for(Annotation anno : methodAnnotations) {
logger.info("annotation Class :: " + anno.getClass().getName());
if(isTarget) break;
if(anno instanceof CreateNode) {
logger.info("CreateNode annotation found");
CreateNode createNode = method.getDeclaredAnnotation(CreateNode.class);
tid = createNode.tid();
isTarget = true;
}
}
if(!isTarget) return;
ReflectionUtils.makeAccessible(method);
/*
Do Somthing with Parameter ...
Do Somthing with Parameter ...
Do Somthing with Parameter ...
Do Somthing with Parameter ...
Do Somthing with Parameter ...
*/
} catch (Exception e ){
logger.error("ERROR", e);
}
}
}
The problem is ... in doWith I could not find ArtistNodeRepository instance.
What should I do with MethodCallback and BeanPostProcessor to achieve the goal?
Good sample codes would be nice as well as good answers.
i think you misunderstood the useage of ReflectionUtils.doWithMethods. it means iterate the class method if match callback. rather than when invoke method callback.
public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) {
// Keep backing up the inheritance hierarchy.
Method[] methods = getDeclaredMethods(clazz);
for (Method method : methods) {
if (mf != null && !mf.matches(method)) {
continue;
}
try {
mc.doWith(method);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
}
}
if (clazz.getSuperclass() != null) {
doWithMethods(clazz.getSuperclass(), mc, mf);
}
else if (clazz.isInterface()) {
for (Class<?> superIfc : clazz.getInterfaces()) {
doWithMethods(superIfc, mc, mf);
}
}
}
i think you can use aspect. like this.
#Around("execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))")
// #Around("#annotation(Repository)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
CreateNode createNode = method.getAnnotation(CreateNode.class);
if(createNode != null) {
Object[] args = joinPoint.getArgs();
// do your business
}
return joinPoint.proceed();
}
hope can help you

Override expectations in JMockit

I want to override a previously defined expectation in JMockit. This is what I tried (see code below) -- I have a private class where I record all the common expectations and I replay it in various test methods. However, one of my method needs most of the common expectations except for few. First, I am calling the CommonNonStrictExpectations private class and then defining test specific expectations in my testMethod1 with a hope that what I defined here overrides what I have defined earlier. I dont think this way of overriding works, is there a way that works?
//MyClassTest.java
import MyClass;
public class MyClassTest {
#Mocked Someobject object;
#Test
public void testMethod1() throws Exception {
new CommonNonStrictExpectations() {};
new NonStrictExpectations() {
{
object.getInt(anyInt); returns (-1);
object.getString(anyInt); returns ("failure");
}
};
System.out.println("position: " + object.getInt(1));
System.out.println("exec status: " + object.getString(1));
MyClass m = new MyClass();
m.method(object, -1);
}
private class CommonNonStrictExpectations extends NonStrictExpectations {
public CommonNonStrictExpectations () throws Exception {
object.getInt(anyInt); returns (anyInt);
object.getString(anyInt); returns ("success");
}
}
}
//MyClass.java
import Someobject;
public class MyClass {
public void method (Someobject someobject, int i) {
String status = someobject.getString(i);
if (status.equalsIgnoreCase("success")) {
print(someobject, "success");
} else if (status.equalsIgnoreCase("failure")) {
print(someobject, "failure");
} else
print(someobject, "");
}
private String print(Someobject someobject, String status) {
return someobject.printMessage (status);
}
}
// Someobject.java
public class Someobject {
public String getString(int i) {
if (i < 0)
return "failure";
else if (i > 0)
return "success";
else
return "";
}
public int getInt(int k) {
return k;
}
public String printMessage (String status) {
return "status is: " + status;
}
}

How do I expect and verify the same method in JMockit

Having the following class:
class ToTest{
#Autowired
private Service service;
public String make(){
//do some calcs
obj.setParam(param);
String inverted = service.execute(obj);
return "<" + inverted.toString() + ">";
}
}
I'd like to add a test that asserts me that service.execute is called with an object with the param X.
I'd do that with a verification. I want to mock this call and make it return something testable. I do that with an expectations.
#Tested
ToTest toTest;
#Injected
Service service;
new NonStrictExpectations(){
{
service.exceute((CertainObject)any)
result = "b";
}
};
toTest.make();
new Verifications(){
{
CertainObject obj;
service.exceute(obj = withCapture())
assertEquals("a",obj.getParam());
}
};
I get a null pointer on obj.getParam(). Apparently the verification does not work. If I remove the expectation it works but then I get a null pointer in inverted.toString().
How would you guys make this work?
The following test class is working fine for me, using JMockit 1.4:
public class TempTest
{
static class CertainObject
{
private String param;
String getParam() { return param; }
void setParam(String p) { param = p; }
}
public interface Service { String execute(CertainObject o); }
public static class ToTest
{
private Service service;
public String make()
{
CertainObject obj = new CertainObject();
obj.setParam("a");
String inverted = service.execute(obj);
return "<" + inverted + ">";
}
}
#Tested ToTest toTest;
#Injectable Service service;
#Test
public void temp()
{
new NonStrictExpectations() {{
service.execute((CertainObject) any);
result = "b";
}};
toTest.make();
new Verifications() {{
CertainObject obj;
service.execute(obj = withCapture());
assertEquals("a", obj.getParam());
}};
}
}
Can you show a complete example test which fails?

Modify a class definition's annotation string parameter at runtime

Imagine there is a class:
#Something(someProperty = "some value")
public class Foobar {
//...
}
Which is already compiled (I cannot control the source), and is part of the classpath when the jvm starts up. I would like to be able to change "some value" to something else at runtime, such that any reflection thereafter would have my new value instead of the default "some value".
Is this possible? If so, how?
Warning: Not tested on OSX - see comment from #Marcel
Tested on OSX. Works fine.
Since I also had the need to change annotation values at runtime, I revisited this question.
Here is a modified version of #assylias approach (many thanks for the inspiration).
/**
* Changes the annotation value for the given key of the given annotation to newValue and returns
* the previous value.
*/
#SuppressWarnings("unchecked")
public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue){
Object handler = Proxy.getInvocationHandler(annotation);
Field f;
try {
f = handler.getClass().getDeclaredField("memberValues");
} catch (NoSuchFieldException | SecurityException e) {
throw new IllegalStateException(e);
}
f.setAccessible(true);
Map<String, Object> memberValues;
try {
memberValues = (Map<String, Object>) f.get(handler);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
Object oldValue = memberValues.get(key);
if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
throw new IllegalArgumentException();
}
memberValues.put(key,newValue);
return oldValue;
}
Usage example:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface ClassAnnotation {
String value() default "";
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface FieldAnnotation {
String value() default "";
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface MethodAnnotation {
String value() default "";
}
#ClassAnnotation("class test")
public static class TestClass{
#FieldAnnotation("field test")
public Object field;
#MethodAnnotation("method test")
public void method(){
}
}
public static void main(String[] args) throws Exception {
final ClassAnnotation classAnnotation = TestClass.class.getAnnotation(ClassAnnotation.class);
System.out.println("old ClassAnnotation = " + classAnnotation.value());
changeAnnotationValue(classAnnotation, "value", "another class annotation value");
System.out.println("modified ClassAnnotation = " + classAnnotation.value());
Field field = TestClass.class.getField("field");
final FieldAnnotation fieldAnnotation = field.getAnnotation(FieldAnnotation.class);
System.out.println("old FieldAnnotation = " + fieldAnnotation.value());
changeAnnotationValue(fieldAnnotation, "value", "another field annotation value");
System.out.println("modified FieldAnnotation = " + fieldAnnotation.value());
Method method = TestClass.class.getMethod("method");
final MethodAnnotation methodAnnotation = method.getAnnotation(MethodAnnotation.class);
System.out.println("old MethodAnnotation = " + methodAnnotation.value());
changeAnnotationValue(methodAnnotation, "value", "another method annotation value");
System.out.println("modified MethodAnnotation = " + methodAnnotation.value());
}
The advantage of this approach is, that one does not need to create a new annotation instance. Therefore one doesn't need to know the concrete annotation class in advance. Also the side effects should be minimal since the original annotation instance stays untouched.
Tested with Java 8.
This code does more or less what you ask for - it is a simple proof of concept:
a proper implementation needs to also deal with the declaredAnnotations
if the implementation of annotations in Class.java changes, the code will break (i.e. it can break at any time in the future)
I have no idea if there are side effects...
Output:
oldAnnotation = some value
modifiedAnnotation = another value
public static void main(String[] args) throws Exception {
final Something oldAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("oldAnnotation = " + oldAnnotation.someProperty());
Annotation newAnnotation = new Something() {
#Override
public String someProperty() {
return "another value";
}
#Override
public Class<? extends Annotation> annotationType() {
return oldAnnotation.annotationType();
}
};
Field field = Class.class.getDeclaredField("annotations");
field.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) field.get(Foobar.class);
annotations.put(Something.class, newAnnotation);
Something modifiedAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("modifiedAnnotation = " + modifiedAnnotation.someProperty());
}
#Something(someProperty = "some value")
public static class Foobar {
}
#Retention(RetentionPolicy.RUNTIME)
#interface Something {
String someProperty();
}
This one works on my machine with Java 8. It changes the value of ignoreUnknown in the annotation #JsonIgnoreProperties(ignoreUnknown = true) from true to false.
final List<Annotation> matchedAnnotation = Arrays.stream(SomeClass.class.getAnnotations()).filter(annotation -> annotation.annotationType().equals(JsonIgnoreProperties.class)).collect(Collectors.toList());
final Annotation modifiedAnnotation = new JsonIgnoreProperties() {
#Override public Class<? extends Annotation> annotationType() {
return matchedAnnotation.get(0).annotationType();
} #Override public String[] value() {
return new String[0];
} #Override public boolean ignoreUnknown() {
return false;
} #Override public boolean allowGetters() {
return false;
} #Override public boolean allowSetters() {
return false;
}
};
final Method method = Class.class.getDeclaredMethod("getDeclaredAnnotationMap", null);
method.setAccessible(true);
final Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) method.invoke(SomeClass.class, null);
annotations.put(JsonIgnoreProperties.class, modifiedAnnotation);
SPRING can do this job very easily , might be useful for spring developer .
follow these steps :-
First Solution :-
1)create a Bean returning a value for someProperty . Here I injected the somePropertyValue with #Value annotation from DB or property file :-
#Value("${config.somePropertyValue}")
private String somePropertyValue;
#Bean
public String somePropertyValue(){
return somePropertyValue;
}
2)After this , it is possible to inject the somePropertyValue into the #Something annotation like this :-
#Something(someProperty = "#{#somePropertyValue}")
public class Foobar {
//...
}
Second solution :-
1) create getter setter in bean :-
#Component
public class config{
#Value("${config.somePropertyValue}")
private String somePropertyValue;
public String getSomePropertyValue() {
return somePropertyValue;
}
public void setSomePropertyValue(String somePropertyValue) {
this.somePropertyValue = somePropertyValue;
}
}
2)After this , it is possible to inject the somePropertyValue into the #Something annotation like this :-
#Something(someProperty = "#{config.somePropertyValue}")
public class Foobar {
//...
}
Try this solution for Java 8
public static void main(String[] args) throws Exception {
final Something oldAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("oldAnnotation = " + oldAnnotation.someProperty());
Annotation newAnnotation = new Something() {
#Override
public String someProperty() {
return "another value";
}
#Override
public Class<? extends Annotation> annotationType() {
return oldAnnotation.annotationType();
}
};
Method method = Class.class.getDeclaredMethod("annotationData", null);
method.setAccessible(true);
Object annotationData = method.invoke(getClass(), null);
Field declaredAnnotations = annotationData.getClass().getDeclaredField("declaredAnnotations");
declaredAnnotations.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) declaredAnnotations.get(annotationData);
annotations.put(Something.class, newAnnotation);
Something modifiedAnnotation = (Something) Foobar.class.getAnnotations()[0];
System.out.println("modifiedAnnotation = " + modifiedAnnotation.someProperty());
}
#Something(someProperty = "some value")
public static class Foobar {
}
#Retention(RetentionPolicy.RUNTIME)
#interface Something {
String someProperty();
}
i am able to access and modify annotaions in this way in jdk1.8,but not sure why has no effect,
try {
Field annotationDataField = myObject.getClass().getClass().getDeclaredField("annotationData");
annotationDataField.setAccessible(true);
Field annotationsField = annotationDataField.get(myObject.getClass()).getClass().getDeclaredField("annotations");
annotationsField.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> annotations = (Map<Class<? extends Annotation>, Annotation>) annotationsField.get(annotationDataField.get(myObject.getClass()));
annotations.put(Something.class, newSomethingValue);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
Annotation attribute values have to be constants - so unless you want to do some serious byte code manipulation it won't be possible. Is there a cleaner way, such as creating a wrapper class with the annotation you desire?

Categories