通过注解实现@Autowired @Lazy @Components的最佳方法是什么?

36

有没有办法通过注释,在工厂中仍然自动装配作为@Component@Lazy加载组件?我发现的问题是,通过在工厂中自动装配我的懒惰组件,它们在工厂加载时将立即全部实例化,从而抵消了懒惰注释。

我已定义了几个延迟加载的bean,例如

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

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

重要的是它们需要懒加载,因为

  • 每当它们之一被创建时,浏览器窗口都会弹出。
  • 我的数据驱动测试可能需要其中一个或全部浏览器,例如一次运行可能全部使用 Firefox,也可能需要 Firefox 和 Chrome。
  • 这更加重要,因为实际上我有六个这样的“bean”- Firefox、Chrome、IE、远程 Firefox、远程 Chrome、远程 IE。
  • 因此,如果我的测试只使用其中一个浏览器,那么我只希望显示该浏览器,而不是所有浏览器。

我有一个用于获取请求浏览器的工厂,但我第一次尝试失败了,因为每当任何类使用该工厂时,所有自动装配的 bean 都会立即实例化,而不考虑它们是否实际被请求。我理解这是因为一旦类被实例化,它必须实例化所有属于它的实例变量。

@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.
   }
}

所以这是我的第二种方法,即通过应用程序上下文请求bean来确保懒加载:

@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;
   }
}

这种方法实现了我的目标,但我觉得它真的否定了使用注释的有用性。有没有一种纯粹基于注释的方法来实现这个目标?

  • JDK 6
  • Spring 3.2.6.RELEASE
  • 无 XML,仅有注释。

  • JDK 6
  • Spring 3.2.6.RELEASE
  • 无需XML,仅需要注解。

当然,“@Lazy”属性是放在bean的使用者上,而不是放在bean的定义上。因此,我希望您将“@Lazy”放在需要延迟加载的每个地方。如果这个地方是到处都需要,那么这个bean将会在第一次使用时创建。 - Ashley Frieze
除非我误解了你的意思,否则@Lazy不会放在bean的用户上。从@Lazy Javadoc中可以看到:可以用于任何直接或间接注释为@Component的类或使用@Bean注释的方法...如果存在并设置为true,则@Bean或@Component将不会在被另一个bean引用或从封闭的BeanFactory中显式检索之前初始化。 - Robert Mark Bram
@AshleyFrieze,在Spring 4中,根据Biju的回答,你是正确的,所以谢谢。 :) - Robert Mark Bram
1个回答

63

在使用@Autowired注解进行组件注入时,需要同时在该组件上添加@Lazy注解。这是因为如果没有@Lazy注解,该组件将会被急切地创建为一个bean;而如果在@Autowired注解上没有@Lazy注解,则该组件也会被急切地创建并注入到bean中。因此,请尝试按照以下方式操作,这样应该就可以正常工作:

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

我相当确定需要Spring 4,因为昨晚在这里阅读了相关内容:http://zezutom.blogspot.com.au/2014/01/spring-series-part-4-lazy-on-injection.html。我们目前正在使用Spring 3,然而同一页似乎解释了如何通过Provider实现相同的效果。我还没有尝试过,因为坦率地说,我还没有理解它。 - Robert Mark Bram
好的,我已经测试过了。 我无法在Spring 3中使用@Autowired @Lazy,但是在Spring 4中可以 - 并且它完美地工作。 我还花时间学习了如何使用javax.inject.Injectjavax.inject.Provider,就像Tom's Blog所描述的那样,但是由于我必须为每个不同的Web驱动程序编写一个“提供程序”类型,因此需要编写太多额外的代码。 - Robert Mark Bram
2
因此,总之,如果我能升级到Spring 4,@Autowired @Lazy正好符合我的需求。如果我必须继续使用Spring 3,我很乐意妥协地继续使用appContext.getBean(..)。所以,谢谢@Biju! - Robert Mark Bram
很惊讶它不能在Spring 3中工作,你能提供你使用的具体版本吗 - 3.2.9?我也可以测试一下并告诉你结果如何。 - Biju Kunjummen
1
它在我的问题底部。 :) Spring 3.2.6.RELEASE - Robert Mark Bram

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接