You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

继承Thread的类中@Autowired注入Bean为空的问题求助

继承Thread的类中Autowired注入EntityManager出现空指针问题

我有一个继承Thread的类,想要用EntityManager修改数据库,但注入的Bean一直报空指针。


1. @Service类中的ejecuteThreadsMap方法

private void ejecuteThreadsMap(Map<Integer, List<McDocsEntity>> map) throws Exception {
    List<ChangeNameThread> listThreads = new ArrayList<ChangeNameThread>();
    int num_threads = Integer.valueOf(threads);
    for (int i = 0; i < num_threads; i++) {
        listThreads.add(new ChangeNameThread());
    }

    ArrayList<Integer> list = new ArrayList<Integer>(map.keySet());
    int actual = 0;
    while (actual < list.size()) {
        if (num_threads > list.size())
            num_threads = list.size();
        for (int i = 0; i < num_threads; i++) {
            if (!listThreads.get(i).isAlive()) {
                System.out.println("actual=" + (actual + 1) + "/" + list.size() + ", porcentaje:" + String.format("%.2f", ((float) ((actual + 1) / (float) list.size()) * 100.0)));
                listThreads.set(i, new ChangeNameThread());
                
                listThreads.get(i).setListaDocs(map.get(list.get(actual)));
                listThreads.get(i).setId(list.get(actual) + "");
                listThreads.get(i).start();
                actual++;
                if (actual == list.size()) {
                    System.out.println ("**** Se completan " + actual + " elementos");
                    break;
                }
            }
        }
    }
}

2. ChangeNameThread类代码

@Component
@ComponentScan
public class ChangeNameThread extends Thread {
    
    private String id;
    boolean isCompleted = false;
    private List<McDocsEntity> listaDocs;
    
    @Autowired
    private EntityManagerCore coreEntityManager; // 此处报空指针
    
    ChangeNameThread() {}

    public List<McDocsEntity> getListaDocs() {
        return listaDocs;
    }

    public void setListaDocs(List<McDocsEntity> listaDocs) {
        this.listaDocs = listaDocs;
    }

    public void run() {
        downloadDocs();
        if (isCompleted)
            System.out.println("Termino el hilo["+this.currentThread().getId()+"-"+this.currentThread().getName()+"] para multilateralId:" + multilateralId );
    }

    public boolean getIsCompleted() {
        return isCompleted;
    }

    public String getMultilateralId() {
        return multilateralId;
    }

    public void setMultilateralId(String multilateralId) {
        this.multilateralId = multilateralId;
    }

    public void downloadDocs() {
        System.err.println("multilateral id:" + multilateralId);
        try {
            System.out.println("Comienza proceso");
            for (McDocsEntity mcDocsEntity : listaDocs) {
                changeName(mcDocsEntity);
            }
            isCompleted = true;
        } catch (Exception e) {
            System.out.println("Error de gestión de proceso" + System.lineSeparator() + e.getMessage());
        }
    }
    
    @Transactional(readOnly = true)
    public void changeName(McDocsEntity mcDocsEntity) {
        try {
            coreEntityManager.updateDocument(mcDocsEntity.getid,mcDocsEntity.getnewName); // 空指针异常
        } catch (Exception e) {
            System.err.println(e);
        } 
    }
}

3. 尝试用ApplicationContext自动装配后的修改(仍报错)

注入ApplicationContext

@Autowired
private ApplicationContext applicationContext;

修改后的ejecuteThreadsMap方法

private void ejecuteThreadsMap(Map<Integer, List> map) throws Exception {
    List listThreads = new ArrayList();
    int num_threads = Integer.valueOf(threads);
    for (int i = 0; i < num_threads; i++) {
        listThreads.add(new ChangeNameThread());
    }

    ArrayList<Integer> list = new ArrayList<Integer>(map.keySet());
    int actual = 0;
    while (actual < list.size()) {
        if (num_threads > list.size())
            num_threads = list.size();
        for (int i = 0; i < num_threads; i++) {
            if (!listThreads.get(i).isAlive()) {
                System.out.println("actual=" + (actual + 1) + "/" + list.size() + ", porcentaje:" + String.format("%.2f", ((float) ((actual + 1) / (float) list.size()) * 100.0)));
                listThreads.set(i, new ChangeNameThread());
                applicationContext.getAutowireCapableBeanFactory().autowireBean(list.get(actual));
                listThreads.get(i).setListaDocs(map.get(list.get(actual)));
                listThreads.get(i).setId(list.get(actual) + "");
                listThreads.get(i).start();
                actual++;
                if (actual == list.size()) {
                    System.out.println ("**** Se completan " + actual + " elementos");
                    break;
                }
            }
        }
    }
}

问题原因及解决方案

核心原因

直接new ChangeNameThread()创建的实例不受Spring容器管理,@Autowired注解不会生效,所以coreEntityManager为空。另外你尝试用autowireBean时传错了对象——传的是list.get(actual)(一个Integer值),而非新创建的线程实例,导致注入失败。

正确解决步骤

方案1:修复手动注入的错误

如果你坚持用手动创建Thread的方式,要确保给新线程实例做Spring注入:

// 替换原代码中listThreads.set(i, new ChangeNameThread());的部分
ChangeNameThread newThread = new ChangeNameThread();
// 给新创建的线程实例注入Spring Bean
applicationContext.getAutowireCapableBeanFactory().autowireBean(newThread);
listThreads.set(i, newThread);

方案2:改用Spring异步注解(更推荐)

完全不用手动管理线程,用Spring的@Async注解简化实现:

  1. 在你的Service类上添加@EnableAsync开启异步支持
  2. 将数据库操作抽到单独的Service类中(保证事务和Bean注入正常)
  3. 标记异步方法,让Spring自动管理线程池

示例代码:

@Service
@EnableAsync
public class YourService {
    @Autowired
    private DocumentService documentService;

    @Async
    public void processDocs(List<McDocsEntity> docs) {
        for (McDocsEntity doc : docs) {
            documentService.changeName(doc);
        }
    }

    // 简化后的原方法
    private void ejecuteThreadsMap(Map<Integer, List<McDocsEntity>> map) {
        for (List<McDocsEntity> docs : map.values()) {
            processDocs(docs);
        }
    }
}

@Service
public class DocumentService {
    @Autowired
    private EntityManagerCore coreEntityManager;

    // 修改操作不要加readOnly=true
    @Transactional
    public void changeName(McDocsEntity mcDocsEntity) {
        coreEntityManager.updateDocument(mcDocsEntity.getid(), mcDocsEntity.getnewName());
    }
}

方案3:实现Runnable接口替代继承Thread

让线程类实现Runnable,由Spring容器管理实例:

@Component
public class ChangeNameTask implements Runnable {
    @Autowired
    private EntityManagerCore coreEntityManager;
    
    private List<McDocsEntity> listaDocs;
    private String multilateralId;

    // 提供setter方法传递参数
    public void setListaDocs(List<McDocsEntity> listaDocs) {
        this.listaDocs = listaDocs;
    }

    public void setMultilateralId(String multilateralId) {
        this.multilateralId = multilateralId;
    }

    @Override
    public void run() {
        // 执行下载和修改逻辑
        for (McDocsEntity mcDocsEntity : listaDocs) {
            changeName(mcDocsEntity);
        }
    }

    @Transactional
    public void changeName(McDocsEntity mcDocsEntity) {
        coreEntityManager.updateDocument(mcDocsEntity.getid(), mcDocsEntity.getnewName());
    }
}

然后在Service中从Spring容器获取实例:

@Autowired
private ApplicationContext applicationContext;

private void ejecuteThreadsMap(Map<Integer, List<McDocsEntity>> map) {
    // ...
    ChangeNameTask task = applicationContext.getBean(ChangeNameTask.class);
    task.setListaDocs(...);
    new Thread(task).start();
    // ...
}

额外注意点

你的changeName方法加了@Transactional(readOnly = true),但这是修改操作,需要去掉readOnly属性,否则事务可能不生效。


内容的提问来源于stack exchange,提问作者Sebastian Ruiz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 20:15:40