Skip to content

Commit

Permalink
Add @BeanTypes Annotation to Limit Injection Types (#665)
Browse files Browse the repository at this point in the history
* Adds BeanTypes annotation
  • Loading branch information
SentryMan authored Aug 27, 2024
1 parent b002969 commit ddabd07
Show file tree
Hide file tree
Showing 14 changed files with 175 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.example.myapp.beantypes;

public abstract class AbstractSuperClass {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.example.myapp.beantypes;

import org.example.myapp.config.AppConfig.SomeInterface;
import org.other.one.SomeOptionalDep;

import io.avaje.inject.BeanTypes;
import io.avaje.inject.Component;
import jakarta.inject.Named;

@Component
@Named("type")
@BeanTypes(AbstractSuperClass.class)
public class BeanTypeComponent extends AbstractSuperClass
implements SomeInterface, SomeOptionalDep, LimitedInterface {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.example.myapp.beantypes;

import io.avaje.inject.Bean;
import io.avaje.inject.BeanTypes;
import io.avaje.inject.Factory;
import jakarta.inject.Named;

@Factory
public class LimitedFactory {

@Bean
@Named("factory")
@BeanTypes(LimitedInterface.class)
BeanTypeComponent bean() {
return new BeanTypeComponent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.example.myapp.beantypes;

public interface LimitedInterface {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.example.myapp.beantypes;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;

import org.junit.jupiter.api.Test;

import io.avaje.inject.BeanScope;

class BeanTypesTest {

@Test
void testBeanTypesRestrictingInjection() {
try (var scope = BeanScope.builder().build()) {

assertFalse(scope.contains(BeanTypeComponent.class));
assertThat(scope.get(AbstractSuperClass.class)).isNotNull();
assertThat(scope.get(LimitedInterface.class)).isNotNull();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ final class AssistBeanReader {
AssistBeanReader(TypeElement beanType) {
this.beanType = beanType;
this.type = beanType.getQualifiedName().toString();
this.typeReader = new TypeReader(UType.parse(beanType.asType()), beanType, importTypes, false);
this.typeReader =
new TypeReader(
Optional.empty(),
UType.parse(beanType.asType()),
beanType,
importTypes,
false);

typeReader.process();
qualifierName = typeReader.name();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,15 @@ final class BeanReader {
this.primary = PrimaryPrism.isPresent(beanType);
this.secondary = !primary && SecondaryPrism.isPresent(beanType);
this.lazy = !FactoryPrism.isPresent(beanType) && LazyPrism.isPresent(beanType);
this.typeReader = new TypeReader(UType.parse(beanType.asType()), beanType, importTypes, factory);
final var beantypes = BeanTypesPrism.getOptionalOn(beanType);
beantypes.ifPresent(p -> Util.validateBeanTypes(beanType, p.value()));
this.typeReader =
new TypeReader(
beantypes,
UType.parse(beanType.asType()),
beanType,
importTypes,
factory);

typeReader.process();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ final class MethodReader {
this.initMethod = initMethod;
this.destroyMethod = destroyMethod;
} else {
this.typeReader = new TypeReader(genericType, returnElement, importTypes);
final var beantypes = BeanTypesPrism.getOptionalOn(element);
beantypes.ifPresent(p -> Util.validateBeanTypes(element, p.value()));
this.typeReader = new TypeReader(beantypes, genericType, returnElement, importTypes);
typeReader.process();
MethodLifecycleReader lifecycleReader = new MethodLifecycleReader(returnElement, initMethod, destroyMethod);
this.initMethod = lifecycleReader.initMethod();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;

import java.util.List;
import java.util.Optional;
import java.util.Set;

import static java.util.stream.Collectors.toList;

final class TypeReader {
Expand All @@ -18,16 +19,37 @@ final class TypeReader {
private final TypeAnnotationReader annotationReader;
private Set<UType> genericTypes;
private String typesRegister;

TypeReader(UType genericType, TypeElement beanType, ImportTypeMap importTypes, boolean factory) {
this(genericType, true, beanType, importTypes, factory);
}

TypeReader(UType genericType, TypeElement returnElement, ImportTypeMap importTypes) {
this(genericType, false, returnElement, importTypes, false);
}

private TypeReader(UType genericType, boolean forBean, TypeElement beanType, ImportTypeMap importTypes, boolean factory) {
private final List<UType> injectsTypes;

TypeReader(
Optional<BeanTypesPrism> injectsTypes,
UType genericType,
TypeElement beanType,
ImportTypeMap importTypes,
boolean factory) {
this(injectsTypes, genericType, true, beanType, importTypes, factory);
}

TypeReader(
Optional<BeanTypesPrism> injectsTypes,
UType genericType,
TypeElement returnElement,
ImportTypeMap importTypes) {
this(injectsTypes, genericType, false, returnElement, importTypes, false);
}

private TypeReader(
Optional<BeanTypesPrism> injectsTypes,
UType genericType,
boolean forBean,
TypeElement beanType,
ImportTypeMap importTypes,
boolean factory) {
this.injectsTypes =
injectsTypes.map(BeanTypesPrism::value).stream()
.flatMap(List::stream)
.map(UType::parse)
.collect(toList());
this.forBean = forBean;
this.beanType = beanType;
this.importTypes = importTypes;
Expand All @@ -41,14 +63,20 @@ String typesRegister() {
}

List<String> provides() {
if (!injectsTypes.isEmpty()) {
return injectsTypes.stream().map(UType::full).collect(toList());
}
return extendsReader.provides().stream().map(UType::full).collect(toList());
}

List<String> autoProvides() {
if (!injectsTypes.isEmpty()) {
return injectsTypes.stream().map(UType::full).collect(toList());
}
return extendsReader.autoProvides().stream()
.filter(u -> u.componentTypes().stream().noneMatch(p -> p.kind() == TypeKind.TYPEVAR))
.map(UType::full)
.collect(toList());
.filter(u -> u.componentTypes().stream().noneMatch(p -> p.kind() == TypeKind.TYPEVAR))
.map(UType::full)
.collect(toList());
}

String providesAspect() {
Expand Down Expand Up @@ -120,8 +148,12 @@ String name() {

private void initRegistrationTypes() {
TypeAppender appender = new TypeAppender(importTypes);
appender.add(extendsReader.baseType());
appender.add(extendsReader.provides());
if (injectsTypes.isEmpty()) {
appender.add(extendsReader.baseType());
appender.add(extendsReader.provides());
} else {
appender.add(injectsTypes);
}
this.genericTypes = appender.genericTypes();
this.typesRegister = appender.asString();
}
Expand Down
14 changes: 14 additions & 0 deletions inject-generator/src/main/java/io/avaje/inject/generator/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
Expand Down Expand Up @@ -377,4 +379,16 @@ static String valhalla() {
}
return "";
}

static void validateBeanTypes(Element origin, List<TypeMirror> beanType) {
TypeMirror targetType =
origin instanceof TypeElement
? origin.asType()
: ((ExecutableElement) origin).getReturnType();
beanType.forEach(type -> {
if (!APContext.types().isAssignable(targetType, type)) {
APContext.logError(origin, "%s does not extend type %s", targetType, beanType);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
@GeneratePrism(Generated.class)
@GeneratePrism(Inject.class)
@GeneratePrism(InjectModule.class)
@GeneratePrism(BeanTypes.class)
@GeneratePrism(Lazy.class)
@GeneratePrism(Named.class)
@GeneratePrism(PreDestroy.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.avaje.inject.generator.models.valid.supertypes;

import io.avaje.inject.Bean;
import io.avaje.inject.BeanTypes;
import io.avaje.inject.Factory;

@Factory
public class LimitedFactory {

@Bean
@BeanTypes(SomeInterface.class)
OtherComponent bean() {
return new OtherComponent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.avaje.inject.generator.models.valid.supertypes;

import io.avaje.inject.BeanTypes;
import io.avaje.inject.Component;

@Component
@BeanTypes(SomeInterface2.class)
public class LimitedOtherComponent extends AbstractSuperClass
implements SomeInterface, SomeInterface2 {}
11 changes: 11 additions & 0 deletions inject/src/main/java/io/avaje/inject/BeanTypes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.avaje.inject;

import java.lang.annotation.*;

/** Limits the types exposed by this bean to the given types. */
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface BeanTypes {

Class<?>[] value();
}

0 comments on commit ddabd07

Please sign in to comment.