JobPersistenceException: Unable to serialize JobDataMap
👉 해당 포스트를 읽는데 도움을 줍니다.
1. 문제 상황
Quartz Clustering
을 구현하면서 다음과 같은 에러를 만났습니다.
BlogServiceImpl 클래스 직렬화(serialization) 에러
Caused by: java.io.NotSerializableException: Unable to serialize JobDataMap for insertion into database because the value of property 'blogService' is not serializable: blog.in.action.service.impl.BlogServiceImpl
at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.serializeJobData(StdJDBCDelegate.java:3083)
at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.updateJobDetail(StdJDBCDelegate.java:647)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1115)
...
에러 로그를 보면 BlogServiceImpl
클래스 정보를 데이터베이스에 저장하기 위한 직렬화 작업이 실패한 것으로 보입니다.
문제가 발생하는 클래스에 Serializable
인터페이스를 추가하여도 MyBatis의 SqlSessionTemplate
클래스를 직렬화할 수 없다는 에러가 발생합니다.
결국 같은 문제에 직면하게 됩니다.
SqlSessionTemplate 클래스 직렬화(serialization) 에러
Caused by: java.io.NotSerializableException: Unable to serialize JobDataMap for insertion into database because the value of property 'blogService' is not serializable: org.mybatis.spring.SqlSessionTemplate
at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.serializeJobData(StdJDBCDelegate.java:3083)
at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.updateJobDetail(StdJDBCDelegate.java:647)
at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeJob(JobStoreSupport.java:1115)
...
문제 상황을 정리해보면 다음과 같았습니다.
QuartzJobBean
을 상속한 클래스에서@Autowired
키워드를 사용한 빈(bean) 주입이 안된다.Quartz Clustering
구축시JobDataMap
파라미터와Setter
메소드를 사용하여 빈(bean) 주입 시 에러가 발생한다.- 비즈니스 로직에서 데이터베이스 기능을 사용하기 위해선
ServiceImpl
빈(bean) 주입이 필요하다.
2. 해결 방법
applicationContext.xml
설정과 Job 클래스를 다음과 같이 수정하면 문제를 해결할 수 있습니다.
2.1. applicationContext.xml 변경
JobDetailFactoryBean
생성시jobDataAsMap
속성 관련 설정을 제거합니다.SchedulerFactoryBean
생성시applicationContextSchedulerContextKey
속성 값을applicationContext
키워드로 지정하는 설정을 추가합니다.
<bean name="blogJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="blog.in.action.job.BlogJob"/>
<property name="durability" value="true"/>
<!-- 제거 -->
<!-- <property name="jobDataAsMap">
<map>
<entry key="blogService" value-ref="blobService"/>
</map>
</property> -->
</bean>
...
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger"/>
</list>
</property>
<!-- 추가 -->
<property name="applicationContextSchedulerContextKey" value="applicationContext"/>
...
</bean>
2.2. BlogJob 클래스
Setter
메소드는 제거합니다.executeInternal
메소드에 파라미터로 전달받은JobExecutionContext
객체에서 스케줄러를 획득합니다.- 스케줄러에서
applicationContext
키워드로 Spring 컨텍스트(context) 정보를 획득합니다. - Spring 컨텍스트에서
getBean
메소드를 통해 원하는 빈(bean)을 꺼내어 사용합니다.
package blog.in.action.job;
import blog.in.action.service.BlogService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class BlogJob extends QuartzJobBean {
private BlogService blogService;
// 제거
// public void setBlogService(BlogService blogService) {
// this.blogService = blogService;
// }
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
// 추가
blogService = ((ApplicationContext) jobExecutionContext.getScheduler().getContext().get("applicationContext")).getBean(BlogService.class);
blogService.updateTest();
} catch (Exception e) {
e.printStackTrace();
}
}
}
댓글남기기