I'm working on my first project with Spring and Hibernate and I would like to create a validator for an IP address.
To create the project I use IntelliJ. When I enter the code like this I get an error on the #Target annotation Attribute value must be a class literal
I do understand what this annotation has to do, but if I keep getting this error, it won't work.
I have looked at the documentation about custom constraints here http://docs.jboss.org/hibernate/validator/5.0/reference/en-US/html_single/#validator-customconstraints-simple and I have looked at several Stackoverflow pages, but I can't get this error solved.
import org.hibernate.annotations.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import static java.lang.annotation.ElementType.FIELD;
/**
* Created by johan on 17-5-17.
*/
#Target({ FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = IpAddressValidator.class)
public #interface IpAddress{
String message() default "{ipAddress.invalid}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
What do I need to do to get this working?
You used the wrong #Target. You meant to use java.lang.annotation.Target but imported org.hibernate.annotations.Target. Change the first import to import java.lang.annotation.Target and it should work fine.
Related
Want to create annotation which will contain multiple annotations but want to make it more configurable so that it can be more feasible to use in all scenarios
#SpringBootApplication
#ServletComponentScan(basepackages="com.example.commons.traceability")
#ComponentScan(basePackages={
"com.example.commons.security",
"com.example.commons.restTemplate",
"com.example.commons.logging",
})
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Microservice{
String[] scanBasePackages() default {};
}
I want to use this application in Spring Application file but also want to make the componentScan more configurable so that apart from the default packages other package can also me included in the component scan
#Microservice(scanBasePackages={"com.example.myproject"})
public class MySpringApplciation{
public static void main (final String[] args){
// some code
}
}
so as in the above code if I pass value of scanBasePackages so those packages should also be included in the component scan.
Is there any way to include values of scanBasePackages inside componentScan in custom annotation ?
Annotations on (custom) annotations are called meta-annotations.
In short: you're lucky. While Java does not allow this out of the box, Spring has a special annotation for this sort of thing: #AliasFor.
I had a similar question, and I found the answer here.
And here is a good source.
Specifically in your case, try this:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AliasFor;
#SpringBootApplication
#ServletComponentScan(basePackages="com.example.commons.traceability")
#ComponentScan(basePackages={
"com.example.commons.security",
"com.example.commons.restTemplate",
"com.example.commons.logging",
})
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Microservice{
// >>> TRY THIS <<<
#AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
}
(I myself am less lucky, since I need this sort of thing for reducing my JUnit5 boilerplate annotations, and #AliasFor is only meaningful in Spring.)
According to JSR 308 (Java Type Annotations) it is possible to annotate any type using ElementType.TYPE_USE:
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Target({ TYPE_USE })
#Retention(RUNTIME)
public #interface MyAnnotation {
String value();
}
How to get the annotated value from a function at runtime?
import java.util.function.Consumer;
import org.junit.Assert;
import org.junit.Test;
public class TestFunctionAnnotation {
#Test
public void test() {
Consumer<TestFunctionAnnotation> fun = #MyAnnotation("NoJoke") TestFunctionAnnotation::test;
Assert.assertEquals("NoJoke", fun.getClass().getAnnotatedSuperclass().getAnnotation(MyAnnotation.class));
// expected:<NoJoke> but was:<null>
}
}
Your #MyAnnotation doesn't appear on the class, or the method, but on the use of the type - something that you can't reflect on. Instead, you'd need a hypothetical "reflection" which could examine the code itself, not just the structure of the types.
Instead, you want to either build a compiler plugin which can read that, or add a task listener inside an annotation processor - see https://stackoverflow.com/a/55288602/860630 for some discussion on this. Once you've done that and can read the annotation, you can generate new code which you could then access at runtime, and do whatever it is you are after here.
I have a gradle file
testCompile('junit:junit')
testCompile('org.powermock:powermock-core:1.6.5')
testCompile('org.powermock:powermock-api-mockito:1.6.5')
testCompile('org.powermock:powermock-module-junit4:1.6.5')
And my test file
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(ExperimentService.class)
There seems to be an error with the #RunWith and I have can't seem to find the problem, it just says that '#RunWith' not applicable to method.
What am I doing wrong?
Thanks.
If you see the RunWith.class, target for this annotation is ElementType.Type which means it can only be applied to Class, enum or interface declaration.
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE})
#Inherited
public #interface RunWith {
Class<? extends Runner> value();
}
You cannot apply this annotation over a method.
Oh never mind I found my mistake, it seems that I put the statement within the class.
I am wanting to generically retrieve annotations based on where a provided is retrieving values. Either from a field or getters/setters. So, a provider needs to return information in a Field annotation. There are other annotations planned, so the implementations needs to be generic.
The method is exactly what is in the java.lang.Class class.
import java.lang.annotation.Annotation;
public interface Setter<Instance, Type>
{
<A extends Annotation> A getAnnotation(Class<A> annotationClass);
}
But, when I use the method..
final Field annotation = setter.getAnnotation(Field.class);
I get a complication error..
incompatible types
found : java.lang.annotation.Annotation
required: Field
This makes nearly no sense because Field is an Annotation!
Are there special considerations to give to annotations while implementing generics?
Why isn't this working?? It is exactly like the Java API.
Just for more information, here is the Field annotation.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.METHOD})
public #interface Field
{
/**
* The effective path of the local or result set value to load into the field.
*/
String value() default "";
}
import java.lang.annotation.ElementType.*;
import java.lang.annotation.Target;
#Target({TYPE, FIELD, METHOD})
public #interface Inter1 {
}
I've seen in other annotations like SuppressWarnings and Deprecated that they use an array of ElementType enum directly without using the enum like the above. But when I try to use it in one of my custom annotations, it gives me an error. I need to give it as ElementType.TYPE or ElementType.FIELD for it to work. What is wrong with this code?
you want to use that annotation this way:
import static java.lang.annotation.ElementType.*;
otherwise, this way:
#Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})