How to create an "Enable" annotation in Spring Boot that can pass values to a configuration?

94 views Asked by At

I'm using Spring Boot 3.2 and I'm trying to write an annotation that imports some spring configuration. This is what I have:

@Configuration
public class CustomAutoConfiguration {

    @Bean
    public CustomBean customBean() {      
        return new CustomBean();
    }
}

and then I have this annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(CustomAutoConfiguration.class)
public @interface EnableCustomAutoConfiguration {
}

which I can then enable like this:

@SpringBootApplication
@EnableCustomAutoConfiguration
public class MySpringBootApplication {

    public static void main(String[] args) {
        SpringApplication.run(MySpringBootApplication.class, args);
    }
}

But I need the CustomBean to include some value specified in the @EnableCustomAutoConfiguration annotation. For example, if I modify the EnableCustomAutoConfiguration like this:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(CustomAutoConfiguration.class)
public @interface EnableCustomAutoConfiguration {
   String someString();
}

I then want the someString to be accessible in CustomAutoConfiguration:

@Configuration
public class CustomAutoConfiguration {

    @Bean
    public CustomBean customBean() {      
        String someString = ?? // How can I get the value of "someString" defined in the annotation?
        return new CustomBean(someString);
    }
}

How can I achieve this?

2

There are 2 answers

0
Dirk Deyne On BEST ANSWER

You could find the annotated bean via the ApplicationContext

something like:

 @Bean
 public CustomBean customBean(ApplicationContext applicationContext) {      
    // get the annotated bean name
    Optional<String> customAutoConfigurationBeanName = 
      applicationContext.getBeansWithAnnotation(EnableCustomAutoConfiguration.class)
                        .keySet().stream().findFirst();
   if (customAutoConfigurationBeanName.isEmpty()) return null;
   // get the EnableCustomAutoConfiguration annotation
   EnableCustomAutoConfiguration enableCustomAutoConfiguration = 
     applicationContext.findAnnotationOnBean(customAutoConfigurationBeanName.get(),
                        EnableCustomAutoConfiguration.class);
  return new CustomBean(enableCustomAutoConfiguration.someString());
}
0
Tony Hank On

If you use @Import, there is no need to use @Configuration again, they all inject the class into the ioc container, if I understand you correctly, you can use them as follows

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

@Slf4j
public class CustomAutoConfiguration implements DeferredImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        AnnotationAttributes attributes = AnnotationAttributes
                .fromMap(metadata.getAnnotationAttributes(EnableCustomAutoConfiguration.class.getName(), true));
        if (attributes == null) {
            log.info("@EnableCustomAutoConfiguration is not present on importing class");
            return new String[0];
        }
        // this  "someString"  is the value of the property under the annotation you defined
        String someString = attributes.getString("someString");

//Depending on the value of the property, 
//determine which configuration class you need to enable to load into the ioc container
        return new String[]{YourConfig.class.getName()};
    }
}