Skip to content

Commit

Permalink
Merge pull request #548 from geoand/single-constructor
Browse files Browse the repository at this point in the history
Add support for single non no-arg constructor injection without @Inject
  • Loading branch information
mkouba authored Jan 18, 2019
2 parents db89eb9 + 052d4c8 commit 660f064
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,24 @@ private List<BeanInfo> findBeans(Collection<DotName> beanDefiningAnnotations, Li
continue;
}

if (!beanClass.hasNoArgsConstructor()
&& beanClass.methods().stream().noneMatch(m -> m.name().equals("<init>") && m.hasAnnotation(DotNames.INJECT))) {
// Must have a constructor with no parameters or declare a constructor annotated with @Inject
continue;
if (!beanClass.hasNoArgsConstructor()) {
int numberOfConstructorsWithoutInject = 0;
int numberOfConstructorsWithInject = 0;
for (MethodInfo m : beanClass.methods()) {
if (m.name().equals("<init>")) {
if (m.hasAnnotation(DotNames.INJECT)) {
numberOfConstructorsWithInject++;
} else {
numberOfConstructorsWithoutInject++;
}
}
}

// a bean without no-arg constructor needs to have either a constructor annotated with @Inject
// or a single constructor
if (numberOfConstructorsWithInject == 0 && numberOfConstructorsWithoutInject != 1) {
continue;
}
}

if (annotationStore.hasAnnotation(beanClass, DotNames.VETOED)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.jboss.protean.arc.processor;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -55,7 +56,7 @@ public class Injection {
static List<Injection> forBean(AnnotationTarget beanTarget, BeanDeployment beanDeployment) {
if (Kind.CLASS.equals(beanTarget.kind())) {
List<Injection> injections = new ArrayList<>();
forClassBean(beanTarget.asClass(), beanDeployment, injections);
forClassBean(beanTarget.asClass(), beanDeployment, injections, true);
return injections;
} else if (Kind.METHOD.equals(beanTarget.kind())) {
if (beanTarget.asMethod().parameters().isEmpty()) {
Expand All @@ -67,7 +68,7 @@ static List<Injection> forBean(AnnotationTarget beanTarget, BeanDeployment beanD
throw new IllegalArgumentException("Unsupported annotation target");
}

private static void forClassBean(ClassInfo beanTarget, BeanDeployment beanDeployment, List<Injection> injections) {
private static void forClassBean(ClassInfo beanTarget, BeanDeployment beanDeployment, List<Injection> injections, boolean isFirstLevel) {

List<AnnotationInstance> injectAnnotations = getAllInjectionPoints(beanDeployment, beanTarget, DotNames.INJECT);

Expand All @@ -86,6 +87,33 @@ private static void forClassBean(ClassInfo beanTarget, BeanDeployment beanDeploy
continue;
}
}
// if the class has a single non no-arg constructor that is not annotated with @Inject,
// the class is not a non-static inner or and it not a superclass of of a bean
// we consider that constructor as an injection
if (isFirstLevel) {
boolean constrInjectionExists = false;
for (Injection injection : injections) {
if (injection.isConstructor()) {
constrInjectionExists = true;
break;
}
}

final boolean isNonStaticInnerClass = beanTarget.name().isInner()
&& !Modifier.isStatic(beanTarget.flags());
if (!isNonStaticInnerClass && !constrInjectionExists) {
List<MethodInfo> nonNoargConstrs = new ArrayList<>();
for (MethodInfo constr : beanTarget.methods()) {
if ("<init>".equals(constr.name()) && constr.parameters().size() > 0) {
nonNoargConstrs.add(constr);
}
}
if(nonNoargConstrs.size() == 1) {
final MethodInfo injectTarget = nonNoargConstrs.get(0);
injections.add(new Injection(injectTarget, InjectionPointInfo.fromMethod(injectTarget.asMethod(), beanDeployment)));
}
}
}

for (DotName resourceAnnotation : beanDeployment.getResourceAnnotations()) {
List<AnnotationInstance> resourceAnnotations = getAllInjectionPoints(beanDeployment, beanTarget, resourceAnnotation);
Expand All @@ -105,7 +133,7 @@ private static void forClassBean(ClassInfo beanTarget, BeanDeployment beanDeploy
if (!beanTarget.superName().equals(DotNames.OBJECT)) {
ClassInfo info = beanDeployment.getIndex().getClassByName(beanTarget.superName());
if (info != null) {
forClassBean(info, beanDeployment, injections);
forClassBean(info, beanDeployment, injections, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2018 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.protean.arc.test.injection.constructornoinject;

import org.jboss.protean.arc.Arc;
import org.jboss.protean.arc.test.ArcTestContainer;
import org.junit.Rule;
import org.junit.Test;

import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Singleton;

import static org.junit.Assert.assertNotNull;

public class SingleNonNoArgConstructorInjectionTest {

@Rule
public ArcTestContainer container = new ArcTestContainer(Head.class, CombineHarvester.class);

@Test
public void testInjection() {
assertNotNull(Arc.container().instance(CombineHarvester.class).get().getHead());
}

@Dependent
static class Head {

}

@Singleton
static class CombineHarvester {

private final Head head;

@Inject
private Head head2;

public CombineHarvester(Head head) {
this.head = head;
}

public Head getHead() {
return head;
}

}
}

0 comments on commit 660f064

Please sign in to comment.