生活随笔
收集整理的這篇文章主要介紹了
JSF基于事件的沟通:过时的方法
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
用JSF編寫(xiě)的Web應(yīng)用程序由相互交互的bean組成。 在開(kāi)發(fā)Web應(yīng)用程序時(shí),bean之間的通信是主要的設(shè)計(jì)模式之一。 有時(shí),一個(gè)bean需要向其他bean發(fā)送事件,以通知它們某些更改或其他任何更改。 我們通常可以將托管bean或Spring bean注入另一個(gè)bean的屬性中,以便另一個(gè)bean可以直接通知注入的bean。 注入是好的,但是它并不是出于交流目的而引入的。 它與動(dòng)態(tài)的松散耦合系統(tǒng)相距甚遠(yuǎn),在該系統(tǒng)中,每個(gè)bean都不了解其他bean。 在松耦合系統(tǒng)中,我們需要一種基于事件的良好通信機(jī)制。 這篇文章將涵蓋兩種設(shè)計(jì)模式:觀察者/事件監(jiān)聽(tīng)器和中介者模式。 這些模式如今已在許多Web應(yīng)用程序中廣泛使用,但是它們具有缺點(diǎn)。 該系統(tǒng)并不是真正與它們松散耦合。 有很多更好的現(xiàn)代方法。 因此,我在帖子名稱中寫(xiě)了“ Old-school approach”。 新學(xué)校的方法將在下一篇文章中公開(kāi)。
觀察員/事件聽(tīng)眾
?
我們將從觀察者(也稱為事件監(jiān)聽(tīng)器)模式開(kāi)始。 一個(gè)稱為主題或可觀察對(duì)象的對(duì)象會(huì)維護(hù)其依賴項(xiàng)的列表(稱為觀察者),并自動(dòng)將狀態(tài)變化通知他們。 在Java中,有一些類java.util.Observer和java.util.Observable可以幫助實(shí)現(xiàn)此模式。 通過(guò)此模式進(jìn)行的基于事件的通信的其他相關(guān)構(gòu)造是類java.util.EventObject和接口java.util.EventListener。 讓我們開(kāi)始編碼。 假設(shè)我們有一個(gè)I18N Web應(yīng)用程序,并且用戶可以在用戶設(shè)置中的某處選擇一種語(yǔ)言(語(yǔ)言環(huán)境)。 假設(shè)我們有一個(gè)名為UserSettingsForm的bean,它負(fù)責(zé)用戶設(shè)置。 某些會(huì)話作用域的Bean可以保留I18N文本/消息,因此,當(dāng)用戶更改當(dāng)前語(yǔ)言時(shí),需要以最后選擇的語(yǔ)言重置以前的文本/消息。 首先,我們需要一個(gè)LocaleChangeEvent。 public class LocaleChangeEvent extends EventObject {Locale locale;public LocaleChangeEvent(Object source, Locale locale) {super(source);this.locale = locale;}public Locale getLocale() {return locale;}
}
其次,我們需要一個(gè)接口LocaleChangeListener。
public interface LocaleChangeListener extends EventListener {void processLocaleChange(LocaleChangeEvent event);
} 我們的UserSettingsForm現(xiàn)在可以通過(guò)注冊(cè)字符串并通知它們來(lái)管理LocaleChangeListener類型的實(shí)例。 @ManagedBean
@SessionScoped
public class UserSettingsForm implements Serializable {private Locale selectedLocale;private List<SelectItem> locales;private List<LocaleChangeListener> localeChangeListeners = new ArrayList<LocaleChangeListener>();public void addLocaleChangeListener(LocaleChangeListener listener) {localeChangeListeners.add(listener);}public void localChangeListener(ValueChangeEvent e) {...// notify listenersLocaleChangeEvent lce = new LocaleChangeEvent(this, this.selectedLocale);for (LocaleChangeListener lcl : localeChangeListeners) {lcl.processLocaleChange(lce);}}...
} 方法localChangeListener()是JSF ValueChangeListener,可以在例如h:selectOneMenu中應(yīng)用。 每個(gè)實(shí)現(xiàn)LocaleChangeListener的bean都應(yīng)該由UserSettingsForm注冊(cè),以便通過(guò)語(yǔ)言環(huán)境更改得到通知。 @ManagedBean
@SessionScoped
public MyBean implements LocaleChangeListener, Serializable {// UserSettingsForm can be injected e.g. via @ManagedProperty annotation or via Spring facilityprivate UserSettingsForm userSettingsForm;@PostConstructpublic void initialize() {userSettingsForm.addLocaleChangeListener(this);}public void processLocaleChange(LocaleChangeEvent event) {// reset something related to I18N data...}
}
就觀察者模式而言,UserSettingsForm是可觀察的,而LocaleChangeListener的實(shí)例(如MyBean)則是觀察者。 討論的模式帶有一些您需要注意的重要問(wèn)題。 豆緊密耦合。 有很多手動(dòng)工作來(lái)重新注冊(cè)bean。 Bean必須實(shí)現(xiàn)定義的接口。 如果您有100個(gè)語(yǔ)義不同的更改通知了bean,則它必須實(shí)現(xiàn)100個(gè)接口。 無(wú)法通知已注冊(cè)的偵聽(tīng)器的子集–即使不需要通知所有偵聽(tīng)器,也總是會(huì)通知他們。 最后但并非最不重要的– 內(nèi)存管理問(wèn)題 。 馬丁·福勒(Martin Fowler)寫(xiě)道: “假設(shè)我們有一些觀察某些域?qū)ο蟮钠聊弧?關(guān)閉屏幕后,我們希望將其刪除,但是域?qū)ο髮?shí)際上通過(guò)觀察者關(guān)系攜帶了對(duì)屏幕的引用。 在內(nèi)存管理的環(huán)境中,壽命長(zhǎng)的域?qū)ο罂赡軙?huì)占據(jù)很多僵尸屏幕,從而導(dǎo)致大量?jī)?nèi)存泄漏。”
調(diào)解員
?
與“觀察者/事件偵聽(tīng)器”模式相比,“中介者”模式改善了基于事件的通信。 使用中介者模式,對(duì)象之間的通信將與中介者對(duì)象一起封裝。 對(duì)象不再彼此直接通信,而是通過(guò)調(diào)解器進(jìn)行通信。 這減少了通信對(duì)象之間的依賴性。 我們將看到它如何用于JSF-Spring Bean(在上面的示例中是標(biāo)準(zhǔn)托管Bean)。 我們將實(shí)現(xiàn)一個(gè)Mediator類來(lái)管理作用域bean之間的通信。 重要的是要理解一個(gè)bean只能通知范圍更廣的另一個(gè)bean。 視圖作用域的bean可以通知視圖作用域的會(huì)話,會(huì)話作用域和應(yīng)用程序作用域的bean,但不能請(qǐng)求作用域較小的作用域的bean。 請(qǐng)遵循此規(guī)則以避免麻煩。 這是作用域Bean的一種特性–您可能還記得,可以始終將作用域更廣的bean注入到作用域更窄的bean中,反之亦然。 為了開(kāi)始使用Mediator,我們將引入兩個(gè)接口MediatorEvent,MediatorListener和中心類Mediator。 public interface MediatorEvent {...
}public interface MediatorListener {public void listenToEvent(MediatorEvent event);
}public class Mediator implements Serializable {private Collection<MediatorListener> collaborators = new HashSet<MediatorListener>();public static Mediator getCurrentInstance() {// access Mediator bean by JSF-Spring facilityreturn ContextLoader.getCurrentWebApplicationContext().getBean("mediator");}public void fireEvent(MediatorEvent event) {for (MediatorListener mediatorListener : collaborators) {mediatorListener.listenToEvent(event);}}public void addCollaborator(MediatorListener collaborator) {collaborators.add(collaborator);}public void removeCollaborator(MediatorListener collaborator) {collaborators.remove(collaborator);}
} 介體是一個(gè)有作用域的bean,可以注冊(cè)并通知協(xié)作者。 協(xié)作者通過(guò)調(diào)解員進(jìn)行注冊(cè)。 在Spring中,bean可以實(shí)現(xiàn)接口InitializingBean,以便在bean實(shí)例化之后自動(dòng)調(diào)用afterPropertiesSet()方法。 這類似于@PostConstruct。 afterPropertiesSet()是此類bean通過(guò)介體注冊(cè)的正確位置。 Bean還應(yīng)該實(shí)現(xiàn)MediatorListener以便被通知(請(qǐng)參見(jiàn)listenToEvent())。 public MyBean implements MediatorListener, InitializingBean, Serializable {public void afterPropertiesSet() throws Exception {...Mediator.getCurrentInstance().addCollaborator(this);}@Overridepublic void listenToEvent(MediatorEvent event) {if (event instanceof LocaleChangeEvent) {// do something}}
} 我們將在UserSettingsForm和區(qū)域設(shè)置更改中使用相同的方案。 由Mediator注冊(cè)的Bean將通過(guò)fireEvent()進(jìn)行通知。 public class LocaleChangeEvent implements MediatorEvent {...
}public class UserSettingsForm implements Serializable {private Locale selectedLocale;private List<SelectItem> locales;public void localChangeListener(ValueChangeEvent e) {...// notify listenersMediator.getCurrentInstance().fireEvent(new LocaleChangeEvent(this, this.selectedLocale));}...
} 調(diào)解器模式提供了豆之間更好的耦合,但是它們?nèi)耘c調(diào)解器耦合。 進(jìn)一步的缺點(diǎn):仍然需要手動(dòng)注冊(cè)bean –請(qǐng)參見(jiàn)附加代碼Mediator.getCurrentInstance()。addCollaborator(this)。 每個(gè)bean仍應(yīng)至少實(shí)現(xiàn)一個(gè)MediatorListener,這會(huì)帶來(lái)另一個(gè)約束– listenToEvent()。 每個(gè)bean都應(yīng)實(shí)現(xiàn)此接口方法! JSF中介體模式的最大缺點(diǎn)可能是它是有作用域的bean。 視圖作用域調(diào)解器只能與視圖作用域的bean一起順利使用。 當(dāng)視圖作用域調(diào)解器被銷(xiāo)毀時(shí),注冊(cè)的視圖作用域Bean將自動(dòng)刪除。 其他情況可能會(huì)導(dǎo)致內(nèi)存泄漏或幾個(gè)問(wèn)題。 例如,應(yīng)該通過(guò)調(diào)用removeCollaborator()手動(dòng)刪除由視圖作用域介體注冊(cè)的請(qǐng)求作用域Bean(很容易忘記)。 會(huì)話作用域的Bean應(yīng)該由會(huì)話作用域的介體注冊(cè),否則銷(xiāo)毀視圖作用域的介體后,它們將不會(huì)得到通知。 等等 實(shí)際上,中介器模式僅比常規(guī)的“觀察者/事件偵聽(tīng)器”概念好一步。 有更靈活的方法,其中“任何方法”都可以捕獲引發(fā)的事件,而不僅可以修復(fù)指定的問(wèn)題,例如listenToEvent()。 在下一篇文章中,我們將看到簡(jiǎn)單而簡(jiǎn)單的方法,如何僅通過(guò)一種方法和其他建議來(lái)捕獲乘法事件。
參考: JSF中基于事件的通信。 我們的JCG合作伙伴 Oleg Varaksin在“ 軟件開(kāi)發(fā)思想”博客上的過(guò)時(shí)做法 。
翻譯自: https://www.javacodegeeks.com/2012/07/jsf-event-based-communication-old.html
總結(jié)
以上是生活随笔為你收集整理的JSF基于事件的沟通:过时的方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。