版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Andyzhu_2005/article/details/81835733
问题描述
在spring中,如果需要在异步线程中注入bean,会发现bean是空的情况。原因据说是spring bean 出于线程安全考虑,不得注入bean至线程类(Runnable)。
代码如下:
public class DealThreadTask implements Runnable{
@Autowired
private DealService dealService;
@Override
public void run() {
// DealService dealService=holder.getBean("dealService");
System.out.println("dealService-->"+dealService);
dealService.deal("andy", "李琳", 100d);
}
}
在controller层中,对上述的service进行调用。
public static void main(String[] args){
ApplicationContext context=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-aware.xml");
DealThreadTask task=new DealThreadTask();
new Thread(task).start();
}
运行结果:
dealService-->null
Exception in thread "Thread-1" java.lang.NullPointerException
at com.test.spring.tx.multi.DealThreadTask.run(DealThreadTask.java:38)
at java.lang.Thread.run(Thread.java:744)
说明spring在DealThreadTask中未能将dealService注入进去。
解决方法
Spring API 中有ApplicationContextAware 这个接口,实现了这个接口的类,可以在容器初始化完成中获得容器,从而可以获得容器中所有的bean。
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextHolder.applicationContext=applicationContext;
System.out.println("applicationContext---->"+applicationContext);
}
public static <T> T getBean(Class<T> clazz){
return applicationContext.getBean(clazz);
}
public static <T> T getBean(String name) {
if (applicationContext==null) {
System.out.println("applicationContext为空");
}
return (T) applicationContext.getBean(name);
}
}
然后,在xml配置文件中,需要将这个类配置进去。
<bean id="applicationContextHolder" class="com.test.spring.tx.multi.ApplicationContextHolder"></bean>
这样,在异步线程中的DealThreadTask 中,通过手动的applicationContextHolder的getBean方法,就可以获取所需要的bean。
public class DealThreadTask implements Runnable{
@Autowired
private ApplicationContextHolder holder;
@Override
public void run() {
DealService dealService=holder.getBean("dealService");
System.out.println("dealService-->"+dealService);
dealService.deal("andy", "李琳", 100d);
}
}
提示
上述的代码中
public static void main(String[] args){
ApplicationContext context=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-aware.xml");
DealThreadTask task=new DealThreadTask();
new Thread(task).start();
}
用了main方法,而没有用junit的test注解进行测试,是因为junit是不会等待异步线程执行完然后结束,而是junit自己本线程的代码执行完就结束了。由于代码中设计到数据库操作,因此如果简单的用junit进行测试,可能的结果是测试完成,但是数据库操作还没有进行。如果一定要用junit进行测试,可以用其他的手段,比如thread的join操作,或者在junit的测试方法中等待键盘输入才结束,这些都是为了让异步线程执行完后才结束junit测试。