Skip to content

Commit 90e7f73

Browse files
committed
#195 - Add @component + @InjectModule(ignoreSingleton = true) ... additional approach co-exist with other DI libraries
1 parent 99aff65 commit 90e7f73

File tree

11 files changed

+101
-20
lines changed

11 files changed

+101
-20
lines changed

blackbox-test-inject/src/main/java/org/example/myapp/r4j/MyExample.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package org.example.myapp.r4j;
22

3-
import jakarta.inject.Singleton;
3+
import io.avaje.inject.Component;
44
import org.example.myapp.resilience4j.MyRetry;
55

6-
@Singleton
6+
@Component
77
public class MyExample {
88

99
public int barfCounter;

blackbox-test-inject/src/main/java/org/example/myapp/resilience4j/RetryProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
package org.example.myapp.resilience4j;
22

3+
import io.avaje.inject.Component;
34
import io.avaje.inject.aop.AspectProvider;
45
import io.avaje.inject.aop.Fallback;
56
import io.avaje.inject.aop.Invocation;
67
import io.avaje.inject.aop.MethodInterceptor;
78
import io.github.resilience4j.retry.Retry;
89
import io.github.resilience4j.retry.RetryConfig;
9-
import jakarta.inject.Singleton;
1010

1111
import java.lang.reflect.Method;
1212
import java.time.Duration;
1313
import java.time.format.DateTimeParseException;
1414
import java.util.concurrent.atomic.AtomicInteger;
1515
import java.util.function.Supplier;
1616

17-
@Singleton
17+
@Component
1818
public class RetryProvider implements AspectProvider<MyRetry> {
1919

2020
@Override

inject-generator/src/main/java/io/avaje/inject/generator/MetaDataOrdering.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ private void checkMissingDependencies(MetaData metaData) {
134134
*/
135135
private void warnOnDependencies() {
136136
if (!missingDependencyTypes.isEmpty()) {
137-
context.logError("Dependencies %s are not provided - missing @Singleton or @Factory/@Bean or specify external dependency via @InjectModule requires attribute", missingDependencyTypes);
137+
context.logError("Dependencies %s are not provided - missing @Singleton, @Component, @Factory/@Bean or specify external dependency via @InjectModule requires attribute", missingDependencyTypes);
138138
} else {
139139
if (!queue.isEmpty()) {
140140
context.logWarn("There are " + queue.size() + " beans with unsatisfied dependencies (assuming external dependencies)");

inject-generator/src/main/java/io/avaje/inject/generator/Processor.java

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.avaje.inject.generator;
22

3+
import io.avaje.inject.Component;
34
import io.avaje.inject.Factory;
45
import io.avaje.inject.InjectModule;
56
import io.avaje.inject.Prototype;
@@ -55,24 +56,19 @@ public Set<String> getSupportedAnnotationTypes() {
5556

5657
@Override
5758
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
58-
Set<? extends Element> controllers = Collections.emptySet();
59+
readScopes(roundEnv.getElementsAnnotatedWith(Scope.class));
60+
readModule(roundEnv);
61+
readChangedBeans(roundEnv.getElementsAnnotatedWith(Factory.class), true);
62+
if (defaultScope.includeSingleton()) {
63+
readChangedBeans(roundEnv.getElementsAnnotatedWith(Singleton.class), false);
64+
}
65+
readChangedBeans(roundEnv.getElementsAnnotatedWith(Component.class), false);
66+
readChangedBeans(roundEnv.getElementsAnnotatedWith(Prototype.class), false);
5967
TypeElement typeElement = elementUtils.getTypeElement(Constants.CONTROLLER);
6068
if (typeElement != null) {
61-
controllers = roundEnv.getElementsAnnotatedWith(typeElement);
69+
readChangedBeans(roundEnv.getElementsAnnotatedWith(typeElement), false);
6270
}
63-
64-
Set<? extends Element> factoryBeans = roundEnv.getElementsAnnotatedWith(Factory.class);
65-
Set<? extends Element> beans = roundEnv.getElementsAnnotatedWith(Singleton.class);
66-
Set<? extends Element> prototypes = roundEnv.getElementsAnnotatedWith(Prototype.class);
67-
Set<? extends Element> scopes = roundEnv.getElementsAnnotatedWith(Scope.class);
68-
Set<? extends Element> proxies = roundEnv.getElementsAnnotatedWith(Proxy.class);
69-
readScopes(scopes);
70-
readModule(roundEnv);
71-
readChangedBeans(factoryBeans, true);
72-
readChangedBeans(beans, false);
73-
readChangedBeans(prototypes, false);
74-
readChangedBeans(controllers, false);
75-
readChangedBeans(proxies, false);
71+
readChangedBeans(roundEnv.getElementsAnnotatedWith(Proxy.class), false);
7672
allScopes.readBeans(roundEnv);
7773
defaultScope.write(roundEnv.processingOver());
7874
allScopes.write(roundEnv.processingOver());

inject-generator/src/main/java/io/avaje/inject/generator/ScopeInfo.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ String type() {
6060
private String moduleShortName;
6161
private JavaFileObject moduleFile;
6262
private boolean emptyModule;
63+
private boolean ignoreSingleton;
6364

6465
/**
6566
* Create for the main/global module scope.
@@ -89,6 +90,10 @@ public String toString() {
8990
'}';
9091
}
9192

93+
boolean includeSingleton() {
94+
return !ignoreSingleton;
95+
}
96+
9297
void details(String name, Element contextElement) {
9398
if (name == null || name.isEmpty()) {
9499
final String simpleName = contextElement.getSimpleName().toString();
@@ -100,6 +105,7 @@ void details(String name, Element contextElement) {
100105
}
101106

102107
private void read(Element element) {
108+
ignoreSingleton = ScopeUtil.readIgnoreSingleton(element);
103109
requires(ScopeUtil.readRequires(element));
104110
provides(ScopeUtil.readProvides(element));
105111
for (String require : ScopeUtil.readRequiresPackages(element)) {

inject-generator/src/main/java/io/avaje/inject/generator/ScopeUtil.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,23 @@ class ScopeUtil {
1010

1111
private static final String INJECT_MODULE = "io.avaje.inject.InjectModule";
1212

13+
static boolean readIgnoreSingleton(Element element) {
14+
if (element == null) {
15+
return false;
16+
}
17+
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
18+
if (INJECT_MODULE.equals(annotationMirror.getAnnotationType().toString())) {
19+
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet()) {
20+
if (entry.getKey().toString().startsWith("ignoreSingleton(")) {
21+
Object value = entry.getValue().getValue();
22+
return "true".equalsIgnoreCase(value.toString());
23+
}
24+
}
25+
}
26+
}
27+
return false;
28+
}
29+
1330
static List<String> readProvides(Element element) {
1431
return readClasses(element, "provides(");
1532
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package io.avaje.inject;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Identify a bean as component with singleton scope that avaje-inject will use.
10+
* <p>
11+
* This is an alternative to using the standard <code>@Singleton</code> annotation.
12+
* We generally use <code>@Component</code> when we:
13+
* <ul>
14+
* <li>Want to use avaje-inject in a project that has some other library using <code>@Singleton</code></li>
15+
* <li>Want to support BOTH <code>javax.inject</code> and <code>jakarta.inject</code></li>
16+
* </ul>
17+
*
18+
* <h3>Example</h3>
19+
* <pre>{@code
20+
*
21+
* @Component
22+
* class MyEmailSender implements EmailSender {
23+
*
24+
* ...
25+
* }
26+
* }</pre>
27+
*
28+
* <h3>Ignoring <em>@Singleton</em></h3>
29+
* <p>
30+
* Set {@link InjectModule#ignoreSingleton()} <code>true</code> to get avaje-inject to ignore
31+
* classes annotated with <code>@Singleton</code>. Typically, we want another DI library to use
32+
* those classes and want avaje-inject to co-exist independently.
33+
* <p>
34+
*
35+
* <pre>{@code
36+
*
37+
* @InjectModule(name = "coffee", ignoreSingleton = true)
38+
* package coffee;
39+
*
40+
* import io.avaje.inject.InjectModule;
41+
*
42+
* }</pre>
43+
*
44+
* @see InjectModule#ignoreSingleton()
45+
*/
46+
@Target(ElementType.TYPE)
47+
@Retention(RetentionPolicy.RUNTIME)
48+
public @interface Component {
49+
}

inject/src/main/java/io/avaje/inject/InjectModule.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@
4141
*/
4242
String name() default "";
4343

44+
/**
45+
* Set to true to ignore anything annotated with <code>@Singleton</code>.
46+
* <p>
47+
* Set this to true when some other library is using <code>@Singleton</code> and we want
48+
* avaje-inject to be completely independent of that by ignoring the standard <code>@Singleton</code>.
49+
* <p>
50+
* We instead use <code>@Component</code> instead of <code>@Singleton</code>.
51+
*/
52+
boolean ignoreSingleton() default false;
53+
4454
/**
4555
* Explicitly define features that are provided by this module and required by other modules.
4656
* <p>

inject/src/main/java/io/avaje/inject/aop/AspectProvider.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*
99
* @param <T> The aspect annotation
1010
*/
11+
@FunctionalInterface
1112
public interface AspectProvider<T extends Annotation> {
1213

1314
/**

inject/src/main/java/io/avaje/inject/aop/Fallback.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* This isn't strictly required but more a helper to make it easier for aspects
99
* that want to use a fallback or recovery method.
1010
*/
11+
@FunctionalInterface
1112
public interface Fallback {
1213

1314
/**

inject/src/main/java/io/avaje/inject/aop/MethodInterceptor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* <p>
99
* MethodInterceptor instances are provided by {@link AspectProvider#interceptor(Method, Annotation)}.
1010
*/
11+
@FunctionalInterface
1112
public interface MethodInterceptor {
1213

1314
/**

0 commit comments

Comments
 (0)