2015-02-02 4 views
6

내 프로젝트에서 Spring의 @Async 주석이 예상대로 작동하는지 테스트하려고합니다. 그러나 그렇지 않습니다.spring testing @async method

@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); 

    } 
} 

당신은 내가 현재의 thread의 이름을 인쇄하고 있습니다 볼 수 있듯이 :

나는이 테스트가 있습니다.

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 방법의 내부에서, 현재의 thread의 이름을 인쇄하고있어, 그리고 이름은 주, 이전과 동일 : @Async 방법을 포함하는 클래스입니다. 그래서 작동하지 않습니다.

GlobalConfiguration은 다음과 같습니다

@Configuration 
@EnableAspectJAutoProxy 
@EnableTransactionManagement 
@ComponentScan({ 
     "com.shn.configurations", 
...some packages... 
}) 
public class GlobalConfiguration {...} 

그리고 지정된 패키지 중 하나가 activityExecutor 콩을 가지고 내부 : 내가 잘못

@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; 
    } 
} 

을하고있어 무엇?

+0

내 호기심 때문에'MessageBusUtil.addQueue'는 내가 가정 한대로 할 수 있습니까? –

답변

3

까다 롭습니다.

프록시를 통해 비동기 동작이 추가되었습니다.

스프링은 실제 개체를 래핑하고 별도의 스레드에서 실제 호출을 수행하는 프록시를 제공합니다.

그것은 봄 세계에서

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); 

이제 (CGLIB 또는 JDK 프록시와 봄 핸들러 동적으로 수행이 대부분 제외)이 같이 보입니다, 당신은 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); 

을 메시지를 보낼 때, 그래서 당신은 프록시 비동기 동작을하지 않는 실제 객체로 전송하고 있습니다.

풀 스프링 솔루션은 MessabeBus (및/또는 대기열)를 완전한 프로세스 (프록시, 자동 와이어 된, 초기화 된) 빈을 주입 할 수있는 스프링 빈으로 만드는 것입니다.현실에서


super 생성자가 호출 될 수 있기 때문에 위의 ProxyListener 사실도 버스에 자신을 추가 할 수 있도록 CGLIB 프록시는, 당신의 유형의 정말 서브 클래스이기 때문에. MessageListener 중 하나만 MKTDestinationNames.ACTIVITY_REGISTRY과 같은 키로 등록 할 수 있습니다. 그렇지 않은 경우 설명을 위해 해당 코드를 더 많이 표시해야합니다. 당신이 activityMessageListener 프록시에 대한 참조를 보유해야하기 때문에

activityMessageListener.doReceive(message); 

는 당신이 비동기 동작을 볼 수 할 경우 테스트에서


.

관련 문제