如何在 Spring Boot 中自动装配服务并将动态参数传递给构造函数

How to autowire a service in Spring Boot and pass dynamic parameters to the constructor

我正在处理 Spring 启动项目,在尝试自动装配服务时遇到了一些问题。这是我当前的代码:

@Component
public class StealFromPocket implements Command {
    private Integer gameId;
    private Player playerFrom;

    @Autowired
    GameService gameService;

    public StealFromPocket(Integer gameId, Player playerFrom) {
        this.gameId = gameId;
        this.playerFrom = playerFrom;
    }

    @Override
    public void execute() {
        gameService.findPlayersByGameId(gameId).forEach(player -> {
            new StealCoinCommand(playerFrom, player).execute();
        });

    }
}

构造函数参数 gameIdplayerFrom 在运行时是已知的,所以我不能对它们进行硬编码或将它们存储在配置文件中。

这是 StealFromPocket 通过反射从 service 实例化的地方(completeClassName 将根据用户交互而改变。在这个例子中,它将是 org.springframework...StealFromPocket:

Class<?> clazz = Class.forName(completeClassName);
Object card = clazz.getDeclaredConstructor(Integer.class, Player.class).newInstance(gameId, playerFrom);
Method method = clazz.getDeclaredMethod("execute");
method.invoke(card);

尝试构建项目时,出现以下错误:

***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 0 of constructor in org.springframework.ntfh.cardlogic.abilitycard.rogue.RobarBolsillos required a bean of type 'java.lang.Integer' that could not be found.


Action:

Consider defining a bean of type 'java.lang.Integer' in your configuration.

我认为问题是我没有将 GameService 作为构造函数参数传递,但由于我项目的性质,这些服务不能作为参数传递,因为它们会因 class 通过反射实例化。

我通过更改 class 的实现来修复此问题,以便 execute 方法接收相应的参数,并且自动装配的字段是 StealFromPocket class。现在看起来像这样:

@Component
public class StealFromPocket {

    @Autowired
    private GameService gameService;

    public void execute(Player playerFrom) {
        Integer gameId = playerFrom.getGame().getId();
        gameService.findPlayersByGameId(gameId).forEach(player -> {
            new StealCoinCommand(playerFrom, player).execute();
        });
    }
}

GameService.java 中,我执行以下操作:

// Get the class from its name
Class<?> clazz = Class.forName(completeClassName);
// Instantiate an object of the class
Object cardCommand = clazz.getDeclaredConstructor().newInstance();
// Autowire the new object's dependencies (services used inside)
AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(cardCommand);
factory.initializeBean(cardCommand, className);
// Get a reference to the method "execute", that receives 2 parameters
Method method = clazz.getDeclaredMethod("execute", Player.class);
// Execute the method with the parameters
method.invoke(cardCommand, playerFrom);