หน้าเว็บ

วันอาทิตย์ที่ 28 กันยายน พ.ศ. 2557

jsf controller view scope ใน spring component : java


เท้าความ

        ในการเขียน Managed bean หรือ Controller ของ JSF (JavaServer Faces) นั้น จะต้องใช้  annotation  @ManagedBean และ annotation Scoped ของ faces อย่างใดอย่างหนึ่งกำหนดไว้เสมอ ได้แก่

  • @ApplicationScoped
  • @SessionScoped
  • @ViewScoped
  • @RequestScoped
ซึ่งเป็นตัวที่บ่งบอกวงจรชีวิตของ managed bean นั้นๆ  ว่าจะมีชีวิตอยู่ได้นานเท่าใด

        ต่อมา ผมต้องการเปลี่ยน managed bean ของ jsf ทั้งหมดที่ใช้ @ManagedBean ไปใช้ @Component ของ SpringFramework จากที่แต่ก่อน instance ของ managed bean ถูกสร้างขึ้นโดย jsf engine ก็ delegate ไปให้เป็นหน้าที่ของ spring เป็นคนสร้างให้แทน  เนื่องจากต้องการให้สามารถ inject service และ component ต่างๆ  ได้ง่ายขึ้น เพราะเป็น spring ด้วยกัน 

ปัญหา

        ปัญหาที่ผมเจอคือ การสร้าง controller ที่เป็น jsf ของ spring จะต้องควบคู่กับ annotation @Scope  ซึ่งมี scope ไม่ครบตาม scope เดิมของ jsf  คือยังขาด scope ที่เป็น view แล้วเราจะทำยังไงล่ะ  เพราะส่วนใหญ่แล้วเราต้องการ scope ที่เป็น view ซ่ะด้วย?

คำตอบ

        ก็สร้างมันขึ้นมาซ่ะสิ

วิธีการ

1. สร้าง scope ที่เป็น view ของ jsf ให้ @Scope
ในที่นี้  เราจะ custom view scope ขึ้นมาเอง  โดยผนวกมันเข้ากับ FacesContext

package com.blogspot.na5cent.spring;

import java.util.Map;

import javax.faces.context.FacesContext;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

/**
 * @author redcrow
 */
public class ViewScope implements Scope {

    private Map<String, Object> getViewMap() {
        return FacesContext.getCurrentInstance()
                .getViewRoot()
                .getViewMap();
    }

    @Override
    public Object get(String name, ObjectFactory objectFactory) {
        Map<String, Object> viewMap = getViewMap();

        if (viewMap.containsKey(name)) {
            return viewMap.get(name);
        } else {
            Object object = objectFactory.getObject();
            viewMap.put(name, object);

            return object;
        }
    }

    @Override
    public Object remove(String name) {
        return getViewMap().remove(name);
    }

    @Override
    public String getConversationId() {
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        //Not supported
    }

    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }
}
2. ที่ file applicationContext.xml (ของ spring framework) ให้ add scope เข้าไปใน component
...
...
...
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
        <property name="scopes">
            <map>
                <entry key="view">
                    <bean class="com.blogspot.na5cent.spring.ViewScope"/> <!-- ***** -->
                </entry>
            </map>
        </property>
    </bean>
...
...
...
3. ใช้งาน
...
...
...
@Component
@Scope("view") //*****
public class TopicIncomeCtrl {

    private static final Logger LOG = LoggerFactory.getLogger(TopicIncomeCtrl.class);
    @Autowired
    private TopicIncomeService service;
    private TopicIncomeLazy lazy;
    private TopicIncome topic;

    @PostConstruct
    public void postContruct() {
        lazy = new TopicIncomeLazy();
    }

    public TopicIncomeLazy getLazy() {
        return lazy;
    }

    public void onCreateTopic() {
        topic = new TopicIncome();
    }

    public TopicIncome getTopic() {
        return topic;
    }

    public void setTopic(TopicIncome topic) {
        this.topic = topic;
    }
    
    public void onAddTopic(){
        service.saveTopic(topic);
    }
}
ก่อนที่จะทำตรงนี้ได้ แนะนำให้อ่าน 2 บทความนี้ก่อนครับ
http://na5cent.blogspot.com/2013/07/jsf-managedbean-can-autowire-spring.html http://na5cent.blogspot.com/2013/09/spring-session-scope.html

ที่มาของภาพ http://www.coyotecanada.ca/view_through_scope_I.jpg

ไม่มีความคิดเห็น:

แสดงความคิดเห็น