Skip to content

Commit

Permalink
#214 - ENH: Add support for using @prototype on @factory @bean methods
Browse files Browse the repository at this point in the history
  • Loading branch information
rbygrave committed May 19, 2022
1 parent ab899e4 commit 0d44ad4
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package org.example.myapp.config;

import io.avaje.inject.Bean;
import io.avaje.inject.Component;
import io.avaje.inject.Factory;
import io.avaje.inject.Prototype;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.example.myapp.HelloData;

@Factory
Expand All @@ -20,8 +24,9 @@ public String helloData() {
}
}

@Prototype
@Bean
Builder newBuilder() {
public Builder newBuilder() {
return new Builder();
}

Expand All @@ -35,4 +40,19 @@ public static class Builder {

public static class Generated {
}

@Component
public static class BuilderUser {

final Provider<Builder> builderProvider;

@Inject
public BuilderUser(Provider<Builder> builderProvider) {
this.builderProvider = builderProvider;
}

public Builder createBuilder() {
return builderProvider.get();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.example.myapp;

import io.avaje.inject.BeanScope;
import org.example.myapp.config.AppConfig;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class FactoryPrototypeMethodTest {

@Test
void test() {
try (BeanScope beanScope = BeanScope.builder().build()) {

AppConfig.Builder one = beanScope.get(AppConfig.Builder.class);
AppConfig.Builder two = beanScope.get(AppConfig.Builder.class);
assertThat(one).isNotNull();
assertThat(one).isNotSameAs(two);

AppConfig.BuilderUser builderUser = beanScope.get(AppConfig.BuilderUser.class);
AppConfig.Builder b0 = builderUser.createBuilder();
AppConfig.Builder b1 = builderUser.createBuilder();

assertThat(b0).isNotNull();
assertThat(b0).isNotSameAs(b1);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ boolean providedByDefaultModule(String dependency) {
void readBeans(RoundEnvironment roundEnv) {
for (Data data : scopeAnnotations.values()) {
for (Element customBean : roundEnv.getElementsAnnotatedWith(data.type)) {
// context.logWarn("read custom scope bean " + customBean + " for scope " + entry.getKey());
data.scopeInfo.read((TypeElement) customBean, false);
if (customBean instanceof TypeElement) {
data.scopeInfo.read((TypeElement) customBean, false);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class MethodReader {
private final ExecutableElement element;
private final String factoryType;
private final String methodName;
private final boolean prototype;
private final String returnTypeRaw;
private final GenericType genericType;
private final String shortName;
Expand All @@ -31,11 +32,12 @@ class MethodReader {
private final boolean optionalType;

MethodReader(ProcessingContext context, ExecutableElement element, TypeElement beanType) {
this(context, element, beanType, null, null);
this(context, element, beanType, null, null, false);
}

MethodReader(ProcessingContext context, ExecutableElement element, TypeElement beanType, Bean bean, Named named) {
MethodReader(ProcessingContext context, ExecutableElement element, TypeElement beanType, Bean bean, Named named, boolean prototype) {
this.isFactory = bean != null;
this.prototype = prototype;
this.element = element;
this.methodName = element.getSimpleName().toString();
TypeMirror returnMirror = element.getReturnType();
Expand Down Expand Up @@ -129,6 +131,31 @@ String builderBuildBean() {
return sb.toString();
}

public void builderAddProtoBean(Append writer) {
if (isVoid) {
writer.append("Error - void @Prototype method ?").eol();
return;
}
if (optionalType) {
writer.append("Error - Optional type with @Prototype method is not supported").eol();
return;
}
String indent = " ";
writer.append(indent).append(" // prototype scope bean method").eol();
writer.append(indent).append(" builder.registerProvider(() -> {").eol();
writer.append("%s return ", indent);
writer.append(String.format("factory.%s(", methodName));
for (int i = 0; i < params.size(); i++) {
if (i > 0) {
writer.append(", ");
}
writer.append(params.get(i).builderGetDependency("builder", true));
}
writer.append(");").eol();
writer.append(indent).append(" });").eol();
writer.append(indent).append("}").eol();
}

void builderBuildAddBean(Append writer) {
if (!isVoid) {
String indent = optionalType ? " " : " ";
Expand Down Expand Up @@ -228,11 +255,15 @@ public void commentBuildMethod(Append writer) {
writer.append(CODE_COMMENT_BUILD_FACTORYBEAN, shortName, factoryShortName, methodName).eol();
}

public boolean isPublic() {
boolean isProtoType() {
return prototype;
}

boolean isPublic() {
return element.getModifiers().contains(Modifier.PUBLIC);
}

public boolean isNotPrivate() {
boolean isNotPrivate() {
return !element.getModifiers().contains(Modifier.PRIVATE);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import java.util.*;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class Processor extends AbstractProcessor {

Expand Down Expand Up @@ -78,9 +83,10 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
private void readScopes(Set<? extends Element> scopes) {
for (Element element : scopes) {
if (element.getKind() == ElementKind.ANNOTATION_TYPE) {
// context.logDebug("detected scope annotation " + element);
TypeElement type = (TypeElement) element;
allScopes.addScopeAnnotation(type);
if (element instanceof TypeElement) {
TypeElement type = (TypeElement) element;
allScopes.addScopeAnnotation(type);
}
}
}
addTestScope();
Expand All @@ -101,9 +107,8 @@ private void addTestScope() {
*/
private void readChangedBeans(Set<? extends Element> beans, boolean factory) {
for (Element element : beans) {
if (!(element instanceof TypeElement)) {
context.logError("unexpected type [" + element + "]");
} else {
// ignore methods (e.g. factory methods with @Prototype on them)
if (element instanceof TypeElement) {
TypeElement typeElement = (TypeElement) element;
final ScopeInfo scope = findScope(typeElement);
if (!factory) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,13 @@ private void writeFactoryBeanMethod(MethodReader method) {
writer.append(" public static void build_%s(%s builder) {", method.getName(), beanReader.builderType()).eol();
method.buildAddFor(writer);
writer.append(method.builderGetFactory()).eol();
writer.append(method.builderBuildBean()).eol();
method.builderBuildAddBean(writer);
writer.append(" }").eol();
if (method.isProtoType()) {
method.builderAddProtoBean(writer);
} else {
writer.append(method.builderBuildBean()).eol();
method.builderBuildAddBean(writer);
writer.append(" }").eol();
}
writer.append(" }").eol().eol();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.avaje.inject.generator;

import io.avaje.inject.Bean;
import io.avaje.inject.Prototype;
import jakarta.inject.Inject;
import jakarta.inject.Named;

Expand Down Expand Up @@ -147,7 +148,8 @@ private void checkForAspect(ExecutableElement methodElement) {
private void addFactoryMethod(ExecutableElement methodElement, Bean bean) {
// Not yet reading Qualifier annotations, Named only at this stage
Named named = methodElement.getAnnotation(Named.class);
factoryMethods.add(new MethodReader(context, methodElement, baseType, bean, named).read());
boolean prototype = methodElement.getAnnotation(Prototype.class) != null;
factoryMethods.add(new MethodReader(context, methodElement, baseType, bean, named, prototype).read());
}

BeanAspects hasAspects() {
Expand Down
5 changes: 1 addition & 4 deletions inject/src/main/java/io/avaje/inject/Prototype.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.avaje.inject;

import jakarta.inject.Scope;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand All @@ -21,8 +19,7 @@
* }
* }</pre>
*/
@Target({ElementType.TYPE})
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Scope
public @interface Prototype {
}

0 comments on commit 0d44ad4

Please sign in to comment.