jface databinding/PojoBindable实现对POJO对象的支持

来源:互联网 发布:java游戏高级编程 编辑:程序博客网 时间:2024/05/22 06:27

POJO对象无法被监控

在jface databinding中,将普通的java bean(有get/set方法但没有通过PropertyChangeSupport实现属性监控)定义为POJO对象。
我们可以对POJO对象通过PojoProperties.value(String propertyName)方法提供IObservableValue实例,但返回的PojoValueProperty实例并没有真正实现对POJO对象的监控(参见PojoValueProperty源码)。
所以UI组件与POJO对象之间建立的数据绑定是单向的,UI组件的数据变化可以同步到POJO对象,但反过来不行。
下面这个示例可以演示这个区别,
运行程序,程序启动时,Text组件的内容被更新成POJO对象属性相同的值。
但按”测试”按钮,修改了POJO对象的属性,但Text控件的值并没有同步变化。

package testwb;import org.eclipse.jface.dialogs.Dialog;import org.eclipse.jface.dialogs.IDialogConstants;import org.eclipse.swt.graphics.Point;import org.eclipse.swt.widgets.Composite;import org.eclipse.swt.widgets.Control;import org.eclipse.swt.widgets.Shell;import org.eclipse.swt.SWT;import org.eclipse.swt.widgets.Text;import org.eclipse.core.databinding.DataBindingContext;import org.eclipse.core.databinding.observable.value.IObservableValue;import org.eclipse.jface.databinding.swt.WidgetProperties;import org.eclipse.swt.widgets.Display;import org.eclipse.core.databinding.observable.Realm;import org.eclipse.jface.databinding.swt.DisplayRealm;import org.eclipse.swt.widgets.Button;import org.eclipse.swt.events.SelectionAdapter;import org.eclipse.swt.events.SelectionEvent;import org.eclipse.core.databinding.beans.PojoProperties;public class TestPojoBinding extends Dialog {    /**     * 数据对象定义     * @author guyadong     *     */    public class Configuration {        private String name;        public Configuration(String name) {            super();            this.name = name;        }        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;            System.out.printf("updated %s\n",this.name);        }    }    private DataBindingContext m_bindingContext;    /**     * 成员变量:数据对象     */    protected Configuration editorConfig=new Configuration("hello!");    private Text myNametext;    /**     * Create the dialog.     * @param parentShell     */    public TestPojoBinding(Shell parentShell) {        super(parentShell);    }    /**     * Create contents of the dialog.     * @param parent     */    @Override    protected Control createDialogArea(Composite parent) {        Composite container = (Composite) super.createDialogArea(parent);        container.setLayout(null);        Button btnNewButton = new Button(container, SWT.NONE);        btnNewButton.addSelectionListener(new SelectionAdapter() {            @Override            public void widgetSelected(SelectionEvent e) {                editorConfig.setName("word");            }        });        btnNewButton.setBounds(38, 154, 80, 27);        btnNewButton.setText("测试");        myNametext = new Text(container, SWT.BORDER);        myNametext.setBounds(38, 27, 80, 23);        return container;    }    /**     * Create contents of the button bar.     * @param parent     */    @Override    protected void createButtonsForButtonBar(Composite parent) {        createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);        m_bindingContext = initDataBindings();    }    /**     * Return the initial size of the dialog.     */    @Override    protected Point getInitialSize() {        return new Point(362, 298);    }    public static void main(String[] args) {        Display display = Display.getDefault();        Realm.runWithDefault(DisplayRealm.getRealm(display), new Runnable() {            public void run() {                try {                    TestPojoBinding setting = new TestPojoBinding(null);                    setting.open();                } catch (Exception e) {                    e.printStackTrace();                }            }        });    }    protected DataBindingContext initDataBindings() {        DataBindingContext bindingContext = new DataBindingContext();        IObservableValue observeTextMyNametextObserveWidget = WidgetProperties.text(SWT.Modify).observe(myNametext);        IObservableValue nameEditorConfigObserveValue = PojoProperties.value("name").observe(editorConfig);        bindingContext.bindValue(observeTextMyNametextObserveWidget, nameEditorConfigObserveValue, null, null);        return bindingContext;    }}

PropertyChangeSupport

如果想要实现上面例子中数据对象属性与Text组件的内容双向同步绑定。解决方案之一就是改造数据对象Person,通过PropertyChangeSupport实现属性监控。

package testwb;import org.eclipse.jface.dialogs.Dialog;import org.eclipse.jface.dialogs.IDialogConstants;import org.eclipse.swt.graphics.Point;import org.eclipse.swt.widgets.Composite;import org.eclipse.swt.widgets.Control;import org.eclipse.swt.widgets.Shell;import org.eclipse.swt.SWT;import org.eclipse.swt.widgets.Text;import java.beans.PropertyChangeListener;import java.beans.PropertyChangeSupport;import org.eclipse.core.databinding.DataBindingContext;import org.eclipse.core.databinding.observable.value.IObservableValue;import org.eclipse.jface.databinding.swt.WidgetProperties;import org.eclipse.swt.widgets.Display;import org.eclipse.core.databinding.observable.Realm;import org.eclipse.jface.databinding.swt.DisplayRealm;import org.eclipse.swt.widgets.Button;import org.eclipse.swt.events.SelectionAdapter;import org.eclipse.swt.events.SelectionEvent;import org.eclipse.core.databinding.beans.BeanProperties;public class TestPojoBinding2 extends Dialog {    public class ModelObject {        private final PropertyChangeSupport changeSupport =                new PropertyChangeSupport(this);        public void addPropertyChangeListener(PropertyChangeListener                listener) {            changeSupport.addPropertyChangeListener(listener);        }        public void removePropertyChangeListener(PropertyChangeListener                listener) {            changeSupport.removePropertyChangeListener(listener);        }        protected void firePropertyChange(String propertyName, Object oldValue,                Object newValue) {            changeSupport.firePropertyChange(propertyName, oldValue, newValue);        }    }    /**     * 数据对象定义,继承ModelObject类,获取属性改变时被监控能力     * @author guyadong     *     */    public class Person extends ModelObject {        private String name;        public Person(String name) {            super();            this.name = name;        }        public String getName() {            return name;        }        public void setName(String name) {            // 修改set方法,在修改属性的同时,调用firePropertyChange通知所有侦听器属性已经改变            firePropertyChange("name", this.name, this.name = name);            System.out.printf("updated %s\n",this.name);        }    }    private DataBindingContext m_bindingContext;    /**     * 成员变量:数据对象     */    protected Person editorConfig=new Person("hello!");    private Text myNametext;    /**     * Create the dialog.     * @param parentShell     */    public TestPojoBinding2(Shell parentShell) {        super(parentShell);    }    /**     * Create contents of the dialog.     * @param parent     */    @Override    protected Control createDialogArea(Composite parent) {        Composite container = (Composite) super.createDialogArea(parent);        container.setLayout(null);        Button btnNewButton = new Button(container, SWT.NONE);        btnNewButton.addSelectionListener(new SelectionAdapter() {            @Override            public void widgetSelected(SelectionEvent e) {                editorConfig.setName("word");            }        });        btnNewButton.setBounds(38, 154, 80, 27);        btnNewButton.setText("测试");        myNametext = new Text(container, SWT.BORDER);        myNametext.setBounds(38, 27, 80, 23);        return container;    }    /**     * Create contents of the button bar.     * @param parent     */    @Override    protected void createButtonsForButtonBar(Composite parent) {        createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);        createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);        m_bindingContext = initDataBindings();    }    /**     * Return the initial size of the dialog.     */    @Override    protected Point getInitialSize() {        return new Point(362, 298);    }    public static void main(String[] args) {        Display display = Display.getDefault();        Realm.runWithDefault(DisplayRealm.getRealm(display), new Runnable() {            public void run() {                try {                    TestPojoBinding2 setting = new TestPojoBinding2(null);                    setting.open();                } catch (Exception e) {                    e.printStackTrace();                }            }        });    }    protected DataBindingContext initDataBindings() {        DataBindingContext bindingContext = new DataBindingContext();        // 为Text组件创建观察对象        IObservableValue observeTextMyNametextObserveWidget = WidgetProperties.text(SWT.Modify).observe(myNametext);        // 为数据对象属性创建观察对象        IObservableValue nameEditorConfigObserveValue = BeanProperties.value("name").observe(editorConfig);        // 数据绑定        bindingContext.bindValue(observeTextMyNametextObserveWidget, nameEditorConfigObserveValue, null, null);        //        return bindingContext;    }}

再运行程序,点击”测试”按钮,Text的值随着数据对象的属性同步改变了。

PojoBindable

上面这个方案已经实现了数据对象和UI组件的双向同步更新,但缺点就是需要对POJO对象进行改造,当项目中有多个POJO对象需要实现与UI组件的双同步更新时,这个工作量也是挺大的。
有没有办法在不改变现有POJO对象的代码的情况下,实现双向同步的目标呢?
有,解决方案就是本文的标题jface databinding/PojoBindable。[注意:这还是个实验项目,使用需谨慎]
PojoBindable利用ASM代码动态修改的技术,通过在运行时为POJO对象添加PropertyChangeSupport 的方法并修改setter方法,提供了一个途径让开发者在不修改自己的POJO类代码的情况下让POJO对象拥有完整的数据绑定能力。
凡事都有代价,使用PojoBindable想不修改POJO对象代码就拥有PropertyChangeSupport能力的话,代价是什么呢?

要修改JVM的运行参数

Pojo Bindable是一个Java Agent,所以为了使用PojoBindable,必须在java程序启动时指定jvm参数,用-javaagent参数指定使用PojoBindable

-javaagent:<your path>/org.eclipse.core.databinding.pojo.bindable_1.0.0.jar

需要-Dbindable.packages指定对哪些pojo对象进行修改java代码

-Dbindable.packages=org.eclipse.core.examples.databinding.pojo.bindable.model

需要 ASM支持

必须将 ObjectWeb ASM加入classpath

关于Pojo Bindable配置的更详细说明参见其官网原文:

https://wiki.eclipse.org/JFace_Data_Binding/PojoBindable#With_Pojo_Bindable

参考资料
《JFace Data Binding/PojoBindable》
《AJFace Data Binding - Tutorial》

0 0
原创粉丝点击