Skip to content

Commit

Permalink
EachBean: Allow to inject the origin bean and the registration (#10902)
Browse files Browse the repository at this point in the history
For each bean functionality, we require a qualifier to properly identify the origin bean, but there are use cases where the bean cannot be modified.

Previously, we would allow each bean without a qualifier, but that will fail when you want to inject an origin; this PR is allowing to inject it.

Another solution for the qualifiers would be to add a qualifier that matches the bean type by the exact class, and maybe it can be reused elsewhere.
  • Loading branch information
dstepanov committed Jun 20, 2024
1 parent f147772 commit c04c2c0
Show file tree
Hide file tree
Showing 25 changed files with 613 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.micronaut.inject.foreach.noqualifier;

import io.micronaut.context.annotation.Requires;
import jakarta.inject.Singleton;

@Requires(property = "spec", value = "EachBeanNoQualifierSpec")
@Singleton
public class Bar implements MyService {
@Override
public String getName() {
return "bar";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.micronaut.inject.foreach.noqualifier

import io.micronaut.context.ApplicationContext
import spock.lang.Specification

class EachBeanNoQualifierSpec extends Specification {

void "test returning a map of beans"() {
given:
ApplicationContext context = ApplicationContext.run([
'spec': 'EachBeanNoQualifierSpec'
])
when:
context.getBean(MyMapEachUser)
then:
def e = thrown(Exception)
e.message.contains "Injecting a map of beans requires `@Named` qualifier. Multiple beans were found missing a qualifier resulting in duplicate keys: Duplicate key myEach1"
cleanup:
context.close()
}

void "test mapping each bean without qualifier"() {
given:
ApplicationContext context = ApplicationContext.run([
'spec': 'EachBeanNoQualifierSpec'
])
when:
def myEach1Users = context.getBean(MyEach1User)
then:
myEach1Users.getAll().size() == 2
myEach1Users.getAll().collect { it.myService.name }.containsAll("foo", "bar")
myEach1Users.getAll().collect { it.myService.name }.containsAll("foo", "bar")
when:
def myEach1s = context.getBeansOfType(MyEach1)
then:
myEach1s.size() == 2
myEach1s.collect { it.myService.name }.containsAll("foo", "bar")

when:
def myEach2Users = context.getBean(MyEach2User)
then:
myEach2Users.getAll().size() == 2
myEach2Users.getAll().collect { it.myServiceBeanReg.bean().name }.containsAll("foo", "bar")
myEach2Users.getAll().collect { context.getBean(it.myServiceBeanReg.definition()).name }.containsAll("foo", "bar")
when:
def myEach2s = context.getBeansOfType(MyEach2)
then:
myEach2s.size() == 2
myEach2s.collect { it.myServiceBeanReg.bean().name }.containsAll("foo", "bar")
myEach2s.collect { context.getBean(it.myServiceBeanReg.definition()).name }.containsAll("foo", "bar")

when:
def myEach3Users = context.getBean(MyEach3User)
then:
myEach3Users.getAll().size() == 2
myEach3Users.getAll().collect { it.myServiceBeanReg.bean().name }.containsAll("foo", "bar")
myEach3Users.getAll().collect { context.getBean(it.myServiceBeanReg.definition()).name }.containsAll("foo", "bar")
myEach3Users.getAll().collect { it.qualifier.toString() }.containsAll("EachBeanQualifier('Definition: io.micronaut.inject.foreach.noqualifier.Bar')", "EachBeanQualifier('Definition: io.micronaut.inject.foreach.noqualifier.Foo')")
when:
def myEach3s = context.getBeansOfType(MyEach3)
then:
myEach3s.size() == 2
myEach3s.collect { it.myServiceBeanReg.bean().name }.containsAll("foo", "bar")
myEach3s.collect { context.getBean(it.myServiceBeanReg.definition()).name }.containsAll("foo", "bar")
myEach3s.collect { it.qualifier.toString() }.containsAll("EachBeanQualifier('Definition: io.micronaut.inject.foreach.noqualifier.Bar')", "EachBeanQualifier('Definition: io.micronaut.inject.foreach.noqualifier.Foo')")

cleanup:
context.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.micronaut.inject.foreach.noqualifier;

import io.micronaut.context.annotation.Requires;
import jakarta.inject.Singleton;

@Requires(property = "spec", value = "EachBeanNoQualifierSpec")
@Singleton
public class Foo implements MyService {
@Override
public String getName() {
return "foo";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.micronaut.inject.foreach.noqualifier;

import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.Requires;

@Requires(property = "spec", value = "EachBeanNoQualifierSpec")
@EachBean(MyService.class)
public class MyEach1 {

private final MyService myService;

public MyEach1(MyService myService) {
this.myService = myService;
}

public MyService getMyService() {
return myService;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.micronaut.inject.foreach.noqualifier;

import io.micronaut.context.annotation.Requires;
import jakarta.inject.Singleton;

import java.util.List;

@Requires(property = "spec", value = "EachBeanNoQualifierSpec")
@Singleton
public class MyEach1User {

private final List<MyEach1> myEach1s;

public MyEach1User(List<MyEach1> myEach1s) {
this.myEach1s = myEach1s;
}

public List<MyEach1> getAll() {
return myEach1s;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.micronaut.inject.foreach.noqualifier;

import io.micronaut.context.BeanRegistration;
import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.Requires;

@Requires(property = "spec", value = "EachBeanNoQualifierSpec")
@EachBean(MyService.class)
public class MyEach2 {

private final BeanRegistration<MyService> myServiceBeanReg;

public MyEach2(BeanRegistration<MyService> myServiceBeanReg) {
this.myServiceBeanReg = myServiceBeanReg;
}

public BeanRegistration<MyService> getMyServiceBeanReg() {
return myServiceBeanReg;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.micronaut.inject.foreach.noqualifier;

import io.micronaut.context.annotation.Requires;
import jakarta.inject.Singleton;

import java.util.List;

@Requires(property = "spec", value = "EachBeanNoQualifierSpec")
@Singleton
public class MyEach2User {

private final List<MyEach2> myEach2s;

public MyEach2User(List<MyEach2> myEach2s) {
this.myEach2s = myEach2s;
}

public List<MyEach2> getAll() {
return myEach2s;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.micronaut.inject.foreach.noqualifier;

import io.micronaut.context.BeanRegistration;
import io.micronaut.context.Qualifier;
import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.Requires;

@Requires(property = "spec", value = "EachBeanNoQualifierSpec")
@EachBean(MyService.class)
public class MyEach3 {

private final BeanRegistration<MyService> myServiceBeanReg;
private final Qualifier<MyService> qualifier;

public MyEach3(BeanRegistration<MyService> myServiceBeanReg, Qualifier<MyService> qualifier) {
this.myServiceBeanReg = myServiceBeanReg;
this.qualifier = qualifier;
}

public BeanRegistration<MyService> getMyServiceBeanReg() {
return myServiceBeanReg;
}

public Qualifier<MyService> getQualifier() {
return qualifier;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.micronaut.inject.foreach.noqualifier;

import io.micronaut.context.annotation.Requires;
import jakarta.inject.Singleton;

import java.util.List;

@Requires(property = "spec", value = "EachBeanNoQualifierSpec")
@Singleton
public class MyEach3User {

private final List<MyEach3> beans;

public MyEach3User(List<MyEach3> beans) {
this.beans = beans;
}

public List<MyEach3> getAll() {
return beans;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.micronaut.inject.foreach.noqualifier;

import io.micronaut.context.annotation.Requires;
import jakarta.inject.Singleton;

import java.util.Map;

@Requires(property = "spec", value = "EachBeanNoQualifierSpec")
@Singleton
public class MyMapEachUser {

private final Map<String, MyEach1> allMap;

public MyMapEachUser(Map<String, MyEach1> allMap) {
this.allMap = allMap;
}

public Map<String, MyEach1> getAllMap() {
return allMap;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.micronaut.inject.foreach.noqualifier;

public interface MyService {

String getName();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.micronaut.inject.foreach.qualifier;

import io.micronaut.context.annotation.Requires;
import jakarta.inject.Named;
import jakarta.inject.Singleton;

@Requires(property = "spec", value = "EachBeanQualifierSpec")
@Named("Bar")
@Singleton
public class Bar implements MyService {
@Override
public String getName() {
return "bar";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package io.micronaut.inject.foreach.qualifier

import io.micronaut.context.ApplicationContext
import spock.lang.Specification

class EachBeanQualifierSpec extends Specification {

void "test returning a map of beans"() {
given:
ApplicationContext context = ApplicationContext.run([
'spec': 'EachBeanQualifierSpec'
])
when:
def bean = context.getBean(MyMapEachUser)
then:
bean.getAllMap().keySet().containsAll(["Foo", "Bar"])
cleanup:
context.close()
}

void "test mapping each bean with qualifier"() {
given:
ApplicationContext context = ApplicationContext.run([
'spec': 'EachBeanQualifierSpec'
])
when:
def myEach1Users = context.getBean(MyEach1User)
then:
myEach1Users.getAll().size() == 2
myEach1Users.getAll().collect { it.myService.name }.containsAll("foo", "bar")
when:
def myEach1s = context.getBeansOfType(MyEach1)
then:
myEach1s.size() == 2
myEach1s.collect { it.myService.name }.containsAll("foo", "bar")

when:
def myEach2Users = context.getBean(MyEach2User)
then:
myEach2Users.getAll().size() == 2
myEach2Users.getAll().collect { it.myServiceBeanReg.bean().name }.containsAll("foo", "bar")
myEach2Users.getAll().collect { context.getBean(it.myServiceBeanReg.definition()).name }.containsAll("foo", "bar")
when:
def myEach2s = context.getBeansOfType(MyEach2)
then:
myEach2s.size() == 2
myEach2s.collect { it.myServiceBeanReg.bean().name }.containsAll("foo", "bar")
myEach2s.collect { context.getBean(it.myServiceBeanReg.definition()).name }.containsAll("foo", "bar")

when:
def myEach3Users = context.getBean(MyEach3User)
then:
myEach3Users.getAll().size() == 2
myEach3Users.getAll().collect { it.myServiceBeanReg.bean().name }.containsAll("foo", "bar")
myEach3Users.getAll().collect { context.getBean(it.myServiceBeanReg.definition()).name }.containsAll("foo", "bar")
myEach3Users.getAll().collect { it.qualifier.toString() }.containsAll("@Named('Foo')", "@Named('Bar')")
when:
def myEach3s = context.getBeansOfType(MyEach3)
then:
myEach3s.size() == 2
myEach3s.collect { it.myServiceBeanReg.bean().name }.containsAll("foo", "bar")
myEach3s.collect { context.getBean(it.myServiceBeanReg.definition()).name }.containsAll("foo", "bar")
myEach3s.collect { it.qualifier.toString() }.containsAll("@Named('Foo')", "@Named('Bar')")

cleanup:
context.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.micronaut.inject.foreach.qualifier;

import io.micronaut.context.annotation.Requires;
import jakarta.inject.Named;
import jakarta.inject.Singleton;

@Requires(property = "spec", value = "EachBeanQualifierSpec")
@Named("Foo")
@Singleton
public class Foo implements MyService {
@Override
public String getName() {
return "foo";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.micronaut.inject.foreach.qualifier;

import io.micronaut.context.annotation.EachBean;
import io.micronaut.context.annotation.Requires;

@Requires(property = "spec", value = "EachBeanQualifierSpec")
@EachBean(MyService.class)
public class MyEach1 {

private final MyService myService;

public MyEach1(MyService myService) {
this.myService = myService;
}

public MyService getMyService() {
return myService;
}
}
Loading

0 comments on commit c04c2c0

Please sign in to comment.