Repository: avaje/avaje-inject (java)

Issue:
java.lang.NoClassDefFoundError: io/swagger/v3/oas/annotations/security/SecurityRequirement
stacktrace:
```
Exception in thread "main" java.lang.NoClassDefFoundError: io/swagger/v3/oas/annotations/security/SecurityRequirement
	at cool.houge.wuneng.controller.RoleController$DI.build(RoleController$DI.java:21)
	at cool.houge.wuneng.WunengModule.build_controller_RoleController(WunengModule.java:241)
	at cool.houge.wuneng.WunengModule.build(WunengModule.java:161)
	at io.avaje.inject.DBeanScopeBuilder.build(DBeanScopeBuilder.java:170)
	at cool.houge.wuneng.main.Application.run(Application.java:64)
	at cool.houge.wuneng.main.Application.main(Application.java:54)
Caused by: java.lang.ClassNotFoundException: io.swagger.v3.oas.annotations.security.SecurityRequirement
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
Caused by: java.lang.ClassNotFoundException: io.swagger.v3.oas.annotations.security.SecurityRequirement

	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
```

controller:
```
@Path("/s/roles")
@Controller
@SecurityRequirement(name = "JWT")
public class RoleController {
  // ...
}
```

source:
```
@Generated("io.avaje.inject.generator")
public final class RoleController$DI  {

  /**
   * Create and register RoleController.
   */
  public static void build(Builder builder) {
    if (builder.isAddBeanFor(RoleController.class, Controller.class, SecurityRequirement.class)) {
      var bean = new RoleController();
      var $bean = builder.register(bean);
      builder.addInjector(b -> {
        // field and method injection
        $bean.roleService = b.get(RoleService.class);
        $bean.beanMapper = b.get(VoBeanMapper.class);
      });
    }
  }

}
```

I expect swagger-annotations to be used only at compile time. Can you specify to remove certain dependencies that are not needed at run time?

Pull request description:
Now doesn't register beans by annotations
- remove registering beans by annotation
- modifies listByAnnotation to find the annotated beans at runtime (only seems to work with annotations with `@Retention(RetentionPolicy.RUNTIME)` )
- fixes generation bug where annotating inject constructor params throws errors

fixes #285

Patch:
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/Dependency.java b/inject-generator/src/main/java/io/avaje/inject/generator/Dependency.java
index c00fcb84..dd896dca 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/Dependency.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/Dependency.java
@@ -8,15 +8,15 @@ final class Dependency {
   Dependency(String name) {
     if (name.startsWith("soft:")) {
       this.softDependency = true;
-      this.name = name.substring(5);
+      this.name = Util.trimAnnotations(name.substring(5));
     } else {
       this.softDependency = false;
-      this.name = name;
+      this.name = Util.trimAnnotations(name);
     }
   }
 
   Dependency(String name, boolean softDependency) {
-    this.name = name;
+    this.name = Util.trimAnnotations(name);
     this.softDependency = softDependency;
   }
 
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/GenericType.java b/inject-generator/src/main/java/io/avaje/inject/generator/GenericType.java
index 1505cd86..3038efa5 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/GenericType.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/GenericType.java
@@ -66,6 +66,7 @@ final class GenericType {
    * Parse and return as GenericType.
    */
   static GenericType parse(String raw) {
+    raw = Util.trimAnnotations(raw);
     raw = trimWildcard(raw);
     if (raw.indexOf('<') == -1) {
       return new GenericType(raw);
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/TypeReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/TypeReader.java
index b2d719c6..ea3c725e 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/TypeReader.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/TypeReader.java
@@ -108,9 +108,6 @@ final class TypeReader {
     TypeAppender appender = new TypeAppender(importTypes);
     appender.add(extendsReader.baseType());
     appender.add(extendsReader.provides());
-    if (forBean) {
-      appender.add(annotationReader.annotationTypes());
-    }
     this.genericTypes = appender.genericTypes();
     this.typesRegister = appender.asString();
   }
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java
index 77c9fc25..044a65fa 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java
@@ -46,6 +46,16 @@ final class Util {
     return type.substring(0, i);
   }
 
+  
+  /** Trim off annotations from the raw type if present. */
+  public static String trimAnnotations(String type) {
+    int pos = type.indexOf(".@");
+    if (pos == -1) {
+      return type;
+    }
+    return type.substring(0, pos + 1) + type.substring(type.lastIndexOf(' ') + 1);
+  }
+  
   static String nestedPackageOf(String cls) {
     int pos = cls.lastIndexOf('.');
     if (pos < 0) {
diff --git a/inject/src/main/java/io/avaje/inject/BeanScope.java b/inject/src/main/java/io/avaje/inject/BeanScope.java
index 532766cb..1e7225f5 100644
--- a/inject/src/main/java/io/avaje/inject/BeanScope.java
+++ b/inject/src/main/java/io/avaje/inject/BeanScope.java
@@ -172,7 +172,7 @@ public interface BeanScope extends AutoCloseable {
   <T> Optional<T> getOptional(Type type, @Nullable String name);
 
   /**
-   * Return the list of beans that have an annotation.
+   * Return the list of beans that have an annotation. The annotation must have a @Retention policy of RUNTIME
    *
    * <pre>{@code
    *
@@ -185,7 +185,7 @@ public interface BeanScope extends AutoCloseable {
    *
    * @param annotation An annotation class.
    */
-  List<Object> listByAnnotation(Class<?> annotation);
+  List<Object> listByAnnotation(Class<? extends Annotation> annotation);
 
   /**
    * Return the list of beans for a given type.
@@ -252,5 +252,6 @@ public interface BeanScope extends AutoCloseable {
   /**
    * Close the scope firing any <code>@PreDestroy</code> lifecycle methods.
    */
+  @Override
   void close();
 }
diff --git a/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java b/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java
index 2ec5357b..9c0a55b5 100644
--- a/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java
+++ b/inject/src/main/java/io/avaje/inject/spi/DBeanScope.java
@@ -12,6 +12,7 @@ import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.*;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
 
 @NonNullApi
 final class DBeanScope implements BeanScope {
@@ -205,8 +206,11 @@ final class DBeanScope implements BeanScope {
   }
 
   @Override
-  public List<Object> listByAnnotation(Class<?> annotation) {
-    final List<Object> values = beans.all(annotation);
+  public List<Object> listByAnnotation(Class<? extends Annotation> annotation) {
+    final List<Object> values = all().stream()
+    	      .filter(entry -> entry.type().isAnnotationPresent(annotation))
+    	      .map(BeanEntry::bean)
+    	      .collect(Collectors.toList());
     if (parent == null) {
       return values;
     }
diff --git a/inject/src/main/java/io/avaje/inject/spi/DBeanScopeProxy.java b/inject/src/main/java/io/avaje/inject/spi/DBeanScopeProxy.java
index 65cfb78c..7b166f86 100644
--- a/inject/src/main/java/io/avaje/inject/spi/DBeanScopeProxy.java
+++ b/inject/src/main/java/io/avaje/inject/spi/DBeanScopeProxy.java
@@ -51,7 +51,7 @@ final class DBeanScopeProxy implements BeanScope {
   }
 
   @Override
-  public List<Object> listByAnnotation(Class<?> annotation) {
+  public List<Object> listByAnnotation(Class<? extends Annotation> annotation) {
     return delegate.listByAnnotation(annotation);
   }
 


Test patch:
diff --git a/inject-test/src/test/java/io/avaje/inject/xtra/ApplicationScope.java b/inject-test/src/test/java/io/avaje/inject/xtra/ApplicationScope.java
index 7d8d5abf..839b7037 100644
--- a/inject-test/src/test/java/io/avaje/inject/xtra/ApplicationScope.java
+++ b/inject-test/src/test/java/io/avaje/inject/xtra/ApplicationScope.java
@@ -2,6 +2,7 @@ package io.avaje.inject.xtra;
 
 import io.avaje.inject.BeanScope;
 
+import java.lang.annotation.Annotation;
 import java.util.List;
 
 /**
@@ -135,7 +136,7 @@ public class ApplicationScope {
    *
    * @param annotation An annotation class.
    */
-  public static List<Object> listByAnnotation(Class<?> annotation) {
+  public static List<Object> listByAnnotation(Class<? extends Annotation> annotation) {
     return appScope.listByAnnotation(annotation);
   }
 
diff --git a/inject-test/src/test/java/org/example/MyCustomScope.java b/inject-test/src/test/java/org/example/MyCustomScope.java
index d7bbe5f6..b9fa23ca 100644
--- a/inject-test/src/test/java/org/example/MyCustomScope.java
+++ b/inject-test/src/test/java/org/example/MyCustomScope.java
@@ -1,10 +1,15 @@
 package org.example;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import org.example.custom.loc.LocalExternal;
+
 import io.avaje.inject.InjectModule;
 import jakarta.inject.Scope;
-import org.example.custom.loc.LocalExternal;
 
 @Scope
+@Retention(RetentionPolicy.RUNTIME)
 @InjectModule(requires = {LocalExternal.class})
 public @interface MyCustomScope {
 }
diff --git a/inject-test/src/test/java/org/example/coffee/CoffeeMaker.java b/inject-test/src/test/java/org/example/coffee/CoffeeMaker.java
index 79adbd10..06af0f9b 100644
--- a/inject-test/src/test/java/org/example/coffee/CoffeeMaker.java
+++ b/inject-test/src/test/java/org/example/coffee/CoffeeMaker.java
@@ -1,6 +1,7 @@
 package org.example.coffee;
 
 import org.example.coffee.grind.Grinder;
+import org.jetbrains.annotations.NotNull;
 
 import jakarta.inject.Singleton;
 
@@ -12,7 +13,7 @@ public class CoffeeMaker {
 
   private final Grinder grinder;
 
-  public CoffeeMaker(Pump pump, Grinder grinder) {
+  public CoffeeMaker(@NotNull Pump pump, Grinder grinder) {
     this.pump = pump;
     this.grinder = grinder;
   }
diff --git a/inject-test/src/test/java/org/example/custom/CustomScopeTest.java b/inject-test/src/test/java/org/example/custom/CustomScopeTest.java
index 8ab2ba15..58ea8bc1 100644
--- a/inject-test/src/test/java/org/example/custom/CustomScopeTest.java
+++ b/inject-test/src/test/java/org/example/custom/CustomScopeTest.java
@@ -92,7 +92,7 @@ class CustomScopeTest {
       assertThat(customBeanEntry.get().bean()).isSameAs(customBean);
       assertThat(customBeanEntry.get().qualifierName()).isEqualTo("hello");
       assertThat(customBeanEntry.get().priority()).isEqualTo(0);
-      assertThat(customBeanEntry.get().keys()).containsExactly(CustomBean.class.getCanonicalName(), MyCustomScope.class.getCanonicalName());
+      assertThat(customBeanEntry.get().keys()).containsExactly(CustomBean.class.getCanonicalName());
 
 
       final Optional<BeanEntry> fooCustomEntry = all.stream()
@@ -104,7 +104,7 @@ class CustomScopeTest {
 
       // only the beans with MyCustomScope annotation
       final List<BeanEntry> myCustomScopeBeans = all.stream()
-        .filter(beanEntry -> beanEntry.hasKey(MyCustomScope.class))
+        .filter(beanEntry -> beanEntry.type().isAnnotationPresent(MyCustomScope.class))
         .collect(toList());
       assertThat(myCustomScopeBeans).hasSize(3);
 
diff --git a/inject-test/src/test/java/org/example/custom2/OciMarker.java b/inject-test/src/test/java/org/example/custom2/OciMarker.java
index f2fd8cd0..7d8ca98f 100644
--- a/inject-test/src/test/java/org/example/custom2/OciMarker.java
+++ b/inject-test/src/test/java/org/example/custom2/OciMarker.java
@@ -1,4 +1,8 @@
 package org.example.custom2;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
 public @interface OciMarker {
 }
diff --git a/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java b/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java
index 0ba4a5b6..2bb2fb79 100644
--- a/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java
+++ b/inject/src/test/java/io/avaje/inject/BeanScopeBuilderTest.java
@@ -275,7 +275,7 @@ class BeanScopeBuilderTest {
     }
 
     @Override
-    public List<Object> listByAnnotation(Class<?> annotation) {
+    public List<Object> listByAnnotation(Class<? extends Annotation> annotation) {
       return null;
     }