문제

I am trying to bootstrap a small javafx application using Weld-SE. I was able to run the application if I remove the @Inject annotation on the Menubar and instantiate it manually. But the moment I add @Inject, the application throws exception. Listed below are the injection point and the Producer method. This is all the configuration I did, am I missing something ?

Injection Point

@Inject MenuBar menuBar

Class with producer method

public class ComponentProducer {

    @Produces
    public MenuBar createMenuBar(){
        return new MenuBar();
    }

}

Exception

    491 [JavaFX Application Thread] INFO org.jboss.weld.Bootstrap - WELD-000101 Transactional services not available. Injection of @Inject UserTransaction not available.    Transactional observers will be invoked synchronously.
    8868 [JavaFX Application Thread] WARN org.jboss.interceptor.model.InterceptionTypeRegistry - Class 'javax.ejb.PostActivate' not found, interception based on it is not enabled
    8868 [JavaFX Application Thread] WARN org.jboss.interceptor.model.InterceptionTypeRegistry - Class 'javax.ejb.PrePassivate' not found, interception based on it is not enabled
    Exception in Application start method
    Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:399)
at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:47)
at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:115)
at java.lang.Thread.run(Thread.java:722)

    Caused by: java.lang.NullPointerException
at net.sourceforge.squirrel_sql.client.MainScene.<init>(MainScene.java:26)
at net.sourceforge.squirrel_sql.client.FXApplication.startup(FXApplication.java:176)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.jboss.weld.util.reflection.SecureReflections$13.work(SecureReflections.java:304)
at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:54)
at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInvocation(SecureReflectionAccess.java:163)
at org.jboss.weld.util.reflection.SecureReflections.invoke(SecureReflections.java:298)
at org.jboss.weld.introspector.jlr.WeldMethodImpl.invokeOnInstance(WeldMethodImpl.java:200)
at org.jboss.weld.introspector.ForwardingWeldMethod.invokeOnInstance(ForwardingWeldMethod.java:59)
at org.jboss.weld.injection.MethodInjectionPoint.invokeOnInstanceWithSpecialValue(MethodInjectionPoint.java:194)
at org.jboss.weld.event.ObserverMethodImpl.sendEvent(ObserverMethodImpl.java:241)
at org.jboss.weld.event.ObserverMethodImpl.notify(ObserverMethodImpl.java:216)
at org.jboss.weld.manager.BeanManagerImpl.notifyObservers(BeanManagerImpl.java:654)
at org.jboss.weld.manager.BeanManagerImpl.fireEvent(BeanManagerImpl.java:647)
at org.jboss.weld.manager.BeanManagerImpl.fireEvent(BeanManagerImpl.java:641)
at org.jboss.weld.event.EventImpl.fire(EventImpl.java:93)
at net.sourceforge.squirrel_sql.client.Main.start(Main.java:180)
at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:315)
at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:174)
at com.sun.javafx.application.PlatformImpl$3.run(PlatformImpl.java:141)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication$2$1.run(GtkApplication.java:79)
... 1 more
도움이 되었습니까?

해결책

ShaggyInjun, you refer to the Initializable interface which indicates you were trying to integrate with the FXMLLoader. All of the information below assumes you are using FXML for your interface definition and only discusses issues around injecting values into FXML controllers.

The FXMLLoader has the concept of a controller factory, which you should use to integrate the controller instantiation with your dependency injection system. There is a brief discussion of controller factories in Oracle's Mastering FXML Tutorial. Andy demonstrates definition of such a factory for Guice in his blog and there is a comprehensive integration of Guice in FXML on github.


For Weld you will need to implement a similar controller factory callback mechanism to realize the dependency injection functionality which Weld provides. The article by Matthieu Brouillard that you linked in a comment FXML & JavaFX—Fueled by CDI & JBoss Weld would seem to provide all of the information you need to both initialize Weld and interface Weld into the FXMLLoader controller factory mechanism. Specifically, the following code is the Weld equivalent of Andy Till's FXML based injection mechanism:

public class FXMLLoaderProducer {
  @Inject Instance<Object> instance;
  @Produces public FXMLLoader createLoader() {
    FXMLLoader loader = new FXMLLoader();
    loader.setControllerFactory(new Callback<Class<?>, Object>() {
      @Override public Object call(Class<?> param) {
        return instance.select(param).get();
      }
    });
    return loader;
  }
}

Even when an FXMLoader controller factory is used, I believe it is the FXMLLoader that is instantiating the controller. So in those cases, you should not make of annotations like @PostConstruct because they only apply when the dependency injection system is maintaining the lifecycle of the object - and that is not the case if the FXMLLoader creates the controller.

There is one other alternative, and that it is to explicitly set the controller to be used by the FXMLLoader using setController. This would allow you to have your dependency injection system instantiate (and inject into) controllers using whatever means it wishes and then you could subsequently pass the instantiated controller to your FXMLLoader. In such cases anotations like @PostConstruct should work as the dependency injection system is now maintaining the objects lifecycle (and @PostConstruct would be invoked by the dependency injection system after the Controller has been created and before you pass the Controller through to the FXMLLoader).


I'll post Andy's Guice based solution here as it is a small and simple example of how similar injection is accomplished in Guice (in case his blog goes offline):

class GuiceControllerFactory implements Callback<Class<?>, Object> {
  private final Injector injector;
  public GuiceControllerFactory(Injector anInjector) {
    injector = anInjector;
  }     
  @Override public Object call(Class<?> aClass) {
    return injector.getInstance(aClass);
  }
}

다른 팁

Is it because you are trying to use menuBar in the constructor or initialiser block?

If so, try using Initializable and using them in initialize.

EJB documentation states that the method annotated with @PostConstruct will be called after the injections are done, and that this is the method to expect variable injections.

@PostConstruct

The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization.

Expecting the variable to be initialized inside the initialize does seem to work however.

According to the Initializable documentation, initialize is Called to initialize a controller after its root element has been completely processed. Initializable

Order of call for initialize and @PostConstruct

I think it is safe to understand that the calling of initialize does not have anything to do with object construction. Instead, initialize is called when the root of the current node graph has been completely processed, so one can process things like event handlers etc. Something on the lines of document.onload or jQuery(document).ready(). If you were to try to attach event handlers before object graph is ready, there is a pretty good chance to see a null pointer because the node is not the object graph yet making it impossible to attach an event handler.

So, you'd instantiate an object (either directly or via injecting) before you load it to the scene-graph. Therefore @PostConstruct gets called before the initialize. But, if you instantiate the object directly then the responsibility to call the method annotated @PostConstruct falls on you, otherwise that post processing will not happen.

Conclusion: It is safe to assume that initialize is always called after the method annotated with @PostConstruct.

EDIT

@Jewelsea has pointed out a few assumptions that I have not listed above. So here it goes, the circumstances where the above worked for me.

  • Using FXML files for views.
  • Controller name specified inside the fxml file.
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top