Spring无法将bean注入到线程中。

7

1.如何将Spring Bean注入到线程中:

2.如何在Spring Bean内部启动一个线程。

以下是我的代码:

MyThread.java

@Component
public class MyThread implements Runnable {

    @Autowired
    ApplicationContext applicationContext;

    @Autowired
    SessionFactory sessionFactory;

    public void run() {

        while (true) {
            System.out.println("Inside run()");
            try {
                System.out.println("SessionFactory : " + sessionFactory);
            } catch (Exception e) {
                e.printStackTrace();
            }

            try {
                Thread.sleep(10000);

                System.out.println(Arrays.asList(applicationContext.getBeanDefinitionNames()));

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

}

我正在从下面的类中调用run方法(如果我在调用Spring bean内部的线程时遵循错误的方法,请提出建议)。
@Component
public class MyServiceCreationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {

        if (event.getApplicationContext().getParent() == null) {
            System.out.println("\nThread Started");
            Thread t = new Thread(new MyThread());
            t.start();

        }
    }
}

Spring没有在MyThread类上执行依赖注入


首先,您不应该在容器中自己启动线程。理想情况下,您应该有一个 TaskExecutor,将要执行的任务实例(一个RunnableCallable)传递给它来执行。这样,您就可以使用支持在servlet容器中使用线程的方式。接下来,您应该将bean设置为 @Scope("prototype"),并在启动线程之前使用ApplicationContext来获取一个实例。现在,您正在在Spring范围之外自己创建一个新实例。 - M. Deinum
但是你在下面的评论中说,不要创建新的上下文,因为会有内存问题和奇怪的应用程序行为。 - suhas_n
我在哪里说你应该创建一个新的ApplicationContext... 你可以直接将上下文@Autowire到你的MyServiceCreationListener中,并使用该实例获取bean。我从未说过你应该创建一个新实例! - M. Deinum
你能在我的例子中展示一下吗?谢谢! - suhas_n
3个回答

12

您的设置存在一些问题。

  1. 您不应该自己创建和管理线程,Java有很好的特性可以用于此,请使用它们。
  2. 您正在自行创建新的bean实例,并期望Spring知道它们并注入依赖项,这是行不通的。

Spring提供了一个抽象来执行任务,即TaskExecutor。您应该配置一个并使用它来执行您的任务,而不是自己创建线程。

将此添加到您的@Configuration类中。

@Bean
public ThreadPoolTaskExecutor taskExecutor() {
    return new ThreadPoolTaskExecutor();
}

您的MyThread应该使用@Scope("prototype")进行注释。

@Component
@Scope("prototype")
public class MyThread implements Runnable { ... }

现在,您可以将这些 bean 和一个 ApplicationContext 注入到您的 MyServiceCreationListener 中。

@Component
public class MyServiceCreationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private ApplicationContext ctx;
    @Autowired
    private TaskExecutor taskExecutor;        

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {

        if (event.getApplicationContext().getParent() == null) {
            System.out.println("\nThread Started");
            taskExecutor.execute(ctx.getBean(MyThread.class));
        }
    }
}

这将为您提供一个预配置的、全新的MyThread实例,并在由当前TaskExecutor选定的Thread上执行它。


无论您用什么进行配置,如果您愿意,也可以使用xml。 - M. Deinum
@M Deinum,请告诉我以下两点:1)即使我没有在类/bean中使用“@Autowire”注释或XML文件中没有使用它,为什么“private TaskExecutor taskExecutor”会自动装配到我的类/bean中;2)如果我们使用了“@Autowire”注释,Spring根据哪些基础来将对象注入到类/bean中。 - suhas_n
我的错误,忘记在这两个字段上添加@Autowired - M. Deinum
让我们在聊天中继续这个讨论 - suhas_n
我认为最好自动装配ObjectFactory<MyThread> myThreadFactory而不是ApplicationContext cox,在另一个bean中自动装配整个上下文并不好。 - Ales
显示剩余4条评论

0

你的 MyThread 是手动创建的,而不是通过 Spring 上下文 new Thread(new MyThread()); 创建的,因此 Spring 无法注入 bean。

相反,你可以使用静态访问 Spring 上下文的技巧,在上下文中获取所需的 bean(请参见 这里这里)。

或者,你可以使用 ThreadLocalInheritableThreadLocal 存储必要的对象以在线程中使用。


我只想在MyServiceCreationListener中启动MyThread... 我该怎么做呢? - suhas_n

0

你正在创建 Thread t = new Thread(new MyThread());。Spring容器不会注入依赖项,也不会维护bean的生命周期。

例如:

@Component
@Scope("prototype")
public class PrintThread extends Thread{

    @Override
    public void run() {

        System.out.println(getName() + " is running");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getName() + " is running");
    }

}

从Spring上下文中访问线程对象。
public class ApplicationContextUtils implements ApplicationContextAware {
 private static ApplicationContext ctx;

 private static final String USER_THREAD = "printThread";

  @Override
  public void setApplicationContext(ApplicationContext appContext)
      throws BeansException {
    ctx = appContext;

  }

  public static ApplicationContext getApplicationContext() {
    return ctx;
  }

  public static UserService getUserService(){return ctx.getBean(USER_THREAD );}

}

不要为了获取单个bean而创建一个新的上下文...这样你就会重新创建整个应用程序。除非你想要一个消耗内存、行为奇怪的应用程序,否则不要这样做。 - M. Deinum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接