我使用Spring来管理我的控制器实例,使用setControllerFactory(见下文),使控制器能够通过自动装配EntityManagerFactory和处理数据库事务来关心实体的持久性。这种方法适用于我迄今为止开发的其他几种控制器。但是,最近我想扩展功能并复制现有的工作控制器脚手架,将其采用到具有相应FXML UI的不同实体。无论出于何种原因,我无法使其发挥作用。发生的事情是控制器被实例化并调用控制器初始化方法,而没有任何声明的FMXL成员被预先注入 - 我调试它,它们都是空的。因此初始化会抛出NullPointerException。如果我评论setControllerFactory行和所有持久的相关命令,它工作正常,但我没有毅力。我花了一个小时来比较工作示例和新工作示例,但找不到根本原因。
请查看以下代码段
实体类
@Entity
@Inheritance
@Access(AccessType.FIELD)
public abstract class Term implements Serializable {
@Transient
private transient final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final long serialVersionUID = -5585030750290575696L;
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String identifier;
private String goal = "tbd."; // what shall be achieved in the given time frame
@OneToOne
private Term predecessor;
@OneToOne
private Term successor;
private int myNumber; // order within release
private int version;
private LocalDateTime publishingDate;
private String owner;
/*
* Note: Use Period.between(begin, end).getUnits() to determine the years, months, days, hours between begin and end
*/
private LocalDate begin;
private LocalDate end;
@Transient
private transient DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(Locale.GERMAN);
/*
* Terms are structured in hierarchical levels as defined by the following finals
*/
public final static String RELEASE = "Release"; // top level
public final static String PHASE = "Phase"; //
public final static String BASELINE = "Baseline";
public final static String ITERATION = "Iteration";
public final static String TASK = "Task"; // bottom level
@Transient
public final static transient ArrayList<String> levels =
new ArrayList<String>(Arrays.asList(RELEASE, PHASE, BASELINE, ITERATION, TASK));
@Transient
public transient static ObservableList<String> chronoUnits = FXCollections.observableArrayList(
EffortEstimate.YEARS, EffortEstimate.MONTHS, EffortEstimate.WEEKS, EffortEstimate.DAYS, EffortEstimate.HOURS);
public abstract String getLevel(); // return one of the above values indicating the hierarchical level
protected String defaultTimeUnit;
/*
* A Term may be broken down into sub-Terms, called children. A child can not be a Term from a higher hierarchical level.
* Between parent and child is a bi-directional relationship
*/
@ManyToOne
private Term parent;
@Transient
private List<Term> children;
@Transient
private transient ObservableList<Term> observableChildren = FXCollections.observableArrayList(); // an observable mirror of field children
/*
* If a requirement should be implemented during a certain time, but it is unclear by which team, then the
* requirement may be temporarily assigned to a term.
*/
@Transient
private List<UserRequirement> userRequirements;
@Transient
private transient final ListProperty<UserRequirement> observableRequirements = new SimpleListProperty<>(FXCollections.observableArrayList());
public enum Type {
Development, Hardening
}
@Enumerated(EnumType.STRING)
protected Type myType; // see enum Type
public enum Status {
Setup, Upcoming, Running, Complete
}
@Enumerated(EnumType.STRING)
protected Status myState; // see enum Status
protected Term() {
children = FXCollections.observableArrayList();
userRequirements = FXCollections.observableArrayList();
publishingDate = LocalDateTime.now();
myState = Status.Setup;
myType = Type.Development;
}
public Term(Term parent, String identifier) {
this();
setIdentifier(identifier);
setParent(parent);
}
private Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public Term getParent() {
return parent;
}
public void setParent(Term parent) {
this.parent = parent;
}
public ObservableList<Term> getObservableChildren() {
return observableChildren;
}
@Access(AccessType.PROPERTY)
@OneToMany(mappedBy="parent", orphanRemoval = true, cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
private List<Term> getChildren() {
return children;
}
@SuppressWarnings("unused") // Used by JPA
private void setChildren(List<Term> children) {
this.children = children;
observableChildren.setAll(FXCollections.observableArrayList(children));
}
public void addChild(Term child) {
children.add(child);
observableChildren.add(child);
Term currentParent = child.getParent();
if(currentParent != null) {
currentParent.delChild(child);
}
child.setParent(this);
}
public boolean delChild(Term child) {
child.setParent(null);
observableChildren.remove(child);
return children.remove(child);
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getGoal() {
return goal;
}
public void setGoal(String goal) {
this.goal = goal;
}
public Term getPredecessor() {
return predecessor;
}
public void setPredecessor(Term predecessor) {
this.predecessor = predecessor;
}
public Term getSuccessor() {
return successor;
}
public void setSuccessor(Term successor) {
this.successor = successor;
}
public LocalDate getBegin() {
return begin;
}
public void setBegin(LocalDate begin) {
this.begin = begin;
}
public LocalDate getEnd() {
return end;
}
public void setEnd(LocalDate end) {
this.end = end;
}
public String getDefaultTimeUnit() {
return defaultTimeUnit;
}
public void setDefaultTimeUnit(String defaultTimeUnit) {
this.defaultTimeUnit = defaultTimeUnit;
}
public ObservableList<UserRequirement> getObservableRequirements() {
return observableRequirements;
}
/*
public ListProperty<UserRequirement> observableRequirementsProperty() {
return observableRequirements;
}
*/
@Access(AccessType.PROPERTY)
@OneToMany(mappedBy="term", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
private List<UserRequirement> getRequirements() {
return userRequirements;
}
@SuppressWarnings("unused") // Used by JPA
private void setRequirements(List<UserRequirement> userRequirements) {
//logger.debug(userRequirements.size() + " reqs to " + this.debug());
this.userRequirements = userRequirements;
if(userRequirements != null) {
observableRequirements.clear();
for(UserRequirement req: userRequirements) {
observableRequirements.add(req);
}
}
observableRequirements.setAll(FXCollections.observableList(userRequirements));
}
public void addRequirement(UserRequirement userRequirement) {
logger.debug(userRequirement.debug() + " to " + this.debug());
userRequirements.add(userRequirement);
observableRequirements.add(userRequirement);
userRequirement.setTerm(this);
}
public void delRequirement(UserRequirement userRequirement) {
logger.debug(userRequirement.debug() + " from " + this.debug());
if(userRequirements.contains(userRequirement)) {
if(!userRequirements.remove(userRequirement)) {
logger.error("Removal of requirement from list failed! Term: " + this.toString());
}
if(!observableRequirements.remove(userRequirement)) {
logger.error("Removal of requirement from observable list failed! Term: " + this.toString());
}
userRequirement.setTerm(null);
}
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public LocalDateTime getPublishingDate() {
return publishingDate;
}
public void setPublishingDate(LocalDateTime publishingDate) {
this.publishingDate = publishingDate;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public List<String> getAllowedChildClasses() {
List<String> list = new ArrayList<String>(levels.subList(levels.indexOf(getLevel())+1, levels.size()));
//list.set(0, "Sub-" + list.get(0));
return list;
}
public int getMyNumber() {
return myNumber;
}
public void setMyNumber(int myNumber) {
this.myNumber = myNumber;
}
public Type getMyType() {
return myType;
}
public void setMyType(Type myType) {
this.myType = myType;
}
public Status getMyState() {
return myState;
}
public void setMyState(Status myState) {
this.myState = myState;
}
@Override
public String toString() {
String beginString = begin == null? "tbd." : begin.format(formatter);
String endString = end == null? "tbd." : end.format(formatter);
return String.format("%s-%s: %s > %s", beginString, endString, identifier, goal);
}
public String debug() {
String tmp = getRequirements() == null ? "0" : Integer.toString(getRequirements().size());
String txt = getId() + "@" + getLevel() + ": " + toString() + "; " + tmp + " userRequirements; " + getChildren().size() + " children";
if(getParent() == null) {
txt += " as root element";
} else {
txt += " owned by " + getParent().getId();
}
return txt;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
//result = prime * result + version;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Term other = (Term) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
/*
if (version != other.version)
return false;
*/
return true;
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Hyperlink?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane fx:id="gridPane" hgap="10.0" maxHeight="1.7976931348623157E308"
maxWidth="1.7976931348623157E308" stylesheets="@../stylesheets/controller.css"
vgap="10.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="com.agiletunes.controllers.terms.TermEditorCtrl">
<columnConstraints>
<ColumnConstraints maxWidth="1.7976931348623157E308" />
<ColumnConstraints maxWidth="1.7976931348623157E308"
minWidth="10.0" />
<ColumnConstraints minWidth="10.0" />
<ColumnConstraints maxWidth="1.7976931348623157E308"
minWidth="10.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
<RowConstraints minHeight="10.0" />
</rowConstraints>
<children>
<DatePicker fx:id="beginDatePicker" onAction="#beginChanged"
GridPane.columnIndex="1" GridPane.rowIndex="4">
<GridPane.margin>
<Insets />
</GridPane.margin>
</DatePicker>
<DatePicker fx:id="endDatePicker" onAction="#endChanged"
GridPane.columnIndex="3" GridPane.rowIndex="4">
<GridPane.margin>
<Insets right="24.0" />
</GridPane.margin>
</DatePicker>
<Label text="Begin" GridPane.halignment="LEFT"
GridPane.rowIndex="4">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<Label text="End" GridPane.columnIndex="2" GridPane.halignment="LEFT"
GridPane.rowIndex="4">
<GridPane.margin>
<Insets left="12.0" right="8.0" />
</GridPane.margin>
</Label>
<Label maxWidth="1.7976931348623157E308" text="Identifier"
GridPane.hgrow="ALWAYS" GridPane.rowIndex="2">
<GridPane.margin>
<Insets left="24.0" right="12.0" top="24.0" />
</GridPane.margin>
</Label>
<TextField fx:id="identifierTextField" onAction="#identifierChanged"
onMouseExited="#identifierChanged" GridPane.columnIndex="1"
GridPane.columnSpan="2147483647" GridPane.rowIndex="2">
<GridPane.margin>
<Insets right="24.0" top="24.0" />
</GridPane.margin>
</TextField>
<Label layoutX="34.0" layoutY="80.0" maxWidth="1.7976931348623157E308"
text="Level" GridPane.rowIndex="5">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<ComboBox fx:id="timeUnitComboBox" maxWidth="1.7976931348623157E308"
onAction="#timeUnitChanged" GridPane.columnIndex="3" GridPane.hgrow="ALWAYS"
GridPane.rowIndex="5">
<GridPane.margin>
<Insets right="24.0" />
</GridPane.margin>
</ComboBox>
<Separator prefWidth="200.0" GridPane.columnSpan="2147483647"
GridPane.rowIndex="7" />
<Label layoutX="34.0" layoutY="39.0" text="Goal"
GridPane.rowIndex="3">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<TextField fx:id="goalTextField" layoutX="116.0" layoutY="34.0"
onAction="#goalChanged" onKeyReleased="#goalChanged" onMouseExited="#goalChanged"
GridPane.columnIndex="1" GridPane.columnSpan="2147483647"
GridPane.rowIndex="3">
<GridPane.margin>
<Insets right="24.0" />
</GridPane.margin>
</TextField>
<Label layoutX="34.0" layoutY="162.0" text="Time unit"
GridPane.columnIndex="2" GridPane.halignment="RIGHT"
GridPane.rowIndex="5">
<GridPane.margin>
<Insets left="12.0" right="8.0" />
</GridPane.margin>
</Label>
<Label layoutX="34.0" layoutY="162.0" maxWidth="1.7976931348623157E308"
text="Type" GridPane.rowIndex="6">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<ComboBox fx:id="typeComboBox" layoutX="116.0" layoutY="157.0"
maxWidth="1.7976931348623157E308" onAction="#typeChanged"
GridPane.columnIndex="1" GridPane.rowIndex="6" />
<Label layoutX="391.0" layoutY="162.0" text="State"
GridPane.columnIndex="2" GridPane.halignment="LEFT"
GridPane.rowIndex="6">
<GridPane.margin>
<Insets left="12.0" right="8.0" />
</GridPane.margin>
</Label>
<ComboBox fx:id="stateComboBox" layoutX="464.0" layoutY="157.0"
maxWidth="1.7976931348623157E308" onAction="#stateChanged"
GridPane.columnIndex="3" GridPane.rowIndex="6">
<GridPane.margin>
<Insets right="24.0" />
</GridPane.margin>
</ComboBox>
<Separator prefWidth="200.0" GridPane.columnSpan="2147483647"
GridPane.rowIndex="1" />
<Label layoutX="34.0" layoutY="99.0" maxWidth="1.7976931348623157E308"
text="Parent">
<GridPane.margin>
<Insets left="24.0" top="24.0" />
</GridPane.margin>
</Label>
<Hyperlink fx:id="parentHyperlink" onMouseClicked="#parentSelected"
text="Hyperlink" GridPane.columnIndex="1" GridPane.columnSpan="2147483647">
<GridPane.margin>
<Insets right="24.0" top="24.0" />
</GridPane.margin>
</Hyperlink>
<ListView fx:id="subTermListView" onMouseClicked="#childSelected"
prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1"
GridPane.columnSpan="2147483647" GridPane.rowIndex="8">
<GridPane.margin>
<Insets bottom="24.0" right="24.0" />
</GridPane.margin>
</ListView>
<Label layoutX="34.0" layoutY="38.0" maxWidth="1.7976931348623157E308"
text="Sub-terms" GridPane.hgrow="ALWAYS" GridPane.rowIndex="8"
GridPane.valignment="TOP">
<GridPane.margin>
<Insets left="24.0" />
</GridPane.margin>
</Label>
<Label fx:id="levelLabel" text="Label" GridPane.columnIndex="1"
GridPane.rowIndex="5" />
</children>
</GridPane>
Controller类:
..
@Controller
public class TermEditorCtrl implements Initializable {
@FXML private DatePicker beginDatePicker;
@FXML private DatePicker endDatePicker;
@FXML private TextField identifierTextField;
@FXML private ComboBox<String> timeUnitComboBox;
@FXML private TextField goalTextField;
@FXML private ComboBox<Term.Type> typeComboBox;
@FXML private ComboBox<Term.Status> stateComboBox;
@FXML private ListView<Term> subTermListView;
@FXML private Label levelLabel;
@FXML private Hyperlink parentHyperlink;
@Autowired private TermService termService;
@Autowired private EntityManagerFactory emf;
protected EntityManager entityManager;
protected EntityTransaction transaction;
protected boolean isDirty;
protected Term myTerm;
public TermEditorCtrl() {
}
@Override
public void initialize(URL location, ResourceBundle resources) {
typeComboBox.setItems(FXCollections.observableArrayList(Term.Type.values()));
stateComboBox.setItems(FXCollections.observableArrayList(Term.Status.values()));
timeUnitComboBox.setItems(FXCollections.observableArrayList(Term.chronoUnits));
}
@Transactional(propagation=Propagation.REQUIRED)
protected void saveIfNeeded() {
if(transaction != null) {
if(isDirty) {
Alert alert = new Alert(AlertType.CONFIRMATION, "Otherwise all changes will be lost\n\n", ButtonType.YES, ButtonType.NO);
alert.setTitle("Requirement changed");
alert.setHeaderText("Save changes?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.YES){
termService.save(myTerm);
transaction.commit();
}
} else {
transaction.rollback();
transaction = null;
}
}
}
@Transactional(propagation=Propagation.REQUIRED)
public void setTerm(Term aTerm) {
saveIfNeeded();
myTerm = aTerm;
entityManager = emf.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
identifierTextField.setText(aTerm.getIdentifier());
beginDatePicker.setValue(aTerm.getBegin());
endDatePicker.setValue(aTerm.getEnd());
goalTextField.setText(aTerm.getGoal());
stateComboBox.getSelectionModel().select(aTerm.getMyState());
typeComboBox.getSelectionModel().select(aTerm.getMyType());
levelLabel.setText(aTerm.getLevel());
subTermListView.setItems(aTerm.getObservableChildren());
timeUnitComboBox.getSelectionModel().select(aTerm.getDefaultTimeUnit());
Term parent = aTerm.getParent();
if (parent != null) {
parentHyperlink.setText(parent.getIdentifier());
} else {
parentHyperlink.setText("");
}
isDirty = false;
}
@FXML
void beginChanged(ActionEvent event) {
myTerm.setBegin(beginDatePicker.getValue());
isDirty = true;
}
@FXML
void endChanged(ActionEvent event) {
myTerm.setEnd(endDatePicker.getValue());
isDirty = true;
}
@FXML
void goalChanged(KeyEvent event) {
myTerm.setGoal(goalTextField.getText());
isDirty = true;
}
@FXML
void identifierChanged(ActionEvent event) {
myTerm.setIdentifier(identifierTextField.getText());
isDirty = true;
}
@FXML
void stateChanged(ActionEvent event) {
myTerm.setMyState(stateComboBox.getSelectionModel().getSelectedItem());
isDirty = true;
}
@FXML
void childSelected(MouseEvent event) {
setTerm(subTermListView.getSelectionModel().getSelectedItem());
}
@FXML
void parentSelected(MouseEvent event) {
setTerm(myTerm.getParent());
}
@FXML
void timeUnitChanged(ActionEvent event) {
myTerm.setDefaultTimeUnit(timeUnitComboBox.getSelectionModel().getSelectedItem());
isDirty = true;
}
@FXML
void typeChanged(ActionEvent event) {
myTerm.setMyType(typeComboBox.getSelectionModel().getSelectedItem());
isDirty = true;
}
public void setStage(Stage aStage) {
aStage.setOnCloseRequest(event -> {
saveIfNeeded();
});
}
}
在ListView
中选择Term对象时加载FXML UI和控制器的代码private ContextMenu getContextMenu(Term term) {
ContextMenu cm = new ContextMenu();
MenuItem editItem = new MenuItem("Edit");
cm.getItems().add(editItem);
editItem.setOnAction(event -> {
try{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/TermEditor.fxml"));
fxmlLoader.setControllerFactory(c -> App.springContext.getBean(c)); // lookup the controller from the spring application context
Parent parent = fxmlLoader.load();
TermEditorCtrl termCtrl = fxmlLoader.getController();
Stage stage = new Stage();
Scene scene = new Scene(parent);
stage.setScene(scene);
stage.show();
termCtrl.setStage(stage);
termCtrl.setTerm(term);
}
catch(IOException e){
logger.debug(e.getLocalizedMessage());
e.printStackTrace();
}
});
错误消息
javafx.fxml.LoadException:
/C:/Users/Alexander/Documents/agiletunes-codespace/agileTunes-implementation/target/classes/fxml/TermEditor.fxml
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
at com.agiletunes.controllers.requirement.PlanningPaneCtrl$TermTreeCell.lambda$0(PlanningPaneCtrl.java:674)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.control.MenuItem.fire(MenuItem.java:462)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1405)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(ContextMenuContent.java:1358)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:381)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:417)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
at com.agiletunes.controllers.terms.TermEditorCtrl.initialize(TermEditorCtrl.java:68)
at com.agiletunes.controllers.terms.TermEditorCtrl$$FastClassBySpringCGLIB$$1861d384.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:669)
at com.agiletunes.controllers.terms.TermEditorCtrl$$EnhancerBySpringCGLIB$$bd8c26eb.initialize(<generated>)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
... 45 more
如果您需要其他信息,代码或类似信息,请告知我们
非常感谢您提前寻求帮助。
答案 0 :(得分:0)
我进一步考虑了这一点。最终我在AopUtils中的canApply(Pointcut pc,Class targetClass,boolean hasIntroductions)中找到了根本原因。问题是控制器的模型已设置,调用setTerm方法。 setTerm的参数是Term对象,Term是抽象类。 Spring在处理抽象参数时显然存在问题。我用5种不同的setter方法替换了setTerm方法,每个具体的Term扩展名为1。现在它有效。
我想知道这是一个错误还是一个功能,因为CglibAopProxy中的拦截方法也会获得一个设置了所有FXML变量的代理对象?!