代码改变世界

多线程Spring注入对象问题的四种解法

2023-05-09 23:55  youxin  阅读(372)  评论(0编辑  收藏  举报

当我们使用多线程时,想给线程注入一个service,但是运行时发现service总是为null。
举个 :

public class MyThread implements Runnable {

@Autowired
Service application;

public void run() {}
}
 
原因
new Thread不在spring容器中,也就无法获得spring中的bean对象。根据SpringBean的生命周期,能够被Spring管理的Bean都是在扫描范围内并且有 @Componen、@Service、@Configuration 等注释,在使用时才能用IOC注入对象。如果使用new 关键字手动生成的对象,Spring感知不到,无法管理。

解决办法
主要有四种,分为两种思路:

继续使用new 关键字
依赖通过构造函数传入
依赖通过 ApplicationContext 手动获取
使用 @Configurable 注解 管理线程内部的依赖
不使用new 关键字
配合 @Scope("prototype") 使用SpringBean的方式创建线程
一 最简单的方法 – 通过构造器传入依赖
public class TaskComments implements Callable<List<String>> {

private RedisClient redisClient;
private TaskComments(RedisClient redisClient){
this.redisClient = redisClient;
}

@Override
public List<String> call() throws Exception {
//业务代码
}
}
 
二 在线程内部使用手动获取的方式
配置类

@Component
public class BeanContext implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        BeanContext.applicationContext = applicationContext;
    }

    public static <T> T getBean(Class<T> clz) throws BeansException {
        return (T) applicationContext.getBean(clz);
    }
}

使用:

public class TaskComments implements Runable<List<String>> {

    private RedisClient redisClient = BeanContext.getBean(RedisClient.class);
    
    @Override
    public void run() throws Exception {
        //业务代码
    }
}

 

三 使用@Configurable
注释的官方解释:
不是基于Spring的应用程序中使用的所有类型(bean)都是Spring管理的。有时,我们需要将一些依赖注入到一些非Spring管理的类型中。最常见的例子是在实体类中注入服务或DAO层。现在实体类的实例使用 new 创建,也即它们不是被Spring管理的。现在我们想要在这个类型中注入对应的Service或DAO类型,被注入类型是被Spring管理的,以上这些操作可以在 @Configurable 的帮助下完成。

@Configurable
public class TaskComments implements Runnable{
    @Autowired
    private Service service;
        
    @Override
    public void run() throws Exception {
        //业务代码
    }
}

四 使用@Scope(“prototype”)
这个注释的意思是在注入Bean时不采用Spring默认的单例模式,每次新创建一个对象。由于在创建线程时,通常是用线程池提交Runable任务,两个都是使用new 关键词创建的,因此两个都得改造。但是线程池对象只需要一个,所以采用传统的单例模式即可。

自己的configuration 类 新增一个bean(也可以使用xml 的方式实现,只要是Spring管理的就好)
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
return new ThreadPoolTaskExecutor();
}
 
使用@Scope(“prototype”)修饰线程任务
@Component
@Scope("prototype")
public class MyThread implements Runnable {
@Autowired
private Service service;

@Override
public void run() throws Exception {
//业务代码
}
}
 
使用
@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");
//注意这里线程池和线程都没有使用 new 关键字新建
taskExecutor.execute(ctx.getBean(MyThread.class));
}
}
}
 

原文链接:https://blog.csdn.net/h2453532874/article/details/108480673

 

Spring线程池结合Spring托管Bean。


本例仍然使用ThreadPoolTaskExecutor类,并使用@Component注释声明Spring的托管Bean。
下面的例子PrintTask2是Spring的托管Bean,使用@Autowired注释简化代码。

 

package com.chszs.thread;
 
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
 
@Component
@Scope("prototype")
public class PrintTask2 implements Runnable {
        String name;
 
        public void setName(String name) {
                this.name = name;
        }
        
        @Override
        public void run(){
                System.out.println(name + " is running.");
                try{
                        Thread.sleep(5000);
                }catch(InterruptedException e){
                        e.printStackTrace();
                }
                System.out.println(name + " is running again.");
        }
}
package com.chszs.config;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
@Configuration
@ComponentScan(basePackages="com.chszs.thread")
public class AppConfig {
        @Bean
        public ThreadPoolTaskExecutor taskExecutor(){
                ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
                pool.setCorePoolSize(5);
                pool.setMaxPoolSize(10);
                pool.setWaitForTasksToCompleteOnShutdown(true);
                return pool;
        }
}
package com.chszs;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
import com.chszs.config.AppConfig;
import com.chszs.thread.PrintTask2;
 
public class App2 {
        public static void main(String[] args) {
                ApplicationContext ctx = 
            new AnnotationConfigApplicationContext(AppConfig.class);
                ThreadPoolTaskExecutor taskExecutor =
            (ThreadPoolTaskExecutor)ctx.getBean("taskExecutor");
                
                PrintTask2 printTask1 = (PrintTask2)ctx.getBean("printTask2");
                printTask1.setName("Thread 1");
                taskExecutor.execute(printTask1);
                
                PrintTask2 printTask2 = (PrintTask2)ctx.getBean("printTask2");
                printTask2.setName("Thread 2");
                taskExecutor.execute(printTask2);
                
                PrintTask2 printTask3 = (PrintTask2)ctx.getBean("printTask2");
                printTask3.setName("Thread 3");
                taskExecutor.execute(printTask3);
                
                for(;;){
                        int count = taskExecutor.getActiveCount();
                        System.out.println("Active Threads : " + count);
                        try{
                                Thread.sleep(1000);
                        }catch(InterruptedException e){
                                e.printStackTrace();
                        }
                        if(count==0){
                                taskExecutor.shutdown();
                                break;
                        }
                }
        }
 
}

参考:

https://developer.aliyun.com/article/671475