2

I am creating a custom AutoConfiguration for Spring Boot. One of the features I was attempting to create was to create one or more Beans dynamically and adding them to the ApplicationContext at runtime.

The problem I ran into was with Autowiring. My @SpringBootApplication class autowires those beans, and since they do not exist yet, autowire fails.

My first solution was to put @Lazy on the autowire, and that solved my problem.

However, I ran into something interesting. I added two beans that I was looking for into the AutoConfiguration code, and of course, it worked. By accident, I only removed one of the beans and re-ran my code. It worked.

@SpringBootApplication
public class SpringBootDemoApplication {
    @Autowired
    @Qualifier("some_name")
    private MyClass myClass;

    @Autowired
    @Qualifier("another_name")
    private MyClass anotherClass;

    ...
}

@Configuration
public class MyAutoConfigurationClass {
    @Bean(name="some_class")
    public MyClass myClass () {
        return null;
    }
}

So the short of it is this. If I defined only one of the beans in my autoconfiguration class, this seems to satisfy Autowired and it does not blow up and when I dynamically add my other beans, both beans are found.

The stipulation is that the Autowired bean that is first, must be the bean that is defined in my autoconfiguration class.

I am running the following:

  • Spring Boot Starter 1.5.7-RELEASE
  • Various Spring Framework 4.3.11-RELEASE

Is this a bug? Or is this the way Autowired is supposed to work?


@SpringBootApplication
public class SpringBootDemoApplication {

    @Autowired
    @Qualifier("myclass")
    private MyClass myClass;

    @Autowired
    @Qualifier("anotherMyClass")
    private MyClass anotherMyClass;
    ...
}

@Configuration
public class MyAutoConfiguration {

    private ConfigurableApplicationContext applicationContext;

    private final BeanFactory beanFactory;

    @Autowired
    private MyClassFactory myClassFactory;

    public MyAutoConfiguration(ApplicationContext applicationContext, BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        this.applicationContext = (ConfigurableApplicationContext) applicationContext;
    }

    @PostConstruct
    public void init() throws IOException, SQLException {
        this.myClassFactory.create(this.applicationContext);
    }

    //  without this @Bean definition SpringBoot will recieve the following error and stop
    //  AnnotationConfigEmbeddedWebApplicationContext - Exception encountered during context initialization
    @Bean(name="myClass")
    public DataSource anyNameWillDoItDoesntMatter() {
        return null;
    };
}

@Component
class MyClassFactory {
    public void create(ConfigurableApplicationContext applicationContext) {
        applicationContext.getBeanFactory().registerSingleton(name, value);
    }
}

So is this expected behavior of @Autowired?

The Architect
  • 75
  • 1
  • 11
  • Can you show us code for the dynamically created beans you're trying to add? What you have here is pretty standard spring and should work fine. – jeff Sep 22 '17 at 20:58
  • Have you thought about using `@Autowired(required = false)`? The spring app will start up even if the bean is missing. – Indra Basak Sep 22 '17 at 21:36
  • I'll update with the sample code, required=false and @Lazy boil down to the same thing. However, my amazement is with the behavior that the second autowired isn't required if the first Autowired is found. – The Architect Sep 22 '17 at 23:20
  • @ochi `@SpringBootApplication` already implies `@EnableAutoConfiguration`. As per the javadoc : This is a convenience annotation that is equivalent to declaring `@Configuration`, `@EnableAutoConfiguration` and `@ComponentScan`. – The Architect Sep 23 '17 at 00:02

0 Answers0