Skip to content

Support for fine-grained by-type references in the bean definition model #23032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jxblum opened this issue May 25, 2019 · 2 comments
Closed
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Milestone

Comments

@jxblum
Copy link

jxblum commented May 25, 2019

After reviewing the core Spring Framework BeanReference implementations, I got to thinking if there was a RuntimeBeanTypeReference like implementation, or something similar?

I am aware of the RuntimeBeanReference and RuntimeBeanNameReference implementations, however, both of these only refer to a “single” bean in the Spring context.

This is useful in situations like the following.

Given:

class A {
    B b;
    // getters/setters omitted
}

class B { 
    ... 
}

Then in a framework (e.g. Spring Data or Spring Session, etc) provided ImportBeanDefinitionRegistrar implementation, I can:

class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(A.class);

        builder.addPropertyReference("b",  "nameOfSingleBeanB");

        // Or alternatively...
        builder.addPropertyValue("b", new RuntimeBeanReference("nameOfSingleBeanB"));

        registry.registerBeanDefinition.register("nameForBeanA", builder.build());
    }
}

However, what if I want to register a BeanDefinition who’s class type has a property that requests all beans of a particular type declared and registered in the Spring container?

Reimagining our class definitions from above:

class A {
    B[] b
    // getters/setters omitted
}

class B {
    ...
}

Then, with a RuntimeBeanTypeReference, I could:

class CustomImportBeanDefinitionRegistrar implement ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(A.class);

        builder.addPropertyReference("b",  new RuntimeBeanTypeReference(B.class));

        registry.registerBeanDefinition("nameForBeanA", builder.build());
    }
}

I know we also have things like Managed[List|Map|Set] which can take 1 or more BeanReference objects, but again, I am constrained by having to know all the bean names possibly registered in the container upfront.

Of course, I could iterate the BeanFactory, beanDefinitionNames and inspect each BeanDefinition to determine if the class of the bean definition matches my desired type (e.g. B).

But, this does not work so well with @Configuration classes as it turns out.

It seems that when looking at the beanDefinitionMap the @Configuration classes have not be resolved with any @Bean definitions they contain, particularly when inspecting the BeanFactory (e.g. getBeansOfType(..)) inside a ImportBeanDefinitionRegistrar.

Therefore, by iterating (prematurely), I would not know if those @Configuration classes (or really, any @Component class), which is allowed to contain other @Bean definitions, would potentially match beans of my desired type (i.e. B).

Hence the thinking behind a RuntimeBeanTypeReference implementation.

I suppose class A could be defined as:

class A {
    @Autowired
    B[] b;
}

But, if annotation config (autowiring) were not enabled (which even though maybe uncommon), would not work.

Hence my thinking behind a RuntimeBeanTypeReference implementation.

Thoughts?

@jxblum jxblum changed the title Support a fine-grained by-type references in the metadata model. Support for fine-grained, by-type references in the metadata model. May 25, 2019
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label May 25, 2019
@jxblum
Copy link
Author

jxblum commented May 25, 2019

Actually, thinking about this further, the RuntimeBeanTypeReference would even work for a single collaborator/dependency reference, as in the original class A definition, as follows:

class A {
    B b;
    // getter/setters omitted
}

The BeanDefinition could still be as follows:

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(A.class);

build.setPropertyValue("b", new RuntimeBeanTypeReference(B.class));

registry.registerBeanDefinition("beanNameForA", builder.build());

Of course, the Spring container would expect only a single bean of type B to be declared and registered in the configuration, otherwise a NoUniqueBeanDefinitionException would be thrown.

Alternately, it might be a nice addition to this feature if the collaborator/dependency could still be resolved, even with multiple beans of type B declared and registered in the Spring container if the desired collaborator was qualified, something like:

class A {

  @Qualifier("QualifiedB")
  B b;
}

Or...

class A {

  B b;

   @Qualifier("QualifiedB")
    public void setB(B b) {
        this.b = b;
    }
}

Or if one of the multiple B type bean instances was marked as the @Primary:

@Configuration
class ApplicationConfiguration {

    @Bean
    @Primary
    B primaryB() {
        return new B();
    }

    @Bean
    B anotherB() {
        return new B();
    }
}

Then, the Spring container would still do the right thing and resolve the correct B bean instance.

Food for thought.

@jhoeller jhoeller self-assigned this May 25, 2019
@jhoeller jhoeller added in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels May 25, 2019
@jhoeller jhoeller added this to the 5.2 M3 milestone May 25, 2019
@jhoeller jhoeller changed the title Support for fine-grained, by-type references in the metadata model. Support for fine-grained by-type references in the bean definition model May 25, 2019
@jhoeller
Copy link
Contributor

jhoeller commented Jun 8, 2019

I've introduced overloaded constructors on RuntimeBeanReference that point to a bean by Class, adapting straight to getBean(Class) analogous to getBean(String). This may be useful for specific purposes, e.g. resolution of an interface-typed dependency to a specific implementation class bean.

For general autowiring purposes, covering individual bean types but also collection types and arrays (following our common dependency resolution rules for autowired injection points), I've introduced an AutowiredPropertyMarker that can be added to the PropertyValues for a specific property of a specific bean, triggering autowired resolution through the resolveDependency algorithm just like an @Autowired annotation or an AUTOWIRE_BY_TYPE setting would. For convenience and discoverability, I've also added an addAutowiredProperty method to BeanDefinitionBuilder.

To be committed soon :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

3 participants