Skip to content

Commit ddabd07

Browse files
authored
Add @BeanTypes Annotation to Limit Injection Types (#665)
* Adds BeanTypes annotation
1 parent b002969 commit ddabd07

File tree

14 files changed

+175
-19
lines changed

14 files changed

+175
-19
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.example.myapp.beantypes;
2+
3+
public abstract class AbstractSuperClass {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.example.myapp.beantypes;
2+
3+
import org.example.myapp.config.AppConfig.SomeInterface;
4+
import org.other.one.SomeOptionalDep;
5+
6+
import io.avaje.inject.BeanTypes;
7+
import io.avaje.inject.Component;
8+
import jakarta.inject.Named;
9+
10+
@Component
11+
@Named("type")
12+
@BeanTypes(AbstractSuperClass.class)
13+
public class BeanTypeComponent extends AbstractSuperClass
14+
implements SomeInterface, SomeOptionalDep, LimitedInterface {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.example.myapp.beantypes;
2+
3+
import io.avaje.inject.Bean;
4+
import io.avaje.inject.BeanTypes;
5+
import io.avaje.inject.Factory;
6+
import jakarta.inject.Named;
7+
8+
@Factory
9+
public class LimitedFactory {
10+
11+
@Bean
12+
@Named("factory")
13+
@BeanTypes(LimitedInterface.class)
14+
BeanTypeComponent bean() {
15+
return new BeanTypeComponent();
16+
}
17+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package org.example.myapp.beantypes;
2+
3+
public interface LimitedInterface {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.example.myapp.beantypes;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import io.avaje.inject.BeanScope;
9+
10+
class BeanTypesTest {
11+
12+
@Test
13+
void testBeanTypesRestrictingInjection() {
14+
try (var scope = BeanScope.builder().build()) {
15+
16+
assertFalse(scope.contains(BeanTypeComponent.class));
17+
assertThat(scope.get(AbstractSuperClass.class)).isNotNull();
18+
assertThat(scope.get(LimitedInterface.class)).isNotNull();
19+
}
20+
}
21+
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ final class AssistBeanReader {
3333
AssistBeanReader(TypeElement beanType) {
3434
this.beanType = beanType;
3535
this.type = beanType.getQualifiedName().toString();
36-
this.typeReader = new TypeReader(UType.parse(beanType.asType()), beanType, importTypes, false);
36+
this.typeReader =
37+
new TypeReader(
38+
Optional.empty(),
39+
UType.parse(beanType.asType()),
40+
beanType,
41+
importTypes,
42+
false);
3743

3844
typeReader.process();
3945
qualifierName = typeReader.name();

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,15 @@ final class BeanReader {
5858
this.primary = PrimaryPrism.isPresent(beanType);
5959
this.secondary = !primary && SecondaryPrism.isPresent(beanType);
6060
this.lazy = !FactoryPrism.isPresent(beanType) && LazyPrism.isPresent(beanType);
61-
this.typeReader = new TypeReader(UType.parse(beanType.asType()), beanType, importTypes, factory);
61+
final var beantypes = BeanTypesPrism.getOptionalOn(beanType);
62+
beantypes.ifPresent(p -> Util.validateBeanTypes(beanType, p.value()));
63+
this.typeReader =
64+
new TypeReader(
65+
beantypes,
66+
UType.parse(beanType.asType()),
67+
beanType,
68+
importTypes,
69+
factory);
6270

6371
typeReader.process();
6472

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ final class MethodReader {
9595
this.initMethod = initMethod;
9696
this.destroyMethod = destroyMethod;
9797
} else {
98-
this.typeReader = new TypeReader(genericType, returnElement, importTypes);
98+
final var beantypes = BeanTypesPrism.getOptionalOn(element);
99+
beantypes.ifPresent(p -> Util.validateBeanTypes(element, p.value()));
100+
this.typeReader = new TypeReader(beantypes, genericType, returnElement, importTypes);
99101
typeReader.process();
100102
MethodLifecycleReader lifecycleReader = new MethodLifecycleReader(returnElement, initMethod, destroyMethod);
101103
this.initMethod = lifecycleReader.initMethod();

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

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import javax.lang.model.element.Element;
44
import javax.lang.model.element.TypeElement;
55
import javax.lang.model.type.TypeKind;
6+
import javax.lang.model.type.TypeMirror;
67

78
import java.util.List;
9+
import java.util.Optional;
810
import java.util.Set;
9-
1011
import static java.util.stream.Collectors.toList;
1112

1213
final class TypeReader {
@@ -18,16 +19,37 @@ final class TypeReader {
1819
private final TypeAnnotationReader annotationReader;
1920
private Set<UType> genericTypes;
2021
private String typesRegister;
21-
22-
TypeReader(UType genericType, TypeElement beanType, ImportTypeMap importTypes, boolean factory) {
23-
this(genericType, true, beanType, importTypes, factory);
24-
}
25-
26-
TypeReader(UType genericType, TypeElement returnElement, ImportTypeMap importTypes) {
27-
this(genericType, false, returnElement, importTypes, false);
28-
}
29-
30-
private TypeReader(UType genericType, boolean forBean, TypeElement beanType, ImportTypeMap importTypes, boolean factory) {
22+
private final List<UType> injectsTypes;
23+
24+
TypeReader(
25+
Optional<BeanTypesPrism> injectsTypes,
26+
UType genericType,
27+
TypeElement beanType,
28+
ImportTypeMap importTypes,
29+
boolean factory) {
30+
this(injectsTypes, genericType, true, beanType, importTypes, factory);
31+
}
32+
33+
TypeReader(
34+
Optional<BeanTypesPrism> injectsTypes,
35+
UType genericType,
36+
TypeElement returnElement,
37+
ImportTypeMap importTypes) {
38+
this(injectsTypes, genericType, false, returnElement, importTypes, false);
39+
}
40+
41+
private TypeReader(
42+
Optional<BeanTypesPrism> injectsTypes,
43+
UType genericType,
44+
boolean forBean,
45+
TypeElement beanType,
46+
ImportTypeMap importTypes,
47+
boolean factory) {
48+
this.injectsTypes =
49+
injectsTypes.map(BeanTypesPrism::value).stream()
50+
.flatMap(List::stream)
51+
.map(UType::parse)
52+
.collect(toList());
3153
this.forBean = forBean;
3254
this.beanType = beanType;
3355
this.importTypes = importTypes;
@@ -41,14 +63,20 @@ String typesRegister() {
4163
}
4264

4365
List<String> provides() {
66+
if (!injectsTypes.isEmpty()) {
67+
return injectsTypes.stream().map(UType::full).collect(toList());
68+
}
4469
return extendsReader.provides().stream().map(UType::full).collect(toList());
4570
}
4671

4772
List<String> autoProvides() {
73+
if (!injectsTypes.isEmpty()) {
74+
return injectsTypes.stream().map(UType::full).collect(toList());
75+
}
4876
return extendsReader.autoProvides().stream()
49-
.filter(u -> u.componentTypes().stream().noneMatch(p -> p.kind() == TypeKind.TYPEVAR))
50-
.map(UType::full)
51-
.collect(toList());
77+
.filter(u -> u.componentTypes().stream().noneMatch(p -> p.kind() == TypeKind.TYPEVAR))
78+
.map(UType::full)
79+
.collect(toList());
5280
}
5381

5482
String providesAspect() {
@@ -120,8 +148,12 @@ String name() {
120148

121149
private void initRegistrationTypes() {
122150
TypeAppender appender = new TypeAppender(importTypes);
123-
appender.add(extendsReader.baseType());
124-
appender.add(extendsReader.provides());
151+
if (injectsTypes.isEmpty()) {
152+
appender.add(extendsReader.baseType());
153+
appender.add(extendsReader.provides());
154+
} else {
155+
appender.add(injectsTypes);
156+
}
125157
this.genericTypes = appender.genericTypes();
126158
this.typesRegister = appender.asString();
127159
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import javax.lang.model.element.AnnotationMirror;
44
import javax.lang.model.element.Element;
5+
import javax.lang.model.element.ExecutableElement;
56
import javax.lang.model.element.Modifier;
7+
import javax.lang.model.element.TypeElement;
68
import javax.lang.model.type.DeclaredType;
79
import javax.lang.model.type.TypeKind;
810
import javax.lang.model.type.TypeMirror;
@@ -377,4 +379,16 @@ static String valhalla() {
377379
}
378380
return "";
379381
}
382+
383+
static void validateBeanTypes(Element origin, List<TypeMirror> beanType) {
384+
TypeMirror targetType =
385+
origin instanceof TypeElement
386+
? origin.asType()
387+
: ((ExecutableElement) origin).getReturnType();
388+
beanType.forEach(type -> {
389+
if (!APContext.types().isAssignable(targetType, type)) {
390+
APContext.logError(origin, "%s does not extend type %s", targetType, beanType);
391+
}
392+
});
393+
}
380394
}

inject-generator/src/main/java/io/avaje/inject/generator/package-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
@GeneratePrism(Generated.class)
1313
@GeneratePrism(Inject.class)
1414
@GeneratePrism(InjectModule.class)
15+
@GeneratePrism(BeanTypes.class)
1516
@GeneratePrism(Lazy.class)
1617
@GeneratePrism(Named.class)
1718
@GeneratePrism(PreDestroy.class)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.avaje.inject.generator.models.valid.supertypes;
2+
3+
import io.avaje.inject.Bean;
4+
import io.avaje.inject.BeanTypes;
5+
import io.avaje.inject.Factory;
6+
7+
@Factory
8+
public class LimitedFactory {
9+
10+
@Bean
11+
@BeanTypes(SomeInterface.class)
12+
OtherComponent bean() {
13+
return new OtherComponent();
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.avaje.inject.generator.models.valid.supertypes;
2+
3+
import io.avaje.inject.BeanTypes;
4+
import io.avaje.inject.Component;
5+
6+
@Component
7+
@BeanTypes(SomeInterface2.class)
8+
public class LimitedOtherComponent extends AbstractSuperClass
9+
implements SomeInterface, SomeInterface2 {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.avaje.inject;
2+
3+
import java.lang.annotation.*;
4+
5+
/** Limits the types exposed by this bean to the given types. */
6+
@Retention(RetentionPolicy.SOURCE)
7+
@Target({ElementType.METHOD, ElementType.TYPE})
8+
public @interface BeanTypes {
9+
10+
Class<?>[] value();
11+
}

0 commit comments

Comments
 (0)