How to customize auto-configured beans

This page explains how to customize or replace the beans that the Spring Boot Starter creates automatically.

How auto-configuration works

The Spring Boot Starter uses @ConditionalOnMissingBean annotations on all auto-configured beans. This means your custom beans take precedence over the defaults. If you define a bean of the same type, the auto-configuration skips creating its version.

How to override a bean

Define a bean of the same type in your configuration class:

@Configuration
public class MyWhatIfConfiguration {

    @Bean
    public IUniqueIdGenerator customIdGenerator() {
        return new MyCustomIdGenerator();
    }
}

The starter detects your bean and skips creating the default IncrementalUniqueIdGenerator.

Common customization scenarios

Custom ID generator

The default ID generator uses an incremental counter starting from the epoch second. To use a different strategy:

@Bean
public IUniqueIdGenerator uuidBasedIdGenerator() {
    return () -> UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE;
}

Custom security manager

To implement custom authorization logic, provide your own IDatabaseSimulationsSecurityManager:

@Bean
public IDatabaseSimulationsSecurityManager customSecurityManager(
        ISimulationPersistenceManager persistenceManager,
        IBranchPermissionsManager branchPermissionsManager) {
    return new MyCustomSecurityManager(persistenceManager, branchPermissionsManager);
}

When you provide a custom security manager, set the security type to custom in your configuration:

atoti:
  what-if:
    security:
      type: custom

See How are user permissions managed for details on implementing custom security managers.

Custom workflow

To modify how simulations are created, executed, or deleted:

@Bean
public IDatabaseSimulationsWorkflow customWorkflow(
        DatabaseSimulationEngine engine,
        ISimulationPersistenceManager persistenceManager,
        IDatabaseSimulationsSecurityManager securityManager,
        IDatabaseService databaseService,
        IUniqueIdGenerator idGenerator) {
    return new MyCustomWorkflow(engine, persistenceManager, securityManager, databaseService, idGenerator);
}

Custom distributed database service

When running in distributed mode, you can provide a custom implementation:

@Bean
public IDatabaseService distributedDatabaseService(
        IDistributedQueryResultsMerger resultsMerger,
        Supplier<Set<String>> addressSupplier,
        Supplier<AAuthenticator> authenticator) {
    return new MyCustomDistributedDatabaseService(resultsMerger, addressSupplier, authenticator);
}

Set the configuration to not create the default distributed service:

atoti:
  what-if:
    distribution:
      enabled: true
      create-distributed-service: false

Custom SessionFactory

To customize the Hibernate SessionFactory beyond what persistence properties provide (such as adding interceptors or event listeners):

@Bean(destroyMethod = "close")
public SessionFactory whatIfSessionFactory(IWhatIfPersistenceProperties persistence) {
    var configuration = new org.hibernate.cfg.Configuration();
    var props = new Properties();
    props.putAll(persistence.getPersistence());

    // Add custom configuration
    props.setProperty("hibernate.cache.use_second_level_cache", "true");

    configuration.addProperties(props);
    configuration.addAnnotatedClass(DatabaseSimulationJPA.class);

    // Add custom interceptors or event listeners
    configuration.setInterceptor(new MyCustomInterceptor());

    return configuration.buildSessionFactory();
}

The destroyMethod = "close" attribute ensures the SessionFactory closes properly when the application shuts down. When you override the SessionFactory, the default ISimulationPersistenceManager automatically uses your custom instance.

Bean dependencies

When customizing beans, be aware of their dependencies. The auto-configuration wires beans together in this order:

  1. DatabaseSimulationEngine and IUniqueIdGenerator (no dependencies)
  2. SessionFactory (requires IWhatIfPersistenceProperties)
  3. ISimulationPersistenceManager (requires SessionFactory)
  4. IDatabaseSimulationsSecurityManager (may require persistence manager)
  5. IDatabaseSimulationsWorkflow (requires engine, persistence manager, security manager, database service, ID generator)
  6. DatabaseSimulationsRestService (requires workflow, persistence manager, database service)

If you override a bean that others depend on, ensure your implementation is compatible.

Verifying your customizations

To verify which beans are active, enable debug logging:

logging:
  level:
    com.activeviam.tools.whatif.configuration: DEBUG

Or inspect the application context at runtime:

@Autowired
private ApplicationContext context;

public void checkBeans() {
    IUniqueIdGenerator generator = context.getBean(IUniqueIdGenerator.class);
    System.out.println("ID Generator: " + generator.getClass().getName());
}