Spring注解【非单例】
花了至少一整天的时间解决了这个问题,必须记录这个纠结的过程,问题不可怕,思路很绕弯。
为了能说清楚自己的问题,我都用例子来模拟。
我有一个类MyThread是这样的:
1 @Service 2 public class MyThread extends Thread {
3 @Autowired
4 MyService myService;
5 ......
6 }
在主线程中有这样一个调用:
1 @Autowired 2 MyThread myThread; 3 ...... 4 public void invoke{ 5 if(condition){ 6 myThread.start(); 7 } 8 } 9 ......
我的invoke存在一个循环调用,此时遇到了第一个问题!
问题一:抛出java.lang.IllegalThreadStateException。
问题一的解决:
1 //@Autowired 2 //MyThread myThread; 3 ...... 4 public void invoke { 5 if(condition){
6 //myThread.start(); 6 MyThread myThread = new MyThread(); 7 myThread.start(); 8 } 9 }
引发的新的问题!
问题二:我用了Spring注解,用new的话无法用注解实现注入,所以myThread中的myService对象抛出空指针。
问题二的解决:放弃了MyThread类,新写了一个类,开始绕弯。
1 @Service 2 public class MyRunable implements Runnable { 3 @Autowired 4 MyService myService; 5 ...... 6 }
相应的,修改主线程中的调用。
1 //@Autowired 2 //MyThread myThread; 3 @Autowired 4 MyRunnable myRunnable; 5 ...... 6 public void invoke{ 7 if(condition){ 8 //MyThread myThread = new MyThread(); 9 //myThread.start(); 10 Thread t = new Thread(myRunnable); 11 t.start(); 12 } 13 } 14 ......
又遇到了新的问题!
我需要对myRunnable线程命名。即在invoke方法中增加这么一行:
1 ...... 2 myRunnable.setName(name);//增加的一行 3 Thread t = new Thread(myRunnable); 4 ......
问题三:后面命名的myRunnable线程名称竟然覆盖前面的命名。
打印myRunnable对象,很容易判断出来这个对象是单例模式,所以每次改动都会影响以前的调用。
查了Spring的相关资料,得到结论,Spring默认管理类的模式就是“单例模式”,我需要改成非单例的。
尝试改动一:
1 //增加了对象使用创建模式的注解 2 @Service 3 @Scope("prototype") 4 public class MyRunable implements Runnable { 5 @Autowired 6 MyService myService; 7 ...... 8 }
运行,问题依旧。然后就卡住了,甚至评估放弃Spring?一路new下去得了……
直到某一刻灵光闪现,发现虽然现在MyRunnble是非单例模式,但是在invoke的那个类中,Spring只在加载这个类时初始化了一个MyRunnable对象啊……
问题三的解决:
1 //@Autowired 2 //MyThread myThread; 3 //@Autowired 4 //MyRunnable myRunnable; 5 ...... 6 public void invoke{ 7 if(condition){ 8 //MyThread myThread = new MyThread(); 9 //myThread.start(); 10 MyRunnable myRunnable = SpringContextUtil.getApplicationContext().getBean("myRunnable",MyRunnable.class); 11 Thread t = new Thread(myRunnable); 12 t.start(); 13 } 14 } 15 ......
这一步需要声明的是SpringContextUtil类有时间需要单独写一篇,先理解原理,就是每次从Spring容器中生成一个全新[scope("prototype")]的对象。
到这里,问题全部解决了……等等!
感觉MyThread类被抛弃好无辜啊!最终改成了这个样子的:
1 MyThread myThread = SpringContextUtil.getApplicationContext().getBean("myThread", MyThread.class);
上善若水,水利万物而不争。