If a class is annotated with an annotation, does the definition of that annotation have to be in the runtime classpath in order to use the class? For example, given the annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface Component {}
can I execute the program
#Component
public class Test {
public static void main(String[] args) {
System.out.println("It worked!");
}
}
without having Component.class in the classpath? (In my test I could, but is this behavior defined by the spec?)
I ask because there are conflicting claims whether using an annotation from a library creates a dependency on that library.
Runtime annotations are meta information to be processed by annotation processor at the runtime. If there is an access to annotation at runtime, you definitely add annotations in the classpath. For example junit definitely need the annotations in the class path determine test methods.
If there is no processing of annotation is done, there is no need to have it the classpath.
Yes you can execute program without having Component.class in the classpath. More details here: Why doesn't a missing annotation cause a ClassNotFoundException at runtime?
Related
We have feature files that have long tests that validate multiple things written in non-english language, the Given -> When -> Then structure doesn't make sense.
I tried replacing feature file keywords with *, and that works just fine, however here are the problems:
When writing a new step in a form of * Some step, and using Alt->Enter shortcut to generate a step definition, IntelliJ IDEA does... Nothing. It only opens the file where I wanted to put the definition without any added code. I've updated IDE and plugins to be latest.
There doesn't seem to be any way to have a "universal" annotation to use for asterisk steps, only Given, When, Then, And, But exists. It's not very logical to have a * Some step feature and #Given("Some step") definition.
Is there any workaround that I might use?
I think you can achieve this with few coding steps.
Say you have the scenario like this:
Feature: Generic annotation
Scenario: Testing annotations
* having asterix
* use custom generic annotation
Add custom annotation to your sources
package click.webelement.cucumber;
import io.cucumber.java.StepDefinitionAnnotation;
import io.cucumber.java.StepDefinitionAnnotations;
import org.apiguardian.api.API;
import java.lang.annotation.*;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#StepDefinitionAnnotation
#Documented
#Repeatable(MyStep.MySteps.class)
#API(status = API.Status.STABLE)
public #interface MyStep {
String value();
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#StepDefinitionAnnotations
#Documented
#interface MySteps {
MyStep[] value();
}
}
Now use it in your Step Definition
package click.webelement.cucumber;
public class StepDef {
#MyStep("having asterix")
public void doOne(){
System.out.println("Running having asterix");
}
#MyStep("use custom generic annotation")
public void doTwo(){
System.out.println("Running use custom generic annotation");
}
}
UPD
In order to make your Idea plugin work with your custom annotation you need to place that annotation to
io.cucumber.java.LANG package where LANG is the sub-package for your chosen language.
To make everything work as default you place it to
io.cucumber.java.en
I would like to use custom Java annotation to insert a value in a private class property using Spring AOP (and/or AspectJ). Quick example:
MyAnnotation.java:
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD })
public #interface MyAnnotation {
}
MyController.java:
public class MyControllerImpl implements MyController {
...
#MyAnnotation
private String var1;
#Override
public String getVarExample() {
// imagine this is a REST API that gets called on #GET
// request and returns a string
System.out.println(this.var1); // <-- I'd like this to be "helloworld"
// this is just for illustration
// of course, I will want to do
// something more meaningful with
// the 'var1' variable
return "ok"; <- unimportant for this example
}
...
MyAspect.java:
#Aspect
#Component
public class MyAspect {
#Pointcut("#annotation(com.mypackage.annotation.MyAnnotation)")
public void fieldAnnotatedWithMyAnnotation() {
}
#Around("fieldAnnotatedWithMyAnnotation()")
public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
// problem #1 - the program never enters here
// problem #2 - I need to figure out how to set up the var1 here
// to "helloworld" , how?
return pjp.proceed();
}
...
}
What would I like to happen?
I will call and get into the getVarExample() and after it returns I would like to see "helloworld" in console or log. I would like to somehow set the var1 to a custom value using AOP. Any property variable that will be annotated with #MyAnnotation will be set to "helloworld". I hope the example above is clear.
What have I tried?
I made sure there is no typo in the package names, also fiddled with different AOP advice annotations like #Around and #Before. I also tried different targets in the MyAnnotation and ended up with ElementType.FIELD which should be correct.
Can you help me to get it working?
I know this can be done, but couldn't find any working example online. Again, I would like to see 2 answers:
1. How to get the pointcut to trigger on MyController entrance? I want to catch a breakpoint inside the enrichVar1(..) method of the MyAspect class.
2. How can I modify the annotated var1 value inenrichVar1(..) method of the MyAspect class?
I don't know what I am doing wrong. Any help will be greatly appreciated. Thank you!
The AOP is set up correctly in my project. I know that because I am already using AOP for different things (logging for example).
Update #1:
Please, note there are not getters or setters for the var1 private variable. The variable will be only used within the MyControllerImpl. To illustrate this better I changed the return value of the getVarExample.
Like I said in my comment:
The pointcut designator #annotation() intercepts annotated methods, not annotated fields. For that, native AspectJ has get() and set(). I.e., the pointcut would also need to be changed if migrating to AspectJ. But I agree that sticking to Spring AOP and annotating getter methods instead of fields is probably enough here.
But because you insist that you want to keep the controller class unchanged, here is the native AspectJ solution. Please read chapter Using AspectJ with Spring Applications for how to configure that with #EnableLoadTimeWeaving and JVM parameter -javaagent:/path/to/aspectjweaver.jar.
In order to demonstrate that this solution really does work independently of Spring, I am using no Spring classes or annotations at all, only POJOs and native AspectJ. You can simply do the same within your Spring application. Please note that native AspectJ aspects do not need #Component annotations, in contrast to Spring AOP aspects.
package de.scrum_master.app;
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 })
public #interface MyAnnotation {}
package de.scrum_master.app;
public interface MyController {
String getVarExample();
}
package de.scrum_master.app;
public class MyControllerImpl implements MyController {
#MyAnnotation
private String var1;
#Override
public String getVarExample() {
System.out.println(this.var1);
return "ok";
}
}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
MyController myController = new MyControllerImpl();
myController.getVarExample();
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class MyAspect {
#Pointcut("get(#de.scrum_master.app.MyAnnotation * *)")
public void fieldAnnotatedWithMyAnnotation() {}
#Around("fieldAnnotatedWithMyAnnotation()")
public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(pjp);
return "helloworld";
}
}
When running Application, the console log is going to be:
get(String de.scrum_master.app.MyControllerImpl.var1)
helloworld
The AspectJ manual explains the syntax of field get and set join point signatures and field patterns.
Note: I think that your use case might be a hack rather than a valid application design. You ought to refactor rather than hack into an application like this.
As it goes from Spring docs Spring AOP does support Spring beans' method execution join points. To make field access join points work you need to use AspectJ's backend with load time weaving for AOP.
But for your case it's not required to use field join points, you can put your annotation on the getter and this should work.
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 written an annotation:
#Documented
#Retention(RUNTIME)
#Target(TYPE)
/**
* #author christian
*
*/
public #interface HeraklesObject {
}
I use it in an osgi environment (eclipse oxygen with tycho) in a bundle.
In another bundle (a test fragment to be specific) i check classes from the first bundle for this annotation, using:
class.getAnnotation(HeraklesObject.class)
I am sure, that the queried class has this annotation, but I get always null result.
The retention policy seems right to me, but I am not sure if a class loader issue is present and I don't know how I check this. Or do you see another cause? Thanks
Christian
Try to use
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE })
Also from How to get class annotation in java?
use
Class newClass = X.class;
for (Annotation annotation : newClass.getDeclaredAnnotations()) {
System.out.println(annotation.toString());
}
Does the bundle with the class whose runtime annotations you want to check contain or import the package with the HeraklesObject annotation class?
A class loader will load a class and, if the class of a runtime annotation class is not visible, just ignore the annotation. So you end up with a silent "failure". So check to make sure the package holding the runtime annotation is visible to the bundle holding the annotated class.
If you use Bnd to build the bundle, it should properly import the runtime annotation's package.