Spring - Quartz를 사용하여 스케쥴러 구현하기

Published on: 2010. 9. 29. 15:43 by krespo

가끔 서버에서 주기적으로 어떠한 작업을 하고자 할때 리눅스에서는 크론탭을 사용하여 주기적으로 어떠한 작업을 처리합니다.
이런 주기적 작업을 처리하기위해 Spring에서 지원해 주는 Quartz스케쥴러를 통해 크론탭과 같은 역할을 하는 스케쥴러를 작성할 수 있습니다.
이번에는 Spring 과 Quartz를 연동하여 스케줄러를 작성해 보겠습니다.

작업순서는
스프링 기본 세팅 -> Quartz 세팅 순으로 작업하겠습니다.

1. 스프링 기본 설정
1) springframework.org 로 이동하셔서 스프링 라이브러리를 다운 받습니다.

위와 같은 페이지가 뜨면 해당사항을 입력하시고 Access Download 를 클릭하셔서 다운로드 페이지로 이동합니다. (귀찮으신 분들은 하단의 파란색으로 "download page"를 선택하시면 입력하시지 않고도 다운로드 페이지로 이동하실수 있습니다.

많은 버전의 라이브러리 중 spring-framework-2.5.6.SEC02.zip 를 다운 받습니다. 다른 버전을 다운 받으셔도 상관없습니다만 버전에 따라 세팅 내용이 조금 씩 달라지므로 같은 버전의 라이브러리로 진행하는 것이 나을 것같네요~^^.

2) 이렇게 라이브러리까지 다운로드 받고 나면 Eclipse와 같은 IDE에서 Dynamic Web Project를 선택하여 Project를 한개 생성합니다.
(저는 SpringQuartz 라는 이름으로 생성했습니다.)

3) 프로젝트가 생성되면 프로젝트 안에 /WEB-INF/lib 디렉토리에 스프링 라이브러리를 압축 푼 곳에 있는 dist/spring.jar 파일을 추가합니다.
* 팁 : 프로젝트를 진행하다 보면 위와같이 라이브러리 버전이 없는 jar파일을 그냥 추가하는 경우가 있는데 나중에 라이브러리를 업데이트 해야 할일이 생기게 되면 위와같이 spring.jar 라고 되어있으면 지금 적용되어 있는 버전이 몇 인지 알수가 없습니다. 그렇기 때문에 항상 라이브러리 추가하실때는 추가하시는 라이브러리의 버전 번호를 파일이름 뒤에 추가하는 습관을 들이 시는게 좋습니다.
     ex) spring-2.5.6.jar

4) 프로젝트 안에 생성된 web.xml에 Spring을 사용하기 위한 세팅을 추가해 줍니다.
* 저는 Quartz를 사용하기 위한 최소한의 Spring 세팅을 해놓았기 때문에 세팅 내용이 단순합니다. 만약 웹프로젝트와 함께 Quartz를 사용하신다면 웹에 맞게 설정하시고 사용하셔야 함을 알려드립니다.^^
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" 	
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 	
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"	id="WebApp_ID" version="2.5">	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>	
	</listener>	
	<context-param>		
		<param-name>contextConfigLocation</param-name>		
		<param-value>/WEB-INF/config/applicationContext*.xml</param-value>
	</context-param>
</web-app>


5) 쿼츠 라이브러리를 다운로드 받고 라이브러리를 추가해 줍니다.
쿼츠 라이브러리 다운로드 하신다음 압축을 풀어 줍니다.
해당 라이브러리를 프로젝트의 lib 디렉토리에 복사하여 넣어줍니다.
- quartz-all-1.8.3.jar
- 압축푼 lib 디렉터리의 log4j-1.2.14.jar
- 압축푼 lib 디렉터리의 slf4j-api-1.5.10.jar
- 압축푼 lib 디렉터리의 slf4j-log4j12-1.5.10.jar
를 추가 해 줍니다.
마지막으로 apache의 commons-logging-1.1.1.jar 를 다운로드 하셔서 위와 같이 프로젝트의 lib에 추가해주시면 라이브러리 추가는 끝이 납니다.

6) Quartz의 핵심적인 기능을 할 /WEB-INF/config/applicationConext.xml 을 작성합니다.
스케쥴러의 핵심 세팅은 3가지 정도 입니다.
하나. 실제 주기적으로 실행될 클래스 등록
둘. 스케줄러가 동작하는 interval time 설정
셋. 실제 동작하게 끔 설정

이런 세가지가 있겠습니다.

스케줄러 동작방식에는 두가지가 존재 합니다.
-Simple : interval time이 간단하게 동작하는 방식으로 몇초, 혹은 몇분, 몇시간 단위로 작동하고 싶을때 사용합니다.
<Simple type setting>
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans   
                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	<!-- 하나.주기적으로 실행될 클래스 설정 -->
	<!-- property name은 jobClass로 fix, value는 사용자가 작성한 class 파일 위치 -->
	<bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">
		<property name="jobClass" value="net.test.quartz.SimpleQuartzJob"/>
	</bean>

	<!-- 둘.스케줄러의 interval time 설정 -->
	<!-- 쿼츠에는 아래와 같이 몇초에 한번씩 돌게 하는 Simple type 과 -->
	<!-- 무슨 요일 몇시에 한번씩 돌게 하는 날짜로 지정하는 Cron type 이 있다. -->
	<!-- 현재는 Simple type으로 세팅 -->
	<!-- jobDetail은 위에서 설정한 실제 동작할 클래스 id를 적어준다 -->
	<!-- startDelay는 서버 시작후 몇초 뒤에 시작할지 세팅(ms 단위)  -->
	<!-- repeatInterval은 몇 초에 한번씩 실행될 건지 세팅(ms 단위: 현재 1초) -->
	<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
		<property name="jobDetail" ref="simpleQuartzJob"/>
		<property name="startDelay" value="1000"/>
		<property name="repeatInterval" value="1000"/>
	</bean>
	<!--셋. 실제 동작하게끔 설정 -->
	<!--ref bean은 위에서 설정한 interval time 아이디를 넣어주면 됨  -->
	<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<ref bean="simpleTrigger"/>
			</list>
		</property>
		<!-- Quartz 실행시 세팅 -->
		<property name="quartzProperties">
			<props>
				<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
    			<prop key="org.quartz.threadPool.threadCount">5</prop>
    			<prop key="org.quartz.threadPool.threadPriority">4</prop>
    			<prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
    			<prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
			</props>
		</property>
	</bean>
</beans>

-Cron : linux 의 Cron tab 과 같은 역할을 하는 타입니다. 즉 몇월, 몇일 몇시에 동작하게 하고 싶으면 Cron type을 사용하시면 됩니다.
<Cron type setting>
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans   
                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	<!--하나. 주기적으로 실행될 클래스 설정 -->
	<bean id="cronQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">
		<property name="jobClass" value="net.test.quartz.CronQuartzJob"/>
	</bean>
	
	<!--둘. 스케줄러의 interval time 설정-->
	<!--cronExpression을 통해서 스캐줄러 주기를 설정한다. -->
	<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
		<property name="jobDetail" ref="cronQuartzJob"/>
		<property name="cronExpression" value="0/1 * * * * ?"/>
	</bean>
	
	<!--셋. 실제 동작하게끔 설정 -->
	<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
		<property name="triggers">
			<list>
				<ref bean="cronTrigger"/>
			</list>
		</property>
		<property name="quartzProperties">
			<props>
				<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
    			<prop key="org.quartz.threadPool.threadCount">5</prop>
    			<prop key="org.quartz.threadPool.threadPriority">4</prop>
    			<prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
    			<prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
			</props>
		</property>
	</bean>
</beans>


Cron type을 사용하려면 CronExpression을 알아야 합니다.

*Cron Expression
cron expression의 각각의 필드는 다음을 나타낸다.(왼쪽 -> 오른쪽 순)
 필드 이름  허용 값  허용된 특수 문자
 Seconds  0 ~ 59  , - * /
 Minutes  0 ~ 59  , - * /
 Hours  0 ~ 23  , - * /
 Day-of-month  1 ~ 31  , - * ? / L W
 Month  1 ~12 or JAN ~ DEC   , - * /
 Day-Of-Week  1 ~ 7 or SUN-SAT  , - * ? / L #
 Year (optional)  empty, 1970 ~ 2099  , - * /

Cron Expression 의 특수문자
'*' : 모든 수를 나타냄. 분의 위치에 * 설정하면 "매 분 마다" 라는 뜻.
'?' : day-of-month 와 day-of-week 필드에서만 사용가능. 특별한 값이 없음을 나타낸다.
'-' : "10-12" 과 같이 기간을 설정한다. 시간 필드에 "10-12" 이라 입력하면 "10, 11, 12시에 동작하도록 설정" 이란 뜻.
',' : "MON,WED,FRI"와 같이 특정 시간을 설정할 때 사용한다. "MON,WED,FRI" 이면 " '월,수,금' 에만 동작" 이란 뜻.
'/' : 증가를 표현합니다. 예를 들어 초 단위에 "0/15"로 세팅 되어 있다면 "0초 부터 시작하여 15초 이후에 동작" 이란 뜻.
'L' : day-of-month 와 day-of-week 필드에만 사용하며 마지막날을 나타냅. 만약 day-of-month 에 "L" 로 되어 있다면 이번 달의 마지막에 실행하겠다는 것을 나타냄.
'W' : day-of-month 필드에만 사용되며, 주어진 기간에 가장 가까운 평일(월~금)을 나타낸다. 만약 "15W" 이고 이번 달의 15일이 토요일이라면 가장가까운 14일 금요일날 실행된다. 또 15일이 일요일이라면 가장 가까운 평일인 16일 월요일에 실행되게 된다. 만약 15일이 화요일이라면 화요일인 15일에 수행된다.
"LW" : L과 W를 결합하여 사용할 수 있으며 "LW"는 "이번달 마지막 평일"을 나타냄
"#" : day-of-week에 사용된다. "6#3" 이면 3(3)번째 주 금요일(6) 이란 뜻이된다.1은 일요일 ~ 7은 토요일 

 Expression  Meaning
 "0 0 12 * * ?"  매일 12시에 실행
 "0 15 10 ? * *"  매일 10시 15분에 실행
 "0 15 10 * * ?"  매일 10시 15분에 실행
 "0 15 10 * * ? *"  매일 10시 15분에 실행
 "0 15 10 * * ?  2010"   2010년 동안 매일 10시 15분에 실행
 "0 * 14 * * ?"  매일 14시에서 시작해서 14:59분 에 끝남
 "0 0/5 14 * * ?"  매일 14시에 시작하여 5분 간격으로 실행되며 14:55분에 끝남
 "0 0/5 14,18 * * ?"  매일 14시에 시작하여 5분 간격으로 실행되며 14:55분에 끝나고, 매일 18시에 시작하여 5분간격으로 실행되며 18:55분에 끝난다.
 "0 0-5 14 * * ?"  매일 14시에 시작하여 14:05 분에 끝난다.

* 시간에 맞춰 돌아가는 스케줄러에서 다른 클래스를 사용하고 싶을 때는 다음과 같이 설정합니다.
<!-- 스프링 DI : 사용할 Service 객체를 생성 -->
<bean id="quartzJobService" class="net.test.quartz.service.impl.QuartzJobServiceImpl"/>

<bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">
	<property name="jobClass" value="net.test.quartz.SimpleQuartzJob"/>
	<!-- 사용하고자 하는 class의 bean id를 등록 -->
	<property name="jobDataAsMap">
		<map>
			<entry key="quartzJobService">
				<ref local="quartzJobService"/>
			</entry>
		</map>
	</property>
</bean>

 *두가지 스케줄러를 동시에 실행 시킬때
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	<property name="triggers">
		<!--트리거를 두개 생성후 아래와 같이 세팅 -->
		<list>
			<ref bean="simpleTrigger"/>
			<ref bean="cronTrigger"/>
		</list>
	</property>
	<property name="quartzProperties">
		<props>
			<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
    			<prop key="org.quartz.threadPool.threadCount">5</prop>
    			<prop key="org.quartz.threadPool.threadPriority">4</prop>
    			<prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
    			<prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
		</props>
	</property>
</bean>


7) 실제 작동할 Class파일 생성
package net.test.quartz;

import net.test.quartz.service.QuartzJobService;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class SimpleQuartzJob extends QuartzJobBean{
	//실행될 클래스는 꼭 QuartzJobBean을 상속받아야 되며 
	//executeInternal method를 override 하면 자동으로 이 메소드가 실행

	//Spring의 DI를 사용하여 Service객체를 setting
	//DI를 사용하지 않는다면 필요 없는 부분
	private QuartzJobService quartzJobService;
	public void setQuartzJobService(QuartzJobService quartzJobService) {
		this.quartzJobService = quartzJobService;
	}


	@Override
	protected void executeInternal(JobExecutionContext ex)throws JobExecutionException {
		quartzJobService.printLog();
	}
	
}


위와 같은 방식으로 Spring과 Quartz를 사용하여 스케줄러를 사용할 수 있습니다.

함께 업로드 하는 파일은 제가 직접 작업한 프로젝트이구요. 함께 확인 해 보시면 쉽게 쿼츠를 사용하실수 있으실 겁니다.~^^

SpringQuartz.war


이준국 | 2011.01.21 18:55 | PERMALINK | EDIT/DEL | REPLY
좋은 정보 감사드립니다.
덕분에 해결했네요 ^^;
krespo | 2011.01.26 10:41 신고 | PERMALINK | EDIT/DEL
하핫..^^ 별말씀을요~^^
정태원 | 2011.01.24 11:07 | PERMALINK | EDIT/DEL | REPLY
정말 친절한 설명으로 도움이 많이 되었습니다~~~ 감사합니다~~ *^^*
krespo | 2011.01.26 10:41 신고 | PERMALINK | EDIT/DEL
감사합니다. ^^ 제 포스팅 보시고 도움이 되셨다니 저도 기분이 좋네요~^^
TasteGod | 2011.02.17 11:18 신고 | PERMALINK | EDIT/DEL | REPLY
쿼츠 기억이 잘 안나서 다시 보는데, 엄청 깔끔히 정리해놓으셨네요. 수고하셨습니다
| 2012.03.23 14:17 | PERMALINK | EDIT/DEL | REPLY
비밀댓글입니다
krespo | 2012.03.27 20:42 신고 | PERMALINK | EDIT/DEL
parsing XML document from class path.. 라고 에러문구가 나온다면 applicationContext.xml파일을 WEB-INF 디렉토리가 아닌 classpath(쉽게 말해 자바 파일이 있는 위치)에 놓았기 때문일 겁니다. 그럴때는 앞에 classpath:applicationContext.xml 이라고 써주시면 될것 같네요.

그리고 제가 다이나믹 웹 프로젝트로 진행한 건.. 별다른 이유는 없습니다. 그냥이죠..;;; 요즘은 STS나 eclipse의 spring plugin기반으로 메뉴로 spring + maven기반의 프로젝트를 뚝닥 만들수 있는데요, 저는 프레임웍 공부할때 자동화 툴보다는 직접 만들어 보고 흐름을 이해하려고 직접 세팅한겁니다. 별다른 이유는 없어요.ㅋㅋ
자바개발자 | 2013.01.10 10:39 | PERMALINK | EDIT/DEL | REPLY
좋은 정보 감사드립니다.
새해 복 많이 받으시고
늘 행복하세요..^^
Huey | 2013.04.05 11:35 신고 | PERMALINK | EDIT/DEL | REPLY
정말 정리를 깔끔하고 정확하게 잘 해놓으셨네요^^ 덕분에 많은 도움 받았습니다.
종종 참고하려고 제 블로그에 링크 좀 해 갈게요. 싫으시면 알려주세요~
지나가던 | 2013.05.13 16:10 | PERMALINK | EDIT/DEL | REPLY
앗! 제가 찾고 싶던거네요!
이거 참조해서 응용해보겠습니다 ㅎㅎ
이 포스트 꼭 지우지 마세요~ 감사합니다
자발개발 | 2013.06.28 16:08 | PERMALINK | EDIT/DEL | REPLY
필요한 정보가 딱! 있군요 너무 감사합니다~
개발개발 | 2015.09.09 10:21 | PERMALINK | EDIT/DEL | REPLY
감사합니다 엄청난 도움이 되었습니다.
Name
Password
Homepage
Secret

Spring - get방식일때 한글 깨질때 해결 방법

Published on: 2009. 10. 26. 17:22 by krespo
web.xml 파일에 uri encoding 설정하는 부분이 있다. 이는 보통 post방식의 인코딩을 지원하지만 get방식은 지원하지 않는다.
이럴때는 톰캣의 server.xml파일을 수정하면 된다.

Connector(두개가 있다.) 부분에 attribute부분에 URIEncoding="EUC-KR" 추가 하면 get방식의 한글 깨짐 현상을 없앨 수 있다

예)
<Connector connectionTimeout="20000" port="8989" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="EUC-KR"/>
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" URIEncoding="EUC-KR"/>
spring입문자 | 2013.02.19 00:36 | PERMALINK | EDIT/DEL | REPLY
한글네임으로 데이터베이스에서 찾아와야되는데 글자가 계속 깨져서 못 찾았었는데 알려주신 방법대로 하니 한글이 안 깨지네요.. 좋은 정보 감사합니다..
Name
Password
Homepage
Secret

Spring - Spring의 Controller들. 3.MultiActionController

Published on: 2009. 9. 13. 17:15 by krespo
<역할>
MultiActionController는 하나의 Controller클래스에서 여러가지 기능을 하는 method를 구현해 놓고 파라미터 값으로 넘어오는 어떠한 값과 MultiActionController에 구현한 method와의 이름을 비교해서 실행을 시킨다.
이 MultiActionContorller는 주로 비슷한 기능을 가진 method끼리 모아 쓸데 없이 클래스를 많이 생성해서 생길수 있는 class overhead를 줄여주는 방법이다.

<적용방법>
1. 일단 구현할 클래스를 MultiActionController를 확장해서 구현한다. method는 overriding을 할 필요 없고 사용자가 원하는 이름으로 구현하면 된다.
보통 리턴값은 ModelAndView로 리턴값을 정하지만 다른 Map,String,void를 선택적으로 사용할 수 있다. 그리고 전달 parameter들은 HttpServletRequest,HttpServletResponse를 사용한다.
예)protected ModelAndView read(HttpServletRequest request,HttpServletResponse response);
package kr.co.springboard.controllers;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import kr.co.springboard.dto.BoardDTO;
import kr.co.springboard.service.BoardService;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.MultiActionController;

public class ReadDeleteMultiActionController extends MultiActionController{
    private BoardService boardService;
    public void setBoardService(BoardService boardService) {
        this.boardService = boardService;
    }

    public ModelAndView boardRead(HttpServletRequest request,
                                  HttpServletResponse response){
        ModelAndView mnv = new ModelAndView();
        int seq = Integer.valueOf(request.getParameter("seq"));
        this.boardService.boardUpdateReadCount(seq);
        BoardDTO boardDto = this.boardService.boardRead(seq);
        mnv.addObject("boardDto",boardDto);
        mnv.setViewName("boardRead");
        return mnv;
       
    }
   
    public ModelAndView boardDelete(HttpServletRequest request,
                                    HttpServletResponse response){
        ModelAndView mnv = new ModelAndView();
        int seq = Integer.valueOf(request.getParameter("seq"));
        this.boardService.boardDelete(seq);
        mnv.setViewName("boardDeleteSuccess");
        return mnv;
    }
}
2.DI를 구현한 부분에 ParameterMethodNameResolver 를 구현하고 이것을 통해서 DI를 설정한다.
<bean id="readDeleteMultiActionController"
          class="kr.co.springboard.controllers.ReadDeleteMultiActionController"
          p:boardService-ref="boardService"
          p:methodNameResolver-ref="boardReadDeleteNameResolver"/>
    

<bean id="boardReadDeleteNameResolver"
          class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
     <property name="paramName" value="mode"/>
</bean>

이렇게 세팅해 주면 된다.
이렇게 세팅하면 주소창에
http://localhost/springboard/board.do?mode=
이렇게 입력한 뒤에 mode뒤에 자기가 실행하고 싶은 method를 입력하면 된다. 클래스의 boardRead ()를 실행하고 싶으면 mode=boardRead, boardDelete를 실행하고 싶으면 mode=boardDelete를 실행 하면 된다.
mode는 ParameterMethodNameResolver에서 property로 선언한 paramName에서 세팅한 것과 동일 한 것이어야 한다.
Name
Password
Homepage
Secret

Spring - Spring의 Controller들. 2.SimpleFormController

Published on: 2009. 9. 13. 16:52 by krespo

<역할>
Controller의 이름에서도 알수 있다 시피 SimpleFormController는 입력받는 Form을 간단하게 만들어 줄수 있는 Controller 이다.
즉 입력과 수정창, 예를 들면 게시판의 글쓰기와 글 수정 창을 따로따로 만들 필요 없이 http get 으로 들어오면 글쓰기 창을 보여주고 글 정보를 쓸때(글쓰기,글 수정) http post 방식으로 들어올때는 글을 디비에 입력하는 이런 따로따로인 기능을 하나의 클래스로 구현하는 것이다.
그렇기 때문에 수정form과 입력form을 두가지 따로따로 만들 필요가 없는 것이다.

<사용방법>
SimpleFormController를 사용하기 위해서는 이 클래스를 상속 받아서 써야 한다. SimpleFormController를 사용하게 되면 formBackingObject() method와 onSubmit() 메소드를 overriding 해서 사용할 수 있다.

게시판에서 예를 들어 보면
게시판에 글을 쓰는 폼을 보여주거나, 게시판의 글을 수정하는 폼을 보여주는 것을 simpleFormController로 구현한다고 하자.
글쓰기나 수정 둘다 form을 요청할때는 get 방식을 사용할 것이다.(왜냐 하면 URL을 통해 form에 접근하기 때문이다)
그리고 사용자로 부터 글을 쓴 정보, 혹은 수정한 정보들은 post 방식으로 컨트롤러에 접근할 것이다.
이때 formBackingObject() method 는 get방식으로 접근할때 실행 되고 onSubmit() method는 post방식으로 데이터가 넘어올때 실행이 된다.
그림으로 보면



다음과 같은 실행 루틴을 거친다.

1. 사용할 클래스에 SimpleFormController를 상속 받아 formBackingObject() 와 onSubmit()를 override 한다.
package kr.co.springboard.controllers;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import kr.co.springboard.dto.BoardDTO;
import kr.co.springboard.service.BoardService;

import org.springframework.validation.BindException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;

public class WriteSimpleFormController extends SimpleFormController {
    private BoardService boardService;
    public void setBoardService(BoardService boardService) {
        this.boardService = boardService;
    }

   
   
    @Override
    protected Object formBackingObject(HttpServletRequest request)
                                       throws Exception {
        int seq = ServletRequestUtils.getIntParameter(request, "seq", 0);
        BoardDTO boardDto = null;
        if(seq == 0){
            boardDto = new BoardDTO();
        }else if(seq > 0){
            boardDto = this.boardService.boardRead(seq);
        }
        return boardDto;
    }



    @Override
    protected ModelAndView onSubmit(HttpServletRequest request,
                                    HttpServletResponse response,
                                    Object command, BindException errors)
                                    throws Exception {
        ModelAndView mnv = new ModelAndView();
        BoardDTO dto = (BoardDTO)command;
        if(dto.getSeq() == 0){
            boardService.boardInsert(dto);
        }else{
            boardService.boardUpdate(dto);
           
        }
        mnv.setViewName(getSuccessView());
       
        return mnv;
    }
   
}


2. DI설정부분에서 formView,successView,commandClass,commandName을 property로 설정한다.
<bean id="writeSimpleFormController" class="kr.co.springboard.controllers.WriteSimpleFormController">
        <property name="commandClass" value="kr.co.springboard.dto.BoardDTO"/>
        <property name="commandName" value="boardDto"/>
        <property name="formView" value="writeForm"/>
        <property name="successView" value="writeSuccess"/>
        <property name="boardService" ref="boardService"/>
</bean>

기본적으로 onSubmit()을 실행할때는 AbstractCommandController에서 command 객체를 통해 입력된 정보를 받아오는데 SimpleFormController의 onSubmit()도 이와 같은 방식을 채택하고 있다.
그렇기 때문에 commandClass와commandName을 통해 command객체를 생성해 준다.
또하나 아주 중요한 역할은 formBackingObject에서 boardDto를 리턴해 주는데 commandName에 설정한 이름으로 request 객체에 담기기 때문에 commandName을 세팅해 주지 않으면 절대로 form 페이지에서 정보를 받을 수가 없다
그리고 formView는 formBackingObject를 실행되었을때 자동적으로 ModelAndView 객체에 세팅이 됨으로 따로 세팅해주어야 할 필요는 없다.
successView는 성공하고 보여질 페이지를 세팅한다. SimpleFormController 안에 있는 getSuccessView method를 통해 값을 가져 올 수 있다.


Name
Password
Homepage
Secret

Spring - Spring의 Controller들. 1.AbastractCommandController

Published on: 2009. 9. 13. 16:31 by krespo
소개
AbstractCommandController는 페이지의 넘어오는 파라미터들을 bean 객체에 자동맵핑이 되어 Controller 에서 사용 할 수 있게 만드는 역할을 한다.

jsp 페이지의 회원가입페이지 에서 파라미터로 name,age... 등등이 넘어오면 선언한 bean의 property인 String name, String age에 대응되어 자동 저장이 된다.

사용방법
DI를 선언한 xml 파일에서 직접 bean의 클래스경로인 "commandClass" 와 commandClass의 이름인 "commandName" 을 파라미터로 넘겨주어야 한다.

예)
<bean id="listAbstractCommandController"
       class="kr.co.springboard.controllers.ListAbstractCommandController">
     <property name="commandClass" value="kr.co.springboard.beans.BoardBean"/>
     <property name="commandName" value="boardBean"/>

     <property name="boardService" ref="boardService"/>
     <property name="viewName" value="boardList"/>
</bean>

이렇게 property 로 설정하는 방법이 있는가 하면 직접 AbstractCommandController를 확장한 클래스에서 다음과 같이 Contructor를 구현해도 된다.
public ListAbstractCommandController(){
  setCommandClass(BoardBean.class);
  setCommandName("boardBean");
 }


위의 2가지 방법으로 선언하면 AbstractCommandController의 handle() method를 통해서 전달 인자인 command를 통해 전송된 데이터를 받을 수 있다.

protected ModelAndView handle(HttpServletRequest request,
                         HttpServletResponse response,
                         Object command,
                         BindException errors) throws Exception {
  ModelAndView mnv = new ModelAndView();
  BoardBean boardBean = (BoardBean)command;
  BoardDTO dto = new BoardDTO();
  dto.setNowPage(boardBean.getNowPage());
  
  if(dto.getNowPage() == 0){
   dto.setNowPage(1);
  }
  dto.setTotalCount(boardService.boardTotalCount(dto));
  dto = (BoardDTO)PagingUtil.setPagingInfo(dto);
  List<BoardDTO> boardList = this.boardService.boardList(dto);
  
  mnv.setViewName(viewName);
  mnv.addObject("boardList", boardList);
  mnv.addObject("dto",dto);
  
  return mnv;
 }

위와 같이 생성자,혹은 xml문서의 property 로 세팅해준 commandClass와 commandName 은 handle() 메소드의 command object를 통해 데이터를 전달 받기 때문에 command instance를 생성해 주는 역할을 한다.
이렇게 전달된 command 객체를 원하는 형으로 형변환을 해서 사용하면 된다.






Name
Password
Homepage
Secret