Skip to content

Commit 64a401d

Browse files
committed
Add RequestScope support
1 parent 6af3dfc commit 64a401d

35 files changed

+987
-58
lines changed

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

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class BeanReader {
4343
private boolean beanLifeCycle;
4444
private boolean primary;
4545
private boolean secondary;
46+
private boolean requestScopedBean;
4647

4748
BeanReader(TypeElement beanType, ProcessingContext context) {
4849
this.beanType = beanType;
@@ -62,6 +63,7 @@ public String toString() {
6263
private void init() {
6364
typeReader.process();
6465
beanLifeCycle = typeReader.isBeanLifeCycle();
66+
requestScopedBean = typeReader.isRequestScopeBean();
6567
name = typeReader.getName();
6668
primary = (beanType.getAnnotation(Primary.class) != null);
6769
secondary = !primary && (beanType.getAnnotation(Secondary.class) != null);
@@ -118,6 +120,10 @@ void read(boolean factory) {
118120
for (MethodReader factoryMethod : factoryMethods) {
119121
factoryMethod.addImports(importTypes);
120122
}
123+
if (requestScopedBean) {
124+
importTypes.add(Constants.REQUESTSCOPEPROVIDER);
125+
importTypes.add(Constants.REQUESTSCOPE);
126+
}
121127
}
122128

123129
private MethodReader findConstructor() {
@@ -256,7 +262,7 @@ boolean isLifecycleRequired() {
256262
* Return true if lifecycle via annotated methods is required.
257263
*/
258264
boolean isLifecycleWrapperRequired() {
259-
return postConstructMethod != null || preDestroyMethod != null;
265+
return !requestScopedBean && (postConstructMethod != null || preDestroyMethod != null);
260266
}
261267

262268
List<MetaData> createFactoryMethodMeta() {
@@ -349,12 +355,16 @@ void setWrittenToFile() {
349355
* <p>
350356
* If request scoped then generate a BeanFactory instead.
351357
*/
352-
boolean isRequestScoped() {
353-
return requestParams.isRequestScoped();
358+
boolean isRequestScopedController() {
359+
return requestParams.isRequestScopedController();
360+
}
361+
362+
boolean isRequestScopedBean() {
363+
return requestScopedBean;
354364
}
355365

356366
String suffix() {
357-
return isRequestScoped() ? "$factory" : "$di";
367+
return isRequestScopedController() ? "$factory" : "$di";
358368
}
359369

360370
/**
@@ -396,4 +406,39 @@ void writeRequestCreate(Append writer) {
396406
writer.append(" }").eol();
397407
}
398408

409+
void buildReq(Append writer) {
410+
writer.append(" builder.requestScope(%s.class, new RequestScopeProvider<%s>() {", shortName, shortName).eol();
411+
writer.append(" @Override").eol();
412+
writer.append(" public %s provide(RequestScope scope) {", shortName).eol();
413+
}
414+
415+
void buildReqEnd(Append writer) {
416+
writer.append(" return bean;").eol();
417+
writer.append(" }").eol();
418+
writer.append(" }");
419+
420+
final String ifaceTypes = typeReader.getTypesRegister();
421+
if (ifaceTypes != null) {
422+
if (name != null && !name.isEmpty()) {
423+
writer.append(", \"%s\"", name);
424+
writer.append(", ");
425+
writer.append(ifaceTypes);
426+
}
427+
}
428+
writer.append(");").eol();
429+
}
430+
431+
void writePostConstruct(Append writer) {
432+
if (postConstructMethod != null) {
433+
writer.append(" bean.%s();", postConstructMethod.getSimpleName()).eol();
434+
}
435+
}
436+
437+
void writePreDestroy(Append writer) {
438+
if (preDestroyMethod != null) {
439+
writer.append(" scope.addClosable(bean::%s);", preDestroyMethod.getSimpleName()).eol();
440+
} else if (typeReader.isClosable()) {
441+
writer.append(" scope.addClosable(bean);").eol();
442+
}
443+
}
399444
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ boolean check(String paramType) {
3131
/**
3232
* Return true if the bean has request scoped dependencies.
3333
*/
34-
boolean isRequestScoped() {
34+
boolean isRequestScopedController() {
3535
return reqScopeHandler != null;
3636
}
3737

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,24 @@
22

33
class Constants {
44

5+
static final String IO_CLOSEABLE = "java.io.Closeable";
56
static final String OPTIONAL = "java.util.Optional";
67
static final String KOTLIN_METADATA = "kotlin.Metadata";
78

89
static final String PROVIDER = "jakarta.inject.Provider";
910
static final String SINGLETON = "jakarta.inject.Singleton";
1011
static final String INJECT = "jakarta.inject.Inject";
12+
static final String REQUEST = "io.avaje.inject.Request";
1113

1214
static final String PATH = "io.avaje.http.api.Path";
1315
static final String CONTROLLER = "io.avaje.http.api.Controller";
16+
static final String REQUESTSCOPEPROVIDER = "io.avaje.inject.RequestScopeProvider";
1417

1518
static final String AT_SINGLETON = "@Singleton";
1619
static final String AT_GENERATED = "@Generated(\"io.avaje.inject.generator\")";
1720
static final String META_INF_FACTORY = "META-INF/services/io.avaje.inject.spi.BeanContextFactory";
1821

22+
static final String REQUESTSCOPE = "io.avaje.inject.RequestScope";
1923
static final String BEANCONTEXT = "io.avaje.inject.BeanContext";
2024
static final String CONTEXTMODULE = "io.avaje.inject.ContextModule";
2125

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ void addImports(Set<String> importTypes) {
2929
importTypes.add(fieldType);
3030
}
3131

32-
String builderGetDependency() {
32+
String builderGetDependency(String builderRef) {
3333
StringBuilder sb = new StringBuilder();
34-
sb.append("b.").append(type.getMethod(nullable));
34+
sb.append(builderRef).append(".").append(type.getMethod(nullable));
3535
sb.append(nm(fieldType)).append(".class");
3636
if (name != null) {
3737
sb.append(",\"").append(name).append("\"");

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.avaje.inject.ContextModule;
44
import io.avaje.inject.Factory;
5+
import io.avaje.inject.Request;
56
import io.avaje.inject.spi.DependencyMeta;
67

78
import javax.annotation.processing.AbstractProcessor;
@@ -78,11 +79,13 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
7879

7980
Set<? extends Element> factoryBeans = roundEnv.getElementsAnnotatedWith(Factory.class);
8081
Set<? extends Element> beans = roundEnv.getElementsAnnotatedWith(Singleton.class);
82+
Set<? extends Element> requestBeans = roundEnv.getElementsAnnotatedWith(Request.class);
8183

8284
readModule(roundEnv);
8385
readChangedBeans(factoryBeans, true);
8486
readChangedBeans(beans, false);
8587
readChangedBeans(controllers, false);
88+
readChangedBeans(requestBeans, false);
8689

8790
mergeMetaData();
8891

@@ -151,7 +154,7 @@ private void readChangedBeans(Set<? extends Element> beans, boolean factory) {
151154
*/
152155
private void mergeMetaData() {
153156
for (BeanReader beanReader : beanReaders) {
154-
if (beanReader.isRequestScoped()) {
157+
if (beanReader.isRequestScopedController()) {
155158
context.logDebug("skipping request scoped processed bean " + beanReader);
156159
} else {
157160
String metaKey = beanReader.getMetaKey();

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

Lines changed: 81 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class SimpleBeanWriter {
1616
private static final String CODE_COMMENT_FACTORY = "/**\n * Generated source - dependency injection factory for request scoped %s.\n */";
1717
private static final String CODE_COMMENT_LIFECYCLE = " /**\n * Lifecycle wrapper for %s.\n */";
1818
private static final String CODE_COMMENT_BUILD = " /**\n * Create and register %s.\n */";
19+
private static final String CODE_COMMENT_BUILD_RSB = " /**\n * Register provider for request scoped %s.\n */";
1920

2021
private final BeanReader beanReader;
2122
private final ProcessingContext context;
@@ -24,6 +25,7 @@ class SimpleBeanWriter {
2425
private final String packageName;
2526
private final String suffix;
2627
private Append writer;
28+
private String indent = " ";
2729

2830
SimpleBeanWriter(BeanReader beanReader, ProcessingContext context) {
2931
this.beanReader = beanReader;
@@ -45,7 +47,7 @@ void write() throws IOException {
4547
writePackage();
4648
writeImports();
4749
writeClassStart();
48-
if (isRequestScoped()) {
50+
if (isRequestScopedController()) {
4951
writeRequestCreate();
5052
} else {
5153
writeStaticFactoryMethod();
@@ -61,8 +63,8 @@ private void writeRequestCreate() {
6163
beanReader.writeRequestCreate(writer);
6264
}
6365

64-
private boolean isRequestScoped() {
65-
return beanReader.isRequestScoped();
66+
private boolean isRequestScopedController() {
67+
return beanReader.isRequestScopedController();
6668
}
6769

6870
private void writeStaticFactoryBeanMethods() {
@@ -89,15 +91,52 @@ private void writeFactoryBeanMethod(MethodReader method) {
8991
}
9092

9193
private void writeStaticFactoryMethod() {
92-
9394
MethodReader constructor = beanReader.getConstructor();
9495
if (constructor == null) {
9596
context.logError(beanReader.getBeanType(), "Unable to determine constructor to use for %s? Add explicit @Inject to one of the constructors.", beanReader.getBeanType());
9697
return;
9798
}
99+
writeBuildMethodStart(constructor);
100+
if (beanReader.isRequestScopedBean()) {
101+
writeReqScopeBean(constructor);
102+
} else {
103+
writeAddFor(constructor);
104+
}
105+
writer.append(" }").eol().eol();
106+
}
98107

108+
private void writeReqScopeBean(MethodReader constructor) {
109+
indent = indent + " ";
110+
beanReader.buildReq(writer);
111+
writeCreateBean(constructor, "scope");
112+
if (beanReader.isExtraInjectionRequired()) {
113+
writeExtraInjection();
114+
}
115+
beanReader.writePostConstruct(writer);
116+
beanReader.writePreDestroy(writer);
117+
beanReader.buildReqEnd(writer);
118+
}
119+
120+
private void writeAddFor(MethodReader constructor) {
121+
beanReader.buildAddFor(writer);
122+
writeCreateBean(constructor, "builder");
123+
beanReader.buildRegister(writer);
124+
if (beanReader.isLifecycleRequired()) {
125+
beanReader.buildAddLifecycle(writer);
126+
}
127+
if (beanReader.isExtraInjectionRequired()) {
128+
writeExtraInjection();
129+
}
130+
writer.append(" }").eol();
131+
}
132+
133+
private void writeBuildMethodStart(MethodReader constructor) {
99134
int providerIndex = 0;
100-
writer.append(CODE_COMMENT_BUILD, shortName).eol();
135+
if (beanReader.isRequestScopedBean()) {
136+
writer.append(CODE_COMMENT_BUILD_RSB, shortName).eol();
137+
} else {
138+
writer.append(CODE_COMMENT_BUILD, shortName).eol();
139+
}
101140
writer.append(" public static void build(Builder builder");
102141
for (MethodReader.MethodParam param : constructor.getParams()) {
103142
if (param.isGenericParam()) {
@@ -112,47 +151,47 @@ private void writeStaticFactoryMethod() {
112151
}
113152
}
114153
writer.append(") {").eol();
154+
}
115155

116-
beanReader.buildAddFor(writer);
117-
writer.append(" %s bean = new %s(", shortName, shortName);
118-
156+
private void writeCreateBean(MethodReader constructor, String builderName) {
157+
writer.append("%s %s bean = new %s(", indent, shortName, shortName);
119158
// add constructor dependencies
120-
List<MethodReader.MethodParam> params = constructor.getParams();
121-
for (int i = 0; i < params.size(); i++) {
159+
writeMethodParams(builderName, constructor);
160+
}
161+
162+
private void writeExtraInjection() {
163+
String builderRef = "b";
164+
String beanRef = "$bean";
165+
if (beanReader.isRequestScopedBean()) {
166+
builderRef = "scope";
167+
beanRef = "bean";
168+
} else {
169+
writer.append(" builder.addInjector(b -> {").eol();
170+
}
171+
writer.append(" // field and method injection").eol();
172+
for (FieldReader fieldReader : beanReader.getInjectFields()) {
173+
String fieldName = fieldReader.getFieldName();
174+
String getDependency = fieldReader.builderGetDependency(builderRef);
175+
writer.append(" %s.%s = %s;", beanRef, fieldName, getDependency).eol();
176+
}
177+
for (MethodReader methodReader : beanReader.getInjectMethods()) {
178+
writer.append(" %s.%s(", beanRef, methodReader.getName());
179+
writeMethodParams(builderRef, methodReader);
180+
}
181+
if (!beanReader.isRequestScopedBean()) {
182+
writer.append(" });").eol();
183+
}
184+
}
185+
186+
private void writeMethodParams(String builderRef, MethodReader methodReader) {
187+
List<MethodReader.MethodParam> methodParams = methodReader.getParams();
188+
for (int i = 0; i < methodParams.size(); i++) {
122189
if (i > 0) {
123190
writer.append(", ");
124191
}
125-
writer.append(params.get(i).builderGetDependency("builder"));
192+
writer.append(methodParams.get(i).builderGetDependency(builderRef));
126193
}
127194
writer.append(");").eol();
128-
129-
beanReader.buildRegister(writer);
130-
if (beanReader.isLifecycleRequired()) {
131-
beanReader.buildAddLifecycle(writer);
132-
}
133-
if (beanReader.isExtraInjectionRequired()) {
134-
writer.append(" builder.addInjector(b -> {").eol();
135-
writer.append(" // field and method injection").eol();
136-
for (FieldReader fieldReader : beanReader.getInjectFields()) {
137-
String fieldName = fieldReader.getFieldName();
138-
String getDependency = fieldReader.builderGetDependency();
139-
writer.append(" $bean.%s = %s;", fieldName, getDependency).eol();
140-
}
141-
for (MethodReader methodReader : beanReader.getInjectMethods()) {
142-
writer.append(" $bean.%s(", methodReader.getName());
143-
List<MethodReader.MethodParam> methodParams = methodReader.getParams();
144-
for (int i = 0; i < methodParams.size(); i++) {
145-
if (i > 0) {
146-
writer.append(", ");
147-
}
148-
writer.append(methodParams.get(i).builderGetDependency("b"));
149-
}
150-
writer.append(");").eol();
151-
}
152-
writer.append(" });").eol();
153-
}
154-
writer.append(" }").eol();
155-
writer.append(" }").eol().eol();
156195
}
157196

158197
private void writeImports() {
@@ -189,20 +228,20 @@ private void writeClassEnd() {
189228
}
190229

191230
private void writeClassStart() {
192-
if (beanReader.isRequestScoped()) {
231+
if (beanReader.isRequestScopedController()) {
193232
writer.append(CODE_COMMENT_FACTORY, shortName).eol();
194233
} else {
195234
writer.append(CODE_COMMENT, shortName).eol();
196235
}
197236
writer.append(Constants.AT_GENERATED).eol();
198-
if (beanReader.isRequestScoped()) {
237+
if (beanReader.isRequestScopedController()) {
199238
writer.append(Constants.AT_SINGLETON).eol();
200239
}
201240
writer.append("public class ").append(shortName).append(suffix).append(" ");
202241
if (beanReader.isLifecycleWrapperRequired()) {
203242
writer.append("implements BeanLifecycle ");
204243
}
205-
if (beanReader.isRequestScoped()) {
244+
if (beanReader.isRequestScopedController()) {
206245
writer.append("implements ");
207246
beanReader.factoryInterface(writer);
208247
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class TypeAnnotationReader {
1717
private final ProcessingContext context;
1818
private final List<String> annotationTypes = new ArrayList<>();
1919
private String qualifierName;
20+
private boolean requestScopeBean;
2021

2122
TypeAnnotationReader(TypeElement beanType, ProcessingContext context) {
2223
this.beanType = beanType;
@@ -35,6 +36,10 @@ String getQualifierName() {
3536
return qualifierName;
3637
}
3738

39+
boolean isRequestScopeBean() {
40+
return requestScopeBean;
41+
}
42+
3843
void process() {
3944
for (AnnotationMirror annotationMirror : beanType.getAnnotationMirrors()) {
4045
DeclaredType annotationType = annotationMirror.getAnnotationType();
@@ -45,7 +50,9 @@ void process() {
4550
} else if (annType.indexOf('.') == -1) {
4651
context.logWarn("skip when no package on annotation " + annType);
4752
} else {
48-
if (IncludeAnnotations.include(annType)) {
53+
if (Constants.REQUEST.equals(annType)) {
54+
requestScopeBean = true;
55+
} else if (IncludeAnnotations.include(annType)) {
4956
annotationTypes.add(annType);
5057
}
5158
}

0 commit comments

Comments
 (0)