【问题标题】:Spring ApplicationContext getBean only works when AutoWired beforeSpring ApplicationContext getBean 仅在之前使用 AutoWired 时有效
【发布时间】:2017-10-02 15:40:16
【问题描述】:

我正在为命令创建一个路由控制器结构。 每个控制器都有一个@ControlController 注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component // Because @Component all controllers will be spring managed.
public @interface ControlController {
}

控制器应该包含带有@CommandMapping注解的方法:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CommandMapping {
    String value();
}

@CommandMapping 注解的值就是命令。所以当值与被调用的命令相同时,应该调用该方法。

在应用程序启动时,调用以下代码来获取所有@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);
            }
        }
    }
}

此代码将找到所有带有@ControlController 注释的bean,并将循环遍历所有方法以找到@CommandMapping 注释。所有方法都将放在Map&lt;String, Method&gt;中。

到目前为止,一切都很完美。

以下方法用于执行属于命令的正确方法:

/**
 * 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();
    }
}

代码在this.applicationContext.getBean(controllerClass);处停止,没有任何异常

我发现当我自动连接 controllerClass 时,它出于某种原因可以正常工作。我在哪个类中自动连接控制器并不重要。但是当然自动装配每个控制器是一个丑陋的修复。

为什么 ApplicationContext.getBean 卡住了,我该如何解决这个问题?

更新: 我刚刚发现在getBean 中使用bean 名称也可以。 示例:

this.applicationContext.getBean(MainController.class); //Doesn't work

this.applicationContext.getBean("mainController"); // Works

更新: 我忘了提一些非常重要的事情(我认为):executeCommand 方法是从线程调用的,但线程是弹簧管理的。当我在没有线程的情况下运行它时,它可以工作,但我真的需要线程。如何让beans 在线程中工作?

【问题讨论】:

  • @Zorglube ApplicationContext 已经正确注入。
  • this.applicationContext.getBean(MainController.class); //Doesn't work this.applicationContext.getBean("mainController"); // Works 这很奇怪...您是否尝试过使用@Component(value = "MyControllerSpecificName") 设置您的控制器名称?
  • 或者MainController controller = this.applicationContext.getBean(MainController.class);
  • 给组件(控制器)命名是通过命名而不是通过给类命名。我真的很想上课,因为这是我从方法中得到的:Class&lt;?&gt; controllerClass = method.getDeclaringClass();MainController controller = this.applicationContext.getBean(MainController.class); 不起作用。

标签: java spring spring-boot reflection dependency-injection


【解决方案1】:

您可以尝试使用“名称”搜索Controller;此解决方案意味着通过获取注释来查找Controller 的名称。

即:

@Service
@Component(value = "statService")
public class Controller {...} 

public class AnnotationFinder {

    public static String findComponentName(Class cls) {
        for (Annotation annotation : cls.getDeclaredAnnotations()) {
            if (annotation.annotationType().equals(Component.class)) {
                return annotation.value();
            }
        }
        return null;
    }
}

当您获得 @Component 时,您将获得 value 成员和 =>

对象控制器 = this.applicationContext.getBean(AnnotationFinder.findComponentName(controllerClass));

【讨论】:

  • 这个解决方案可行,但我已经找到了导致奇怪行为的部分。
【解决方案2】:

我发现 Web 应用程序也无法正常工作。

问题在于接受连接的循环没有在单独的线程中运行,而只是在组件的@PostConstruct 中运行,因此应用程序从未完全启动,但服务器(My SocketServer)正在运行。

由于应用程序没有完全启动,bean 没有按预期工作。所以它与我发布的代码无关......

我希望其他人仍然可以知道我的答案。

【讨论】:

    猜你喜欢
    • 2014-03-17
    • 1970-01-01
    • 1970-01-01
    • 2015-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多