I'll first give some context before showing the error. I am developing a Rest API with Spring Boot (spring boot 2.6.6 and Java 17), in which I started getting the following error when trying to run (with IntelliJ). The complete error queue.
Error creating bean with name 'deleteAccountResource' defined in file [C:\ruta...\resources\DeleteAccountResource.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springCommandBus' defined in file [C:\path...\SpringCommandBus.class]: Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [dev.facturador.global.infrastructure.adapters.SpringCommandBus]: Constructor threw exception; nested exception is java.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
what I want to fix:
class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
I understand that the problem could be in the "SpringCommandBuss" class or in the "DeleteAccountResource" class. First I go with "SpringCommandBuss" this is a class that serves as a service for my architecture to work, it is in charge of choosing the commandHandler that is going to be executed according to the command that you pass to it. On the other hand "DeleteAccountResource", this class is an endpoint (which does literally what the name of the class says).
Now I show the components that are part of the implementation of a command (they are 3 Command, CommandHandler and CommandBuss).
//The command is in charge of transporting the data as a POJO (this is an abstract implementation)
public class Command {
}
//The handler receives the command without knowing what it is and executes the action
@FunctionalInterface
public interface CommandHandler<T extends Command> {
void handle(T command) throws Exception;
}
Now the SpringCommandBuss class
@Service
@Primary
public class SpringCommandBus implements CommandBus {
private final Map<Class, CommandHandler> handlers;
/**It takes care of finding all possible implementations of the command handler.*/
public SpringCommandBus(List<CommandHandler> commandHandlerImplementations) {
this.handlers = new HashMap<>();
commandHandlerImplementations.forEach(commandHandler -> {
Class<?> commandClass = getCommandClass(commandHandler);
handlers.put(commandClass, commandHandler);
});
}
/**Check if there is a command handler for the command
*within the implementations it found, and run the command handler if found
*/
@Override
public void handle(Command command) throws Exception {
//Si no existe un Handler con este comando da error
if (!handlers.containsKey(command.getClass())) {
throw new Exception(String.format("No handler for %s", command.getClass().getName()));
}
handlers.get(command.getClass()).handle(command);
}
/**Find the implementation class using the java.reflection library*/
public Class<?> getCommandClass(CommandHandler handler) {
Type commandInterface = ((ParameterizedType) handler.getClass()
.getGenericInterfaces()[0]).getActualTypeArguments()[0];
return getClass(commandInterface.getTypeName());
}
/**Once it retrieves the controller's type name, it retrieves the controller's class*/
public Class<?> getClass(String name) {
try {
return Class.forName(name);
} catch (Exception ex) {
return null;
}
}
}
On the DeleteAccountResource endpoint the commandBus is called like this:
@RestController
@RequestMapping(path = "/api/accounts")
public class DeleteAccountResource {
private final CommandBus commandBus;
public DeleteAccountResource(CommandBus commandBus) {
this.commandBus = commandBus;
}
/**
* @param username Username of the account you want to delete
* @return RespomseEmtity void with code 204
*/
@PreAuthorize("isAuthenticated()")
@DeleteMapping("/{username}")
public HttpEntity<Void> deleteAccount(@PathVariable @NotEmpty String username) throws Exception {
var command = AccountDeleteCommand.Builder.getInstance()
.username(username).build();
commandBus.handle(command);
return ResponseEntity.noContent().build();
}
}
To provide more information, I also add the command handler implementation in this case. What is executed, after passing the command to the bus is this
@AllArgsConstructor
@Service
@Transactional
public class AccountDeleteCommandHandler implements CommandHandler<AccountDeleteCommand> {
private final ChecksAccountService checkUseCase;
private final AccountRepository repository;
/**
* Handle deletion of a user account
*
* @param command Command contains the data to delete a user account
* @throws Exception
*/
@Override
public void handle(AccountDeleteCommand command) throws Exception {
//Check that this account exists before deleting
if (!checkUseCase.checkAccountExistsByUsername(command.getUsername())) {
throw new ResourceNotFound("No existe una cuenta con este username");
}
repository.deleteByOwnerUserUsername(command.getUsername());
}
}
As another extra fact, the command structure is part of my way of implementing the CQS (Command Query Separation) pattern. If you can think of a way to fix the bug or improve my implementation (and this improvement fixes the bug), I'd appreciate it. It may be that I am missing something and the error is somewhere else, so add the full error queue.
Aucun commentaire:
Enregistrer un commentaire