Best way to achieve @Autowired @Lazy @Components via annotations?

Robert Mark Bram picture Robert Mark Bram · Oct 22, 2014 · Viewed 38.6k times · Source

Is there a way to have @Lazy loading @Components that are still @Autowired in a factory via annotations? The problem I found is that by autowiring my lazy components in the factory, they will all be instantiated immediately once the factory is loaded, negating the lazy annotation.

I have defined several lazy beans such as

@Component
@Lazy
public final class CloseableChromeWebDriver
      extends ChromeDriver
      implements DisposableBean {
...
}

@Component
@Lazy
public final class CloseableFirefoxWebDriver
      extends FirefoxDriver
      implements DisposableBean {
...
}

It is important that they be lazy because

  • Whenever one of them is created, the browser window pops up.
  • my data driven tests may or may not require any one or all of them i.e. one run may be all Firefox, or may require Firefox and Chrome.
  • this is more important because in fact I have six such beans - Firefox, Chrome, IE, Remote Firefox, Remote Chrome, Remote IE.
  • So, if my tests only use one of them, then I only want that one browser to show, not all them.

I have a Factory for getting the requested browser, but my first attempt at this failed because whenever any class used the factory, all of the autowired beans were being instantiated straight away, irrespective of whether they were actually requested. I understand this is because once the class is instantiated, it must instantiate all of the instance variables belonging to it.

@Component
public final class WebDriverFactory {
   @Autowired
   private CloseableChromeWebDriver chromeWebDriver;
   @Autowired
   private CloseableFirefoxWebDriver firefoxWebDriver;

   public synchronized WebDriver getWebDriver(final Browser browser) {
     // get whatever webdriver is matched by the enum Browser.
   }
}

So here is my second approach which is to ensure lazy loading by requesting the bean through the application context:

@Component
public final class WebDriverFactory {
   private CloseableChromeWebDriver chromeWebDriver;
   private CloseableFirefoxWebDriver firefoxWebDriver;
   @Autowired
   private ApplicationContext appContext;

   public synchronized WebDriver getWebDriver(final Browser browser) {
      WebDriver driver = null;
      switch (browser) {
         case FIREFOX:
            if (firefoxRemoteWebDriver == null) {
               firefoxRemoteWebDriver = appContext.getBean("closeableRemoteFirefoxWebDriver",
                     CloseableRemoteFirefoxWebDriver.class);
            }
            driver = firefoxRemoteWebDriver;
            break;
      // etc...
      return driver;
   }
}

This approach achieves my goal, but I feel it really negates the usefulness of using annotations. Is there a purely annotation based way to achieve this?

  • JDK 6
  • Spring 3.2.6.RELEASE
  • No xml, just annotations.

Answer

Biju Kunjummen picture Biju Kunjummen · Oct 22, 2014

You have to have @Lazy annotation on your component as well at the point where it is @Autowired in. This is because if you don't have @Lazy on your component, then it is eagerly created as a bean, and if you have don't have an @Lazy on your @Autowired then again it is eagerly created and injected into the bean. So try the following and it should just work:

@Component
public final class WebDriverFactory {
   @Autowired @Lazy
   private CloseableChromeWebDriver chromeWebDriver;
   @Autowired @Lazy
   private CloseableFirefoxWebDriver firefoxWebDriver;