I am creating a route controller structure for commands.
Every controller has a @ControlController
annotation:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component // Because @Component all controllers will be spring managed.
public @interface ControlController {
}
The controller should contain methods with the @CommandMapping
annotation:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CommandMapping {
String value();
}
The value of the @CommandMapping
annotation is the command. So the method should be called when the value is the same as the command that is called.
At the start of the application the following code is called to fetch all the @CommandMappings
:
/**
* Load all controller mappings.
*/
private void fetchControllers() {
// Get all beans with the ControlController annotation.
Map<String, Object> controllers = this.applicationContext.getBeansWithAnnotation(ControlController.class);
for (Map.Entry<String, Object> entry : controllers.entrySet()) {
Class controller = entry.getValue().getClass();
for (Method method: controller.getMethods()) {
// Check every method in a controller for the CommandMapping annotation.
// When the annotation is present the method is a command mapping.
if (method.isAnnotationPresent(CommandMapping.class)) {
CommandMapping commandMapping = method.getAnnotation(CommandMapping.class);
// Add the command mapping to the controller list.
this.controllers.put(commandMapping.value(), method);
}
}
}
}
This code will find all the beans with the @ControlController
annotation and will loop trough all the methods to find the @CommandMapping
annotation. All the methods will be put in a Map<String, Method>
.
Until this far everything works perfect.
The following method is used to execute the right method that belongs to a command:
/**
* Execute a command for a client.
*
* @param client The client.
* @param command The command.
*/
public void executeCommand(Client client, String command) {
// Get the method that belongs to the command.
Method method = this.controllers.get(command);
Class<?> controllerClass = method.getDeclaringClass();
// The the controller that belongs to the method.
Object controller = this.applicationContext.getBean(controllerClass); // Here the code just stops.
System.out.println("Yeah"); // This isn't executed.
try {
List<Object> arguments = new ArrayList<>();
for (Parameter parameter: method.getParameters()) {
// Add arguments based on the parameter type.
}
method.invoke(controller, arguments.toArray(new Object[arguments.size()]));
} catch (Exception exception) {
exception.printStackTrace();
}
}
The code just stops without any exception at the this.applicationContext.getBean(controllerClass);
I found out that when I AutoWire the controllerClass
it for some reason works. It doesn't matter in what class I autowire the controllers. But of course AutoWiring every controller is an ugly fix.
Why does the ApplicationContext.getBean get stuck and how can I fix this?
UPDATE:
I just found out that using the bean name in getBean
also works.
Example:
this.applicationContext.getBean(MainController.class); //Doesn't work
this.applicationContext.getBean("mainController"); // Works
UPDATE:
I forgot to mention something very important(I think): The executeCommand
method is called from a thread, but the thread is spring managed. When I run it without a thread it works, but I really need threads. How can I make beans
work in a thread?