Spring method annotations null - java

I'am trying to get the methods that are using a custom annotation, but when i get the method, i can't get any annotation from it, every paramter that cites "annotation" is null.
My annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String value() default "";
}
Class using annotation:
public interface Interface {
void doSomething();
}
#Repository
public class ImplementationClass implements Interface {
#Override
#MyAnnotation("some_value")
public void doSomething() {
}
}
Getting annotation:
#Configuration
public class MyAnnotationScanner implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
for (String beanName : applicationContext.getBeanNamesForAnnotation(Repository.class)) {
Object bean = applicationContext.getBean(beanName);
for (Method beanMethod : bean.getClass().getDeclaredMethods()) {
if (beanMethod.isAnnotationPresent(MyAnnotation.class))
// do something
}
}
}
}
I'm able to get the correct method, but when i check with intellij it has no annotations and the "isAnnotationPresent" method always returns false.

I found a solution in using the AnnotationUtils.findAnnotation() method.
Basically for every beanMethod, you get the annotation using the findAnnotation method.
var annotation = AnnotationUtils.findAnnotation(beanMethod, MyAnnotation.class);

I was using isAnnotationPresent method but it was always returning false. I have then used AnnotationUtils.findAnnotation(method, <Annotation.class>)
Here is how I am finding method that is using custom annotation:
for (String beanName : applicationContext.getBeanDefinitionNames()) {
Object bean = applicationContext.getBean(beanName);
Class<?> objClass = bean.getClass();
for (Method m : objClass.getDeclaredMethods()) {
Annotation a = AnnotationUtils.findAnnotation(m, <Annotation_NAME>.class);
if (a != null) {

Related

Get the annotation on method from the instance that the method created

Here is an example
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface Annotation {
}
#Configuration
public class Configuration {
#Bean
#Annotation
public Test getTest() {
return new Test();
}
}
public class Test() {
public void test() {
// how can get the annotation `#Annotation` here?
}
}
Here is what I have tried getClass().getAnnotations() but this returns empty array. I can see why since getClass() return Test.class which does not have the annotation. How can I get the method that creates this instance and then get the annotation?
You could, in theory, inspect the current Thread stack to determine the name of your caller, then look up the class definition, locate the method, and read its annotations:
var t = Thread.currentThread().getStackTrace()[2];
var className = t.getClassName();
Class<?> clazz;
try {
clazz = Test.class.getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Caller was loaded by a different ClassLoader :-(");
}
for (var method : clazz.getDeclaredMethods()) {
if (method.getName().equals(t.getMethodName())) {
return method.getAnnotation(YourAnnotation.class).value();
}
}
throw new RuntimeException("Method not found - I might have found the wrong class definition");
However:
inspecting the stack is rather slow, in particular if the stack is deep
inspecting the stack is brittle with respect to refactorings (people don't expect that factoring out code into a utility method will change behaviour)
the compiler can not check that the caller provides the required annotation
this only works reliably if all code is loaded by the same ClassLoader
this can not distinguish overloaded methods
This is therefore a rather brittle hack. Are you sure that there is no better option? For instance, requiring the caller to pass the value as a method parameter would have none of these shortcomings ...
You can use ConfigurableListableBeanFactory to get metadata about any Bean by name. Use BeanNameAware interface to retrieve Bean name.
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Qualifier
public #interface CustomAnnotation {
}
#org.springframework.context.annotation.Configuration
public static class ContextConfiguration {
#Bean(name = "TEST")
#CustomAnnotation
public TestObject getTest() {
return new TestObject();
}
}
public class TestObject implements BeanNameAware {
private String beanName;
#Autowired
ConfigurableListableBeanFactory beanFactory;
#Override
public void setBeanName(String name) {
this.beanName = name;
}
public void test() {
CustomAnnotation customAnnotation = (CustomAnnotation) getBeanAnnotation(beanName, CustomAnnotation.class);
}
private Annotation getBeanAnnotation(String beanName, java.lang.Class<? extends Annotation> clazz) {
Annotation annotation = null;
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if( beanDefinition != null && beanDefinition.getSource() instanceof StandardMethodMetadata) {
StandardMethodMetadata metadata = (StandardMethodMetadata) beanDefinition.getSource();
annotation = Arrays.stream(metadata.getIntrospectedMethod().getDeclaredAnnotations()).filter(annot -> annot.annotationType().equals(clazz)).findFirst().orElse(null);
}
return annotation;
}
}

How force BeanPostProcessor proxy to look up on classes not interfaces?

I am not sure if question is correctly asked because I am still newbie in this stuff.
I want to complete the following scenario using a BeanPostProcessor:
Filter all beans that are marked with #Service annotation.
Filter all methods that have the marker annotation #Refreshable over themselves.
Perform the specified method on the return objects of these methods.
Below is my working example:
#Retention(RUNTIME)
public #interface Refreshable {
}
public interface VisitServiceI {
#Refreshable
VisitDtoOut addVisitToPatient(UUID idPatient, VisitDtoIn visitDtoIn);
}
public interface RefreshableDto {
void copyId();
}
#Component
public class MethodBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof VisitServiceI) {
ProxyFactory factory = new ProxyFactory(bean);
factory.addInterface(VisitServiceI.class);
factory.addAdvice((AfterReturningAdvice) (returnValue, method, args, target) -> {
if (method.isAnnotationPresent(Refreshable.class)) {
var refreshableDto = (RefreshableDto) returnValue;
if (refreshableDto != null) {
refreshableDto.copyId();
}
}
});
factory.setExposeProxy(true);
return factory.getProxy();
}
return bean;
}
}
Is this possible to get rid of that useless interfaces like: VisitServiceI
I want to force BeanPostProcessor to somehow works when I will give him standard classes instead of interfaces of them.
Ditch the BeanPostProcessor and just write an aspect instead, let Spring do the heavy lifting.
#Aspect
#Component
public RefreshableAspect {
#AfterReturn("within(#Service) && #annotation(#Refreshable)", returning="retVal")
public void refresh(Object retVal) {
if (retVal instanceof RefreshableDto) {
((RefreshableDto) retVal).copyId();
}
}
}
Something like that will accomplish what you need without interfaces and without an additional BeanPostProcessor.
But if you really want the complex route do something like this
#Component
public class MethodBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (AnnotationUtils.findAnnotation(bean.getClass(), Service.class) != null) {
ProxyFactory factory = new ProxyFactory(bean);
factory.setProxyTargetClass(true);
factory.addAdvice((AfterReturningAdvice) (returnValue, method, args, target) -> {
if (method.isAnnotationPresent(Refreshable.class)) {
var refreshableDto = (RefreshableDto) returnValue;
if (refreshableDto != null) {
refreshableDto.copyId();
}
}
});
factory.setExposeProxy(true);
return factory.getProxy();
}
return bean;
}
}

Get annotation information from JAX-RS annotation [duplicate]

I was wondering if it is possible to do the following trick with jersey restful resources:
I have an example jersey resource:
#Path("/example")
public class ExampleRessource {
#GET
#Path("/test")
#CustomPermissions({"foo","bar"})
public Response doStuff() {
//implicit call to checkPermissions(new String[] {"foo","bar"})
}
private void checkPermissions(String[] permissions) {
//stuff happens here
}
}
What I want to achieve is: before executing each resource's method to implicitly check the rights from the annotation by calling the checkPermissions method without actually writing the call inside the method body. Kind of "decorating" each jersey method inside this resource.
Is there an elegant solution? For example with jersey Provider?
Thx!
With Jersey 2 can use ContainerRequestFilter.
#Provider
public class CheckPermissionsRequestFilter
implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext crc) throws IOException {
}
}
We can get the annotation on the called method through the ResourceInfo class
#Context
private ResourceInfo info;
#Override
public void filter(ContainerRequestContext crc) throws IOException {
Method method = info.getResourceMethod();
CheckPermissions annotation = method.getAnnotation(CheckPermissions.class);
if (annotation != null) {
String[] permissions = annotation.value();
}
}
You can use this annotation
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface CheckPermissions {
String[] value();
}
And annotate the resource class or the resource method with #CheckPermissions({...})
See more at Filters and Interceptors
UPDATE
The annotation above allows for annotating classes also. Just for completeness, you'll want to check the class also. Something like
Class resourceClass = info.getResourceClass();
CheckPermissions checkPermissions = resourceClass.getAnnotation(CheckPermissions.class);
if (checkPermissions != null) {
String[] persmission = checkPermissions.value();
}

pass result of annotation work to annotated method

Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Multipart {
Class acceptClass();
}
Annotated method:
#Multipart (acceptClass = SomeClass.class)
public void someMethod(SomeClass a){
//do stuff..
}
MultipartAspect:
#Aspect
public class MultipartAspect {
#Autowired(required=true)
private HttpServletRequest request;
#Pointcut(value = "#annotation(Multipart)", argNames = "multipart")
public void before(JoinPoint jp, Multipart multipart) {}
#Before("before()")
public SomeClass doStuffBeforeThing() {
SomeClass sc = new SomeClass(); //object of passed class
//do something..
return sc; //return this to annotated method(somemethod)
}
}
I want before method works execute annotation, create object of passed class(SomeClass) and the pass object of this class to annotated method. Could I do this?
You should use #Around advice instead of #Before.

Spring AOP CGLIB proxy's field is null

Description
Using the vlcj component, the custom component appears as a result of the AOP proxy object null.
MediaList Class
public class MediaList {
private libvlc_media_list_t mediaListInstance;
public MediaList(LibVlc libvlc, libvlc_instance_t instance, libvlc_media_list_t mediaListInstance) {
this.libvlc = libvlc;
this.instance = instance;
createInstance(mediaListInstance);
}
private void createInstance(libvlc_media_list_t mediaListInstance) {
logger.debug("createInstance()");
if(mediaListInstance == null) {
mediaListInstance = libvlc.libvlc_media_list_new(instance);
}
else {
libvlc.libvlc_media_list_retain(mediaListInstance);
}
this.mediaListInstance = mediaListInstance; // <- assignment
logger.debug("mediaListInstance={}", mediaListInstance);
mediaListEventManager = libvlc.libvlc_media_list_event_manager(mediaListInstance);
logger.debug("mediaListEventManager={}", mediaListEventManager);
registerEventListener();
}
public final libvlc_media_list_t mediaListInstance() {
return mediaListInstance; // <- proxy object return null, if use aop
}
}
Custom MediaList Class
public class TestMediaList extends MediaList {
public TestMediaList(LibVlc libvlc, libvlc_instance_t instance) {
super(libvlc, instance);
}
public void xTest(String test){
System.out.println(test);
}
}
Spring Configuration Class
#Configuration
public class PlayerBeanConfig {
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
#Resource
public TestMediaList testMediaList(LibVlc libvlc, libvlc_instance_t instance) {
return new TestMediaList(libvlc, instance);
}
}
AOP Configuration Class
#Aspect
public class MediaListAspect {
#Pointcut("execution(* TestMediaList.xTest(..))")
private void anyMethod() {
}
#Around("anyMethod()")
public Object lockAndUnlock(ProceedingJoinPoint joinPoint) throws Throwable {
Object object = joinPoint.proceed();
return object;
}
}
Test Code
public static void main(String[] args) {
boolean b = new NativeDiscovery().discover();
if (b) {
springContext = new AnnotationConfigApplicationContext(PlayerBeanConfig.class);
String[] kkk = new String[]{};
TestMediaList list = springContext.
getBean(TestMediaList.class, LibVlc.INSTANCE, LibVlc.INSTANCE.libvlc_new(kkk.length, kkk));
System.out.println(list.mediaListInstance()); // <- proxy object return null
} else {
logger.error("Cannot find vlc lib, exit application");
}
}
I try to single step tracking, when TestMediaList the build is complete. MediaListInstance () of the method to return to normal values, but when the spring returns to the proxy object, null is returned. At the same time, I also try to return the value correctly if you don't use AOP.
Therefore, I determine the basic problem in AOP dynamic proxy, but I don't know why, did not previously encountered such a situation.
Minimal example
all class in package : vod.demo
TargetClass
public class TargetClass {
private String returnValue;
public TargetClass() {
this.returnValue = "Hello World";
}
public final String test() {
System.out.println("TargetClass.test();");
return returnValue;
}
}
Aspect Class
#Aspect
public class AspectClass {
#Pointcut("execution(* vod.demo.TargetClass.*(..))")
private void targetMethod() {
}
#Around("targetMethod()")
public Object aroundTarget(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("AspectClass.aroundTarget();");
return joinPoint.proceed();
}
}
Spring Config Class
#Configuration
#EnableAspectJAutoProxy
#Import(AspectClass.class)
public class SpringConfig {
#Bean
public TargetClass target() {
return new TargetClass();
}
}
Client Class
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
TargetClass target = context.getBean(TargetClass.class);
System.out.println("Client invoke:" + target.test()); // <- output null
}
}
This is a combination of potentially unexpected behaviors. First, Spring uses CGLIB to proxy your beans for AOP. CGLIB proxies are instances of a dynamic subtype of your class that delegate all method calls to a real instance of your class. However, even though the proxy is of a subtype, its fields are not initialized (ie. your TargetClass super constructor is not invoked). A lengthier explanation can be found here.
Additionally, your method
public final libvlc_media_list_t mediaListInstance() {
return mediaListInstance; // <- proxy object return null, if use aop
}
or
public final String test() {
System.out.println("TargetClass.test();");
return returnValue;
}
are final. CGLIB therefore cannot override them to delegate to the real instance. This would be hinted at in Spring logs. For example, you would see
22:35:31.773 [main] INFO o.s.aop.framework.CglibAopProxy - Unable to proxy method [public final java.lang.String com.example.root.TargetClass.test()] because it is final: All calls to this method via a proxy will NOT be routed to the target instance.
Put all of the above together and you get a proxy instance where the field is null and where the proxy cannot delegate to the real instance's method. So your code will actually invoke
public final String test() {
System.out.println("TargetClass.test();");
return returnValue;
}
for an instance where the returnValue field is null.
If you can, change your method, remove the final modifier. If you can't, you'll have to rethink your design.

Categories

Resources