Spring AOP proxies does not work with JavaFX -
good day, i'm working on project use spring aop on javafx, unfortunately, when try wrap interface used in javafx scenes receive null pointer. here stack trace.
exception in application start method java.lang.reflect.invocationtargetexception @ sun.reflect.nativemethodaccessorimpl.invoke0(native method) @ sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62) @ sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43) @ java.lang.reflect.method.invoke(method.java:483) @ com.sun.javafx.application.launcherimpl.launchapplicationwithargs(launcherimpl.java:363) @ com.sun.javafx.application.launcherimpl.launchapplication(launcherimpl.java:303) @ sun.reflect.nativemethodaccessorimpl.invoke0(native method) @ sun.reflect.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62) @ sun.reflect.delegatingmethodaccessorimpl.invoke(delegatingmethodaccessorimpl.java:43) @ java.lang.reflect.method.invoke(method.java:483) @ sun.launcher.launcherhelper$fxhelper.main(launcherhelper.java:767) caused by: java.lang.runtimeexception: exception in application start method @ com.sun.javafx.application.launcherimpl.launchapplication1(launcherimpl.java:875) @ com.sun.javafx.application.launcherimpl.lambda$launchapplication$147(launcherimpl.java:157) @ com.sun.javafx.application.launcherimpl$$lambda$48/756185697.run(unknown source) @ java.lang.thread.run(thread.java:745) caused by: java.lang.nullpointerexception @ javafx.scene.node.getscene(node.java:907) @ javafx.scene.scene$9.invalidated(scene.java:1074) @ javafx.beans.property.objectpropertybase.markinvalid(objectpropertybase.java:111) @ javafx.beans.property.objectpropertybase.set(objectpropertybase.java:145) @ javafx.scene.scene.setroot(scene.java:1038) @ javafx.scene.scene.<init>(scene.java:325) @ javafx.scene.scene.<init>(scene.java:181) @ com.hccs.sample.aspectj.main.start(main.java:42) @ com.sun.javafx.application.launcherimpl.lambda$launchapplication1$153(launcherimpl.java:821) @ com.sun.javafx.application.launcherimpl$$lambda$51/50630420.run(unknown source) @ com.sun.javafx.application.platformimpl.lambda$runandwait$166(platformimpl.java:323) @ com.sun.javafx.application.platformimpl$$lambda$44/1051754451.run(unknown source) @ com.sun.javafx.application.platformimpl.lambda$null$164(platformimpl.java:292) @ com.sun.javafx.application.platformimpl$$lambda$47/1570685826.run(unknown source) @ java.security.accesscontroller.doprivileged(native method) @ com.sun.javafx.application.platformimpl.lambda$runlater$165(platformimpl.java:291) @ com.sun.javafx.application.platformimpl$$lambda$46/1775282465.run(unknown source) @ com.sun.glass.ui.invokelaterdispatcher$future.run(invokelaterdispatcher.java:95) @ com.sun.glass.ui.win.winapplication._runloop(native method) @ com.sun.glass.ui.win.winapplication.lambda$null$141(winapplication.java:102) @ com.sun.glass.ui.win.winapplication$$lambda$37/1109371569.run(unknown source) ... 1 more exception running application com.hccs.sample.aspectj.main
here main method, create instance of myscene interface , wrap proxy.
public class main extends application { private static applicationcontext context; public static void main(string[] args) { context = new annotationconfigapplicationcontext(appconfig.class); application.launch(main.class); } @override public void start(stage primarystage) throws exception { myscene scene = context.getbean(myscene.class); if (scene == null) { system.out.println("scene null on creation"); system.exit(0); } else { system.out.println("scene not null on creation."); } // these code source of problem, when these code // commented, works fine. scene = proxywrapper.wrap(scene); if (scene == null) { system.out.println("scene null on wrapping"); system.exit(0); } else { system.out.println("scene not null on wrapping"); } // comment here // no problem manual invocation of methods scene.eventone(); scene.eventtwo(); scene.eventthree(); // null pointer occurs primarystage.setscene(new scene((parent) scene)); primarystage.show(); } }
here application config.
@configuration @componentscan(basepackages = { "com.hccs.sample.aspectj" }) public class appconfig { }
here scene interface , implementation needs wrapped.
public interface myscene { public void initialize(); public void eventone(); public void eventtwo(); public void eventthree(); } @lazy @component public class mysceneimpl extends borderpane implements myscene { @fxml private button cmdone; @fxml private button cmdtwo; @fxml private button cmdthree; public mysceneimpl() { try { fxmlloader loader = new fxmlloader(getclass().getresource( "/com/hccs/sample/aspectj/resources/myscene.fxml")); loader.setroot(this); loader.setcontroller(this); loader.load(); system.out.println("\nkards!!\n"); } catch (exception e) { e.printstacktrace(); } } @fxml @override public void initialize() { } @fxml @override public void eventone() { system.out.println("one"); } @fxml @override public void eventtwo() { looptoten(); } @override public void eventthree() { new thread() { @override public void run() { looptoten(); } }.start(); } private void looptoten() { (int c = 0; c < 10; c++) { try { thread.sleep(100); system.out.println(c + 1); } catch (interruptedexception e) { e.printstacktrace(); } } } }
here fxml used in scene,
<fx:root type="borderpane" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8"> <center> <hbox alignment="center" spacing="10.0" borderpane.alignment="center"> <children> <button fx:id="cmdone" mnemonicparsing="false" onaction="#eventone" text="one" /> <button fx:id="cmdtwo" mnemonicparsing="false" onaction="#eventtwo" text="two" /> <button fx:id="cmdthree" mnemonicparsing="false" onaction="#eventthree" text="three" /> </children> </hbox> </center> <padding> <insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> </padding> </fx:root>
this class wraps scene.
public class proxywrapper { @suppresswarnings("unchecked") public static <t> t wrap(t scene) { mypointcut pointcut = new mypointcut(); mymethodinterceptor aspect = new mymethodinterceptor(); advisor advisor = new defaultpointcutadvisor(pointcut, aspect); proxyfactory pf = new proxyfactory(); pf.settarget(scene); pf.addadvisor(advisor); return (t) pf.getproxy(); } }
this pointcut , methodinterceptor classes.
public class mypointcut extends dynamicmethodmatcherpointcut { @override public classfilter getclassfilter() { return new classfilter() { @override public boolean matches(class<?> clazz) { return clazz.getname().contains("myscene"); } }; } @override public boolean matches(method method, class<?> clazz, object[] key) { system.out.println(method.getname() + " == event " + method.getname().contains("event")); return method.getname().contains("eventt"); } } @aspect public class mymethodinterceptor implements methodinterceptor { @override public object invoke(methodinvocation invocation) throws throwable { system.out.println("start: " + invocation.getmethod().getname()); object val = invocation.proceed(); system.out.println("end: " + invocation.getmethod().getname()); return val; } }
and pom.xml of project.
<project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelversion>4.0.0</modelversion> <groupid>com.hccs.sample</groupid> <artifactid>sample-aspectj</artifactid> <version>1.0.0</version> <name>sample-aspectj</name> <dependencies> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-core</artifactid> <version>4.1.6.release</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> <version>4.1.6.release</version> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-aop</artifactid> <version>4.1.6.release</version> </dependency> <dependency> <groupid>org.aspectj</groupid> <artifactid>aspectjrt</artifactid> <version>1.8.5</version> </dependency> <dependency> <groupid>org.aspectj</groupid> <artifactid>aspectjweaver</artifactid> <version>1.8.5</version> </dependency> <dependency> <groupid>aopalliance</groupid> <artifactid>aopalliance</artifactid> <version>1.0</version> </dependency> </dependencies> </project>
if there's way me make javafx recognize spring aop proxies, please share , me, thanks!
i can't quite figure out why fails, making mysceneimpl
subclass of borderpane
causing problem. basically, proxy that's created in order intercept methods not initialize borderpane
instance correctly.
the fix use aggregation instead of inheritance. add method myscene
:
public parent getview() ;
and update implementation class accordingly:
@lazy @component public class mysceneimpl implements myscene { @fxml private button cmdone; @fxml private button cmdtwo; @fxml private button cmdthree; private final borderpane view ; public mysceneimpl() { view = new borderpane(); try { fxmlloader loader = new fxmlloader(getclass().getresource( "/application/myscene.fxml")); loader.setroot(view); loader.setcontroller(this); loader.load(); system.out.println("\nkards!!\n"); } catch (exception e) { e.printstacktrace(); } } @fxml @override public void initialize() { } @override public parent getview() { return view ; } @fxml @override public void eventone() { system.out.println("one"); } @fxml @override public void eventtwo() { looptoten(); } @override public void eventthree() { new thread() { @override public void run() { looptoten(); } }.start(); } private void looptoten() { (int c = 0; c < 10; c++) { try { thread.sleep(100); system.out.println(c + 1); } catch (interruptedexception e) { e.printstacktrace(); } } } }
here, instead of making mysceneimpl
subclass of borderpane
, holds reference borderpane
instance. reference passed fxmlloader
's setroot()
method, gets populated buttons defined in fxml. finally, returned getview()
method.
now update main
class call getview()
:
// primarystage.setscene(new scene((parent) scene)); primarystage.setscene(new scene(scene.getview()));