仅知道实体类型时访问正确的 Spring 服务
Accessing the correct Spring service when only knowing the entity type
我构建了 Tab class (JavaFX) 的扩展,它带有一些参数,例如实体 class 类型,实际上围绕实体 class 构建了整个 GUI (table 显示、功能性添加/编辑/删除按钮/从 class 成员动态创建的功能搜索字段等)。这个 class 需要扩展以适应每个实体类型,但到目前为止我有 4 个实体使用它并且我对结果非常满意,它节省了很多代码。
我遇到的一个问题是在初始化选项卡时动态创建搜索字段。我所做的是遍历实体 class' 字段并创建一个 TextField 以在 String 字段上搜索,或者如果该字段与另一个实体类型具有 ManyToOne 关系,则创建一个填充不同选项的 ComboBox。为了实现这一点,我构建了一个名为 TFHEntity 的标记接口,我的所有实体都实现了该接口。我还有一个 @HiddenField 注释来排除 ID、设置双向映射等字段。请参阅下面的代码(注意,友好的名称只是为了给提示文本提供法语,因为我的字段名称是英文的):
//Create Search Fields Based on Entity Class Members and Annotations
int i = 0;
for (Field field : entityClass.getDeclaredFields())
{
if (!isFieldHidden(field))
{
if (isFieldEntityType(field))
{
FriendlyName friendlyName = field.getAnnotation(FriendlyName.class);
ComboBox searchField = new ComboBox();
searchField.setPromptText("Recherche " + friendlyName.value());
searchField.setMaxWidth(Double.MAX_VALUE);
searchField.getSelectionModel().selectedItemProperty().addListener((observable) ->
{
showEntities();
});
if (field.getType().getName().contains("Category"))
{
ObservableList<Category> categories = FXCollections.observableArrayList(categoryService.findAll());
categories.add(0, new Category(null, "Recherche " + friendlyName.value(), null, null));
searchField.setItems(categories);
}
else if (field.getType().getName().contains("Supplier"))
{
ObservableList<Supplier> fournisseurs = FXCollections.observableArrayList(supplierService.findAll());
fournisseurs.add(0, new Supplier(null, "Recherche " + friendlyName.value(), null, null, null, null, null, null));
searchField.setItems(fournisseurs);
}
searchFields.put(i, searchField);
}
else
{
FriendlyName friendlyName = field.getAnnotation(FriendlyName.class);
TextField searchField = new TextField();
searchField.setPromptText("Recherche " + friendlyName.value());
searchField.setOnKeyReleased((event) ->
{
showEntities();
});
searchFields.put(i, searchField);
}
i++;
}
}
private boolean isFieldEntityType(Field field)
{
for (Class c : field.getType().getInterfaces())
{
if (c.getName().contains("TFHEntity"))
{
return true;
}
}
return false;
}
private boolean isFieldHidden(Field field)
{
HiddenField hiddenField = field.getAnnotation(HiddenField.class);
return hiddenField != null;
}
我必须为 ComboBox 构建 ObservableList 的方式困扰着我。我想有一种方法可以动态调用正确服务的 findAll() 方法来填充这些列表,而无需像我现在所做的那样检查类型和重复代码。我已经构建了一个地图,其中实体 class 类型作为键,服务作为值(它们显然也都需要自动装配)但我正在努力想办法实现这一点:
private final Map<Class, TFHService> serviceMap = new HashMap<>();
private void initServiceMap()
{
serviceMap.put(Bill.class, billService);
serviceMap.put(Category.class, categoryService);
serviceMap.put(Customer.class, customerService);
serviceMap.put(Parameter.class, parameterService);
serviceMap.put(Product.class, productService);
serviceMap.put(Sequence.class, sequenceService);
serviceMap.put(Supplier.class, supplierService);
}
我确信反思会让我做到这一点(我知道某处有一个调用方法)但我对这个话题还不够了解,无法将我所知道的一切放在一起并实现这一点。我今天学到了很多东西,我的大脑有点痛,我对自己取得的成就感到非常满意,但这限制了可扩展性,因为我需要为每种类型的 ComboBox 修改 super class。
我希望这是清楚的,这段代码对我来说似乎很复杂,我很难用语言表达我的问题。如果您知道使用我目前构建的方法调用正确服务的 findAll() 方法并且没有丑陋的限制 if / else 语句的方法,请提供帮助。
谢谢!
我想通了!我让我所有的服务接口都扩展了一个 MasterService 接口。我将他们的 findAll() 方法移动到具有以下签名的顶部接口:
List<?> findAll();
此方法的最终实现 return List<?>
但它们各自调用的相应 dao 层知道 return 正确的类型。话虽这么说,我将 serviceMap 移动到配置 class 并且我 return 它作为一个 bean :
@Bean
public Map<Class, TFHService> serviceMap()
{
Map<Class, TFHService> serviceMap = new HashMap<>();
serviceMap.put(Bill.class, billService);
serviceMap.put(Category.class, categoryService);
serviceMap.put(Customer.class, customerService);
serviceMap.put(Parameter.class, parameterService);
serviceMap.put(Product.class, productService);
serviceMap.put(Sequence.class, sequenceService);
serviceMap.put(Supplier.class, supplierService);
return serviceMap;
}
所有这些服务都需要自动连接,但它们隐藏在配置中 class,我永远看不到它们。
最后,我的 MasterService 也包含这个方法:
TFHEntity createDummy();
然后我的所有服务都可以 return 一个只有名称的虚拟对象(Recherche 在法语中的意思是搜索):
@Override
public TFHEntity createDummy()
{
return new Category(null, "Recherche", null, null);
}
完成所有这些后,我丑陋的代码现在变成了:
int i = 0;
for (Field field : entityClass.getDeclaredFields())
{
if (!isFieldHidden(field))
{
if (isFieldEntityType(field))
{
ComboBox searchField = new ComboBox();
searchField.setPromptText("Recherche");
searchField.setMaxWidth(Double.MAX_VALUE);
searchField.getSelectionModel().selectedItemProperty().addListener((observable) ->
{
showEntities();
});
TFHService service = serviceMap.get(field.getType());
ObservableList items = FXCollections.observableArrayList(service.findAll());
items.add(0, service.createDummy());
searchField.setItems(items);
searchFields.put(i, searchField);
}
else
{
TextField searchField = new TextField();
searchField.setPromptText("Recherche");
searchField.setAlignment(Pos.CENTER);
searchField.setOnKeyReleased((event) ->
{
showEntities();
});
searchFields.put(i, searchField);
}
i++;
}
}
这样做的好处是让我可以将新的子 classes 添加到我的 EntityTab 基础 class 而无需触及其代码。
干杯!
我构建了 Tab class (JavaFX) 的扩展,它带有一些参数,例如实体 class 类型,实际上围绕实体 class 构建了整个 GUI (table 显示、功能性添加/编辑/删除按钮/从 class 成员动态创建的功能搜索字段等)。这个 class 需要扩展以适应每个实体类型,但到目前为止我有 4 个实体使用它并且我对结果非常满意,它节省了很多代码。
我遇到的一个问题是在初始化选项卡时动态创建搜索字段。我所做的是遍历实体 class' 字段并创建一个 TextField 以在 String 字段上搜索,或者如果该字段与另一个实体类型具有 ManyToOne 关系,则创建一个填充不同选项的 ComboBox。为了实现这一点,我构建了一个名为 TFHEntity 的标记接口,我的所有实体都实现了该接口。我还有一个 @HiddenField 注释来排除 ID、设置双向映射等字段。请参阅下面的代码(注意,友好的名称只是为了给提示文本提供法语,因为我的字段名称是英文的):
//Create Search Fields Based on Entity Class Members and Annotations
int i = 0;
for (Field field : entityClass.getDeclaredFields())
{
if (!isFieldHidden(field))
{
if (isFieldEntityType(field))
{
FriendlyName friendlyName = field.getAnnotation(FriendlyName.class);
ComboBox searchField = new ComboBox();
searchField.setPromptText("Recherche " + friendlyName.value());
searchField.setMaxWidth(Double.MAX_VALUE);
searchField.getSelectionModel().selectedItemProperty().addListener((observable) ->
{
showEntities();
});
if (field.getType().getName().contains("Category"))
{
ObservableList<Category> categories = FXCollections.observableArrayList(categoryService.findAll());
categories.add(0, new Category(null, "Recherche " + friendlyName.value(), null, null));
searchField.setItems(categories);
}
else if (field.getType().getName().contains("Supplier"))
{
ObservableList<Supplier> fournisseurs = FXCollections.observableArrayList(supplierService.findAll());
fournisseurs.add(0, new Supplier(null, "Recherche " + friendlyName.value(), null, null, null, null, null, null));
searchField.setItems(fournisseurs);
}
searchFields.put(i, searchField);
}
else
{
FriendlyName friendlyName = field.getAnnotation(FriendlyName.class);
TextField searchField = new TextField();
searchField.setPromptText("Recherche " + friendlyName.value());
searchField.setOnKeyReleased((event) ->
{
showEntities();
});
searchFields.put(i, searchField);
}
i++;
}
}
private boolean isFieldEntityType(Field field)
{
for (Class c : field.getType().getInterfaces())
{
if (c.getName().contains("TFHEntity"))
{
return true;
}
}
return false;
}
private boolean isFieldHidden(Field field)
{
HiddenField hiddenField = field.getAnnotation(HiddenField.class);
return hiddenField != null;
}
我必须为 ComboBox 构建 ObservableList 的方式困扰着我。我想有一种方法可以动态调用正确服务的 findAll() 方法来填充这些列表,而无需像我现在所做的那样检查类型和重复代码。我已经构建了一个地图,其中实体 class 类型作为键,服务作为值(它们显然也都需要自动装配)但我正在努力想办法实现这一点:
private final Map<Class, TFHService> serviceMap = new HashMap<>();
private void initServiceMap()
{
serviceMap.put(Bill.class, billService);
serviceMap.put(Category.class, categoryService);
serviceMap.put(Customer.class, customerService);
serviceMap.put(Parameter.class, parameterService);
serviceMap.put(Product.class, productService);
serviceMap.put(Sequence.class, sequenceService);
serviceMap.put(Supplier.class, supplierService);
}
我确信反思会让我做到这一点(我知道某处有一个调用方法)但我对这个话题还不够了解,无法将我所知道的一切放在一起并实现这一点。我今天学到了很多东西,我的大脑有点痛,我对自己取得的成就感到非常满意,但这限制了可扩展性,因为我需要为每种类型的 ComboBox 修改 super class。
我希望这是清楚的,这段代码对我来说似乎很复杂,我很难用语言表达我的问题。如果您知道使用我目前构建的方法调用正确服务的 findAll() 方法并且没有丑陋的限制 if / else 语句的方法,请提供帮助。
谢谢!
我想通了!我让我所有的服务接口都扩展了一个 MasterService 接口。我将他们的 findAll() 方法移动到具有以下签名的顶部接口:
List<?> findAll();
此方法的最终实现 return List<?>
但它们各自调用的相应 dao 层知道 return 正确的类型。话虽这么说,我将 serviceMap 移动到配置 class 并且我 return 它作为一个 bean :
@Bean
public Map<Class, TFHService> serviceMap()
{
Map<Class, TFHService> serviceMap = new HashMap<>();
serviceMap.put(Bill.class, billService);
serviceMap.put(Category.class, categoryService);
serviceMap.put(Customer.class, customerService);
serviceMap.put(Parameter.class, parameterService);
serviceMap.put(Product.class, productService);
serviceMap.put(Sequence.class, sequenceService);
serviceMap.put(Supplier.class, supplierService);
return serviceMap;
}
所有这些服务都需要自动连接,但它们隐藏在配置中 class,我永远看不到它们。
最后,我的 MasterService 也包含这个方法:
TFHEntity createDummy();
然后我的所有服务都可以 return 一个只有名称的虚拟对象(Recherche 在法语中的意思是搜索):
@Override
public TFHEntity createDummy()
{
return new Category(null, "Recherche", null, null);
}
完成所有这些后,我丑陋的代码现在变成了:
int i = 0;
for (Field field : entityClass.getDeclaredFields())
{
if (!isFieldHidden(field))
{
if (isFieldEntityType(field))
{
ComboBox searchField = new ComboBox();
searchField.setPromptText("Recherche");
searchField.setMaxWidth(Double.MAX_VALUE);
searchField.getSelectionModel().selectedItemProperty().addListener((observable) ->
{
showEntities();
});
TFHService service = serviceMap.get(field.getType());
ObservableList items = FXCollections.observableArrayList(service.findAll());
items.add(0, service.createDummy());
searchField.setItems(items);
searchFields.put(i, searchField);
}
else
{
TextField searchField = new TextField();
searchField.setPromptText("Recherche");
searchField.setAlignment(Pos.CENTER);
searchField.setOnKeyReleased((event) ->
{
showEntities();
});
searchFields.put(i, searchField);
}
i++;
}
}
这样做的好处是让我可以将新的子 classes 添加到我的 EntityTab 基础 class 而无需触及其代码。
干杯!