Skip to content
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

Provide additional conditional registration capabilities in BeanRegistrar #21497

Open
spring-projects-issues opened this issue Jun 20, 2018 · 7 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jun 20, 2018

Dave Syer opened SPR-16959 and commented

There is a gap in the the functional bean registration features in BeanDefinitionBuilder and GenericApplicationContext. Once you register a Supplier you have committed to provide an instance of the class you register, whereas in a lot of use cases you don't know whether or not you want to provide it until the bean factory is available (e.g. conditional on another instance of the same class being available). I guess the change needs to be in the BeanDefinitionRegistry interface, for example to support a Predicate<ConditionContext> as well as the Supplier. If the interface doesn't change, I suppose returning null from the Supplier might be an option, but that seems a bit ugly, and might be too late, since the BeanDefinition has already been registered at that point.


Issue Links:

@spring-projects-issues
Copy link
Collaborator Author

Andy Wilkinson commented

I've been experimenting a bit with conditions in functional bean registration building on top of Framework. It has become apparent that we really need support at the Framework level so that condition evaluation and bean registration can occur at the right time. As Dave alludes to above, the biggest stumbling block at the moment is with bean-based conditions where we'd need a callback at the point where @Configuration class parsing is performing bean registration. This would, I think, allow a mixture of functional-based and annotation-based conditional registration in the same application context.

As things stand, condition evaluation is tied to the @Configuration programming model. It feels to me like this could be generalised so that @Configuration classes are just one source of conditional beans. Rather than driving the registration from ConfigurationClassPostProcessor it could be driven from something general that calls multiple different types of sources of conditional beans. That might result in a new callback that's specifically intended for functional bean registration, rather than the current approach that typically seems to involve the use of the more general ApplicationContextInitializer which doesn't have a well-defined lifecycle for when it's called.

The thoughts above have come from some experimentation and prototyping for reducing memory usage and startup time of Boot apps. Functional registration appears to be one way of doing that but we're quite a way short of declaring that it's the way to do it. As such, we (the Boot team) don't have a concrete need for this functionality just yet, although I do think that conditional beans becoming a broader concept, rather than being confined to the annotation-based programming model as they are today, would be generally useful beyond what Boot may or may not need.

@spring-projects-issues spring-projects-issues added type: enhancement A general enhancement in: core Issues in core modules (aop, beans, core, context, expression) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 5.x Backlog milestone Jan 11, 2019
@sdeleuze sdeleuze added status: pending-design-work Needs design work before any code can be developed status: on-hold We can't start working on this issue yet labels Oct 25, 2023
@snicoll snicoll removed their assignment Dec 11, 2023
@jhoeller jhoeller modified the milestones: 6.x Backlog, General Backlog Dec 23, 2023
@bclozel bclozel removed their assignment Mar 25, 2024
@sdeleuze sdeleuze changed the title Support for conditional registration of functional bean definitions [SPR-16959] Support for conditional registration of functional bean definitions Mar 6, 2025
@sdeleuze
Copy link
Contributor

sdeleuze commented Mar 6, 2025

Spring Framework 7 introduces a new BeanRegistrar contract that allows to combine @Conditional (and variants, likely including Spring Boot ones even if I have not yet tested) and programmatic conditions, see #18353 (comment).

We should explore if more is needed or if that's flexible enough.

@sdeleuze
Copy link
Contributor

sdeleuze commented Mar 10, 2025

@dsyer @rwinch @wilkinsona @philwebb I am now targeting Spring Framework 7.0.0-M4 for the resolution of this issue. I suggest we explore concrete use cases for additional programmatic conditional capabilities in BeanRegistrar BeanRegistry and hopefully provide meaningful related APIs that are not covered by the existing Environment parameter.

For example, we could provide an equivalent of @ConditionalOnClass with a method like BeanRegistry#isClassPresent(String) that would use internally the bean class loader to perform such check. Native support would be possible via #34564.

That does not mean we will add support for all current annotation conditions support at Spring Boot level. For example, for now we don't necessarily plan to add support for an equivalent of @ConditionalOnBean at Spring Framework level, but maybe using @ConditionalOnBean on the @Configuration class that imports the bean registrar will be good enough (note I have not yet tested it works).

The most challenging limitation we will have to handle is that BeanRegistrar#register should not get (by design) arbitrary bean outside of a bean instance supplier. That probably means we will have to support more narrow use cases for the use cases we have like the "configuration from a DSL as described by Rob in the original #18353 description. I am currently not sure if and how we will be able to support that, but we can identify and explore those use cases and see if we can find a reasonable tradeoff between what BeanRegistrar can support in both AOT and non-AOT mode, and the need on usage side (either application, Spring Boot or portfolio level like in Spring Security).

@sdeleuze sdeleuze self-assigned this Mar 10, 2025
@philwebb
Copy link
Member

I'm not sure that BeanRegistrar would be the best place for a isClassPresent method, I think perhaps I'd look at adding something to BeanRegistry.Spec since it feels like conditions are part predicates on the spec to determine if the bean definitions get added or not.

Perhaps something like

Spec.condition(Pedicate<ConditionContext> predicate) where ConditionContext could provide access to thing like the bean classloader etc.

@sdeleuze
Copy link
Contributor

sdeleuze commented Mar 10, 2025

My mistake, I meant BeanRegistry not BeanRegistrar (now fixed in my comment).

@sdeleuze sdeleuze modified the milestones: 7.0.0-M4, General Backlog Mar 21, 2025
@sdeleuze
Copy link
Contributor

sdeleuze commented Mar 21, 2025

So we discussed the Spring Security DSL use case with @rwinch who proposed a really interesting idea where Spring Security DSL could be used inside a BeanRegistrar implementation written by the user. I refined it a bit with an abstract class that Spring Security could provide, and that gives something like below which is already supported in what has been shipped in Spring Framework 7.0.0-M3 and does not require additional conditions. cc @wakingrufus @OlgaMaciaszek @rstoyanchev @dsyer

@Configuration
@Import(MySecurity.class)
class MyConfiguration {
}

class MySecurity extends SecurityRegistrar {

    @Override
    public void register(HttpSecurity registry, Environment env) {
        http.formLogin();
    }
}

// Could be provided by Spring Security
abstract class SecurityRegistrar implements BeanRegistrar {


    public abstract void register(HttpSecurity http, Environment env);

    @Override
    public void register(BeanRegistry registry, Environment env) {
        HttpSecurity http = new HttpSecurity();
        register(http, env);

        if (http.hasFormLogin()) {
            registry.registerBean(...);
        }
        // ...
    }
}

For other conditions, as discussed recently we would prefer to give more time post Framework 7.0 / Boot 4.0 GA to the Spring Boot team to provide feedback on potential programmatic equivalent of @ConditionalOnBean, @ConditionalOnMissingBean and similar advanced capabilities.

For classpath checks, it is unclear if we should let users just use if statements with ClassUtils#isPresent, if we should expose a ClassLoader or if we should provide something like BeanRegistry#isClassPresent(String). As we don't have a clear use case for now, I am leaning to wait for Spring Boot team feedback post GA as well.

As a consequence, I move back this issue to the general backlog for potential exploration in Spring Framework 7.x after the 7.0 GA in collaboration with the Boot team. Does not prevent to add specific capabilities before 7.0.0 if some very well defined needs arise via more specific issues.

@sdeleuze sdeleuze changed the title Support for conditional registration of functional bean definitions Provide additional conditional registration capabilities in BeanRegistrar Mar 21, 2025
@OlgaMaciaszek
Copy link
Contributor

The ability to work with DSL looks very interesting and might help us to work around the lifecycle issues for users interacting directly with Interface Clients Registry (CC @rstoyanchev ). Will try it out and provide any feedback that we have.

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

7 participants