Skip to main content

Wiring JavaFX objects with Spring - Tread with Caution

Posted by srikanth on June 12, 2010 at 12:50 PM PDT

I am drawn to JavaFX these days. Not because it is cool (which, it is) or because I want to do whiz bang effects, but just as an explorer to do an unbiased check on whether it can be a tool useful to create regular corporate UIs in any better fashion than regular Swing. (Granted, I squirmed a bit when using it on my first project due to lack of UI components, but JFxtras http://www.jfxtras.org has helped me on that front.)

I like Spring framework. Who doesn't? (Well some don't. But anyway, that’s not the point). I wanted to see if I could combine the both to see if it is a match made in heaven. In other words, I attempted to see if I could wire JavaFX components using Spring. After trying a couple of scenarios, I found at least one scenario, where this may not work as expected. Here are the details:

I started with bare basics of using JavaFX with Spring first. Look at the code in Listing 1 for wiring a simple JavaFX class using Spring.

Listing1 

<bean id="myfxobj" class="a.b.SomeJavaFXClass">

  <property name="variable1" value="Initial Value1">

  <property name="variable2" value="Initial Value2">

</bean>

Needless to say that I cannot do constructor injection since there is no concept of constructor in JavaFX. So, my next resort is setter injection. It worked and Listing 2 below shows my JavaFX class.

Listing 2

public class SomeJavaFXClass {

    public var variable1:String on replace {

        println("variable1 repacement called");

    }

    public-init var variable2:String on replace {

        println("variable2 repacement called");

    }


    init {

        println("init called");

    }


    postinit {

        println("postinit called");

    }


    public function setVariable1(s:String):Void {

        println("setVariable1 called");

        variable1 = s;

    }


    public function setVariable2(s:String):Void {

        println("setVariable2 called");

        variable2 = s;

    }

}

 Nothing outstanding, but here are the concessions I made (or slightly violated JavaFX principle)

  1. Generally it is not a norm to write setters in JavaFX. But nonetheless I wrote the setters on the JavaFX class.
  2. Notice that variable2 is declared as public-init. In JavaFX it means, the variable should be initialized only when the object is created. By providing a setter injection, I am essentially violating that principle in theory. But it did not break anything.

Emboldened by the initial success, I went a step further to wire the JavaFX UI itself with Spring (Normally, when I create Swing UIs, I like to wire the UIs itself in Swing). Listing 3 shows the wiring that I could do (but I did not do)

Listing 3

<bean id="uiNode" class="a.b.MyNode">

  <property name="variable1" value="Initial Value1">

  <property name="variable2" value="Initial Value2">

</bean>

In JavaFX, as you know, CustomNode is the preferred base class for all custom UI. So, I create a subclass of CustomNode. Listing 4 shows this subclass. Pretty simple – nothing fancy. Amon other things, you will also notice the create() function. This is an abstract function in CustomNode. It has to be implemented by concrete subclasses of CustomNode. The create method is your passport to creating all the UI, so JavaFX runtime could use it when needed.

Listing 4

public class MyNode extends CustomNode {

    public-init var variable1:String on replace {

        println("variable1 repacement called");

    }


    public var variable2:String on replace {

        println("variable2 repacement called");

    }


    init {

        println("init called");

    }


    postinit {

        println("postinit called");

    }


    public override function create() {

        println("create called");

        return HBox {

            content: [  Text { content: "Default Node" }  ]
        }

    }


    public function setVariable1(s:String):Void {

        println("setVariable1 called");

        variable1 = s;

    }


    public function setVariable2(s:String):Void {

        println("setVariable2 called");

        variable2 = s;

    }

}

 Instead of calling Spring, I used traditional JavaFX invocation to look at the order of invocation. as in Listing 5 i.e. by initializing the node in the traditional way .

Listing 5

var myNode:MyNode = MyNode {
    variable1: "init blah1"
    variable2: "init blah2"
};

myNode.setVariable1("Later blah1");
myNode.setVariable2("Later blah2");

Stage {
    title: "Application title"
    width: 250
    height: 80
    scene: Scene {
        content: [ myNode ]
    }
}

Listing below shows the output I got.

variable1 repacement called
variable2 repacement called
create called
init called
postinit called
setVariable1 called
variable1 repacement called
setVariable2 called
variable2 repacement called

See the output carefully. This is te key takeaway from this blog.

Notice that create() gets implicitly called even before init(), postInit() and setters. That means if the UI creation logic in create() depended on the initialization of variables for some reason, then Spring setter injection results in bugs.

Granted, UIs do not always depend on initialization variables, but there are enough cases where they do. In such cases, keep this gotcha in mind and tweak your implementation to rely on other mechanisms.

One such mechanism is to write a plain vanilla create and then bind the visibility of UI elements to the instance variables. These variables will be initialized in the very next step anyway.

This way, you keep an eye out for this Spring “gotcha” coding pattern in create() and quash the problem before it even occurs.

Happy JavaFX’ing with Spring
 

Related Topics >>