【发布时间】:2020-11-22 08:03:13
【问题描述】:
我目前正在开发一个 Spring Boot 项目,但在自动装配方面遇到了一些问题。
首先,这是程序的功能:
有一个外部程序,用户可以在其中键入命令。这些命令在我使用的库(包装 API 的库)中触发我的代码需要处理的事件。此事件包含原始命令文本,如“帮助”,我将其与可用命令列表匹配。如果找到匹配的命令,它将被执行(包含所有事件数据,因为它可能包含特定信息)。
我有一个命令接口和该接口的基本实现。程序可以处理的每个命令都是该基本实现的子类。在我使用 Spring 之前,我只是将文本与工厂中的命令进行匹配,并将拟合命令作为新对象返回
(如return new Help();)。由于所有命令都是该接口实现的子类,因此它们都有执行命令的方法。对数据库的每个请求都发生在另一个类中,该类具有从不同命令执行方法中调用的静态方法。
现在有了 Spring,我有机会使用处理所有数据库通信的存储库,因此我不需要依赖这样的类。但是,为了使命令能够使用自动装配的存储库,它们需要由 Spring 控制。目前,我使用ApplicationContext 按名称接收bean 并将所有命令定义为@Bean 在@Configuration 类中。 (即使示例命令只使用一个,也有多个存储库!)
命令界面:
public interface Command {
/* Executes the command and forwards the event received by the library
* to the execution method which uses its information.
*/
void execute(SomeEvent event);
// other methods...
}
基本命令实现:
@Service
public class CommandImpl implements Command {
// Can be used for unknown commands, does not do anything.
@Override
public void execute(SomeEvent event) {
// subclasses contain the information, this one does not do anything
}
// other interface method implementations...
}
一个简单的命令实现:
public class Help extends CommandImpl {
// SomeRepository refers to an Interface which extends a Spring JPA Repository like CrudRepository
private final SomeRepository repo;
@Autowired
public Help(SomeRepository repo) {
this.repo = repo;
}
@Override
public void execute(SomeEvent event) {
/* gets data from the SomeEvent object (like caller) and uses
* SomeRepository to receive database settings and permissions.
* Displays help for the specific caller (only commands he/she can use
* based on the permissions received from the repository)
*/
}
}
Spring ApplicationContext 包装器:
@Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
private static final Logger LOGGER = LoggerFactory.getLogger(SpringContext.class);
/*
* Get a Spring-controlled object of a command implementation by name.
*/
public static Command getCommandBean(String name) {
Command command;
try {
command = context.getBean(name, Command.class);
} catch (NoSuchBeanDefinitionException e) {
LOGGER.error("The command \"" + name + "\" does not exist or is not defined as a Bean!");
// returning the base implementation as that will do nothing on execution.
command = new CommandImpl();
}
return command;
}
/*
* Spring controlled method which provides the ApplicationContext on startup.
*/
@Override
public void setApplicationContext(@NotNull ApplicationContext context) throws BeansException {
SpringContext.context = context;
}
}
不同命令的 Bean 配置:
@Configuration
public class BeanConfig {
@Bean
@Autowired
Help help(SomeRepository repo) {
return new Help(repo);
}
// other commands defined as @Bean...
}
被库事件触发并执行拟合命令的监听器:
@Service
public class CommandListener extends SomeLibraryListenerAdapter {
@Override
public void onSomeEvent(@NotNull final SomeEvent event) {
// perform some checks if event contains a command and if caller is authorized to use it...
// execute command
final String commandName = identifyCommand(...); // commandName is a guaranteed match to some command when it arrives here
final Command command = SpringContext.getCommandBean(commandName);
command.execute(event);
}
}
我读到最好不要使用ApplicationContext,因为它违反了 Spring 的依赖注入原则,但我不太明白如何避免使用它。此外,在我看来,因为我确实可以控制所有这些命令,所以必须有一种方法可以让我以某种方式使用 Spring,这样我就不需要使用 ApplicationContext 来获得所需的合适 bean自动装配。 \
我尝试将命令类标记为@Service 并在其字段上使用@Autowired,因为我不能使用构造函数,因为这样工厂就需要知道哪个命令需要哪个存储库。另一个问题是我不能再使用new,因此工厂的想法不起作用。
我还读到了BeanFactory,但我并没有真正掌握它背后的概念。我读到的关于它的文本定义了 XML 文件中实体的值,我认为这对我不起作用,因为我不使用值对象而是使用命令实现。
我看到的最后一件事是 @Configurable 可以使用的注释,但这需要 AspectJ 和某种编织,这对于这样一个基本功能来说似乎是多余的。但是,如果没有其他方法,我可能会继续使用 ApplicationContext 而不是使用该解决方案。
总而言之,我的问题是如何重组我的代码以在不使用 ApplicationContext 的情况下对我的命令使用自动装配,而是使用非常简单和干净的 Spring 注释;或者一些正确方向的提示,比如在这里使用什么模式/功能。
如果没有办法,那么我会很高兴简短地解释一下为什么它不起作用。
亲切的问候
银河系
【问题讨论】:
标签: java spring spring-boot autowired