Is it possible to receive annotation value inside a field, that was annotated?
Imagine that I have this interface:
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {
String value();
}
And I have such implementation:
class SomeClass {
#MyAnnotation("Annotation")
private MyClass myClass;
}
What I want to understand is: is it possible to receive value of MyAnnotation inside MyClass? I want to implement a method inside class MyClass, which will return a value of assigned annotation. So, that myClass.getAssignedAnnotationValue() will return "Annotation".
If it is not possible, please inform me.
is it possible to know annotation value inside annotated field
It's not possible.
You may have 2 different classes
class SomeClass {
#MyAnnotation("Annotation")
private MyClass myClass;
public SomeClass(MyClass myClass) {
this.myClass=myClass;
}
}
and
class SomeClassNo Annotation {
private MyClass myClass;
public SomeClassNo(MyClass myClass) {
this.myClass=myClass;
}
}
Then you create an instance of MyClass
MyClass instance = new MyClass();
then 2 classes instances
new SomeClass(instance) and new SomeClassNo(instance) both have reference to the same instance. So the instance does not know whether the reference field annotated or not.
The only case when it is possible is to pass somehow the container reference to MyClass.
There is no straight forward way of implementing what you are asking.
WorkAround:
Limitations:
This workaround doesn't enforce any kind of compile time check and it is completely your responsibility to handle it.
This only works if MyClass is going to be a spring bean.
class MyClass {
public String annotatedValue;
}
You can write a Spring BeanPostProcessor the following way.
public class SampleBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
if (field instanceof MyClass && field.isAnnotationPresent(MyAnnotation.class)) {
String value = field.getDeclaredAnnotation(MyAnnotation.class).value();
((MyClass) field).annotatedValue = value;
return bean;
}
}
return bean;
}
}
The above BeanPostProcessor will be called for every bean during the app start up. It will check all the fields of a given bean to see if the field is of type MyClass. If it is, it will extract the value from the annotation and set it in the annotatedValue field.
The problem with this approach is that you can use MyAnnotation on any property in any class. You cannot enforce the annotation to be used only on MyClass.
Related
Is it possible, when using custom oval annotation and custom class for check, to access the annotation and retrieve the used annotation attributes ?
Reference for oval: https://sebthom.github.io/oval/USERGUIDE.html#custom-constraint-annotations
Minimal example
Lets assume we have class Foo.
It has two annotated fields.
Each time, the annotation has a different myValue – a and b.
class Foo {
#CustomAnnotation(myValue = "a")
public String first;
#CustomAnnotation(myValue = "b")
public String second;
}
This is the annotation.
It is noted that a check should be performed using MyCheck.class, also setting some default value for myValue.
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
#Constraint(checkWith = MyCheck.class)
public #interface CustomAnnotation {
String myValue() default "";
}
Now we want to use oval to validate this field.
Most importantly, we want to extract the value a or b from the annotation's myValue and use it inside our validation logic.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
// how to get the value of `myValue`, which is `a` or `b` or empty string as default
}
}
What I have tried and failed:
validatedObject is Foo.class. You can easily get its fields and annotations. However, there is no way to differentiate between the two annotations.
valueToValidate is in this case String value – what first or second holds.
context not useful, you can get compile time type from it, which is String
validator not useful ?
After some digging in the superclass I have found that you can override method
configure
This method gets as the only parameter the annotation that is currently being checked at the field.
You can then read the myValue.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
private String myValue;
#Override
public void configure(CustomAnnotation customAnnotation) {
super.configure(customAnnotation);
this.myValue = customAnnotation.myValue();
}
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
if (myValue.equals("a")) {}
else if (myValue.equals("b")){}
else {}
}
I am using Springs BeanPostProcessor to loop through all the declared fields for the passed in bean, and if any of them has a particular annotation, then I want to modify that fields value in some way.
The code to do this (omitting the annotation declaration) looks roughly like the below, and seems work correctly (so far):
#Component
public class AnnotationProcessingBeanPostProcessor implements BeanPostProcessor {
#Override
public final Object postProcessAfterInitialization(final Object bean, final String beanName) {
return bean;
}
#Override
public final Object postProcessBeforeInitialization(Object bean, final String beanName) {
Field[] fields = bean.getClass().getDeclaredFields();
List<Field> fieldsWithAnno = new ArrayList<>();
for (Field field : fields) {
if (field.isAnnotationPresent(SomeAnno.class)) {
try {
field.setAccessible(true);
field.set(bean, "HELLO WORLD!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
return bean;
}
}
My concern is, I believe the beans themselves are wrapped in some sort of proxy object, so the actual instance of the bean here is not a DIRECT instance of my particular bean.
So could doing this sort of thing cause some unforseen issue?
What I am ACTUALLY trying to achieve
I want to annotate fields with something like:
#GetStringFromFile(fileName = "whatever.txt")
private String somePrivField;
Then when I find a field with that annotation, I will read the file in, and inject it's String value, and if the file doesn't exist or can't be read properly, throw an exception, and stop my spring app from starting up.
I need the ability to find the parameterized type of a method parameter on a Spring bean.
I'm using Spring Boot 2.0.1.
I have a Spring bean that looks like the following:
public class MyBean {
public void doFoo(List<Integer> param) {...}
}
I need to be able to find out the parameterized type on the param parameter. Normally I could just do this:
MyBean myBean = getMyBean();
ParameterizedType pt = (ParameterizedType)myBean
.getClass()
.getMethod("doFoo", List.class)
.getGenericParameterTypes()[0];
Class<?> listOfX = (Class<?>)pt.getActualTypeArguments()[0];
However, when doing this against a Spring bean, the getGenericParameterTypes() method always returns an array of Class objects instead of an array of Type objects.
The reason for this is that Spring uses CGLIB extensively to provide runtime-recompilation of classes to support AOP. So instead of getting an instance of MyBean, I instead get an instance of MyBean$$EnhancerBySpringCGLIB$$xxxxxxxx, and that "recompiled" class loses all generics information (apparently because CGLIB was written before generics existed and is no longer actively supported?).
I could use some ideas on how I might be able to get to the parameterized information on this parameter if given nothing more that a Spring bean instance. I've been trying to look for ways to get access to the MyBean.class from MyBean$$EnhancerBySpringCGLIB$$xxxxxxxx and from there be able to find the real method that I can use reflection against. But I haven't found a solution yet. I'm trying to avoid Class.forName(...) at all costs.
It looks like the following code works in my particular case to find the underlying real class:
if (myBean instanceof org.springframework.aop.framework.Advised) {
Class<?> realClass = ((Advised)r).getTargetClass();
}
From there I can use standard reflection. But I'm not sure how kosher this solution is or whether I can expect all Spring beans to implement the Advised interface.
One possible solution is to implement BeanPostProcessor.
You can get access to underlying class before bean initialization.
Here example:
#Component
public class ParametrizedBeanFactoryPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// bean is your actual class
try {
if ("myBean".equals(beanName)) {
ParameterizedType pt = (ParameterizedType) bean
.getClass()
.getMethod("doFoo", List.class)
.getGenericParameterTypes()[0];
Class<?> listOfX = (Class<?>) pt.getActualTypeArguments()[0];
System.out.println(listOfX); //class java.lang.Integer
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// bean is proxy object
return bean;
}
}
I have multiple class with a Qualifier that I created:
#ServiceComponent(restPath = "/trucks")
public class TruckService {
}
#ServiceComponent(restPath = "/cars")
public class CarService {
}
here is the Qualifier (not important for the question)
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({TYPE, FIELD})
public #interface ServiceComponent {
public boolean exposeAsRest() default true;
#Nonbinding public String restPath() default "";
#Nonbinding public String restGetPrefix() default "get,find,all";
#Nonbinding public String restPostPrefix() default "create,new,post";
}
in another class, I inject those instance using javax.enterprise.inject.Instance<>
class SomeConfigurationClasss {
#Inject
#ServiceComponent()
Instance<Object> _restComponents;
#Override
public void iterate() throws Exception {
//iterate
for(Object obj : _restComponents){
somefuncion(obj);
}
//List.of(_restComponents)
//.flatMap(obj -> somefuncion(obj));
}
}
if I execute the "normal" iteration (for...) I get the Object (TruckService or CarService) given as parameter to the somefunction().
but if I use javaslang's List.of(...) I get the Instance itself. Which I think it's the expected behavior
Is there a possibility to use List.of on a Instance that can contain one or multiple bean (depending on the injection binding). (I already try to call iterator(), select() on the Instance)
Instance<T> extends Iterable<T> so you should use List#ofAll(Iterable)
I am Using Spring 3.2.2 and was wondering if there was a way to inject beans by class type without explicitly giving them a string name. Ex:
#Named
public MyClass{
}
#Named
public MyOtherClass extends MyClass{
}
#Named
public class Foo{
public void blah(){
MyClass myClass = context.getBean(MyClass.class);
}
}
This will generate:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [MyClass] is defined: expected single matching bean but found 2: myClass,myOtherClass
Is there a way to say "Use the one that matches the class name exactly" without using String names?
In other words I don't want to do:
#Named("MyClass")...
#Named("MyOtherClass")...
MyClass myClass = context.getBean("MyClass");
Thanks!
This is what § 5.4.5 of Spring manual suggest whenever you encounter non-unique dependency bean definition:
Abandon autowiring in favor of explicit wiring.
Avoid autowiring for a bean definition by setting its autowire-candidate attributes to false as described in the next section.
Designate a single bean definition as the primary candidate by setting the primary attribute of its element to true.
If you are using Java 5 or later, implement the more fine-grained control available with annotation-based configuration, as described in Section 5.9, “Annotation-based container configuration”.
For the example, you could do something like:
#Named
public class Foo{
public void blah(){
MyClass myClass = getBean(MyClass.class);
}
private <T> T getBean(Class<T> type) {
return context.getBean(Introspector.decapitalize(type.getSimpleName()), type);
}
}
But this will not work when using #Inject or #Autowire.
To force strict class matching when autowiring, you could replace the default AutowireCandidateResolver on the BeanFactory with a BeanFactoryPostprocessor, but don't seem a good idea as #Resource or #Qualify can solve the NUBDE problem.
For example: (Not tested)
public class StrictClassAutowireCandidateResolver implements AutowireCandidateResolver {
#Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
if (!bdHolder.getBeanDefinition().isAutowireCandidate()) {
return false;
}
if (descriptor == null) {
return true;
}
String className = null;
if (descriptor.getField() != null) {
className = descriptor.getField().getType().getName();
}
else if (descriptor.getMethodParameter() != null) {
className = descriptor.getMethodParameter().getParameterType().getName();
}
Class<?> clazz = null;
try {
clazz = Class.forName(className);
}
catch (Exception e) {
return false;
}
if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
// have no chances to be strict, let BeanFactory to find implementations.
return true;
}
return bdHolder.getBeanDefinition().getBeanClassName().equals(className);
}
#Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
}
Try using #Component (Service, Repository, Controller) on your classes, and #Autowired when you're injecting a bean.
EDIT: my bad, I didn't read the question too well. The problem is that you're actually having 2 instances of MyClass (since MyOtherClass extends from MyClass). Hence there's no other way than giving the classes names, or you'll always end up with NoUniqueBeanDefinitionExceptions.