gpt4 book ai didi

Spring测试@async方法

转载 作者:行者123 更新时间:2023-12-02 06:59:56 24 4
gpt4 key购买 nike

我正在尝试测试 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相同。所以它不起作用。

全局配置是:

@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 方法,该方法将通过委托(delegate)调用 ActivityMessageListener#doReceive ,您将得到你的异步行为。

但是,你处于半个 Spring 世界中。

这里

public ActivityMessageListener() {
MessageBusUtil.addQueue(MKTDestinationNames.ACTIVITY_REGISTRY, this);
}

引用this实际上是指真正的ActivityMessageListener,而不是代理。因此,大概当您在此处的公交车上发送消息时

MessageBusUtil.sendMessage(MKTDestinationNames.ACTIVITY_REGISTRY,      message);

您将其发送到真实对象,该对象不具有代理异步行为。

完整的 Spring 解决方案是让 MessabeBus(和/或其队列)成为 Spring bean,您可以在其中注入(inject)完整的进程(代理、 Autowiring 、初始化)bean。

<小时/>

实际上,由于 CGLIB 代理实际上只是类型的子类,因此上面的 ProxyListener 实际上也会将自身添加到总线,因为将调用 super 构造函数。似乎只有一个 MessageListener 可以使用 key 注册自己,例如 MKTDestinationNames.ACTIVITY_REGISTRY。如果情况并非如此,您就必须显示更多该代码以进行解释。

<小时/>

在你的测试中,如果你这样做

activityMessageListener.doReceive(message);

您应该看到异步行为,因为 activityMessageListener 应该保存对代理的引用。

关于Spring测试@async方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28287987/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com