Spring 3.0에서의 비동기 메소드 호출.
비동기 호출을 사용해야 하는 일이 발생해서, 알아봄. @_@
Spring 3.0에서 task scheduling 과 asynchronous method드 호출을 지원하기 위한 어노테이션들이 추가된다.
이중에서 @Aysnc에 대해서 알아보고 사용예를 살펴보자.
The @Async annotation
The @Async 어노테이션은 비동기적으로 메소드를 호출할 수 있도록 해준다. 즉 호출하는 쪽에서 호출을 즉시 반환한다.(비동기 이기 때문에 호출한후 기다리지 않고 바로 반환한다는 뜻인듯)
@Async 의 한가지 사용예에는 사용자가 회원가입과 가입 완료 메일을 보내는 예가 있고 아니면 이와 비슷한 사용자의 작업을 정지 시키지 않는 작업을 들 수 있다.
예를 살펴 봄으로서 좀더 명확하게 설명을 해보도록 하자.
applicationContext.xml
<?xml version=”1.0″ encoding=”UTF-8″?>
<beans xmlns=”http://www.springframework.org/schema/beans” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns:p=”http://www.springframework.org/schema/p” xmlns:context=”http://www.springframework.org/schema/context”
xsi:schemaLocation=”
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd”>
<context:component-scan base-package=”cs”/>
</beans>
테스트 코드를 살펴보면
TestService.java
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cs.service.RegularService;
public class TestService {
public static void main(String args[]){
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {
“applicationContext.xml”});
RegularService regService = (RegularService) appContext.getBean(“regularService”);
regService.registerUser(“Skill-Guru”);
}
}
ReqularService.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cs.async.MailUtility;
@Service
public class RegularService {
@Autowired
private MailUtility mailUtility ;
public void registerUser(String userName){
System.out.println(” User registration for “+userName +” complete”);
mailUtility.sendMail(userName);
System.out.println(” Registration Complete. Mail will be send after 5 seconds “);
}
}
MailUtility.java
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class MailUtility {
@Async
public void sendMail(String name){
System.out.println(” I Will be formatting html mail and sending it “);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(” Asynchronous method call of send email — Complete “);
}
}
TestService.java 테스크를 실행시켜 보면 출력은 아래와 같은 것이다.
I Will be formatting html mail and sending it
Asynchronous method call of send email — Complete
Registration Complete. Mail will be send after 5 seconds
잠시 생각해보면, 위의 결과는 비동기 호출로 보이지 않는다. 뭐가 잘 못된 것일까?
applicationContext.xml에서 <task:annontaion-driven /> 설정을 빼먹었다.
수정된 applicationContext.xml
<?xml version=”1.0″ encoding=”UTF-8″?>
<beans xmlns=”http://www.springframework.org/schema/beans” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns:p=”http://www.springframework.org/schema/p” xmlns:context=”http://www.springframework.org/schema/context”
xmlns:task=”http://www.springframework.org/schema/task”
xsi:schemaLocation=”
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd“>
<context:component-scan base-package=”cs”/>
<task:annotation-driven/>
</beans>
굵게 처리된 부분을 확인하자.
테스트를 한번도 돌려 보면 출력은 아래와 같다.
Registration Complete. Mail will be send after 5 seconds
I Will be formatting html mail and sending it
보는것 처럼 5초 후에 프로그램이 종료되지 않을 것이다.
registerUser가 sendMail을 호출 한 후 바로 컨트롤을 넘겨주는것을 볼 수 있다.
참고!!
위 예제는 테스트 케이스가 아닌 테스트 클래스를 통해 진행 되었기 때문에 문제가 없다.
테스트 케이스로 해서 진행 할 경우에는 비동기의 특징인 반환을 바로 해버리기 때문에 sleep이후에 문자가 출력되지 않고테스트 케이스가 종료되는 것을 볼 수 있다.
이 경우에는 테스트 코드가 종료되고 별로도 sleep를 넣어 줘서, 테스트가 종료되지 않도록 하여 thread가 완료 될 수 있는것을 확인 하도록 하자.(말 그대로 확인만.. ci서버가 있을 경우에 테스트 수행 시간이 오래 걸릴 수 있다.)
@Sync가 적용된 메서드의 리턴 타입은 void 나 Feature 타입이어야 한다.
아니면 비동기로 동작하지 않는다.