1

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:

  • configure number of beans via spring configuration;
  • pick this configuration up via @ConfigurationProperties;
  • create & inject @Beans based on said config from an autoconfiguration module;
  • be able to @Autowire created beans in application.

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?

laur
  • 498
  • 9
  • 17

1 Answers1

0

If this is a Spring Boot project look at:

https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/boot-features-caching.html#boot-features-caching-provider-caffeine

Lara
  • 21
  • 1
  • Thanks, but the question isn't _really_ about caching; added it just as an example of how the @Autowireable beans should be able to be configured. – laur Nov 21 '17 at 17:19