2012-12-10 44 views
4

比方說,我對語言的變化事件在我的應用程序(它是基於Vaadin)的接口:註冊自動豆番石榴EventBus與Spring IoC的

public interface ILanguageChangeListener{ 
    @Subscribe onLanguageChange(LanguageChangeEvent event); 
} 

而且我有很多豆子實現此接口與註解@Component,因此它們在Spring IoC中可用。我也有一個EventBus豆:

<bean id="languageSwitcher" class="com.google.common.eventbus" scope="session" /> 

現在,從國際奧委會得到任何bean的實例後,我也得到languageSwitcher的實例,並在其與註冊newely創建豆:

languageSwitcher.register(myNewBean); 

爲了接收這個事件。是否有可能以某種方式告訴IoC,我想在每個實現ILanguageChangeListener的新bean上調用languageSwitcher bean的註冊方法?

+1

好的,爲什麼不爲你的EventBus創建一個工廠bean,它獲得注入的ILanguageChangeListeners列表並且只是在一個循環中註冊它們......它的手冊,但是你不必把代碼註冊到每個bean裏面ILanguageChangeListener的實例。 – ElderMael

+0

因爲我沒有在開始時聽到這個事件的所有對象 - 請參閱我對@mael答案的評論。 – fracz

+1

然後也許你的ILanguageChangeListener實現[BeanPostProcessor](http://static.springsource.org/spring/docs/3.1.3.RELEASE/javadoc-api/org/springframework/beans/factory/config/BeanPostProcessor.html) 。我假設他們是原型? – ElderMael

回答

9

OK對它進行註釋,使用了BeanPostProcessor,註冊接口的每個bean:

public class EventBusRegisterBeanPostProcessor implements BeanPostProcessor, 
     ApplicationContextAware { 

    private ApplicationContext context; 

    @Autowired 
    private EventBus eventBus; // The only event bus i assume... 

    public Object postProcessBeforeInitialization(Object bean, String beanName) 
      throws BeansException { 

     return bean; 
    } 

    public Object postProcessAfterInitialization(Object bean, String beanName) 
      throws BeansException { 

     if (bean instanceof ILanguageChangeListener) { 
      registerToEventBus(bean); 
     } 

     return bean; 
    } 

    private void registerToEventBus(Object bean) { 
     this.eventBus.register(bean); 
    } 

    public void setApplicationContext(ApplicationContext applicationContext) 
      throws BeansException { 
     this.context = applicationContext; 
    } 

} 

注意,如果你有很多EventBus bean,你應該使用ApplicationContext.getBean(String)來獲得你需要的EventBus。

我從javadoc引證:

在一個FactoryBean的情況下,該回調將兩者的 FactoryBean實例和由創建的FactoryBean(如 彈簧2.0的)的對象上調用。後處理器可以通過相應的bean instanceof FactoryBean檢查來決定是將FactoryBean應用於 還是應用於創建的對象。

+1

非常感謝,像魅力一樣工作。另外請注意,當使用EventBus來達到這個目的時,你應該意識到Guava不會對聽衆使用弱引用,所以只要它們未註冊,它們就不會被垃圾收集。當涉及到Spring原型範圍時,這有點棘手。這裏描述的問題是http://code.google.com/p/guava-libraries/issues/detail?id=807,就我而言,我已經從評論#14中引入瞭解決方案。 – fracz

+0

非常有趣,你有一個回購或你的實施東西? – ElderMael

+0

我沒有這個項目的公開回購,但至於我根據你的回答實現了什麼 - 它看起來像這樣:http://pastebin.com/tX55ih4C – fracz

0

一種方法是使用界面InitializingBean並在您的bean中注入語言切換器。事情是這樣的:

public class NotVeryUsefulLanguageListener implements ILanguageChangeListener, 
     InitializingBean { 

    @Autowired 
    private EventBus languageSwitcher; 

    public void afterPropertiesSet() throws Exception { 

     this.languageSwitcher.register(this); 
    } 

    //... getters, setters, etc 

} 

如果NotVeryUsefulLanguageLisetener是單身,它只會發生一次......如果原型,則每次從Spring

如果你正在使用XML,那麼你可以使用的東西,得到一個實例像這樣:

<bean id="languageListenerA" init-method="afterPropertiesSet" class="org.company.whatever.NotVeryUsefulLanguageListener"> 
     <!-- stuff --> 
    </bean> 
+0

我可以使用@PostConstruct具有相同的效果。這遠不是自動化的,因爲我必須手動調用register()。 – fracz

+0

好的,所以你希望它們在彈簧加載後被該bean的實例註冊一次? – ElderMael

1

爲您的事件總線使用工廠bean,並在您的上下文中注入所有ILanguageChangeListener bean的列表。

public class EventBusFactoryBean implements FactoryBean<EventBus> { 

    @Autowired 
    private List<ILanguageChangeListener> languageChangeListeners; 

    private EventBus instance; 

    @PostConstruct 
    public void init() { 

     this.instance = new EventBus(); 

     for (ILanguageChangeListener listener : this.languageChangeListeners) { 
      this.instance.register(listener); 
     } 
    } 

    public EventBusFactoryBean() { 

    } 

    public EventBus getObject() throws Exception { 
     return this.instance; 
    } 

    public Class<?> getObjectType() { 
     return EventBus.class; 
    } 

    public boolean isSingleton() { 
     return true; 
    } 

    public List<ILanguageChangeListener> getLanguageChangeListeners() { 
     return languageChangeListeners; 
    } 

    public void setLanguageChangeListeners(
      List<ILanguageChangeListener> languageChangeListeners) { 
     this.languageChangeListeners = languageChangeListeners; 
    } 

} 

然後在Spring bean定義文件中定義的bean或@Component

+0

但是您認爲應用程序生命週期中存在一段時間,當我知道實現ILanguageChangeListener的類的所有實例時。但事實並非如此 - 例如,我的應用程序中的每個視圖都實現了它,但它們並不都是在應用程序啓動過程中創建的。它們是在用戶瀏覽並打開新頁面(視圖)時創建的(從IoC中提取)。所以 - 我在尋找的是以某種方式告訴IoC在它注入任何ILanguageChangeListener之前,它應該在EventBus中註冊。希望我現在清楚。 – fracz

+0

毫米,這很棘手,但可能。 – ElderMael

2

IMO,它甚至更好(少耦合),而不是實現標記接口,使用類級別註釋來標記應該註冊的bean。下面是修改的bean後處理器代碼:

public class EventBusListenersRegistererBeanPostProcessor implements BeanPostProcessor{ 

    Logger log = LoggerFactory.getLogger(this.getClass()); 

    @Inject 
    private EventBus bus; 

    @Override 
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
     return bean; 
    } 

    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 
     if(bean.getClass().isAnnotationPresent(RegisterWithEventBus.class)){ 
      log.info("Event Bus is registering bean named \"{}\" of class {}.", beanName, bean.getClass().getCanonicalName()); 
      bus.register(bean); 
     } 

     return bean; 
    } 
} 

與註解:

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.TYPE) 
@Inherited // important when working with dynamically generated proxies i.e. CGLib 
public @interface RegisterWithEventBus {} 

注意註釋接口有@Inherited元註解。這在使用CGLIB代理的Spring應用程序中是必需的,因爲註釋不會在對象的實際(動態)類上,而是在父類上。