【问题标题】:Not able to get spring service bean in Thread无法在 Thread 中获取 spring 服务 bean
【发布时间】:2017-10-24 11:29:05
【问题描述】:

我有一个 Spring Web 应用程序,其中有多个 Service 和 DAO 类。在正常的 crud 操作中,一切正常。但是我在线程中注入依赖时遇到了问题。

在我的应用程序中,我需要创建一个线程并按需执行它。为此,我在控制器类中创建了如下线程。

TestThread testThread = new TestThread();
Thread thread = new Thread(testThread);
thread.start(); 

在我的线程中,我试图获得如下所示的依赖项。

WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();        
TestService testService = (TestService) context.getBean("myService");

我的服务类如下所示。

@Service("myService")
public class TestServiceImpl implements TestService {
    some methods...
}

但每次我都遇到异常。

Exception in thread "Thread-21" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myService' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:685)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1199)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1081)

【问题讨论】:

  • 不要自己创建线程,可以使用@Async 代替。
  • 我为什么要使用@Async 而不是创建线程。
  • 这是因为 Spring 检测了它操作的所有内容。如果你自己实例化一个新线程,那不关 Spring 的事。你不能创建一个名为 myTestThread 的 Spring bean,它会被 Spring 正确和完全初始化,包括你的线程中的服务?
  • 如果我需要创建多个Callable线程并在前一个线程成功返回后一个一个执行?我能做到吗?
  • ...显然“myService”(真的)不在您的“当前 Web 应用程序上下文”中(可能在另一个(例如根)上下文中!??;)

标签: java spring multithreading


【解决方案1】:

建议您实现一个Runnable 接口,而不是实现您自己的Thread

class MyTask implements Runnable {
    @Override
    public void run() {
         //your logic is here
    }
}

这个类你甚至可以创建a prototype bean(例如使用@Scope("prototype"))。在这种情况下,每次要求 bean 注入所有依赖项时,spring 都会创建一个 MyTask 的新实例。您可以通过 setter 方法完成任何因请求而异的额外初始化。

任务实例完全初始化后,您可以通过多种不同方式运行它。其中最简单的是手动实例化专用Thread

MyTask task = context.getBean("myTask");
//additional initialization
Thread taskRunner = new Thread(task);
taskRunner.start();

但它不符合你的要求

在前一个线程成功返回后,一一执行

为此,您可以使用Executor 服务。你可以用 spring 注入它或者在你的控制器中实例化一个:

Executor executor = Executors.newFixedThreadPool(1);

然后执行您的任务将如下所示:

MyTask task = context.getBean("myTask");
//additional initialization
executor.execute(task); //enqueues the task for future execution

如果您在 Java 8 上运行它,您可以使用 lambda 以避免实现 MyTask

executor.execute(() -> {
    // here you have access to all the injected beans of the controller
    // as well as to arguments of the handler method
});

【讨论】:

    【解决方案2】:

    正如另一个所说,如果您自己实例化一个新线程,它将脱离 Spring 业务。

    您可以在 Spring 上下文中做两件事。首先是声明一个原型组件线程,并要求它在需要时弹出:

    @Component
    @Scope("prototype")
    public class CustomThread extends Thread{
    
        @Override
        public void run() {
            System.out.println(“Thread is running");
        }
    
    }
    

    由于它是作用域原型,每次你向spring上下文请求它时,每次都会创建一个新实例。

    如果您不喜欢这种方法,您可以将 taskExecutor 定义为 spring bean,然后提交您自定义的实现 Runnable 接口的 CustomTask

    public class CustomTask implements Runnable {
    
        @Override
        public void run() {
            System.out.println("CustomTask is running");
        }
    
    }
    

    这可以是任务执行器的配置:

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        return executor;
    }
    

    如果您愿意,可以查看涵盖这些情况的帖子 Spring and Java Thread example

    【讨论】:

      猜你喜欢
      • 2018-06-13
      • 2018-04-03
      • 2023-03-28
      • 1970-01-01
      • 2015-06-11
      • 1970-01-01
      • 2018-05-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多