How to get annotation value from AnnotationDescription.Loadable in Bytebuddy - java

I want get annotation value form a specific annotation, such as #Callcount, which has a field named key.
MethodList<MethodDescription.InDefinedShape> methods = typeDefinition.getDeclaredMethods();
for (MethodDescription.InDefinedShape method : methods) {
AnnotationDescription.Loadable<CalledCount> callCountAnno;
if ((callCountAnno = method.getDeclaredAnnotations().ofType(CalledCount.class)) != null) {
callCountAnno.getValue(?);//how can i do here?
}
}
i don't know how to build the parameter of method callCountAnno.getValue(), what i shoud do?

The easiest solution would be to load the annotation which allows you to access the value in a type-safe manner. You can do so via the load or loadSilent methods. Alternatively, you need to provide the propery you want to resolve. You can provide a loaded method reference via: MethodDescription.ForLoadedMethod( ... ).

Related

How to call a method with a parameter decorated with #Vaule annotation in a spring project?

When there is a parameter decorated with #Value annotaion in a method, it is ofcourse no compile error. Just like the code bellow:
public void Sample0(#Value("${hmac.key}") Optional<String> key) {
if (key.isPresent()) {
System.out.println(key.get());
} else {
System.out.println("can not find key");
}
}
My Question is how to call a function like this? I try to call it directory, but it failed. The calling function code bellow.
#Test
public void sampleTest0() {
JwtService.Sample0();
}
So anyone can tell me what's the right way to call the function? Thx.
I think another way of doing this is to save #Value as local variable and then use it into function.
I think you can use #Value in parameters only in constructors. Your Sample0 is not a constructor, just a regular method because it has a return type, i.e. void.
If you use #Value in constructor parameter, you can autowire Sample0 and spring will inject the value while instantiating it.
If you need to pass a value to a regular method instead, you can use #Value on a field in the calling class and then pass that field as an argument to this method in Sample0.

Map a collection with parameter with mapstruct

To map a certain object with mapstruct I need some custom post processing which needs an additional parameter to do it's work:
#Mapper
public abstract class AlertConfigActionMapper {
#Mappings({ #Mapping(target = "label", ignore = true)})
public abstract AlertConfigActionTO map (AlertConfigAction action, Locale userLanguage);
#AfterMapping
public void setLabel (AlertConfigAction action, #MappingTarget AlertConfigActionTO to, Locale userLanguage) {
for (AlertConfigActionLabel label : action.getAlertConfigActionLabels()) {
if (label.getLanguage().equals(userLanguage)) {
to.setLabel(label.getLabel());
break;
} else if (label.getLanguage().equals(Locale.ENGLISH)) {
to.setLabel(label.getLabel());
}
}
}
}
This works just fine.
The problem starts when I add following method to this mapper:
public abstract ArrayList<AlertConfigActionTO> mapList (List<AlertConfigAction> actions, Locale userLanguage);
I need to pass this parameter (userLanguage) as well but mapstruct seems to 'break down' in this case: I generates following code for this part (which naturally gives a compilation error):
#Override
public List<AlertConfigActionTO> mapList(List<AlertConfigAction> actions, Locale userLanguage) {
if ( actions == null && userLanguage == null ) {
return null;
}
List<AlertConfigActionTO> list = new List<AlertConfigActionTO>();
return list;
}
I'm sure it is related to the parameter since if I remove it (from all mapping methods) then the mapList method is generated correctly.
What is needed to be done to allow custom parameters in this case?
What you describe is not possible (yet). Could you open a feature request in our issue tracker? We should provide means of denoting parameters as some sort of "context" which is passed down the call stack.
As a work-around for the time being, you might take a look at using a ThreadLocal which you set before invoking the mapping routine and which you access in your after-mapping customization. It's not elegant - and you need to make sure to clean up the thread local to avoid memory leaks - but it should do the trick.
I know that this question is quiet old, but I run into this issue, and starting at version 1.2 of mapstruct you can resolve it using #Context
So declaring the mapping for the list need to be like this :
public abstract ArrayList<AlertConfigActionTO> mapList (List<AlertConfigAction> actions, #Context Locale userLanguage);
Now, you juste need to add another non abstract mapping like this :
public AlertConfigActionTO mapConcrete (AlertConfigAction action, #Context Locale userLanguage){
return map (action, userLanguage);
}
I don't think it is possible. At least not that way. Problem is that you prepare interface/abstract class - and rest is done by the engine. And that engine expects methods with one parameter... There are decorators, but they go the same way. I would try to inject language. Create bean, mark it as session scoped, and find out. With Spring, you would use ScopedProxyMode for that... Not sure how that goes with CDI.
Other option is more workaround, then solution - maybe that AlertConfigAction can pass that information?

Java annotation dynamic typecast

I have 2 java annotation types, let's say XA and YA. Both have some method(). I parse the source code and retrieve Annotation object. Now I'd like to dynamicaly cast the annotation to the real type of it to be able to call the method(). How can I do it without the instanceof statement? I really want to avoid switch-like source. I need something like this:
Annotation annotation = getAnnotation(); // I recieve the Annotation object here
String annotationType = annotation.annotationType().getName();
?_? myAnnotation = (Class.forName(annotationType)) annotation;
annotation.method(); // this is what I need, get the method() called
?_? means I have no idea what would be myAnnotation type. I cannot use the base class for my XA and YA annotations since the inheritance in annotations is not allowed. Or is it possible to do somehow?
Thanks for any suggestion or help.
Why don't you use the typesafe way to retrieve your annotation ?
final YourAnnotationType annotation = classType.getAnnotation(YourAnnotationType.class);
annotation.yourMethod();
If your annotation can't be found, null is returned.
Please note that this also works with fields and methods.
One way is to invoke the method dynamically using the name of it:
Annotation annotation = getAnnotation();
Class<? extends Annotation> annotationType = annotation.annotationType();
Object result = annotationType.getMethod("method").invoke(annotation);
This approach is quite risky and totally compromise the code refactoring if needed.

Spring AOP: get access to argument names

I'm using Spring 3.x, Java 6.
I have an #Around aspect with the following joinpoint:
#Around("execution(public * my.service.*.*Connector.*(..))")
So, I'm basically interested in intercepting all calls to public methods of classes with the class name ending with "Connector". So far so good.
Now, in my aspect I would like to access the actual argument names of the methods:
public doStuff(String myarg, Long anotherArg)
myarg and anotherArg
I understand that using:
CodeSignature signature = (CodeSignature)jointPoint.getSignature();
return signature.getParameterNames();
will actually work but only if I compile the code with the "-g" flag (full debug) and I would rather not do it.
Is there any other way to get access to that kind of runtime information.
Thanks
L
Unfortunately you can't do this :-(. It is a well known limitation of JVM/bytecode - argument names can't be obtained using reflection, as they are not always stored in bytecode (in the contrary to method/class names).
As a workaround several frameworks/specification introduce custom annotations over arguments like WebParam (name property) or PathParam.
For the time being all you can get without annotations is an array of values.
Check the implementations of org.springframework.core.ParameterNameDiscoverer.
Annotations like #RequestParam used by spring inspect the parameter name if no value is set. So #RequestParam String foo will in fact fetch the request parameter named "foo". It uses the ParameterNameDiscoverer mechanism. I'm just not sure which of the implementations are used, by try each of them.
The LocalVariableTableParameterNameDiscoverer reads the .class and uses asm to inspect the names.
So, it is possible. But make sure to cache this information (for example - store a parameter name in a map, with key = class+method+parameter index).
But, as it is noted in the docs, you need the debug information. From the docs of #PathVariable:
The matching of method parameter names to URI Template variable names can only be done if your code is compiled with debugging enabled. If you do not have debugging enabled, you must specify the name of the URI Template variable name in the #PathVariable annotation in order to bind the resolved value of the variable name to a method parameter
So, if you really don't want to include that information, Tomasz Nurkiewicz's answer explains the workaround.
In Java 8 there is a new compiler flag that allows additional metadata to be stored with byte code and these parameter names can be extracted using the Parameter object in reflection. See JDK 8 spec. In newer versions of hibernate org.springframework.core.ParameterNameDiscoverer uses this feature. To use it compile using javac with this flag:
-parameters Generate metadata for reflection on method parameters
Access parameters using reflection's Parameter class.
I am not sure if its a best way, but I added a Annotation on my method:
My Annotation:
#Retention (RetentionPolicy.RUNTIME)
#Target (ElementType.METHOD)
public #interface ReadApi
{
String[] paramNames() default "";
}
#ReadApi (paramNames={"name","id","phone"})
public Address findCustomerInfo(String name, String id, String phone)
{ ..... }
And in the Aspect:
#Around("aspect param && #annotation(readApi)")
public Object logParams(ProceedingJoinPoint pjp,
ReadApi readApi)
{
//use pjp.getArgs() and readApi.paramNames();
}
This is probably a hack but i did not want to compile with more options to get information. Anyways, its working well for me. Only downside is that i need to keep the names in annotation and method in sync.

Java annotation returns cryptic class names

I am somewhat new to Java so perhaps I misunderstand the use cases for annotations in java. My issue is the following:
After annotating a method I receive class names such as $Proxy31 when inspecting the annotations on the method. I am curious why I am receiving class names for my annotations that are similar to this, and what I can do to fix this problem.
Method m = this.remoteServiceClass.getMethod(rpcRequest.getMethod().getName());
RequiredPermission a = m.getAnnotation(RequiredPermission.class);
This returns a null annotation even though I know that the method it is looking up has the RequiredPermission annotation implemented.
for(Annotation a : m.getAnnotations())
{
System.out.println(a.getClass().getName());
}
This prints out the $Proxy31 class names.
Given Annotation a, you need to call annotationType(), not getClass() to determine the type of the annotation. An Annotation object is just a proxy that represents that instance of the annotation on that class.
Object o = ...;
Class c = o.getClass();
Annotation[] as = c.getAnnotations();
for (Annotation a : as) {
// prints out the proxy class name
System.out.println(a.getClass().getName());
// prints out the name of the actual annotation
System.out.println(a.annotationType().getName());
}
When you add annotations in the source code, Java actually creates a bunch of interfaces and classes "under the hood" to allow you (or your tools) to ask the program things about the annotations using restrictions. Method annotations create "dyanmic proxies", and accordingly Java creates classes for you, probably with the name Proxy.
If you are interested in this, read on java.lang.reflect.InvocationHandler and its subtype, AnnotationInvocationHandler
That being said, you should not have to worry about what Java actually generates. I suspect you are not using reflection correctly to inspect your annotations from within a Java program.
also.. remember to set this:
#Retention(RetentionPolicy.RUNTIME)
on your annotation so that it lives beyond the compile.

Categories

Resources