继承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注解简化实现:
- 在你的Service类上添加
@EnableAsync开启异步支持 - 将数据库操作抽到单独的Service类中(保证事务和Bean注入正常)
- 标记异步方法,让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
相关产品推荐
相关产品推荐

