2015-04-23 5 views
1

Добрый день, я работаю над проектом использования Spring AOP на JavaFX, к сожалению, когда я пытаюсь обернуть интерфейс, который используется в сценариях JavaFX, я получаю нулевой указатель. Вот трассировка стека.Spring AOP прокси не работает с JavaFX

Exception in Application start method 
java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:483) 
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:363) 
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:303) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:483) 
    at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767) 
Caused by: java.lang.RuntimeException: Exception in Application start method 
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:875) 
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$147(LauncherImpl.java:157) 
    at com.sun.javafx.application.LauncherImpl$$Lambda$48/756185697.run(Unknown Source) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: java.lang.NullPointerException 
    at javafx.scene.Node.getScene(Node.java:907) 
    at javafx.scene.Scene$9.invalidated(Scene.java:1074) 
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:111) 
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:145) 
    at javafx.scene.Scene.setRoot(Scene.java:1038) 
    at javafx.scene.Scene.<init>(Scene.java:325) 
    at javafx.scene.Scene.<init>(Scene.java:181) 
    at com.hccs.sample.aspectj.Main.start(Main.java:42) 
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$153(LauncherImpl.java:821) 
    at com.sun.javafx.application.LauncherImpl$$Lambda$51/50630420.run(Unknown Source) 
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$166(PlatformImpl.java:323) 
    at com.sun.javafx.application.PlatformImpl$$Lambda$44/1051754451.run(Unknown Source) 
    at com.sun.javafx.application.PlatformImpl.lambda$null$164(PlatformImpl.java:292) 
    at com.sun.javafx.application.PlatformImpl$$Lambda$47/1570685826.run(Unknown Source) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$165(PlatformImpl.java:291) 
    at com.sun.javafx.application.PlatformImpl$$Lambda$46/1775282465.run(Unknown Source) 
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95) 
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) 
    at com.sun.glass.ui.win.WinApplication.lambda$null$141(WinApplication.java:102) 
    at com.sun.glass.ui.win.WinApplication$$Lambda$37/1109371569.run(Unknown Source) 
    ... 1 more 
Exception running application com.hccs.sample.aspectj.Main 

Вот Основной метод, в котором я создаю экземпляр интерфейса MyScene и оберните его с прокси.

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 is null on creation"); 
      System.exit(0); 
     } else { 
      System.out.println("Scene is not null on creation."); 
     } 

     // these code are the source of the problem, when these code are 
     // commented, it works fine. 
     scene = ProxyWrapper.wrap(scene); 
     if (scene == null) { 
      System.out.println("Scene is null on wrapping"); 
      System.exit(0); 
     } else { 
      System.out.println("Scene is not null on wrapping"); 
     } 
     // comment it up to here 

     // No problem with manual invocation of methods 
     scene.eventOne(); 
     scene.eventTwo(); 
     scene.eventThree(); 

     // Where null pointer occurs 
     primaryStage.setScene(new Scene((Parent) scene)); 
     primaryStage.show(); 
    } 
} 

Вот конфигурация приложения.

@Configuration 
@ComponentScan(basePackages = { "com.hccs.sample.aspectj" }) 
public class AppConfig { 

} 

Здесь находится интерфейс и реализация сцены, которые необходимо обернуть.

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() { 
     for (int c = 0; c < 10; c++) { 
      try { 
       Thread.sleep(100); 
       System.out.println(c + 1); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 

    } 

} 

Здесь FXML используется в сцене,

<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> 

Это класс, который обертывает эту сцену.

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(); 
    } 
} 

Это классы Pointcut и MethodInterceptor.

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; 
    } 
} 

И, наконец, это pom.xml проекта.

<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> 

Если есть способ для меня, чтобы сделать JavaFX признать Spring AOP прокси, пожалуйста, поделитесь им и помочь мне, спасибо!

+0

Я нахожусь в подобной проблемой, но я не могу получить его работу на всех - FXML компоненты всегда нуль (я использую 'spring.aop.proxy- target-class = true'). Любая помощь? https://stackoverflow.com/questions/45933616/cant-create-aspect-in-spring-boot?noredirect1_comment78826078_45933616 – Mejmo

ответ

1

Я не могу точно понять, почему это не удается, но создание MySceneImpl вызывает проблему подкласс BorderPane. В принципе, прокси, созданный для перехвата методов, не инициализирует экземпляр BorderPane правильно.

Исправление заключается в использовании агрегации вместо наследования. Добавить метод MyScene:

public Parent getView() ; 

и обновить класс реализации соответственно:

@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() { 
     for (int c = 0; c < 10; c++) { 
      try { 
       Thread.sleep(100); 
       System.out.println(c + 1); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 

    } 

} 

Здесь, вместо того, чтобы MySceneImpl подкласс BorderPane, он держит ссылку на BorderPane инстанции. Эта ссылка передается методу FXMLLoadersetRoot(), поэтому он заполняется кнопками, определенными в FXML. Наконец, он возвращается из метода getView().

Теперь просто обновить Main класс назвать getView():

// primaryStage.setScene(new Scene((Parent) scene)); 
primaryStage.setScene(new Scene(scene.getView())); 
+0

Спасибо, что решает исключение, с которым я столкнулся. Но перехватчики метода выполняются только тогда, когда методы интерфейса MyScene вызываются вручную.Когда вызываются кнопки этапа javafx, метод перехватчиков не срабатывает. –

+0

А, да. Проблема в том, что вы вызываете 'loader.setController (this)', явно передавая ссылку this this. Поскольку вызываемый здесь конструктор вызывается на целевой объект (т. Е. Не на прокси), контроллер, на который ссылается «FXMLLoader», является целью, а не прокси. Поэтому методы обработчика не оформлены. Вероятно, вам придется немного перестроить его. В частности, «FXMLLoader» должен будет генерировать контроллер из контекста приложения: вы можете сделать это с помощью фабрики контроллера, но для этого завода требуется ссылка на контекст приложения. –

+0

Спасибо, я попробую. Вернитесь к вам, если я заработаю, еще раз спасибо! –

Смежные вопросы