弹簧测试@async方法
我试图测试Spring的@Async
注释是否在我的项目上按预期工作。 但事实并非如此。
我有这个测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = GlobalConfiguration.class)
public class ActivityMessageListenerTest {
@Autowired
private ActivityMessageListener activityMessageListener;
private Long USER_ID = 1l;
private Long COMPANY_ID = 2l;
private Date DATE = new Date(10000000);
private String CLASSNAME = "className";
private Long CLASSPK = 14l;
private Integer TYPE = 22;
private String EXTRA_DATA = "extra";
private Long RECIVED_USER_ID = 99l;
@Before
public void setup() throws Exception {
}
@Test
public void testDoReceiveWithException() throws Exception {
System.out.println("Current thread " + Thread.currentThread().getName());
Map<String, Object> values = new HashMap();
values.put(ActivityMessageListener.PARAM_USER_ID, USER_ID);
values.put(ActivityMessageListener.PARAM_COMPANY_ID, COMPANY_ID);
values.put(ActivityMessageListener.PARAM_CREATE_DATE, DATE);
values.put(ActivityMessageListener.PARAM_CLASS_NAME, CLASSNAME);
values.put(ActivityMessageListener.PARAM_CLASS_PK, CLASSPK);
values.put(ActivityMessageListener.PARAM_TYPE, TYPE);
values.put(ActivityMessageListener.PARAM_EXTRA_DATA, EXTRA_DATA );
values.put(ActivityMessageListener.PARAM_RECEIVED_USER_ID, RECIVED_USER_ID);
Message message = new Message();
message.setValues(values);
MessageBusUtil.sendMessage(MKTDestinationNames.ACTIVITY_REGISTRY, message);
}
}
正如你所看到的,我正在打印当前线程的名称。 包含@Async
方法的类是:
public class ActivityMessageListener extends BaseMessageListener {
public static final String PARAM_USER_ID = "userId";
public static final String PARAM_COMPANY_ID = "companyId";
public static final String PARAM_CREATE_DATE = "createDate";
public static final String PARAM_CLASS_NAME = "className";
public static final String PARAM_CLASS_PK = "classPK";
public static final String PARAM_TYPE = "type";
public static final String PARAM_EXTRA_DATA = "extraData";
public static final String PARAM_RECEIVED_USER_ID = "receiverUserId";
public ActivityMessageListener() {
MessageBusUtil.addQueue(MKTDestinationNames.ACTIVITY_REGISTRY, this);
}
@Override
@Async(value = "activityExecutor")
public void doReceive(Message message) throws Exception {
System.out.println("Current " + Thread.currentThread().getName());
if (1> 0)
throw new RuntimeException("lalal");
Map<String, Object> parameters = message.getValues();
Long userId = (Long)parameters.get(ActivityMessageListener.PARAM_USER_ID);
Long companyId = (Long)parameters.get(ActivityMessageListener.PARAM_COMPANY_ID);
Date createDate = (Date)parameters.get(ActivityMessageListener.PARAM_CREATE_DATE);
String className = (String)parameters.get(ActivityMessageListener.PARAM_CLASS_NAME);
Long classPK = (Long)parameters.get(ActivityMessageListener.PARAM_CLASS_PK);
Integer type = (Integer)parameters.get(ActivityMessageListener.PARAM_TYPE);
String extraData = (String)parameters.get(ActivityMessageListener.PARAM_EXTRA_DATA);
Long receiverUserId = (Long)parameters.get(ActivityMessageListener.PARAM_RECEIVED_USER_ID);
ActivityLocalServiceUtil.addActivity(userId, companyId, createDate, className, classPK, type, extraData, receiverUserId);
}
}
在这里,我在@Async
方法中打印当前线程的名称,名称与之前的main相同。 所以它不工作。
GlobalConfiguration是:
@Configuration
@EnableAspectJAutoProxy
@EnableTransactionManagement
@ComponentScan({
"com.shn.configurations",
...some packages...
})
public class GlobalConfiguration {...}
并且在其中一个指定的包中有activityExecutor bean:
@Configuration
@EnableAsync(proxyTargetClass = true)
public class ExecutorConfiguration {
@Bean
public ActivityMessageListener activityMessageListener() {
return new ActivityMessageListener();
}
@Bean
public TaskExecutor activityExecutor()
{
ThreadPoolTaskExecutor threadPoolTaskExecutor =
new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(10);
threadPoolTaskExecutor.setMaxPoolSize(10);
threadPoolTaskExecutor.setQueueCapacity(100);
return threadPoolTaskExecutor;
}
}
我做错了什么?
棘手。
异步行为通过代理添加。
Spring为您提供了一个包装实际对象并在单独的线程中执行实际调用的代理。
它看起来像这样(除了大多数这是用CGLIB或JDK代理和Spring处理程序动态完成的)
class ProxyListener extends ActivityMessageListener {
private ActivityMessageListener real;
public ProxyListener(ActivityMessageListener real) {
this.real = real;
}
TaskExecutor executor; // injected
@Override
public void doReceive(Message message) throws Exception {
executor.submit(() -> real.doReceive(message)); // in another thread
}
}
ActivityMessageListener real = new ActivityMessageListener();
ProxyListener proxy = new ProxyListener(real);
现在,在Spring世界中,您需要引用proxy
对象,而不是ActivityMessageListener
。 那是
ActivityMessageListener proxy = applicationContext.getBean(ActivityMessageListener.class);
会返回对ProxyListener
的引用。 然后,通过多态性,调用doReceive
将转到重写的Proxy#doReceive
方法,该方法将通过委托调用ActivityMessageListener#doReceive
,您将获得异步行为。
但是,你处于半个春天的世界。
这里
public ActivityMessageListener() {
MessageBusUtil.addQueue(MKTDestinationNames.ACTIVITY_REGISTRY, this);
}
参考this
实际上指的是真正的ActivityMessageListener
,而不是代理。 所以,大概,你在这里把你的信息发送到巴士上
MessageBusUtil.sendMessage(MKTDestinationNames.ACTIVITY_REGISTRY, message);
您将它发送给没有代理异步行为的真实对象。
完整的Spring解决方案应该是让MessabeBus(和/或它的队列)成为Spring bean,你可以在其中注入完整的进程(代理,自动装载,初始化)bean。
实际上,由于CGLIB代理实际上只是类型的子类,所以上面的ProxyListener
实际上也会将它自己添加到总线,因为super
构造函数将被调用。 似乎只有一个MessageListener
可以使用一个键注册自己,例如MKTDestinationNames.ACTIVITY_REGISTRY
。 如果不是这种情况,你必须展示更多的代码来解释。
在你的测试中,如果你这样做
activityMessageListener.doReceive(message);
您应该看到自activityMessageListener
应该持有对代理的引用以来的异步行为。