I am running this code into a junit test. However, the annotations are not found and nothing is outputted. What could cause this.
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Datafield {
}
public class EntityTest
{
#Datafield
StandardFieldText username;
#Test
public void TestEntityAnnotation() throws NoSuchFieldException, SecurityException
{
EntityTest et = new EntityTest();
Annotation[] annos = et.getClass().getAnnotations();
for(Annotation a : annos)
System.out.println(a);
}
}
You're requesting the annotations of EntityTest class which indeed has no annotations.
In order to get the annotation above the field you should try:
Field f = ep.getDeclaredField("username");
Annotation[] annos = f.getDeclaredAnnotations();
You requested the annotations of the class itself. You should loop through methods, fields, etc. to retrieve the annotations of these elements: http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html
For instance:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Datafield {
}
public class EntityTest
{
#Datafield
StandardFieldText username;
#Test
public void TestEntityAnnotation() throws NoSuchFieldException, SecurityException
{
EntityTest et = new EntityTest();
for(Method m : et.getClass().getDeclaredMethods()) {
Annotation[] annos = m.getDeclaredAnnotations();
for(Annotation a : annos)
System.out.println(a);
}
}
}
Related
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;
}
}
I created annotation to check permissions on the APIs. Annotation worked when placed in class or method. But when I use it for both class and method at the same time, the annotation placed on the method is ignored
Here is an example:
#RestController
#RequestMapping("/user")
#CustomAnnotation(permission="manageUser")
public class UserController {
#CustomAnnotation(permission="updateUser")
#PutMapping("/{id}")
public UserDto getUser(HttpServletRequest request) {
...
}
}
Now #CustomAnnotation(permission="updateUser") in the getUser() method will be ignored and only execute annation in class UserController
My custom code
#Target(value = {ElementType.METHOD, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface CustomAuthorize {
public String permission() default "";
public String attribute() default "";
}
#Aspect
#Component
public class CustomAnnotationAspect {
#Autowired
CustomAuthorizationImpl authBean;
#Pointcut("#annotation(customAuthorize) || #within(customAuthorize)")
public void pointcutForCustomAnnotation(CustomAuthorize customAuthorize) {
// Do nothing.
}
#Around("pointcutForCustomAnnotation(customAuthorize)")
public Object customAspect(ProceedingJoinPoint pjp, CustomAuthorize customAuthorize) throws Throwable {
if (Objects.isNull(customAuthorize)) {
System.out.println("Call from method");
// Check permission
return pjp.proceed();
}
System.out.println("Call from class");
ExpressionParser elParser = new SpelExpressionParser();
if (!customAuthorize.permission().isBlank()) {
// check permission
}
return pjp.proceed();
}
}
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) {
I want to use byte-buddy to intercept methods with java annotation. A simplified example is as follows:
Annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface TraceLog {
String logName() default "";
}
The Interceptor class is
public class TraceLogInterceptor {
#RuntimeType
public static Object handleTraceLog(#Origin Method method,
#AllArguments Object[] args,
#SuperCall Callable<?> superCall) throws Exception {
TraceLog traceLogAnnotation = method.getAnnotation(TraceLog.class);
System.out.println(traceLogAnnotation.logName());
superCall.call();
}
}
The service class is
public class HelloServiceImpl {
#TraceLog(logName = "myLog")
public String writeHello(String name) {
return "Hello " + name;
}
}
The test method is
#Test
public void writeHello() throws IllegalAccessException, InstantiationException, IOException, NoSuchMethodException, InvocationTargetException {
DynamicType.Unloaded<?> dynamicType =
new ByteBuddy()
.subclass(HelloServiceImpl.class)
.method(ElementMatchers.isAnnotatedWith(TraceLog.class))
.intercept(MethodDelegation.to(TraceLogInterceptor.class))
.make();
HelloServiceImpl helloService = (HelloServiceImpl) dynamicType.load(this.getClass().getClassLoader())
.getLoaded()
.getConstructor()
.newInstance();
System.out.println(helloService.writeHello("XXXXX"));
}
It can work. However, I want to know how to inject the interceptor(TraceLogInterceptor) to spring. And I also want to know how to intercept all methods with the #TraceLog annotation and generate the corresponding proxy class without specifying the superclass(new ByteBuddy().subclass(xxx)). Thanks!
This is my own Annotation:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.SOURCE)
public #interface Autowire {
public String id();
}
then, I define a class, and set a property with the annotation:
#Component(id="businessObject")
public class BusinessObject {
#Autowire(id="dataAccessInterface")
private DataAccessInterface dai;
public void print() {
System.out.println(dai.queryFromTableA());
}
public void setDai(DataAccessInterface dai) {
this.dai = dai;
}
}
The #Component is also mine.
after all, I define a tracker:
public class BeanFactory {
private HashMap<String, Object> beanPool;
private HashMap<String, String> components;
public BeanFactory() {
beanPool = new HashMap<>();
scanComponents();
}
private void scanComponents() {
components = ComponentScanner.getComponentClassName("com.oolong.javase.annotation");
}
public Object getBean(String id) throws ClassNotFoundException,
InstantiationException, IllegalAccessException, NoSuchMethodException,
SecurityException, IllegalArgumentException, InvocationTargetException {
if (beanPool.containsKey(id)) {
return beanPool.get(id);
}
if (components.containsKey(id)) {
Object bean = Class.forName(components.get(id)).newInstance();
bean = assemblyMember(bean);
beanPool.put(id, bean);
return getBean(id);
}
throw new ClassNotFoundException();
}
private Object assemblyMember(Object obj) throws ClassNotFoundException,
InstantiationException, IllegalAccessException, NoSuchMethodException,
SecurityException, IllegalArgumentException, InvocationTargetException {
Class cl = obj.getClass();
for (Field f : cl.getDeclaredFields()) {
Autowire at = f.getAnnotation(Autowire.class);
if (at != null) {
Method setMethod = cl.getMethod("set" + captureName(f.getName()), f.getType());
setMethod.invoke(obj, getBean(at.id()));
}
}
return obj;
}
public static String captureName(String name) {
char[] cs=name.toCharArray();
cs[0]-=32;
return String.valueOf(cs);
}
}
the problem is, when I got the field annotation with #Autowire, and want to get the annotation, I can't get the annotation:
Autowire at = f.getAnnotation(Autowire.class);
the at is null.
why?
(Sorry, I am not good at English!)
You are using the SOURCE retention policy. The quote from sdk:
/**
* Annotations are to be discarded by the compiler.
*/
Try to use the RUNTIME retention policy instead:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Autowire {
public String id();
}
According to documentation:
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*