JavaBeanProperty:jdk7 vs JDK8-WeakOnfeet vs StrongInArm

JavaBeanProperty:jdk7 vs JDK8-WeakOnfeet vs StrongInArm,java,swing,binding,javafx-2,javafx-8,Java,Swing,Binding,Javafx 2,Javafx 8,(有些人称之为-feature:)是所有fx绑定安装的所有侦听器的弱点。因此,如果不保持对链中每个环节的强引用,我们就无法构建属性的“链” 这种链链接的一种特殊类型是javabean属性:其目的是将javabean属性调整为fx属性。通常,没有人对适配器本身感兴趣,因此它的用法如下 private Parent createContentBean() { ... // local ref only Property property = createJavaBeanPro

(有些人称之为-feature:)是所有fx绑定安装的所有侦听器的弱点。因此,如果不保持对链中每个环节的强引用,我们就无法构建属性的“链”

这种链链接的一种特殊类型是javabean属性:其目的是将javabean属性调整为fx属性。通常,没有人对适配器本身感兴趣,因此它的用法如下

private Parent createContentBean() {
    ...
    // local ref only
    Property property = createJavaBeanProperty();
    Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
。。为什么标签没有更新。将属性更改为强引用将按预期工作(让我puzzeld知道谁负责喂养假人,但这是另一个问题):

很长的介绍,但就快到了:jdk8以某种方式改变了实现,因此第一种方法现在可以工作了,不再需要保留对JavaBeanProperty的强引用。另一方面,“链链接”的定制实现仍然需要一个强有力的参考

问题:

  • 行为的改变是故意的吗?如果是,为什么
  • 它是如何实现的?代码看起来非常相似。。。我想在定制适配器中尝试类似的东西
一个完整的示例:

public class BeanAdapterExample extends Application {

    private Counter counter;

    public BeanAdapterExample() {
        this.counter = new Counter();
    }

    Property property;
    private Parent createContentBean() {
        VBox content = new VBox();
        Label label = new Label();
        // strong ref
        property = createJavaBeanProperty();
        // local property
        Property property = createJavaBeanProperty();
        Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
        Slider slider = new Slider();
        slider.valueProperty().bindBidirectional(property);
        Button button = new Button("increase");
        button.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent paramT) {
                counter.increase();
            }

        });
        content.getChildren().add(label);
        content.getChildren().add(slider);
        content.getChildren().add(button);
        return content;
    }

    protected JavaBeanDoubleProperty createJavaBeanProperty(){
        try {
            return JavaBeanDoublePropertyBuilder.create()
                    .bean(counter).name("count").build();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void start(Stage stage) throws Exception {
        Scene scene = new Scene(createContentBean());
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }


    public static class Counter {

        private double count;

        public Counter() {
            this(0);
        }

        public Counter(double count) {
            this.count = count;
        }

        /**
         * Increases the counter by 1.
         */
        public void increase() {
            setCount(getCount()+ 1.);
        }

        /**
         * @return the count
         */
        public double getCount() {
            return count;
        }

        /**
         * @param count the count to set
         */
        public void setCount(double count) {
            double old = getCount();
            this.count = count;
            firePropertyChange("count", old, getCount());
        }

        PropertyChangeSupport support = new PropertyChangeSupport(this);

        public void addPropertyChangeListener(PropertyChangeListener l) {
            support.addPropertyChangeListener(l);
        }

        public void removePropertyChangeListener(PropertyChangeListener l) {
            support.removePropertyChangeListener(l);
        }

        protected void firePropertyChange(String name, Object oldValue,
                Object newValue) {
            support.firePropertyChange(name, oldValue, newValue);
        }

    }

}
公共类BeanAdapterExample扩展应用程序{
私人柜台;
公共BeanAdapterExample(){
this.counter=新计数器();
}
财产;
私有父createContentBean(){
VBox内容=新的VBox();
标签=新标签();
//强参考
property=createJavaBeanProperty();
//地方财产
Property=createJavaBeanProperty();
Bindings.bindBidirective(label.textProperty(),property,NumberFormat.getInstance());
滑块=新滑块();
slider.valueProperty().bindBidirectional(属性);
按钮=新按钮(“增加”);
setOnAction(新的EventHandler(){
@凌驾
公共无效句柄(ActionEvent参数){
计数器增加();
}
});
content.getChildren().add(标签);
content.getChildren().add(滑块);
content.getChildren().add(按钮);
返回内容;
}
受保护的JavaBeanDoubleProperty createJavaBeanProperty(){
试一试{
返回JavaBeanDoublePropertyBuilder.create()
.bean(counter).name(“count”).build();
}捕获(无此方法例外){
e、 printStackTrace();
}
返回null;
}
@凌驾
public void start(Stage)引发异常{
场景=新场景(createContentBean());
舞台场景;
stage.show();
}
公共静态void main(字符串[]args){
应用程序启动(args);
}
公共静态类计数器{
私人重复计数;
公众柜位(){
这(0);
}
公共柜台(重复计数){
this.count=计数;
}
/**
*将计数器增加1。
*/
公共空间增加(){
setCount(getCount()+1.);
}
/**
*@返回计数
*/
公共双精度getCount(){
返回计数;
}
/**
*@param count要设置的计数
*/
公共无效设置计数(双重计数){
double old=getCount();
this.count=计数;
firePropertyChange(“count”,old,getCount());
}
PropertyChangeSupport=新的PropertyChangeSupport(此);
public void addPropertyChangeListener(PropertyChangeListener l){
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l){
support.removePropertyChangeListener(l);
}
受保护的无效firePropertyChange(字符串名称、对象值、,
对象(新值){
support.firePropertyChange(名称、旧值、新值);
}
}
}

顺便说一句:添加了Swing标记,因为在迁移过程中,调整核心bean将是一项经常性的任务。

提醒我去年遇到的一个问题-绑定不会创建强引用,因此如果属性是方法本地字段,则该属性将被垃圾收集。

尝试回答我自己的部分答案:

  • 试探性猜测:这不是故意的。看起来现在JavaBeanProperty永远不会被垃圾收集,这不可能是必需的
  • 我能找到的唯一区别是对属性的phantomReference(代码段中的Cleaner),它是在它的构造函数中创建的:它看起来足够强大,永远不会被释放。如果我在自定义属性中模仿这一点,它们在链中“工作”,但也不会被垃圾收集。不行,依我看
属性的jdk8构造函数:

JavaBeanDoubleProperty(PropertyDescriptor descriptor, Object bean) {
    this.descriptor = descriptor;
    this.listener = descriptor.new Listener<Number>(bean, this);
    descriptor.addListener(listener);
    Cleaner.create(this, new Runnable() {
        @Override
        public void run() {
            JavaBeanDoubleProperty.this.descriptor.removeListener(listener);
        }
    });
}
要复制非集合,请将下面的代码片段添加到问题中的示例代码中,在jdk7/8中运行,并使用您最喜欢的工具(使用VisualVM)进行监视:运行时,单击“创建”以创建100k自由飞行的JavaBeanProperties。在jdk7中,它们甚至从未出现在内存采样器中。在jdk8中,它们被创建(slooowly!因此您可以减少数量)并建立起来。强制垃圾收集没有效果,即使在将绑定到的基础bean置空之后也是如此

Button create100K = new Button("create 100k properties");
create100K.setOnAction(new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent paramT) {
        Property propertyFX;
        /// can't measure any effect
        for (int i = 0; i < 100000; i++) {
            propertyFX = createCountProperty();
        }
        LOG.info("created 100k adapters");
    }

});
Button releaseCounter = new Button("release counter");
releaseCounter.setOnAction(new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent paramT) {
        counter = null;
    }

});
Button create100K=新建按钮(“创建100k属性”);
create100K.setOnAction(新的EventHandler-它已经被标记为已修复,这很快!不幸的是,修复版本是8u20,在此之前不知道该做什么。我想到的唯一一件事是c&p所有JavaBeanxProperty/Builders并添加修复。代价是严重警告和在安全受限环境中不可用。此外,我们又回到了jdk7行为(如果吃了蛋糕,仍然有蛋糕,那就太幸运了:-
protected SimpleDoubleProperty createPhantomedProperty(final boolean phantomed) {
    SimpleDoubleProperty adapter = new SimpleDoubleProperty(){
        {
            // prevents the property from being garbage collected
            // must be done here in the constructor
            // otherwise reclaimed immediately
            if (phantomed) {
                Cleaner.create(this, new Runnable() {
                    @Override
                    public void run() {
                        // empty, could do what here?
                        LOG.info("runnable in cleaner");
                    }
                });
            }
        }

    };

    return adapter;
}
Button create100K = new Button("create 100k properties");
create100K.setOnAction(new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent paramT) {
        Property propertyFX;
        /// can't measure any effect
        for (int i = 0; i < 100000; i++) {
            propertyFX = createCountProperty();
        }
        LOG.info("created 100k adapters");
    }

});
Button releaseCounter = new Button("release counter");
releaseCounter.setOnAction(new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent paramT) {
        counter = null;
    }

});