lundi 2 octobre 2017

Spring ApplicationContext getBean only works when AutoWired before

I am creating a route controller structure for commands. Every controller has a @ControlController annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ControlController {
}

The controller should contain method 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 saved in a Map<String, Method>.

Until this far everything work 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 stopt 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?





Aucun commentaire:

Enregistrer un commentaire