laur
laur

Reputation: 590

Create Autowireable @Beans from autoconfiguration

Related:

This question is pretty much the same as this, but the answer using BeanFactoryAware doesn't solve my problem as the created @Beans are not @Autowireable.

Requirements:

Example: Make it possible to declare number of caffeine-based Cache @Beans via spring config. Following is implementation based on the accepted answer from the similar question:

First, the boot autoconfiguration module:

@Component
@ConfigurationProperties
@Data
public class Props {

    private LocalCacheConf cache = new LocalCacheConf();

    @Data
    public static class LocalCacheConf {

        /**
        * List of caches to create, with accompanying configuration.
        */
        private List<CacheDef> caches = new ArrayList<>();

        @Data
        public static class CacheDef {

            private String name;
            private CaffeineCacheConf properties;
        }

        @Data
        public static class CaffeineCacheConf {

            private long maximumSize = -1;
        }
    }
}


@Configuration
@Slf4j
public class LocalCacheConfig implements BeanFactoryAware {

    @Autowired private Props props;
    @Setter private BeanFactory beanFactory;
    private CaffeineCacheManager localCacheManager;

    @PostConstruct
    public void configure() {
        setCacheManager();
        createCaches( props.getCache() );
    }

    private void createCaches( LocalCacheConf locCacheConf ) {
        ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;

        for ( CacheDef cacheProps : locCacheConf.getCaches() ) {
            Cache cache = createAndConfigureCache(
                    cacheProps.getName(),
                    cacheProps.getProperties()
            );
            configurableBeanFactory.registerSingleton( cacheProps.getName(), cache );
        }
    }

    private Cache createAndConfigureCache( String name, CaffeineCacheConf props ) {
        Caffeine<Object, Object> c = Caffeine.newBuilder().recordStats();

        if ( props.getMaximumSize() >= 0 ) {
            c.maximumSize( props.getMaximumSize() );
        }
        // can extend config to include additional properties

        localCacheManager.setCaffeine( c );
        log.info( "building [{}] cache with config: [{}]", name, c.toString() );

        return localCacheManager.getCache( name );
    }

    private void setCacheManager() {
        log.info( "creating {}...", "localCacheManager" );
        localCacheManager = new CaffeineCacheManager();
        ( (ConfigurableBeanFactory) beanFactory )
                .registerSingleton( "localCacheManager", localCacheManager );
    }
}

And finally, the using application would define its caches via configuration (yaml here):

cache:
  caches:
    - name: c1
      properties:
        maximumSize: 50
    - name: c2
      properties:
        maximumSize: 5000

This example is based on the answer from the similar question, but the cache @Beans created this way can't be autowired in the application.

Feels like registering bean definitions in ImportBeanDefinitionRegistrar could work, but don't think beanDefs can be used with Caffeine instances, as those are created via builder, not property setters.

How could this be implemented?

Upvotes: 1

Views: 710

Answers (1)

Related Questions