'50.Spring 2.X,3.0, MVC, etc.../Spring'에 해당되는 글 27건

  1. 2014.07.23 [펌] Spring에서 SQL Eception 처리
  2. 2014.01.22 [펌] Spring - Quartz를 사용하여 스케줄러 구현하기
  3. 2014.01.22 [펌] Spring mvc에서 Quartz 적용하기
  4. 2014.01.15 Spring Cache namespace problem
  5. 2014.01.10 [펌] spring 3.2 No setter found for property 'cronExpression' in class
  6. 2013.09.09 [펌] Spring 프로젝트를 assembly (jar-with-dependencies) 하여서 배포하였을 때, beans 스키마 못찾는 오류
  7. 2012.02.13 [펌] 이클립스 + 톰캣 + 스프링 MVC + maven 개발환경 구축
  8. 2010.10.29 ajax & spring MVC 게시판 소스
  9. 2010.03.26 Spring - Java/J2EE Application Framework
  10. 2010.02.19 spring Load Two DataSources
  11. 2010.02.19 spring BasicDataSource from apache
  12. 2010.02.10 Spring Framework Reference Documentation 3.0
  13. 2009.12.28 Spring Security
  14. 2009.12.28 N사 신입 사원 교육을 위한 Spring 프레임워크 강의
  15. 2009.12.21 Spring - Java/J2EE Application Framework 1
  16. 2009.12.21 Spring MVC 초간단 예제 1
  17. 2009.12.21 framework jpetstore 샘플 분석기
  18. 2009.12.21 Simplest Spring 3 Application, 초간단 Spring 3 어플
  19. 2009.09.21 [펌] spring 2.5 자동화 유틸
  20. 2009.02.26 Spring 프레임워크
  21. 2008.08.19 Spring 프레임워크_javajigi 자료
  22. 2008.08.19 Spring Framework 강의자료 링크
  23. 2008.08.19 Spring 처음설치부터 배워보기
  24. 2008.08.18 [Spring Framework ] !!!!기초정리 !!!!!
  25. 2008.08.17 Spring 어플리케이션 프레임워크 모듈 구성
  26. 2008.08.17 Spring 이란?
  27. 2008.08.17 SpringFramework Helloworld 찍어보기
반응형

if (ex instanceof DataAccessException) {

       SQLException se = (SQLException) ((DataAccessException) ex).getRootCause();

       msg = se.getMessage();

       LOGGER.debug("****** DataAccessException : {} // {}", se.getErrorCode(), se.getMessage());

}


if (ex instanceof BadSqlGrammarException) {

       SQLException se = ((BadSqlGrammarException) ex).getSQLException();

       LOGGER.debug("**BadSqlGrammarException {} ", se.getErrorCode());

}

else if (ex instanceof InvalidResultSetAccessException) {

       SQLException se = ((InvalidResultSetAccessException) ex).getSQLException();

       LOGGER.debug("**InvalidResultSetAccessException {} ", se.getErrorCode());

}

else if (ex instanceof DuplicateKeyException) {

       LOGGER.debug("**DuplicateKeyException {} ", ex.getMessage());

}

else if (ex instanceof DataIntegrityViolationException) {

       LOGGER.debug("**DataIntegrityViolationException {} ", ex.getMessage());

}

else if (ex instanceof DataAccessResourceFailureException) {

       LOGGER.debug("**DataAccessResourceFailureException {} ", ex.getMessage());

}

else if (ex instanceof CannotAcquireLockException) {

       LOGGER.debug("**CannotAcquireLockException {} ", ex.getMessage());

}

else if (ex instanceof DeadlockLoserDataAccessException) {

       LOGGER.debug("**DeadlockLoserDataAccessException {} ", ex.getMessage());

}

else if (ex instanceof CannotSerializeTransactionException) {

       LOGGER.debug("**CannotSerializeTransactionException {} ", ex.getMessage());

}

org.springframework.jdbc.support.SQLErrorcodesSQLExceptionTranslator Class


Posted by 1010
반응형

Scheduling 서비스

개요

Scheduling 서비스는 어플리케이션 서버 내에서 주기적으로 발생하거나 반복적으로 발생하는 작업을 지원하는 기능으로서 유닉스의 크론(Cron) 명령어와 유사한 기능을 제공한다.
실행환경 Scheduling 서비스는 오픈소스 소프트웨어로 Quartz 스케쥴러를 사용한다. 본 장에서는 Quartz 스케쥴러의 기본 개념을 살펴본 후, IoC 서비스를 제공하는 Spring과 Quartz 스케쥴러를 통합하여 사용하는 방법을 살펴본다.

설명

Quartz 스케쥴러

Quartz 스케쥴러 실행과 관계된 주요 요소는 Scheduler, Job, JobDetail, Trigger 가 있다.

  • Scheduler 는 Quartz 실행 환경을 관리하는 핵심 개체이다.
  • Job 은 사용자가 수행할 작업을 정의하는 인터페이스로서 Trigger 개체를 이용하여 스케쥴할 수 있다.
  • JobDetail 는 작업명과 작업그룹과 같은 수행할 Job에 대한 상세 정보를 정의하는 개체이다.
  • Trigger 는 정의한 Job 개체의 실행 스케쥴을 정의하는 개체로서 Scheduler 개체에게 Job 수행시점을 알려주는 개체이다.

Quartz 스케쥴러는 수행 작업을 정의하는 Job과 실행 스케쥴을 정의하는 Trigger를 분리함으로써 유연성을 제공한다. Job 과 실행 스케쥴을 정의한 경우, Job은 그대로 두고 실행 스케쥴만을 변경할 수 있다. 또한 하나의 Job에 여러 개의 실행 스케쥴을 정의할 수 있다.

Quartz 스케쥴러 사용 예제

Quartz 스케쥴러의 이해를 돕기 위해 간단한 예제를 살펴본다. 다음 예는 Quartz 매뉴얼에서 참조한 것으로 Quartz를 사용하는 방법과 사용자 Job을 설정하는 방법을 보여준다.

사용자 정의 Job

사용자는 Job 개체를 생성하기 위해 org.quartz.Job 인터페이스를 구현하고 심각한 오류가 발생한 경우 JobExecutionException 예외를 던질 수 있다. Job 인터페이스는 단일 메소드로 execute()을 정의한다.

 public class DumbJob implements Job {
    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      System.out.println("DumbJob is executing.");
    }
  }
  • DumbJob은 Job 인터페이스의 execute() 메소드를 구현한다.
  • execute() 메소드는 단순히 Job이 수행됨을 표시하는 메시지를 출력한다.
Quartz 사용 코드
  JobDetail jobDetail = 
            new JobDetail("myJob",// Job 명
              sched.DEFAULT_GROUP,  // Job 그룹명('null' 값인 경우 DEFAULT_GROUP 으로 정의됨)
              DumbJob.class);       // 실행할 Job 클래스
 
  Trigger trigger = TriggerUtils.makeDailyTrigger(8, 30);  // 매일 08시 30분 실행
  trigger.setStartTime(new Date()); // 즉시 시작
  trigger.setName("myTrigger");
 
  sched.scheduleJob(jobDetail, trigger);
  • 우선 Job 설정을 위해 JobDetail 클래스를 정의한다.
  • TriggerUtils을 이용하여 매일 8시30분 실행하는 Trigger를 생성한다.
  • 마지막으로, Scheduler에 JobDetail과 Trigger를 등록한다.

Spring 과 Quartz 통합

Spring은 Scheduling 지원을 위한 통합 클래스를 제공한다. Spring 2.5 는 JDK 1.3 버전부터 포함된 Timer 와 오픈소스 소프트웨어인 Quartz 스케쥴러를 지원한다. 여기서는 Quartz 스케쥴러와 Spring을 통합하여 사용하는 방법을 살펴본다. 
Quartz 스케쥴러와의 통합을 위해 Spring은 Spring 컨텍스트 내에서 Quart Scheduler와 JobDetail, Trigger 를 빈으로 설정할 수 있도록 지원한다. 다음은 예제를 중심으로 Quartz 작업 생성과 작업 스케쥴링, 작업 시작 방법을 살펴본다.

작업 생성

Spring은 작업 생성을 위한 방법으로 다음 두 가지 방식을 제공한다.

  • JobDetailBean을 이용한 방법으로, QuartzJobBean을 상속받아 Job 클래스를 생성하는 방법
  • MethodInvokingJobDetailFactoryBean을 이용하여 Bean 객체의 메소드를 직접 호출하는 방법
JobDetailBean을 이용한 작업 생성

JobDetail는 작업 실행에 필요한 정보를 담고 있는 객체이다. Spring은 JobDetail 빈 생성을 위해 JobDetailBean을 제공한다. 예를 들면 다음과 같다.

JobDetailBean 소스 코드

package egovframework.rte.fdl.scheduling.sample;
 
public class SayHelloJob extends QuartzJobBean {
 
	private String name;
 
	public void setName (String name) {
		this.name = name;	
	}
 
	@Override
	protected void executeInternal (JobExecutionContext ctx) throws JobExecutionException {
		System.out.println("Hello, " + name);
	}
}
  • SayHelloJob 클래스는 작업 생성을 위해 QuartzJobBean의 executeInternal(..) 함수를 오버라이드한다.

JobDetailBean 설정

 <bean id="jobDetailBean"
	class="org.springframework.scheduling.quartz.JobDetailBean">
	<property name="jobClass" value="egovframework.rte.fdl.scheduling.sample.SayHelloJob" />
	<property name="jobDataAsMap">
		<map>
			<entry key="name" value="JobDetail"/>
		</map>
	</property>
  </bean>
  • jobDataAsMap 개체를 이용하여 JobDetail 개체에 Job 설정에 필요한 속성 정보를 전달한다.
MethodInvokingJobDetailFactoryBean을 이용한 작업 생성

소스 코드

package egovframework.rte.fdl.scheduling.sample;
 
public class SayHelloService {
 
	private String name;
 
	public void setName (String name) {
		this.name = name;	
	}
 
	public void sayHello () {
		System.out.println("Hello, " + this.name);
	}
}
  • 작업 수행을 할 Bean 클래스를 정의한다.

설정

<bean id="sayHelloService" class="egovframework.rte.fdl.scheduling.sample.SayHelloService">
	<property name="name" value="FactoryBean"/>
</bean>
 
<bean id="jobDetailFactoryBean"
	class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	<property name="targetObject" ref="sayHelloService" />
	<property name="targetMethod" value="sayHello" />
	<property name="concurrent" value="false" />
</bean>
  • 정의한 Bean 객체의 메소드를 직접 호출하는 작업을 생성하기 위해 MethodInvokingJobDetailFactoryBean을 정의한다.

작업 스케쥴링

Spring에서 주로 사용되는 Trigger타입은 SimpleTriggerBean과 CronTriggerBean 이 있다. SimpleTrigger 는 특정 시간, 반복 회수, 대기 시간과 같은 단순 스케쥴링에 사용된다. CronTrigger 는 유닉스의 Cron 명령어와 유사하며, 복잡한 스케쥴링에 사용된다. CronTrigger 는 달력을 이용하듯 특정 시간, 요일, 월에 Job 을 수행하도록 설정할 수 있다. 다음은 SimpleTriggerBean과 CronTriggerBean을 이용하여 앞서 생성한 작업을 스케쥴링하는 방법을 살펴본다.

SimpleTriggerBean을 이용한 설정

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
	<property name="jobDetail" ref="jobDetailBean" />
        <!-- 즉시 시작 -->
	<property name="startDelay" value="0" />
   	<!-- 매 10초마다 실행 -->
	<property name="repeatInterval" value="10000" />
</bean>
  • 앞서 JobDetailBean 을 이용하여 생성한 작업을 스케쥴링을 위한 Trigger 에 등록한다. SimpleTriggerBean은 즉시 시작하고 매 10초마다 실행하도록 설정하였다.

CronTriggerBean을 이용한 설정

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
   	<property name="jobDetail" ref="jobDetailFactoryBean" />
   	<!-- 매 10초마다 실행 -->
   	<property name="cronExpression" value="*/10 * * * * ?" />
</bean>
  • 앞서 MethodInvokingJobDetailFactoryBean 을 이용하여 생성한 작업을 스케쥴링을 위한 Trigger 에 등록한다. CronTriggerBean은 매 10초마다 실행하도록 설정하였다. 크론 표현식에 대한 자세한 설명은Quartz Cron 표현식를 참조한다.

작업 시작하기

스케쥴링한 작업의 시작을 위해 Spring 은 SchedulerFactoryBean을 제공한다.

설정

<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	<property name="triggers">
		<list>
			<ref bean="simpleTrigger" />
			<ref bean="cronTrigger" />
		</list>
	</property>
</bean>
  • SchedulerFactoryBean 을 이용하여 SimpleTriggerBean 과 CronTriggerBean 기반의 각 Trigger 작업을 시작한다.

참고자료



링크 : http://javastore.tistory.com/96 

링크 : http://kamsi76.egloos.com/470846 

링크 : http://briansjavablog.blogspot.kr/2012/09/spring-quartz-tutorial.html 

Posted by 1010
반응형

The error I get is as follows:

SEVERE: StandardWrapper.Throwable
org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [org.springframework.scheduling.quartz.JobDetailBean] for bean with name ‘runMeJob’ defined in ServletContext resource [/WEB-INF/quartz-servlet.xml]: problem with class file or dependent class; nested exception is java.lang.IncompatibleClassChangeError: class org.springframework.scheduling.quartz.JobDetailBean has interface org.quartz.JobDetail as super class

 

이런 오류 메시지가 나올때는

 

quartz 다운로드 페이지에서 Quartz 1.8.6 버전으로 lib 교체해주면 된다.



출처 - http://jhroom.tistory.com/129



===================================================================================


WAS를 이용해서 주기적으로 특정 프로그램 실행하는 방법이 spring에서는 2가지가 있다. (뭐 더 있을수도 있다 -_-)

하나는 spring batch이고 다른 하나는 quartz를 이용하는 방법이다.

뭐가좋은지는 나도 잘 모른다. 일단 quartz가 어떤식으로 동작하는지 먼저 공부했으므로 이것부터 글 적는다.

 

1. 샘플 프로젝트 생성

STS 에서 [File] - [New] - [Spring Template Project] 메뉴를 클릭한 후 Spring MVC Project를 이용해 샘플 프로젝트를 생성한다.

 

2. pom.xml 파일 수정

quartz 관련 모듈을 이용하기 위해서 pom.xml 파일에 관련 라이브러리들 넣는다.

spring-tx, quartz, commons-collections, javax.transaction 4개 넣어줘야한다.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<!-- Quartz framework and dependencies -->
<dependency>
    <groupId>opensymphony</groupId>
    <artifactId>quartz</artifactId>
    <version>1.6.3</version>
    <scope>compile</scope>
</dependency>
<!-- Quartz 1.6.0 depends on commons collections -->
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.1</version>
    <scope>runtime</scope>
</dependency>
<!-- Quartz 1.6.0 requires JTA in non J2EE environments -->
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>jta</artifactId>
    <version>1.1</version>
    <scope>runtime</scope>
</dependency>

 

3. 주기적으로 실행 될 Bean 생성

2개의 Bean을 만든다.

CronQuartz1.java

package com.mungchung.sample;
 
import java.text.SimpleDateFormat;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
 
public class CronQuartz1 extends QuartzJobBean{
    @Override
    protected void executeInternal(JobExecutionContext arg0)
            throws JobExecutionException {
        long time = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        System.out.println("Cron trigger 1 (5 second): current time = " + sdf.format(time));
    }
 
}

CronQuartz2.java

package com.mungchung.sample;
 
import java.text.SimpleDateFormat;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
 
public class CronQuartz2 extends QuartzJobBean{
    @Override
    protected void executeInternal(JobExecutionContext arg0)
            throws JobExecutionException {
        long time = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        System.out.println("Cron trigger 2 (1 minute): current time = " + sdf.format(time));
    }
}

 

4. quartz Bean과 위에서 생성한 Bean 연동

2개의 Bean이 각각 다른 시간차이로 실행되도록 설정해줄것이다.

CronQuartz1.java - 5초마다 실행

CronQuartz2.java - 1분마다 실행

 

/src/main/webapp/WEB-INF/spring/root-context.xml

<!-- 1. Cron 대상이 되는 클래스 정의 -->
<bean id="cronQuartz1" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.mungchung.sample.CronQuartz1"/>
</bean>
<bean id="cronQuartz2" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.mungchung.sample.CronQuartz2"/>
</bean>
 
<!-- 2. Cron 시간 설정 -->
<bean id="cronTrigger1" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="cronQuartz1"/>
    <property name="cronExpression" value="0/5 * * * * ?"/>
</bean>
<bean id="cronTrigger2" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="cronQuartz2"/>
    <property name="cronExpression" value="0 0/1 * * * ?"/>
</bean>
 
<!-- 3. Cron 실행 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger1"/>
            <ref bean="cronTrigger2"/>
        </list>
    </property>
    <property name="quartzProperties">
        <props>
            <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
            <prop key="org.quartz.threadPool.threadCount">3</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>

 

 

Cron Expression

총 7개의 필드 있고 마지막 필드(년도)는 생략 가능하다

 필드이름허용 값 
초(Seconds)0 ~ 59 
분(Minutes)0 ~ 59 
시간(Hours)0 ~ 23
달의 날짜(Day-of-month)1 ~ 31
달(Month) 1 ~ 12 or JAN ~ DEC
주의 날짜(Day-of-week)1 ~ 7 or SUN-SAT
년도(Year) (선택가능) 빈값, 1970 ~ 2099

Cron Expression의 특수문자
Expression설명 예시 
    * 모든 수를 나타냄  
     -값의 사이를 의미* 10-13 * * * *     10,11,12,13분에 동작함 
     ,특정값 지칭* 10,11,13 * * * *      10,11,13분에 동작함
     /값의 증가를 표현* 0/5 * * * *       0분부터 시작해서 5분마다 동작 
     ?특별한 값이 없음을 나타냄(day-of-month, day-of-week 필드만 사용) 
     L마지막 날을 나타냄(day-of-month, day-of-week 필드만 사용) 

6. 실행결과

실행결과를 보면 하나는 5초마다, 다른 하나는 1분마다 Bean이 실행되고 있음을 알수 있다.

01.png





===================================================================================


sping mvc template project로 구성 시

quartz-1.8.6과 sprint-tx를 적용하여 문제 없이 구현 하였다.


spring-tx를 pom.xml에 추가 하지 않을 경우 다음과 같이 에러 발생

심각: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerFactory' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/transaction/TransactionException


===================================================================================


What Is Quartz

   

원문http://www.onjava.com/lpt/a/6207

저자Chuck Cavaness

2005-09-28

   

Quartz

Quartz는 오픈 소스 작업 스케줄링 프레임워크이다. Quartz는 완전히 자바로 작성되어 있으며, J2SE와 J2EE 어플리케이션 모두에서 사용될 목적으로 설계되었다. Quartz는 매우 유연하며 단순한 구조를 제공한다. 간단한 작업은 물론 복잡한 작업 모두에 대한 스케줄링을 작성할 수 있다. Quartz는 또한 EJB, JavaMail 등을 위한 데이터베이스 지원, 클러스터링, 플러그 인, 미리 내장된 작업들을 포함하고 있으며, cron과 유사한 표현식도 지원한다.

   

매일 또는 매주 오후 11시 30분 또는 매달 말일에만 실행하는 작업을 수행하는 어플리케이션을 작성해 본 적이 있는가? 수작업 없이 자동으로 실행될 수 있는 작업이 실행되는 동안 만약 실행하는 동안 심각한 오류가 발생할 경우, 어플리케이션은 잘못되었다는 것을 스스로 알아내 알려주고 이를 다시 실행시키도록 시도해야 할 것인가? 여러분과 팀이 자바로 프로그램을 작성하고 있는가? 만약 이들 질문들에 대한 대답이 "그렇다"라면, Quartz Scheduler를 사용해 보아라.

   

Job Scheduling Made Easy

Quartz는 오픈 소스 작업 스케줄링 프레임워크로 완전히 자바로 작성되었다. 작업 스케줄링 이라는 용어에 대해 너무 겁먹지 말아라. Quartz 프레임워크는 수많은 기능들이 매우 단순한 형태로 무장되어 있으며, 놀라울 정도로 매우 사용하기 쉽게 되어 있다.

   

org.quartz.Job 인터페이스를 구현하는 자바 클래스를 작성하기만 하면 된다. Job 인터페이스는 다음과 같은 하나의 메소드만을 포함하고 있다.

   

public void execute(JobExecutionContext context) throws JobExecutionException;

   

여러분이 작성한 Job 클래스에서 execute() 메소드에 몇 가지 로직을 추가한다. Job 클래스와 스케줄을 설정하게 되면, Quartz는 그 나머지 작업을 처리해준다. Scheduler가 Job에 알려줄 시간이라고 판단하게 되면, Quartz 프레임워크는 Job 클래스의 execute() 메소드를 실행하여 작업을 수행하도록 만들어준다. Scheduler에 어떠한 것도 보고해줄 필요가 없으며, 어떤 특별한 메소드를 호출할 필요도 없다. 단순히 Job 내에 있는 작업들만 수행해주면 끝이다. 만약 여러분이 이후에 다시 Job이 호출되도록 설정했다면, Quartz 프레임워크는 적절한 때에 다시 이를 호출하는 부분을 담당해준다.

   

만약 여러분이 Apache Struts와 같은 유명한 오픈 소스 프레임워크를 써본 경험이 있다면, Quartz의 설계 내용과 컴포넌트에도 쉽게 익숙해질 것이다. 비록 이 두 오픈 소스 프로젝트가 전혀 서로 다른 문제점들을 해결해주고 있지만, 오픈 소스 소프트웨어를 항시 사용하는 사람들이라면 편안한 느낌을 받을 것이다. Quartz는 표준 독립형 J2SE 어플리케이션 내에서라든지, 웹 어플리케이션 내부, 심지어는 J2EE 어플리케이션 서버 내에서 사용될 수 있다.

   

The History behind Quartz

Quartz가 주목을 받기 시작한 것은 올해부터지만, 나온 지는 좀 되었다. Quartz는 James House에 의해 개발되었으며, 원래는2001년 봄에 SourceForge 프로젝트에 추가되었다. 수년이 지나면서 많은 기능들이 추가되어 배포되었지만, 두각을 나타내기 시작하면서 주목을 받기 시작한 것은 OpenSymphony 프로젝트의 일부로 되면서 새로운 사이트로 옮겨지게 된 때부터였다.

   

House는 자신을 도와주는 여러 파트 타임 개발자와 함께 여전히 개발 작업의 많은 부분에 참여하고 있다. Quartz 개발 팀은 올 해 1.5 배포판을 포함해 다양한 새로운 버전들을 배포할 수 있었으며, 이는 현재 candidate 릴리즈 단계에 있다.

   

Getting Your Hands on Quartz

Quartz 프로젝트는 OpenSymphony 사이트에 호스팅 되어 있다. 이 사이트에는 JavaDocs, 튜토리얼, CVS, 사용자와 개발자 포럼 및 다운로드 링크와 같은 여러 유용한 정보들이 제공되고 있다.

   

다운로드 링크에서 배포판을 다운로드 한 후, 적절한 위치에 압축을 푼다. 이 안에 보면 어플리케이션에서 사용할 수 있는 Quartz바이너리 파일이 포함되어 있다. Quartz 프레임워크에서 요구 되는 다른 의존적인 라이브러리는 거의 없다.

   

배포판에 보면, <Quartz 설치 디렉터리>/lib/core와 <Quartz 설치 디렉터리>/lib/optional 디렉터리에 있는 다른 의존 라이브러리들을 프로젝트에 추가하면 된다. 이들 대부분은 표준 Jakarta Commons 라이브러리들로, 여러분도 잘 알고 있는 Commons Logging, Commons BeanUtils 등이다.

   

The quartz.properties File

Quartz에는 quartz.properties라는 환경 설정 파일이 포함되어 있다. 이 파일을 사용하면 Quartz 프레임워크의 런타임 환경을 수정할 수 있다. 디폴트로, Quartz 바이너리 내에 포함되어 있는 파일이 사용된다. 이 파일을 복사하여 클래스들이 있는 디렉터리에 두면 클래스 로더가 이를 참조할 수 있다. 예제 1은 quartz.properties 파일에 대한 간단한 예이다.

   

예제 1. quartz.properties 파일을 통해 Quartz 런타임을 변경할 수 있다.

#===============================================================

# Configure Main Scheduler Properties

#===============================================================

   

org.quartz.scheduler.instanceName = QuartzScheduler

org.quartz.scheduler.instanceId = AUTO

   

#===============================================================

# Configure ThreadPool

#===============================================================

   

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadCount = 5

org.quartz.threadPool.threadPriority = 5

   

#===============================================================

# Configure JobStore

#===============================================================

   

org.quartz.jobStore.misfireThreshold = 60000

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

   

일단 Quartz 바이너리와 의존 라이브러리들을 프로젝트에 추가했고, quartz.properties 파일을 클래스패스 디렉터리에 추가했다면, 이제 몇몇 Job들을 작성해보자. 그러나, 이 작업을 해보기 전에 Quartz 아키텍처에 관해 간단하게 살펴보도록 하자.

   

Inside the Quartz Architecture

크기로 보자면, Quartz는 다른 대부분의 오픈 소스 프레임워크들과 유사하다. Quartz는 약 300 개의 자바 클래스들과 인터페이스들을 포함하고 있으며, 이들은 약 12개의 패키지로 구성되어 있다. 이는 Apache Struts에 있는 11개의 패키지에 약 325개의 클래스들과 인터페이스들에 비견될 수 있다. 비록 이 크기가 프레임워크의 질을 결정하는데 사용되는 특징이 될 수는 없겠지만, 여기에서 말하고자 하는 점은 Quartz 내에 수많은 기능들이 포함되어 있다는 사실이다. 그리고, 그러한 기능들이 바로 프레임워크의 질을 결정하는데 사용되는 하나의 요소이다. 이는 오픈 소스에만 국한되는 내용은 아니다.

   

The Quartz Scheduler

Quartz 프레임워크의 핵심은 Scheduler 이다. Scheduler는 Quartz 어플리케이션을 위한 런타임 환경을 관리하는 책임을 지니고 있다. Scheduler 그 자체가 모든 작업들 수행하는 것은 아니다. Scheduler는 프레임워크 내부에 있는 매우 중요한 몇몇 컴포넌트들에 그 작업을 의존하고 있다. 확장성을 보장하기 위해, Quartz는 멀티스레드 아키텍처를 기반으로 하고 있다. Quartz 프레임워크가 시작될 때, 프레임워크는 스케줄링 된 Job들을 실행하기 위해 Scheduler가 사용하는 "worker 스레드들"을 초기화 한다. Quartz가 동시에 수많은 Job들을 실행 가능한 이유가 바로 여기에 있다. Quartz는 스레드 환경을 관리하기 위해 ThreadPool관리 컴포넌트들에 의존하고 있는데, 그 결합도는 느슨하다. 이 글을 통해 여러 번 언급할 것이지만, 이는 Quartz에 있는 모든 것들은 환경 설정이 가능하거나 사용자가 정의해 지정할 수 있음을 의미한다. 예를 들면, 여러분이 정의한 "ThreadPool" 관리 기능을 플러그 인 형태로 끼워 넣고 싶은 경우, 이러한 작업이 가능하다.

   

Jobs, Jobs, and More Jobs

Quartz의 용어를 사용하자면, Job은 작업을 수행하는 간단한 자바 클래스이다. 이 작업은 자바에서 코드로 작성 가능한 그 어떠한 것이라도 될 수 있다. 필수적으로 요구되는 사항은, org.quartz.Job 인터페이스를 구현하고, 심각한 오류 발생 시JobExecutionException을 발생시키는 것뿐이다. 여러분은 이미 앞에서 Job 인터페이스와 포함된 execute() 메소드를 보았다.

   

Job 인터페이스와 execute() 메소드를 구현했다면, Quartz는 Job 실행 시기가 되었을 때, Job을 호출한다. execute() 메소드 안에서 무엇을 수행하는가는 전적으로 개발자에게 달려 있다. 다음은 Job 내부에서 실행할 작업들에 대한 몇몇 예이다.

   

● JavaMail이나 Commons Net과 같은 다른 메일 프레임워크를 사용하여 전자메일 전송

● EJB에 대한 원격 인터페이스를 생성한 후, 이 인터페이스의 메소드 호출

● Hibernate 세션을 얻어 관계형 데이터베이스에 있는 데이터 질의와 갱신

● OSWorkflow를 사용하여 Job으로부터 워크플로우 호출

● FTP를 사용해 파일 옮기기

● Ant 빌드 스크립트를 호출해 스케줄링 되어 있는 빌드 작업 시작

   

수많은 여러 작업들이 가능하며, 이것이 바로 Quartz 프레임워크를 매우 강력하게 만들어주는 이유이다. Quartz는 매우 일반적이고 반복적인 스케줄을 작성해주는 메커니즘을 제공하기 때문에, 개발자는 단지 실행을 위해 호출될 자바 클래스들만 작성하면 된다.

   

Job Management and Storage

Job들의 스케줄이 지정되었다면, Scheduler는 이러한 Job들을 기억하고 이들을 실행시킬 시간을 지속적으로 추적해야 한다. 만약 여러분의 Job이 30분 늦게 시작되거나 30초 일찍 시작된다면 Quartz는 그렇게 유용하지 않을 것이다. 사실, 스케줄이 지정된Job들 상에 있는 execute() 메소드를 호출하는 시간은 매우 정확해야 한다. Job 저장과 관리는 Quartz에서 JobStore로 일컬어지는 개념을 통해 이루어진다.

   

Available JobStores

Quartz 프레임워크에서는 두 가지 기본적인 JobStore 타입을 제공한다. Scheculer 정보를 유지하는데 일반적인 메모리(RAM)을 사용하는 첫 번째 타입은 RAMJobStore라 불린다. 이러한 타입의 JobStore는 설정 및 실행이 매우 간단하다. 많은 어플리케이션들에 대해 이러한 JobStore만으로도 충분할 것이다. 그러나, Scheduler 정보가 JVM에 할당되어 있는 메모리에 저장되기 때문에, 어플리케이션이 멈추게 되면, 스케줄과 관련된 모든 정보가 사라진다. 만약 어플리케이션이 재 시작하는 경우에도 이러한 스케줄 정보를 유지할 필요가 있다면, 두 번째 유형의 JobStore을 사용해야 할 것이다.

   

두 번째 타입의 JobStore는 실제로 Quartz 프레임워크에서 두 가지의 서로 다른 형태로 구현되어 제공되고 있지만, 이 둘 모두 일반적으로 JDBC JobStore로 일컬어지고 있다. 이 두 가지 모두의 JDBC JobStore는 JDBC 드라이버를 사용하며, 스케줄 정보를 유지하고 있는 관계형 데이터베이스로부터 정보를 가져온다. 이 두 가지 타입은 데이터베이스 트랜잭션을 제어하는지의 여부나 BEA의 WebLogic이나 JBoss와 같은 어플리케이션 컨테이너에 제어를 넘기는지의 여부에 그 차이점이 존재한다 (이는J2EE에서의 BMT와 CMT 사이의 차이점과 유사하다).

   

두 가지 유형의 JDBC JobStore는 다음과 같다.

   

● JobStoreTX: 트랜잭션을 제어하고 싶은 경우나, 서버 환경 없이 어플리케이션을 운영하려 할 때 사용된다.

● JobStoreCMT: 어플리케이션 서버 환경 내에서 어플리케이션이 운영되며 컨테이너가 트랜잭션을 관리하도록 하고 싶은 경우 사용된다.

   

JDBC JobStore는 어플리케이션이 중지되고 다시 시작된 후에라도 스케줄링 정보를 유지하여 Scheduler가 실행되도록 만들어야 할 경우를 위해 설계되었다.

   

Job and Triggers

Quartz 설계자들은 Job과 스케줄을 분리하였다. Quartz에서 Trigger는 Job이 트리거링 되거나 발생되어야 할 때, Scheduler에게 알려주는데 사용된다. Quartz 프레임워크에서는 간단한 Trigger 타입들을 제공하고 있는데, SimpleTrigger와 CronTrigger가 가장 일반적으로 사용된다.

   

SimpleTrigger는 스케줄을 간단히 발생시키는데 사용될 목적으로 설계되었다. 일반적으로, 주어진 시간에 Job을 발생시켜 (m)초 간격을 두고 여러 번(n) 이를 실행할 필요가 있을 경우, SimpleTrigger가 적합한 선택이 된다. 반면, Job에 요구되는 스케줄링이 복잡할 경우, CronTrigger가 적합할 것이다.

   

CronTrigger는 달력과 유사한 스케줄에 기반하고 있다. 만약 여러분의 Job이 매주 토요일과 일요일을 제외한, 매일 오전 10시30분마다 실행되어야 하는 경우에, CronTrigger가 사용된다. 이 이름이 암시하고 있듯, CronTrigger는 Unix의 cron 표현식을 기반으로 하고 있다. 예를 들면, 다음의 Quartz cron 표현식은 월요일부터 금요일에 걸쳐 매일 오전 10시 15분에 Job을 실행할 것이다.

   

0 15 10 ? * MON-FRI

   

그리고 다음 표현식은 2002, 2003, 2004, 2005년 동안 매월 마지막 금요일 오후 10시 15분에 Job을 실행할 것이다.

   

0 15 10 ? * 6L 2002-2005

   

이러한 작업은 SimpleTrigger로  수행할 수 없다. 이 둘 모두 Job에 사용될 수 있다. 어떠한 것을 사용할 지에 관한 선택은 스케줄링 될 작업 성격에 달려 있다.

   

Scheduling a Job

이제 예제 Job을 살펴보면서 실제 사용에 관한 부분에 대해 토의해보자. 클라이언트가 자신의 FTP 사이트에 파일을 저장할 때마다 부서에 전자 메일로 통지할 필요가 있는 상황을 여러분이 관리하고 있다고 가정해보자. 우리의 Job은 원격 서버에 있는 파일들을 다운로드 하는 것이 된다. 그런 후, Job은 발견된 파일들의 다운로드 횟수를 포함하고 있는 전자 메일을 전송하게 된다. 이Job은 누군가가 하루 동안 이러한 작업을 수작업으로 전송할 필요가 없도록 편리하게 만들어준다. 우리는 이러한 Job이 일주일 내내 하루 24시간 동안 매 60초마다 검사하도록 설정할 수 있다. 이는 바로 Quartz 프레임워크를 완벽하게 사용하는 예이다.

   

첫 번째 단계는 FTP와 Email 로직을 수행하는 Job 클래스를 작성하는 것이다. 다음 예제는 Quartz Job 클래스를 나타낸 것으로, org.quartz.Job 인터페이스를 구현하고 있다.

   

예제 2. FTP 사이트에서 파일들을 다운로드 받고 Email을 전송하는 Quartz Job

   

public class ScanFTPSiteJob implements Job {

    private static Log logger = LogFactory.getLog(ScanFTPSiteJob.class);

   

    /*

     * 정확한 시간에 스케줄러 프레임워크에 의해 호출된다.

    */

    public void execute(JobExecutionContext context) throws JobExecutionException {

        JobDataMap jobDataMap = context.getJobDataMap();

   

        try {

            // FTP 사이트에서 파일들 검사

            File[] files = JobUtil.checkForFiles(jobDataMap);

            JobUtil.sendEmail(jobDataMap, files);

        } catch (Exception ex) {

            throw new JobExecutionException(ex.getMessage());

        }

    }

}

   

이 글에서는 일부러 ScanFTPSiteJob을 매우 간단하게 구성하였다. 또한 이 글에서는 이 예제를 위해 JobUtil이라는 유틸리티 클래스를 작성하였다. 이 클래스는 Quartz의 부분이 아니지만, 다양한 Job들에서 재사용 할 수 있는 여러분만의 유틸리티 성격의 라이브러리를 만드는 것이 좋다. 이 글에서는 Job 클래스와 Quartz Scheduler 내부에 모든 코드들을 쉽게 둘 수도 있었지만, Quartz를 사용하는 것 때문에 재사용을 고려하지 않을 수는 없었다.

   

JobUtil.checkForFiles()와 JobUtil.sendEmail()이 사용하는 파라미터들은 JobDataMap 객체를 사용하고 있는데, 이 객체는Quartz가 생성한 객체이다. 이 인스턴스는 Job이 실행될 때마다 생성되며, 이를 사용해 Job 클래스로 환경 설정 파라미터들을 넘겨준다.

   

JobUtil의 구현 부분은 여기에 나타내지 않았지만, 우리는 FTP와 Email 기능을 모두 구현하고 있는 Jakarta의 Commons Net을 통해 매우 쉽게 사용할 수 있었다.

   

Calling Your Jobs with the Scheduler

Job을 생성하는 것이 첫 번째 작업이지만, Scheduler에 의해 Job이 호출되도록 하기 위해서는 Scheduler에게 얼마나 자주, 언제 Job이 호출되어야 하는지 알려주어야 한다. 이 작업은 Trigger를 Job에 연관시킴으로써 이루어진다. 우리는 Scheduler가 계속 매 60초마다 Job을 호출하는데 관심을 두고 있기 때문에, SimpleTrigger를 사용할 것이다.

   

Job과 Trigger의 스케줄은 Quartz Scheduler 인터페이스를 통해 이루어진다. Scheduler의 인스턴스를 얻기 위해서는 팩토리로부터 인스턴스를 얻어와야 한다. 이를 위한 가장 쉬운 방법은 StdSchedulerFactory 클래스의 static 메소드인getDefaultScheduler()를 호출하는 것이다.

   

Quartz 프레임워크를 사용할 때, 반드시 start() 메소드를 호출하여 Scheduler를 시작시켜야 한다. 예제 3에 있는 코드는 대부분의 Quartz 어플리케이션의 일반적인 패턴을 따르고 있다. 대부분의 Quartz 어플리케이션에서는, 하나 또는 그 이상의 Job들을 생성하고 Trigger들을 생성하고 설정한 후, Scheduler에 Job과 Trigger들에 대한 스케줄을 정하고 Scheduler를 시작시킨다(주: Scheduler를 먼저 시작시켜도 된다. 이는 중요하지 않다).

   

예제 3. Quartz Job들은 Quartz Scheduler를 통해 스케줄이 지정되어야 한다.

   

public class MyQuartzServer {

    public static void main(String[] args) {

        MyQuartzServer server = new MyQuartzServer();

   

        try {

            server.startScheduler();

        } catch(SchedulerException ex) {

            ex.printStackTrack();

        }

    }

   

    protected void startScheduler() throws SchedulerException {

        // 팩토리를 사용해 Scheduler 인스턴스를 생성

        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

   

        // JobDetail은 Job들에 대한 정의를 포함한다.

        JobDetail jobDetail = new JobDetail("ScanFTPJob", Scheduler.DEFAULT_GROUP,

                                          ScanFTPSiteJob.class);

        // execute() 내에서 사용될 job 파라미터들을 저장

        jobDetails.getJobDataMap().put("FTP_HOST", "\home\cavaness\inbound");

   

        // 여기에 Job 파라미터들에 필요한 기타 다른 내용들이 온다.

   

        // 매 60초마다 발생하는 Trigger 인스턴스 생성

        Trigger trigger = TriggerUtils.makeSecondlyTrigger(60);

   

        // Scheduler에 Job과 Trigger를 설정

        scheduler.scheduleJob(jobDetail, trigger);

   

        // Scheduler 실행 시작

        scheduler.start();

    }

}

   

Programmatic vs. Declarative Scheduling

예제 3에서, 우리는 프로그래밍을 통해 ScanFTPSiteJob 스케줄을 작성했다. 즉, Scheduler에 Job과 Trigger를 설정하기 위해 자바 코드를 사용하였다는 의미이다. Quartz 프레임워크에서는 XML 파일들에 Job 스케줄을 선언적으로 설정할 수 있는 기능을 지원하고 있다. 선언적인 접근 방법을 통해 어떠한 Job이 언제 실행되어야 하는지를 보다 빠르게 지정할 수 있다.

   

Quartz 프레임워크에는 Quartz 어플리케이션이 시작하자마자, Job과 Trigger 정보를 포함하고 있는 XML 파일을 읽어 들이는"플러그 인"을 포함하고 있다. XML 내에 있는 모든 Job들은 이들과 관련된 Trigger들과 함께 Scheduler에 추가된다. 물론 Job클래스들을 작성해야 하지만, 그러한 Job들을 갖는 Scheduler의 환경을 설정하는 것은 매우 동적으로 이루어진다. 예제 4는 예제 3의 코드에 있는 동일한 로직을 수행하는데, 선언적인 방법으로 구성되어 있다.

   

예제 4. Quartz Job들은 XML 파일을 사용하여 스케줄링 될 수 있다.

   

<?xml version="1.0" encoding="utf-8"?>

<quartz>

    <job>

        <job-detail>

            <name>ScanFTPSiteJob</name>

            <group>DEFAULT</group>

            <description>A job that scans an ftp site for files</description>

            <job-class>ScanFTPSiteJob</job-class>

   

            <job-data-map allows-transient-data="true">

                <entry>

                    <key>FTP_HOST</key>

                    <value>homecavanessinbound</value>

                </entry>

   

                <!-- 다른 필요한 Job 파라미터들을 여기에 둔다 -->

            </job-data-map>

        </job-detail>

   

        <trigger>

            <simple>

                <name>ScanFTPSiteJobTrigger</name>

                <group>DEFAULT</group>

                <job-name>ScanFTPSiteJob</job-name>

                <job-group>DEFAULT</job-group>

                <start-time>2005-09-11 6:10:00 PM</start-time>

                <!-- 계속 60초마다 반복 실행 -->

                <repeat-count>-1</repeat-count>

                <repeat-interval>60000</repeat-interval>

            </simple>

        </trigger>

    </job>

</quartz>

   

예제 4에 있는 XML 엘리먼트들을 예제 3에 있는 자바 코드와 비교해볼 수도 있다. 이들은 개념적으로 동일하다. 예제 4에 보여지는 것과 같은 선언적인 접근 방법의 장점은 유지보수가 매우 간단해진다는 것이다. XML 파일만 변경하고 Quartz 어플리케이션을 재 시작 하기만 하면 되기 때문이다. 소스 코드를 수정하고 재 컴파일 하여 배포할 필요가 없다.

   

Stateful and Stateless Jobs

이 글에서 살펴보았던 Quartz Job 예제는 모두 상태 정보를 가지고 있지 않다. 즉, 각각의 Job이 실행에 대해, Job이 실행되는 동안 JobDataMap에 가해진 어떠한 변경 사항들도 유지되지 않는다는 것을 의미한다. 만약 JobDataMap에 값을 추가, 변경 또는 삭제하는 기능이 필요하며, 다음 실행에 이러한 변경 사항들이 Job에 반영되도록 해야 한다면, Quartz Stateful Job이 필요하다.

   

만약 여러분이 EJB 개발을 경험해 보았다면, Stateful이라는 것이 부정적인 의미를 담고 있다는 점 때문에 여러분은 지금 움찔하고 있을 것이다. 이는 주로 "Stateful EJB"가 가지고 있는 확장성 이슈로부터 기인한다. Quartz Stateful Job은org.quartz.StatefulJob 인터페이스를 통해 구현된다. Stateless Job과 Stateful Job 간의 주요 차이점은, Stateful Job은 한 번에Job을 실행하는 인스턴스가 오직 하나를 가질 수 있다는 점이다. 그러므로, 예제 3의 경우, "ScanFTPJob" Job을 실행하는 인스턴스는 한 번에 하나만 가지게 된다. 대부분의 경우, 이는 커다란 문제점을 나타내지는 않는다. 그러나, 만약 자주 실행될 필요가 있는 Job을 가지고 있다거나, 작업 완료까지 오랜 시간을 필요로 하는 Job을 가지고 있을 경우, Stateful Quartz Job은 확장성에 문제를 가져다 줄 수 있다.

   

Other Features of the Quartz Framework

Quartz 프레임워크는 매우 다양한 기능들을 가지고 있다. 사실, 한 번에 이 모든 기능들을 나열하기에는 너무나 많다. 다음 목록은 이 글에서는 자세히 언급할 시간이 없는 Quartz 내의 여러 기능들 중 몇몇을 간단하게나마 설명한 것이다.

   

Listeners and Plugins

오늘날 어떠한 오픈 소스 프레임워크라도 사용하고 있는 개념이 바로 이 둘이다.

   

Quartz Listener는 주요 이벤트가 발생할 때, 프레임워크 내부로부터 콜백을 받는 자바 클래스이다. 예를 들면, Job이 스케줄 되어 있거나 스케줄 되어 있지 않을 때, 또는 Trigger가 끝났거나, 더 이상 발생시키지 않을 때, 이러한 모든 것들은 Listener로 통지되도록 설정될 수 있다. Quartz 프레임워크는 Scheduler, Job, Trigger들을 위한 Listener들을 포함하고 있다. 또한, Job Listener와 Trigger Listener들을 특정한 한 Job이나 Trigger에 적용되도록 만들거나 전체에 걸쳐 적용되도록 설정할 수도 있다.

   

일단 Listener가 호출되면, 이 정보를 사용해 Listener 클래스 내에서 수행하려는 어떠한 로직이라도 실행시킬 수 있다. 예를 들면, 만약 Job이 완료될 때마다 전자 메일을 보내고 싶은 경우, 이를 Job에 프로그래밍 해 넣을 수 있다. 또한, JobListener를 사용할 수도 있다. 이 JobListener는 결합도를 느슨하게 만들어 보다 나은 설계를 만들어주는데 도움을 줄 수도 있다.

   

Quartz Plugin은 Quartz 소스를 수정하지 않고도 Quartz 프레임워크에 기능을 추가시켜주는 새로운 기능이다. 이것은 Quartz프레임워크를 확장해야 하는데, 변경한 기능을 Quartz 개발 팀에게 보내주고 다음 버전에 반영되기까지 기다릴 시간이 없는 개발자들을 위한 기능이다. 여러분이 Struts 플러그 인에 익숙하다면, Quartz 플러그 인을 사용하는 방법을 더 쉽게 이해할 수 있을 것이다.

   

Clustering Quartz Applications

Quartz 어플리케이션은 여러분의 요구 사항에 따라 수직/수평적으로 모두 클러스터링 될 수 있다. 클리스터링을 통해 다른 클러스터링 타입과 마찬가지의 이점들을 제공받을 수 있다.

   

● 확장성

● 높은 가용성

● 로드 밸런싱

   

현재 Quartz는 관계형 데이터베이스와 JDBC JobStore중 하나의 도움을 통해 클러스터링을 지원한다. 향후 버전에서는, 이러한 제한이 사라져, 데이터베이스 없이도 RAMJobStore에서 사용 가능해질 것이다.

   

The Quartz Web Application

Quartz 프레임워크를 2-3주나 몇 달간 사용한 후 보통 Quartz 사용자들이 가장 많이 요구하는 것들 중 하나는 Quartz를 GUI에 통합하는 것에 대한 것이다. 자바 서블릿을 사용하여 Quartz를 초기화 하고 시작시킬 수 있는 기능이 현재 프레임워크에 제공되고 있다. 일단 여러분이 Scheduler 인스턴스에 접근하게 되면, 이 인스턴스는 웹 컨테이너의 ServletContext에 저장하고Scheduler 인터페이스를 통해 스케줄링 환경을 관리할 수 있다.

   

다행히도, 몇몇 Quartz 개발자들은 Quartz Scheduler 환경을 보다 잘 관리하는데 사용될 수 있는 독립형 Quartz Web Application에 대해 작업해 오고 있다. Struts와 Spring과 같은 다수의 유명한 오픈 소스 프로젝트를 기반으로 구축되어 이 GUI는 많은 기능들을 지원하는데, 이들은 간단한 인터페이스에 랩핑 되어 있다. 그림 1은 이 GUI를 잡아낸 것이다.

   

   

그림 1. Quartz 환경을 보다 쉽게 관리해주는데 도움을 주는 Quartz Web Application

   

What's down the Road?

이미 다음 주요 릴리즈에 대한 움직임도 진행되고 있을 정도로 Quartz 프로젝트는 활발하게 진행되고 있다. OpenSymphony의wiki에서 Quartz 2.0에서 고려되고 있는 기능들과 설계에 대한 정보를 얻을 수도 있다.

   

항상 그렇듯이, 날마다 Quartz 사용자들은 프레임워크에서 고려될 수 있는 갖가지 기능들에 대한 제안이나 설계에 관해 자유롭게 제안하고 있다.

   

Find Out More about Quartz

Quartz 프레임워크의 보다 많은 기능들을 사용해 나갈수록, User and Developer Forum은 Quartz 사용자들과의 질문/답변 및 커뮤니케이션을 위한 매우 유용한 자원이 될 것이다.

   

출처 : http://blog.empas.com/kkamdung/12297998

[출처] Quartz - java 스케쥴러|작성자 순짱

 

===================================================================================

 

가끔 서버에서 주기적으로 어떠한 작업을 하고자 할때 리눅스에서는 크론탭을 사용하여 주기적으로 어떠한 작업을 처리합니다.
이런 주기적 작업을 처리하기위해 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 사용하시면 됩니다<?xml version="1.0" encoding="UTF-8" ?>.
<Cron type setting>

<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

 

출처 - http://javastore.tistory.com/96


===================================================================================


매번 cronTab 으로 배치작업을 실행시키며, 이게 아직 쓰는 놈인지, 언제 만들었는지,
수정하기 번거롭네 등 에로사항이 많았으.. 기억도 안나고.. 그때그때 만든 것들은 버전관리도 안되고,,
해서. 스프링 프로젝트이니, 스프링에서 동작하는 배치를 쓰는게 낫겠다는 생각이 들어 이참에, 배치를 몽땅 quartz 
로 만들어서 정리를 해야겠다는 생각이 들었심.

스프링 프로젝트 환경이 세팅이 되어있다고 치고,

 

*필자의 경우는 
Dynamic Web Project 로 qbatch 라는 프로젝트를 만들었고,


디렉토리 구조는
qbatch
  |_src
  |_WebContent
          |_index.jsp
          |_META-INF
          |_WEB-INF
              |_classes
              |_lib
              |_views
              |_action-servlet.xml
              |_web.xml
              
이렇게 되어있고, 
src 폴더에는 비지니스를 구현한 java 파일들(cotroller/service/dao 등),
classes 에는 src 의 컴파일된 class 파일들 과 applicationContext.xml 및 각종 properties 파일들,
lib 폴더에는 spring.jar, activation.jar, log4j.jar, mysql-connector-java-xxx.jar 등이,
views 에는 각종 jsp 파일들이 위치해있다.

 

암튼 시작해보면,

 

1. 일단 quartz 라이브러리를 다운받는다.
http://terracotta.org/downloads/open-source/destination?name=quartz-1.8.5.tar.gz&bucket=tcdistributions&file=quartz-1.8.5.tar.gz
압축을 풀고 quartz-all-1.8.5.jar 파일을 찾아서 
WEB-INF/lib 폴더 밑에 복사해 넣으면 준비끝.

 

2. 실제 비지니스를 실행할 java 파일을 만든다.            
나의 비지니스 로직은 System.out.println("음. 배치가 실행되었구만~!!!!"); 이다.
이 로직만 빼고, 나머지는 코드는 동일하게 쓰면되심.

src/com/batch/service/SystemOutTestService.java 를 아래와같이 만든다.

 

 

package com.batch.service;

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

public class SystemOutTestService extends QuartzJobBean {
 
 
 @Override
 protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
  try {
   
   System.out.println("음. 배치가 실행되었구만~!!!!");
  } catch (Exception e) {
   e.printStackTrace();
  }
  
 }

}

 


3. 이제 저 SystemOutTestService.java 가 실행스케줄링 되도록 applicationContext.xml 을 만져주자.
1. 실행할 비지니스로직 클래스를 빈으로 등록.
2. 해당빈이 스케줄을 만들 트리거 설정
3. 실제 동작되도록 설정.

이렇게 3가지만 세팅하면 되심. 아래와같이.

<beans 어쩌구저쩌구.... >

<!-- 1. 실행할 비지니스로직 클래스를 빈으로 등록. -->
<bean id="SystemOutTest" class="org.springframework.scheduling.quartz.JobDetailBean">
 <property name="jobClass" value="com.batch.service.SystemOutTestService"/>
 </bean>

<!-- 2. 해당빈이 스케줄을 만들 트리거 설정 -->
 <bean id="SystemOutTestTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail" ref="SystemOutTest"/>
  <property name="repeatInterval" value="3000"/> <!--  every 1000(1초)  -->
  <property name="startDelay" value="2000" /><!--  at first execution 2000(2초) 후에 실행  -->
 </bean>
 
<!-- 3. 실제 동작되도록 설정. -->
 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" autowire="no">
  <property name="triggers">
   <list>
    <ref bean="SystemOutTestTrigger"/>
   </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">600000</prop>
   </props>
  </property>
 </bean>
 
 
이제 톰캣 재시작 하면 3초마다 "음. 배치가 실행되었구만~!!!!" 이라고 찍힐거임.
비지니스 로직이 넘 초라하니, 클래스 추가하여 실제 필요한 동작을 하게끔 비지니스 로직에
시간을 들이심이 좋을듯 해서 초간단으로 적었심. 더 세심한 설정은 구글형이나 네이년에
물어보시면 찾으실 수 있으실거임.


출처 - http://jejoong76.blog.me/70125014485


===================================================================================


Spring에서 Quartz JOB 설정 예제

  • 한 시간에 한번 keyGeneratorJob을 실행 시키는 예제 입니다.

applicationContext.xml 설정

<bean id="keyPool" class="com.oracleclub.web.support.KeyPool" init-method="init" lazy-init="true">
  <property name="keySize" value="1000"/>		
</bean>

<!-- jobClass 설정 -->
<bean id="keyGeneratorJob" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass" value="com.oracleclub.web.job.KeyGeneratorJob"/>
  <property name="jobDataAsMap">
	<map>
      <entry key="keyPool">
   	    <ref bean="keyPool"/>
  	  </entry>
 	</map>
  </property>
</bean>

<!-- jobTrigger 설정 -->
<bean id="keyGeneratorTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail" ref="keyGeneratorJob" />
  <!--  1000 == 1 second, 60minuts=3600000, 1day=86400000-->
  <property name="startDelay" value="3600000" />
  <property name="repeatInterval" value="3600000" />
</bean>

<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >		
  <property name="triggers">
    <list>
      <ref bean="keyGeneratorTrigger"/>
    </list>
  </property>
</bean>

JAVA 예제 소스

  • 위에서 JobClass로 지정한 com.oracleclub.web.job.KeyGeneratorJob 소스 입니다.
  • 아래 소스는 한 시간에 한번씩 암호화 키를 다시 생성하는 예제 입니다.
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * Key Generator Job
 * 
 * @author : oramaster
 * 
 */
public class KeyGeneratorJob extends QuartzJobBean {
  private KeyPool keyPool;
  private final static Log LOG = LogFactory.getLog(KeyGeneratorJob.class);


  @Override
  protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

    List<KeyGenerator> newList = new ArrayList<KeyGenerator>();
    KeyGenerator keyGenerator;

    try {
      //배치때 실행되는 비지니스 로직
      for (int i = 0; i < keyPool.getKeySize(); i++) {
        keyGenerator = new KeyGenerator();
        byte[] originKey = XORMask.getGeneratorKey();

        keyGenerator.setBlowFishKey(BlowFish.encrypt(originKey, BlowFish.getKey()));
        keyGenerator.setOriginKey(originKey);

        newList.add(keyGenerator);
      }

      keyPool.setKeyList(newList);

    } catch (Exception e) {
      LOG.error(e.getMessage());
      throw new JobExecutionException(e.getMessage());
    }
  }

  public void setKeyPool(KeyPool keyPool) {
    this.keyPool = keyPool;
  }
}

문서정보


===================================================================================


Chapter 19. Quartz 혹은 Timer 를 사용한 스케쥴링

19.1. 소개

Spring은 스케쥴링을 지원하는 통합 클래스들을 제공한다. 현재적으로, Spring은 1.3 이후버전 JDK의 일부분인 Timer와 Quartz 스케쥴러 (http://www.quartzscheduler.org)를 지원하고 있다. 이 두개의 스케쥴러들은 각각 Timer 혹은 Triggers에 대한 선택적 참조를 가지는 FactoryBean을 사용하여 세팅된다. 게다가 당신이 타겟 object의 메써드를 편리하게 호출할 수 있도록 도와주는 Quartz 스케쥴러와 Timer에 대한 편의 클래스를 제공한다.(이것은 일반적인 MethodInvokingFactoryBeans와 비슷하다.)

19.2. OpenSymphony Quartz 스케쥴러 사용하기

Quartz는 TriggersJobs 그리고 모든 종류의 jobs를 인식하고 있는 JobDetail를 사용한다. Quartz에 깔려 있는 기본적인 개념을 알고 싶다면, http://www.opensymphony.com/quartz를 찾아보길 바란다. 편리한 사용을 위해서, Spring은 Spring 기반 어플리케이션 내에서 Quartz의 사용을 손쉽게 만들어주는 두 개의 클래스들을 제공한다.

19.2.1. JobDetailBean 사용하기

JobDetail 객체는 job을 실행하기 위해 필요한 모든 정보를 가지고 있다. Spring은 소위 JobDetailBean이라고 불리는 클래스를 제공하는데, 이것은 JobDetail을 합리적인 디폴트값을 가진 실질적인 JavaBean 객체로 만들어준다. 다음의 예제를 보도록 하자.

<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass">
    <value>example.ExampleJob</value>
  </property>
  <property name="jobDataAsMap">
    <map>
      <entry key="timeout"><value>5</value></entry>
    </map>
  </property>
</bean>
			

위의 job detail bean은 job(ExampleJob)을 실행하기 위한 모든 정보를 가지고 있다. 타임아웃은 job data map으로 기술되었다. job data map은 (실행시 넘겨지는) JobExecutionContext를 통해 이용할 수 있지만, JobDetailBean 역시 job data map으로부터 실질적인 job의 프라퍼티들을 매핑할 수 있다. 때문에 이러한 경우, 만약 ExampleJob이 timeout이라는 프라퍼티를 가지고 있다면, JobDetailBean은 그것을 자동으로 적용할 것이다.

package example;

public class ExampleJob extends QuartzJobBean {

  private int timeout;
  
  /**
   * Setter called after the ExampleJob is instantiated
   * with the value from the JobDetailBean (5)
   */ 
  public void setTimeout(int timeout) {
    this.timeout = timeout;
  }
  
  protected void executeInternal(JobExecutionContext ctx)
  throws JobExecutionException {
      // do the actual work
  }
}
			

당신은 job detail bean의 모든 부가적인 세팅들 역시 마찬가지로 이용할 수 있다.

주의: name과 group 프라퍼티를 사용함으로써, 당신은 job의 name과 group을 변경할 수 있다. default로 job의 이름은 job detail bean의 이름과 동일하다.(위의 예에서는 exampleJob이 된다.)

19.2.2. MethodInvokingJobDetailFactoryBean 사용하기

종종 당신은 특정한 객체의 메써드를 호출할 필요가 있을 것이다. 당신은 MethodInvokingJobDetailFactoryBean을 사용하여 다음과 같이 할 수 있다.

<bean id="methodInvokingJobDetail" 
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
</bean>

위의 예는 (아래에 있는) exampleBusinessObject의 doIt을 호출하는 것을 의미한다.

public class BusinessObject {
  
  // properties and collaborators
  
  public void doIt() {
    // do the actual work
  }
}
			

<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>
			

MethodInvokingJobDetailFactoryBean을 사용할 때, 메써드를 호출할 한줄짜리 jobs를 생성할 필요가 없으며, 당신은 단지 실질적인 비지니스 객체를 생성해서 그것을 묶기만 하면된다.

default로는 Quartz Jobs는 비상태이며, 상호 작용하는 jobs의 가능성을 가진다. 만약 당신이 동일한 JobDetail에 대해 두 개의 triggers를 명시한다면, 첫번째 job이 끝나기 이전에 두번째가 시작할지도 모른다. 만약 JobDetail 객체가 상태 인터페이스를 구현한다면, 이런 일은 발생하지 않을 것이다. 두번째 job은 첫번째가 끝나기 전에는 시작하지 않을 것이다. MethodInvokingJobDetailFactoryBean를 사용한 jobs가 동시작용하지 않도록 만들기 위해서는, concurrent 플래그를 false로 세팅해주어야 한다.

<bean id="methodInvokingJobDetail"
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
    <property name="concurrent"><value>false</value></property>
</bean>
			

주의: 기본적으로 jobs는 concurrent 옵션에 따라 실행될 것이다.

19.2.3. triggers 와 SchedulerFactoryBean을 사용하여 jobs를 묶기

우리는 job details과 jobs를 생성했고, 당신이 특정 객체의 메써드를 호출할 수 있도록 하는 편의클래스 bean을 살펴보았다. 물론, 우리는 여전히 jobs를 그자체로 스케쥴할 필요가 있다. 이것은 triggers와 SchedulerFactoryBean을 사용하여 이루어진다. 여러가지 triggers는 Quartz 내에서 이용할 수 있다. Spring은 편의를 위해 2개의 상속받은 triggers를 기본적으로 제공한다.:CronTriggerBean과 SimpleTriggerBean이 그것이다.

Triggers는 스케쥴될 필요가 있다. Spring은 triggers를 세팅하기 위한 프라퍼티들을 드러내는 SchedulerFactoryBean을 제공하고 있다. SchedulerFactoryBean은 그 triggers와 함께 실질적인 jobs를 스케쥴한다.

다음 두가지 예를 보자.

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail">
    <!-- see the example of method invoking job above -->    
    <ref bean="methodInvokingJobDetail"/>
  </property>
  <property name="startDelay">
    <!-- 10 seconds -->
    <value>10000</value>
  </property>
  <property name="repeatInterval">
    <!-- repeat every 50 seconds -->
    <value>50000</value>
  </property>
</bean>

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail">
    <ref bean="exampleJob"/>
  </property>
  <property name="cronExpression">
    <!-- run every morning at 6 AM -->
    <value>0 0 6 * * ?</value>
  </property>
</bean>
			

OK, 이제 우리는 두 개의 triggers를 세팅했다. 하나는 10초 늦게 실행해서 매 50초마다 실행될 것이고, 다른 하나는 매일 아침 6시에 실행될 것이다. 모든 것을 완료하기 위해서, 우리는 SchedulerFactoryBean을 세팅해야 한다.

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
    <list>
      <ref local="cronTrigger"/>
      <ref local="simpleTrigger"/>
    </list>
  </property>
</bean>
			

당신이 세팅할 수 있는 더욱 많은 속성들이 SchedulerFactoryBean에 있다. 이를테면, job details에 의해 사용되는 calendars라던가, Quartz를 커스터마이징할 수 있게 하는 프라퍼티같은 것들이 말이다. 더 많은 정보를 위해서는 JavaDoc(http://www.springframework.org/docs/api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html)을 참조하도록 해라.

19.3. JDK Timer support 사용하기

Spring에서 스케쥴링 업무를 처리하는 또다른 방법은 JDK Timer 객체들을 사용하는 것이다. Timers 자체에 대한 더 많은 정보는http://java.sun.com/docs/books/tutorial/essential/threads/timer.html에서 찾아볼 수 있다. 위에서 살펴 본 기본개념들은 Timer support에도 마찬가지로 적용된다. 당신은 임의의 timers를 생성하고 메써드들을 호출하기 위해 timer를 사용한다. TimerFactoryBean을 사용하여 timers를 묶는다.

19.3.1. 임의의 timers 생성하기

당신은 TimerTask를 사용하여 임의의 timer tasks를 생성할 수 있다. 이것은 Quartz jobs와 유사하다

public class CheckEmailAddresses extends TimerTask {

  private List emailAddresses;
  
  public void setEmailAddresses(List emailAddresses) {
    this.emailAddresses = emailAddresses;
  }
  
  public void run() {
    // iterate over all email addresses and archive them
  }
}
			

이것을 묶는 것 역시 간단하다:

<bean id="checkEmail" class="examples.CheckEmailAddress">
  <property name="emailAddresses">
    <list>
      <value>test@springframework.org</value>
      <value>foo@bar.com</value>
      <value>john@doe.net</value>
    </list>
  </property>
</bean>

<bean id="scheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
  <!-- wait 10 seconds before starting repeated execution -->
  <property name="delay">
    <value>10000</value>
  </property>
  <!-- run every 50 seconds -->
  <property name="period">
    <value>50000</value>
  </property>
  <property name="timerTask">
    <ref local="checkEmail"/>
  </property>
</bean>
			

task를 단지 한번만 실행하고자 한다면, period 속성을 -1(혹은 다른 음수값으)로 바꿔주면 된다.

19.3.2. MethodInvokingTimerTaskFactoryBean 사용하기

Quartz support와 비슷하게, Timer 역시 당신이 주기적으로 메써드를 호출할 수 있도록 하는 요소들을 기술한다.

<bean id="methodInvokingTask" 
  class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
</bean>

위의 예제는 (아래와 같은) exampleBusinessObject에서 호출되는 doIt에서 끝날 것이다

public class BusinessObject {
  
  // properties and collaborators
  
  public void doIt() {
    // do the actual work
  }
}
			

ScheduledTimerTask가 언급된 위의 예제의 참조값을 methodInvokingTask로 변경하면 이 task가 실행될 것이다.

19.3.3. 감싸기 : TimerFactoryBean을 사용하여 tasks를 세팅하기

TimerFactoryBean은 실질적인 스케쥴링을 세팅한다는 같은 목적을 제공한다는 점에서 Quartz의 SchedulerFactoryBean과 비슷하다. TimerFactoryBean는 실질적인 Timer를 세팅하고 그것이 참조하고 있는 tasks를 스케쥴한다. 당신은 대몬 쓰레드를 사용할 것인지 말것인지를 기술할 수 있다.

<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
  <property name="scheduledTimerTasks">
    <list>
      <!-- see the example above -->
      <ref local="scheduledTask"/>
    </list>
  </property>
</bean>
			

끝!


출처 - http://openframework.or.kr/framework_reference/spring/ver1.2.2/html/scheduling.html

Posted by 1010
반응형

 인터넷이 되지 않는 내부망에서 spring project 생성시 caching에 문제가 생길수 있다.

이때 spring-context-support.jar and spring-context.jar on the classpath. 를 잡아주면 어느정도 문제점은 해결된다.

eclipse 가 제대로 인식하지 못하는 버그가 있음

element 'cache:annotation-driven' of schema namespace 'http://www.springframework.org

you need spring-context-support.jar and spring-context.jar on the classpath.

 

Posted by 1010
반응형

spring 3.2 No setter found for property 'cronExpression' in class


Quartz Scheduler 2.x 적용 중 다음과 같은 오류 발생


spring 3.2 No setter found for property 'cronExpression' in class


해결


spring reference에 나와있는 CronTriggerBean 대신 CronTriggerFactoryBean class를 사용함


1
2
3
4
5
<bean class="org.springframework.scheduling.quartz.CronTriggerBean" id="cronTrigger">
<property ref="exampleJob" name="jobDetail">
<!-- run every morning at 6 AM -->
<property name="cronExpression" value="0 0 6 * * ?">
</property></property></bean>


대신에 아래와 같이 CronTriggerFactoryBean을 사용함.


1
2
3
4
5
<bean class="org.springframework.scheduling.quartz.CronTriggerFactoryBean" id="cronTrigger">
<property ref="exampleJob" name="jobDetail">
<!-- run every morning at 6 AM -->
<property name="cronExpression" value="0 0 6 * * ?">
</property></property></bean>


참고


If You are using Spring 3.x & Quartz 2.1.x…

Then do only two changes IN YOUR configuration file 1st : for Simple Trigger

Use class=”org.springframework.scheduling.quartz.SimpleTriggerFactoryBean”> instead of class=”org.springframework.scheduling.quartz.SimpleTriggerBean”>

2nd : for Cron Trigger useclass=”org.springframework.scheduling.quartz.CronTriggerFactoryBean” instead ofclass=”org.springframework.scheduling.quartz.CronTriggerBean”


Spring 3 + Quartz 2 error

 

출처 :http://shuiky.tistory.com/728

Posted by 1010
반응형

출처 : http://slothink.tistory.com/m/post/view/id/74

 

maven 의 assmbly 기능을 이용해서 단일 dependency 로 만들었을 경우(jar-with-dependencies 사용)에 만들어진 jar 파일로 실행하면 spring.scheas 파일들이 중복되는 오류가 발생한다.

해당 이슈에 대하여서 Spring JIRA 에 등록되어 있다. http://jira.codehaus.org/browse/MASSEMBLY-360

여하튼, 해결 방법은 assembly:assembly 를 시행할 프로젝트의 리소스 폴더에 META-INF 하위로 스프링 스키마 파일들을 복사해놓고 assembly 시키는 것이다.

예를 들어
디렉터리 구조
src/main/resources/META-INF/spring.handlers
src/main/resources/META-INF/spring.schemas

console>mvn assembly:assembly

하면 된다. 스키마 파일들은 스프링 jar 파일들을 풀면 구할 수 있다.

 

Posted by 1010
반응형
이클립스 + 톰캣 + 스프링 MVC + maven 개발환경 구축 - 1장 - http://springmvc.egloos.com/429363
이클립스 + 톰캣 + 스프링 MVC + maven 개발환경 구축 - 2장 - http://springmvc.egloos.com/429570
이클립스 + 톰캣 + 스프링 MVC + maven 개발환경 구축 - 3장 - http://springmvc.egloos.com/429779
이클립스에서 SpringMVC 테스트(JUnit) 환경 구축하기 - http://springmvc.egloos.com/438345


출처 : http://springmvc.egloos.com/429363
Posted by 1010
반응형
Posted by 1010
반응형

Spring - Java/J2EE Application Framework

Reference Documentation

Rod Johnson

Juergen Hoeller

Alef Arendsen

Colin Sampaleanu

Darren Davison

Dmitriy Kopylenko

Thomas Risberg

Mark Pollack

Rob Harrop

Version 1.2.2

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

(Work in progress)


Table of Contents

서문
1. 소개
1.1. 개요
1.2. 사용 시나리오
2. 배경 지식
2.1. 제어의 역행 / 의존 주입(Inversion of Control / Dependency Injection)
3. Beans, BeanFactory 그리고 ApplicationContext
3.1. 소개
3.2. BeanFactory 와 BeanDefinitions - 기초
3.2.1. BeanFactory
3.2.2. BeanDefinition
3.2.3. bean 클래스
3.2.3.1. 생성자를 통한 Bean 생성
3.2.3.2. 정적 factory메소드를 통한 Bean 생성
3.2.3.3. 인스턴스 factory메소드를 통한 Bean 생성
3.2.4. bean 구분자 (id 와 name)
3.2.5. 싱글톤이나 비-싱글톤(non-singleton)
3.3. 프라퍼티, 협력자(collaborators), autowiring 과 의존성 체크
3.3.1. bean프라퍼티와 협력자(collaborators) 셋팅하기
3.3.2. 생성자의 인자 분석
3.3.2.1. 생성자의 인자 타입 대응(match)
3.3.2.2. 생성자의 인자 인덱스
3.3.3. bean프라퍼티와 상세화된 생성자의 인자
3.3.3.1. value와 ref 간략화한(shortcut) 폼
3.3.4. 메소드 삽입
3.3.4.1. 룩업(Lookup) 메소드 삽입
3.3.4.2. 임의의 메소드 교체
3.3.5. depends-on 사용하기
3.3.6. Autowiring 협력자
3.3.7. 의존성을 위한 체크
3.4. bean의 성질을 커스터마이징하기.
3.4.1. Lifecycle 인터페이스
3.4.1.1. InitializingBean / init-method
3.4.1.2. DisposableBean / destroy-method
3.4.2. 당신이 누구인지 알고 있다.(Knowing who you are)
3.4.2.1. BeanFactoryAware
3.4.2.2. BeanNameAware
3.4.3. FactoryBean
3.5. 추상 그리고 자식 bean정의
3.6. BeanFactory와 상호작동하기
3.6.1. BeanFactory의 생성물이 아닌 FactoryBean 얻기
3.7. BeanPostprocessors로 bean 커스터마이징하기
3.8. BeanFactoryPostprocessors를 가진 bean factory커스터마이징하기
3.8.1. PropertyPlaceholderConfigurer
3.8.2. PropertyOverrideConfigurer
3.9. 추가적인 사용자지정 PropertyEditors 등록하기
3.10. 존재하는 bean을 위한 별칭을 추가하기 위한 별칭 요소 사용하기.
3.11. ApplicationContext에 대한 소개
3.12. ApplicationContext에 추가된 기능
3.12.1. MessageSource 사용하기
3.12.2. events 전파하기
3.12.3. Spring내에서 자원(resources) 사용하기
3.13. ApplicationContext내에서 사용자정의 행위
3.13.1. ApplicationContextAware 표시자(marker) 인터페이스
3.13.2. BeanPostProcessor
3.13.3. BeanFactoryPostProcessor
3.13.4. PropertyPlaceholderConfigurer
3.14. 추가적인 사용자정의 PropertyEditors 등록하기
3.15. 프라퍼티 표현에서 bean프라퍼티 또는 생성자의 인자를 셋팅하기.
3.16. 필드값으로부터 bean프라퍼티 또는 생성자의 인자를 셋팅하기.
3.17. 다른 메소드를 호출하고 선택적으로 반환값을 사용한다.
3.18. 하나의 파일로부터 다른것으로 bean정의를 끌어오기
3.19. 웹 애플리케이션으로부터 ApplicationContext생성하기.
3.20. Glue 코드와 좋지않은 싱글톤
3.20.1. SingletonBeanFactoryLocator 와 ContextSingletonBeanFactoryLocator을 사용하기
4. PropertyEditors, data binding, validation and the BeanWrapper
4.1. 소개
4.2. Binding data를 사용한DataBinder
4.3. Bean 조작(manipulation)과 BeanWrapper
4.3.1. Setting 과 getting 기본과 내포된 설정들
4.3.2. 내장 PropertyEditors, 변환 타입들(Built-in PropertyEditors, converting types)
4.3.3. 언급할 가치가 있는 다른 기능들.
5. Spring AOP: Spring을 이용한 Aspect 지향적인 프로그래밍
5.1. 개념
5.1.1. AOP 개념
5.1.2. Spring AOP의 기능과 대상
5.1.3. Spring 내 AOP 프록시
5.2. Spring내 Pointcuts
5.2.1. 개념
5.2.2. pointcuts에서의 작업(operation)
5.2.3. 편리한 pointcut 구현물
5.2.3.1. 정적 pointcuts
5.2.3.2. 동적 pointcuts
5.2.4. Pointcut 수퍼클래스(superclasses)
5.2.5. 사용자지정 pointcuts
5.3. Spring 내 Advice타입들
5.3.1. Advice 생명주기
5.3.2. Spring내 Advice 타입들
5.3.2.1. Interception around advice
5.3.2.2. 전(Before) advice
5.3.2.3. Throws advice
5.3.2.4. advice를 반환한 후(after returning advice)
5.3.2.5. Introduction advice
5.4. Spring내 Advisors
5.5. AOP프록시를 생성하기 위한 ProxyFactoryBean사용하기
5.5.1. 기본
5.5.2. 자바빈 프라퍼티
5.5.3. 프록시 인터페이스
5.5.4. 프록시 클래스
5.5.5. 'global' advisor 사용하기
5.6. 편리한 프록시 생성
5.6.1. TransactionProxyFactoryBean
5.6.2. EJB 프록시
5.7. 간결한 프록시 정의
5.8. ProxyFactory로 프로그램적으로 AOP프록시를 생성하기.
5.9. advised 객체 조작하기.
5.10. "autoproxy" 기능 사용하기
5.10.1. autoproxy bean정의
5.10.1.1. BeanNameAutoProxyCreator
5.10.1.2. DefaultAdvisorAutoProxyCreator
5.10.1.3. AbstractAdvisorAutoProxyCreator
5.10.2. 메터데이타-지향 자동 프록시 사용하기.
5.11. TargetSources 사용하기
5.11.1. 핫 스왑가능한 대상 소스
5.11.2. 풀링 대상 소스
5.11.3. 프로토 타입 대상 소스
5.11.4. ThreadLocal 대상 소스
5.12. 새로운 Advice 타입을 정의하기
5.13. 추가적으로 읽을거리와 자원들
5.14. 로드맵
6. AspectJ 통합
6.1. 개요
6.2. Spring IoC를 사용하여 AspectJ 설정하기.
6.2.1. "싱글톤" aspects
6.2.1.1. 예제
6.2.1.2. 정렬 이슈
6.2.2. 싱글톤 형식이 아닌 aspect
6.2.3. Gotchas
6.3. 목표 Spring advice를 위한 AspectJ 포인트컷(pointcut) 사용하기
6.4. AspectJ를 위한 Spring aspect
7. 트랜잭션 관리
7.1. Spring 트랜잭션 추상화
7.2. 트랜잭션 전략
7.3. 프로그래밍적인 트랜잭션 관리
7.3.1. TransactionTemplate 사용하기
7.3.2. PlatformTransactionManager 사용하기
7.4. 선언적 트랜잭션 관리
7.4.1. BeanNameAutoProxyCreator, 또 다른 선언적 접근방법
7.5. 프로그래밍적/선언적 트랜잭션 관리 중 선택하기
7.6. 트랜잭션 관리를 위한 어플리케이션 서버가 필요한가?
7.7. 공통적인 문제
8. 소스 레벨 메타데이타 지원
8.1. 소스-레벨 메타데이타
8.2. Spring의 메타데이타 지원
8.3. Jakarta Commons Attributes과 통합
8.4. 메타데이타와 Spring AOP 자동 프록시
8.4.1. 기초
8.4.2. 선언적인 트랜잭션 관리
8.4.3. 풀링(Pooling)
8.4.4. 사용자정의 메타데이타
8.5. MVC 웹티어 설정을 최소화하기 위한 속성 사용하기
8.6. 메타데이타 속성의 다른 사용
8.7. 추가적인 메타데이타 API를 위한 지원 추가하기
9. DAO support
9.1. 소개
9.2. 일관된 예외 구조
9.3. DAO지원을 위한 일관된 추상클래스
10. JDBC를 사용한 데이터 접근
10.1. 소개
10.2. 기본적인 JDBC처리와 에러 처리를 위한 JDBC Core클래스 사용하기
10.2.1. JdbcTemplate
10.2.2. DataSource
10.2.3. SQLExceptionTranslator
10.2.4. Statements 실행하기
10.2.5. 쿼리문 실행하기
10.2.6. 데이터베이스 수정하기
10.3. 데이터베이스에 연결하는 방법을 제어하기
10.3.1. DataSourceUtils
10.3.2. SmartDataSource
10.3.3. AbstractDataSource
10.3.4. SingleConnectionDataSource
10.3.5. DriverManagerDataSource
10.3.6. DataSourceTransactionManager
10.4. 자바 객체처럼 JDBC작업을 모델링 하기.
10.4.1. SqlQuery
10.4.2. MappingSqlQuery
10.4.3. SqlUpdate
10.4.4. StoredProcedure
10.4.5. SqlFunction
11. 객체-관계 연결자(O/R Mappers)를 이용한 데이터 접근
11.1. 소개
11.2. Hibernate
11.2.1. 자원 관리
11.2.2. 애플리케이션 컨텍스트내에서 자원 정의
11.2.3. Inversion of Control: Template and Callback
11.2.4. 탬플릿 대신에 AOP인터셉터 적용하기.
11.2.5. 프로그램의 트랜잭션 구분(Demarcation)
11.2.6. 선언적인 트랜잭션 구분
11.2.7. 트랜잭션 관리 전략
11.2.8. 컨테이너 자원 대 로컬 자원
11.2.9. 샘플들
11.3. JDO
11.4. iBATIS
11.4.1. 1.3.x and 2.0 사이의 개요와 차이점
11.4.2. iBATIS 1.3.x
11.4.2.1. SqlMap을 셋업하기
11.4.2.2. SqlMapDaoSupport 사용하기
11.4.2.3. 트랜잭션 관리
11.4.3. iBATIS 2
11.4.3.1. SqlMap 셋업하기
11.4.3.2. SqlMapClientDaoSupport 사용하기
12. 웹 MVC framework
12.1. 웹 MVC framework 소개
12.1.1. 다른 MVC구현물의 플러그인 가능성
12.1.2. Spring MVC의 특징
12.2. DispatcherServlet
12.3. 컨트롤러
12.3.1. AbstractController 와 WebContentGenerator
12.3.2. 간단한 다른 컨트롤러
12.3.3. MultiActionController
12.3.4. CommandControllers
12.4. Handler mappings
12.4.1. BeanNameUrlHandlerMapping
12.4.2. SimpleUrlHandlerMapping
12.4.3. HandlerInterceptors 추가하기
12.5. view와 view결정하기
12.5.1. ViewResolvers
12.5.2. ViewResolvers 묶기(Chaining)
12.6. 로케일 사용하기.
12.6.1. AcceptHeaderLocaleResolver
12.6.2. CookieLocaleResolver
12.6.3. SessionLocaleResolver
12.6.4. LocaleChangeInterceptor
12.7. 테마(themes) 사용하기
12.8. Spring의 multipart (파일업로드) 지원
12.8.1. 소개
12.8.2. MultipartResolver 사용하기
12.8.3. 폼에서 파일업로드를 다루기.
12.9. 예외 다루기
13. 통합 뷰 기술들
13.1. 소개
13.2. JSP & JSTL
13.2.1. 뷰 해결자(View resolvers)
13.2.2. 'Plain-old' JSPs 대(versus) JSTL
13.2.3. 추가적인 태그들을 쉽게 쓸수 있는 개발
13.3. Tiles
13.3.1. 의존물들(Dependencies)
13.3.2. Tiles를 통합하는 방법
13.3.2.1. InternalResourceViewResolver
13.3.2.2. ResourceBundleViewResolver
13.4. Velocity & FreeMarker
13.4.1. 의존물들(Dependencies)
13.4.2. 컨텍스트 구성(Context configuration)
13.4.3. 생성 템플릿들(Creating templates)
13.4.4. 진보한 구성(Advanced configuration)
13.4.4.1. velocity.properties
13.4.4.2. FreeMarker
13.4.5. 바인드(Bind) 지원과 폼(form) 핸들링
13.4.5.1. 바인드(bind) 매크로
13.4.5.2. 간단한 바인딩
13.4.5.3. 폼 input 생성 매크로
13.4.5.4. HTML회피를 오버라이드하고 XHTML호환 태그를 만든다.
13.5. XSLT
13.5.1. 나의 첫번째 단어
13.5.1.1. Bean 정의
13.5.1.2. 표준적인 MVC 컨트롤러 코드
13.5.1.3. 모델 데이터를 XML로 변환하기
13.5.1.4. view프라퍼티 정의하기
13.5.1.5. 문서 변형
13.5.2. 요약
13.6. 문서 views (PDF/Excel)
13.6.1. 소개
13.6.2. 설정 그리고 셋업
13.6.2.1. 문서 view정의
13.6.2.2. 컨트롤러 코드
13.6.2.3. Excel view를 위한 하위클래스 만들기
13.6.2.4. PDF view를 위한 하위클래스 만들기
13.7. JasperReports
13.7.1. 의존성
13.7.2. 설정
13.7.2.1. ViewResolver 설정하기
13.7.2.2. View 설정하기
13.7.2.3. 리포트 파일에 대해
13.7.2.4. JasperReportsMultiFormatView 사용하기
13.7.3. ModelAndView 활성화하기
13.7.4. 하위-리포트로 작동하기
13.7.4.1. 하위-리포트 파일 설정하기
13.7.4.2. 하위-리포트 데이터소스 설정하기
13.7.5. 전파자(Exporter) 파라미터 설정하기
14. 다른 웹 프레임워크들과의 통합
14.1. 소개
14.2. JavaServer Faces
14.2.1. DelegatingVariableResolver
14.2.2. FacesContextUtils
14.3. Struts
14.3.1. ContextLoaderPlugin
14.3.1.1. DelegatingRequestProcessor
14.3.1.2. DelegatingActionProxy
14.3.2. ActionSupport 클래스들
14.4. Tapestry
14.4.1. 아키텍쳐
14.4.2. 구현체
14.4.2.1. 샘플 어플리케이션 컨텍스트
14.4.2.2. Tapestry pages에서 빈들을 얻어오기
14.4.2.3. 어플리케이션 컨텍스트를 Tapestry에 드러내기
14.4.2.4. Component 정의 파일들
14.4.2.5. abstract accessors 추가하기
14.4.3. 개요
14.5. WebWork
15. JMS
15.1. 소개
15.2. 도메인 단일화(unification)
15.3. JmsTemplate
15.3.1. ConnectionFactory
15.3.2. 트랜잭션 관리
15.3.3. 목적지(destination) 관리
15.4. JmsTemplate 사용하기
15.4.1. 메시지 보내기
15.4.2. 동기적으로 받기(Receiving)
15.4.3. 메시지 변환기(converter) 사용하기
15.4.4. SessionCallback 과 ProducerCallback
16. EJB에 접근하고 구현하기
16.1. EJB에 접근하기
16.1.1. 개념
16.1.2. local SLSBs에 접근하기
16.1.3. remote SLSB에 접근하기
16.2. Spring의 편리한 EJB구현물 클래스를 사용하기.
17. Spring을 사용한 원격(Remoting)및 웹서비스
17.1. 소개
17.2. RMI를 사용한 서비스 드러내기
17.2.1. RmiServiceExporter를 사용하여 서비스 내보내기
17.2.2. 클라이언트에서 서비스 링크하기
17.3. HTTP를 통해 서비스를 원격으로 호출하기 위한 Hessian 이나 Burlap을 사용하기.
17.3.1. Hessian을 위해 DispatcherServlet을 묶기.
17.3.2. HessianServiceExporter를 사용하여 bean을 드러내기
17.3.3. 클라이언트의 서비스로 링크하기
17.3.4. Burlap 사용하기
17.3.5. Hessian 이나 Burlap을 통해 드러나는 서비스를 위한 HTTP 기본 인증 적용하기
17.4. HTTP호출자를 사용하여 서비스를 드러내기
17.4.1. 서비스 객체를 드러내기
17.4.2. 클라이언트에서 서비스 링크하기
17.5. 웹 서비스
17.5.1. JAX-RPC를 사용하여 서비스를 드러내기
17.5.2. 웹 서비스에 접근하기
17.5.3. Register Bean 맵핑
17.5.4. 자체적인 핸들러 등록하기
17.5.5. XFire를 사용하여 웹 서비스를 드러내기
17.6. 자동-탐지(Auto-detection)는 원격 인터페이스를 위해 구현되지 않는다.
17.7. 기술을 선택할때 고려사항.
18. Spring 메일 추상 계층을 사용한 이메일 보내기
18.1. 소개
18.2. Spring 메일 추상화 구조
18.3. Spring 메일 추상화 사용하기
18.3.1. 플러그인할 수 있는 MailSender 구현클래스들
18.4. JavaMail MimeMessageHelper 사용하기
18.4.1. 간단한 MimeMessage 를 생성하고 보내기
18.4.2. 첨부파일들과 inline 리소스들을 보내기
19. Quartz 혹은 Timer 를 사용한 스케쥴링
19.1. 소개
19.2. OpenSymphony Quartz 스케쥴러 사용하기
19.2.1. JobDetailBean 사용하기
19.2.2. MethodInvokingJobDetailFactoryBean 사용하기
19.2.3. triggers 와 SchedulerFactoryBean을 사용하여 jobs를 묶기
19.3. JDK Timer support 사용하기
19.3.1. 임의의 timers 생성하기
19.3.2. MethodInvokingTimerTaskFactoryBean 사용하기
19.3.3. 감싸기 : TimerFactoryBean을 사용하여 tasks를 세팅하기
20. JMX 지원
20.1. 소개
20.2. 당신의 bean을 JMX로 내보내기(Exporting)
20.2.1. MBeanServer 생성하기
20.2.2. 늦게 초기화되는(Lazy-Initialized) MBeans
20.2.3. MBean의 자동 등록
20.3. 당신 bean의 관리 인터페이스를 제어하기
20.3.1. MBeanInfoAssembler 인터페이스
20.3.2. 소스레벨 메타데이타(metadata) 사용하기
20.3.3. JDK 5.0 Annotations 사용하기
20.3.4. 소스레벨 메타데이타 타입들
20.3.5. AutodetectCapableMBeanInfoAssembler 인터페이스
20.3.6. 자바 인터페이스를 사용하여 관리 인터페이스 정의하기
20.3.7. MethodNameBasedMBeanInfoAssembler 사용하기
20.4. 당신의 bean을 위한 ObjectName 제어하기
20.4.1. Properties로 부터 ObjectName 읽기
20.4.2. MetadataNamingStrategy 사용하기
20.5. JSR-160 연결자(Connectors)로 당신의 bean을 내보내기
20.5.1. 서버측 연결자(Connectors)
20.5.2. 클라이언트측 연결자
20.5.3. Burlap/Hessian/SOAP 곳곳의 JMX
20.6. 프록시를 통해서 MBean에 접속하기
21. Testing
21.1. 단위 테스팅
21.2. 통합 테스팅
21.2.1. 컨텍스트 관리와 캐슁
21.2.2. 테스트 클래스 인스턴스들의 의존성 주입
21.2.3. 트랜잭션 관리
21.2.4. 편리한 변수들
21.2.5. 예시
21.2.6. 통합 테스트 실행하기
A. spring-beans.dtd
Posted by 1010
반응형
File: context.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:util="http://www.springframework.org/schema/util"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
                           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
                           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">


    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@oracle.devcake.co.uk:1521:INTL"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>

    <bean id="mysqlDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://dbhost-prospring-psql/prospring"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>

    <bean id="lobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler">
        <property name="nativeJdbcExtractor" ref="nativeJdbcExtractor"/>
    </bean>

    <bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor"/>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>


</beans>


File: Main.java

import java.sql.Types;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlUpdate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

class Main {
  public static void main(String args[]) throws Exception {
    ApplicationContext ac = new ClassPathXmlApplicationContext("context.xml", Main.class);
    DataSource dataSource = (DataSource) ac.getBean("dataSource");
    DataSource mysqlDataSource = (DataSource) ac.getBean("mysqlDataSource");

    Insert insert = new Insert(dataSource);
    insert.update(new Object[] { 3L, "A", "B", null, null });

  }
}

class Insert extends SqlUpdate {
  private static final String SQL = "insert into t_customer (id, first_name, last_name, last_login, "
      + "comments) values (?, ?, ?, ?, ?)";

  Insert(DataSource dataSource) {
    super(dataSource, SQL);
    declareParameter(new SqlParameter(Types.INTEGER));
    declareParameter(new SqlParameter(Types.VARCHAR));
    declareParameter(new SqlParameter(Types.VARCHAR));
    declareParameter(new SqlParameter(Types.TIMESTAMP));
    declareParameter(new SqlParameter(Types.CLOB));
  }

  void insert(long id, String firstName, String lastName, Date lastLogin, String comments) {
    update(new Object[] { id, firstName, lastName, lastLogin, comments });
  }
}
Posted by 1010
반응형
File: EmployeeDaoImpl.java

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.object.MappingSqlQuery;
import org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor;

public class EmployeeDaoImpl extends JdbcDaoSupport {
  private LastNameAndAgeQuery lastNameAndAgeQuery;

  protected void initDao() throws Exception {
    super.initDao();
    getJdbcTemplate().setNativeJdbcExtractor(new SimpleNativeJdbcExtractor());
    lastNameAndAgeQuery = new LastNameAndAgeQuery(getDataSource());

  }

  public List getEmployeesForLastNameAndAge(String lastName, Integer age) {
    return lastNameAndAgeQuery.execute(new Object[] { lastName, age });
  }

}

class LastNameAndAgeQuery extends MappingSqlQuery {
  private static final String SQL_QUERY = "SELECT * FROM employee WHERE name_last = ? AND age = ?";

  public LastNameAndAgeQuery(DataSource ds) {
    super(ds, SQL_QUERY);
    declareParameter(new SqlParameter("name_last", Types.VARCHAR));
    declareParameter(new SqlParameter("age", Types.INTEGER));
    compile();
  }

  protected Object mapRow(ResultSet resultSet, int row) throws SQLException {
    Employee employee = new Employee();
    employee.setId(resultSet.getInt("id"));
    employee.getName().setFirst(resultSet.getString("name_first"));
    employee.getName().setMiddle(resultSet.getString("name_middle"));
    employee.getName().setLast(resultSet.getString("name_last"));
    employee.getAddress().setLine1(resultSet.getString("address_line1"));
    employee.getAddress().setLine2(resultSet.getString("address_line2"));
    employee.getAddress().setCity(resultSet.getString("address_city"));
    employee.getAddress().setState(resultSet.getString("address_state"));
    employee.getAddress().setZip(resultSet.getString("address_zip"));
    employee.setAge(resultSet.getInt("age"));
    return employee;
  }
}

class Employee {
  private Integer id;

  private Name name = new Name();

  private Integer age;

  private Sex sex;

  private Address address = new Address();

  private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();

  public Employee() {
  }

  public Employee(String firstName, String lastName) {
    this.getName().setFirst(firstName);
    this.getName().setLast(lastName);
  }

  void setId(Integer id) {
    this.id = id;
  }

  public Integer getId() {
    return id;
  }

  public Address getAddress() {
    return address;
  }

  public Integer getAge() {
    return age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public Name getName() {
    return name;
  }

  public List<PhoneNumber> getPhoneNumbers() {
    return Collections.unmodifiableList(phoneNumbers);
  }

  public void addPhoneNumber(PhoneNumber phoneNumber) {
    this.phoneNumbers.add(phoneNumber);
  }

  public void removePhoneNumber(PhoneNumber phoneNumber) {
    this.phoneNumbers.remove(phoneNumber);
  }

  public void removePhoneNumber(int index) {
    this.phoneNumbers.remove(index);
  }

  public Sex getSex() {
    return sex;
  }

  public void setSex(Sex sex) {
    this.sex = sex;
  }
}

abstract class Sex {

  public static final Sex MALE = new Male();

  public static final Sex FEMALE = new Female();

  public boolean equals(Object o) {
    if (o == null) {
      return false;
    }
    return getClass().equals(o.getClass());
  }
}

class PhoneNumber {

}

class Address {
  private String line1;

  private String line2;

  private String city;

  private String state;

  private String zip;

  public void setLine1(String line1) {
    this.line1 = line1;
  }

  public String getLine1() {
    return this.line1;
  }

  public void setLine2(String line2) {
    this.line2 = line2;
  }

  public String getLine2() {
    return this.line2;
  }

  public void setCity(String city) {
    this.city = city;
  }

  public String getCity() {
    return this.city;
  }

  public void setState(String state) {
    this.state = state;
  }

  public String getState() {
    return this.state;
  }

  public void setZip(String zip) {
    this.zip = zip;
  }

  public String getZip() {
    return this.zip;
  }
}

final class Male extends Sex {
  protected Male() {

  }
}

final class Female extends Sex {
  protected Female() {

  }
}

class Name {
  private String first;

  private String middle;

  private String last;

  public void setFirst(String first) {
    this.first = first;
  }

  public String getFirst() {
    return this.first;
  }

  public void setMiddle(String middle) {
    this.middle = middle;
  }

  public String getMiddle() {
    return this.middle;
  }

  public void setLast(String last) {
    this.last = last;
  }

  public String getLast() {
    return this.last;
  }
}


File: Main.java

import java.util.Date;
import java.util.GregorianCalendar;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

class Main {
  public static void main(String args[]) throws Exception {
    ApplicationContext ctx = new ClassPathXmlApplicationContext(
        "context.xml");
    EmployeeDaoImpl ws = (EmployeeDaoImpl) ctx.getBean("employeeDao");

  }
}


File: context.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
      destroy-method="close">
  <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
  <property name="url" value="jdbc:hsqldb:mem:."/>
  <property name="username" value="sa"/>
  <property name="password" value=""/>
</bean>

<bean id="employeeDao" class="EmployeeDaoImpl">
  <property name="dataSource" ref="dataSource"/>
</bean>

</beans>




Posted by 1010
반응형
Spring Framework

Reference Documentation

3.0

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.


Table of Contents

I. Overview of Spring Framework
1. Introduction to Spring Framework
1.1. Dependency Injection and Inversion of Control
1.2. Modules
1.2.1. Core Container
1.2.2. Data Access/Integration
1.2.3. Web
1.2.4. AOP and Instrumentation
1.2.5. Test
1.3. Usage scenarios
1.3.1. Dependency Management and Naming Conventions
1.3.1.1. Spring Dependencies and Depending on Spring
1.3.1.2. Maven Dependency Management
1.3.1.3. Ivy Dependency Management
1.3.2. Logging
1.3.2.1. Not Using Commons Logging
1.3.2.2. Using SLF4J
1.3.2.3. Using Log4J
II. What's New in Spring 3.0
2. New Features and Enhancements in Spring 3.0
2.1. Java 5
2.2. Improved documentation
2.3. New getting started tutorial
2.4. New module organization and build system
2.5. Overview of new features
2.5.1. Core APIs updated for Java 5
2.5.2. Spring Expression Language
2.5.3. The Inversion of Control (IoC) container
2.5.3.1. Java based bean metadata
2.5.3.2. Defining bean metadata within components
2.5.4. General purpose type conversion system and field formatting system
2.5.5. The Data Tier
2.5.6. The Web Tier
2.5.6.1. Comprehensive REST support
2.5.6.2. @MVC additions
2.5.7. Declarative model validation
2.5.8. Early support for Java EE 6
2.5.9. Support for embedded databases
III. Core Technologies
3. The IoC container
3.1. Introduction to the Spring IoC container and beans
3.2. Container overview
3.2.1. Configuration metadata
3.2.2. Instantiating a container
3.2.2.1. Composing XML-based configuration metadata
3.2.3. Using the container
3.3. Bean overview
3.3.1. Naming beans
3.3.1.1. Aliasing a bean outside the bean definition
3.3.2. Instantiating beans
3.3.2.1. Instantiation with a constructor
3.3.2.2. Instantiation with a static factory method
3.3.2.3. Instantiation using an instance factory method
3.4. Dependencies
3.4.1. Dependency injection
3.4.1.1. Constructor-based dependency injection
3.4.1.2. Setter-based dependency injection
3.4.1.3. Dependency resolution process
3.4.1.4. Examples of dependency injection
3.4.2. Dependencies and configuration in detail
3.4.2.1. Straight values (primitives, Strings, and so on)
3.4.2.2. References to other beans (collaborators)
3.4.2.3. Inner beans
3.4.2.4. Collections
3.4.2.5. Null and empty string values
3.4.2.6. XML shortcut with the p-namespace
3.4.2.7. Compound property names
3.4.3. Using depends-on
3.4.4. Lazy-initialized beans
3.4.5. Autowiring collaborators
3.4.5.1. Limitations and disadvantages of autowiring
3.4.5.2. Excluding a bean from autowiring
3.4.6. Checking for dependencies
3.4.7. Method injection
3.4.7.1. Lookup method injection
3.4.7.2. Arbitrary method replacement
3.5. Bean scopes
3.5.1. The singleton scope
3.5.2. The prototype scope
3.5.3. Singleton beans with prototype-bean dependencies
3.5.4. Request, session, and global session scopes
3.5.4.1. Initial web configuration
3.5.4.2. Request scope
3.5.4.3. Session scope
3.5.4.4. Global session scope
3.5.4.5. Scoped beans as dependencies
3.5.5. Custom scopes
3.5.5.1. Creating a custom scope
3.5.5.2. Using a custom scope
3.6. Customizing the nature of a bean
3.6.1. Lifecycle callbacks
3.6.1.1. Initialization callbacks
3.6.1.2. Destruction callbacks
3.6.1.3. Default initialization and destroy methods
3.6.1.4. Combining lifecycle mechanisms
3.6.1.5. Startup and shutdown callbacks
3.6.1.6. Shutting down the Spring IoC container gracefully in non-web applications
3.6.2. ApplicationContextAware and BeanNameAware
3.7. Bean definition inheritance
3.8. Container extension points
3.8.1. Customizing beans using the BeanPostProcessor Interface
3.8.1.1. Example: Hello World, BeanPostProcessor-style
3.8.1.2. Example: The RequiredAnnotationBeanPostProcessor
3.8.2. Customizing configuration metadata with BeanFactoryPostProcessor interface
3.8.2.1. Example: the PropertyPlaceholderConfigurer
3.8.2.2. Example: the PropertyOverrideConfigurer
3.8.3. Customizing instantiation logic with the FactoryBean Interface
3.9. Annotation-based container configuration
3.9.1. @Required
3.9.2. @Autowired and @Inject
3.9.3. Fine-tuning annotation-based autowiring with qualifiers
3.9.4. CustomAutowireConfigurer
3.9.5. @Resource
3.9.6. @PostConstruct and @PreDestroy
3.10. Classpath scanning and managed components
3.10.1. @Component and further stereotype annotations
3.10.2. Automatically detecting classes and registering bean definitions
3.10.3. Using filters to customize scanning
3.10.4. Defining bean metadata within components
3.10.5. Naming autodetected components
3.10.6. Providing a scope for autodetected components
3.10.7. Providing qualifier metadata with annotations
3.11. Java-based container configuration
3.11.1. Basic concepts: @Configuration and @Bean
3.11.2. Instantiating the Spring container using AnnotationConfigApplicationContext
3.11.2.1. Simple construction
3.11.2.2. Building the container programmatically using register(Class<?>...)
3.11.2.3. Enabling component scanning with scan(String...)
3.11.2.4. Support for web applications with AnnotationConfigWebApplicationContext
3.11.3. Composing Java-based configurations
3.11.3.1. Using the @Import annotation
3.11.3.2. Combining Java and XML configuration
3.11.4. Using the @Bean annotation
3.11.4.1. Declaring a bean
3.11.4.2. Injecting dependencies
3.11.4.3. Receiving lifecycle callbacks
3.11.4.4. Specifying bean scope
3.11.4.5. Customizing bean naming
3.11.4.6. Bean aliasing
3.12. Registering a LoadTimeWeaver
3.13. Additional Capabilities of the ApplicationContext
3.13.1. Internationalization using MessageSource
3.13.2. Standard and Custom Events
3.13.3. Convenient access to low-level resources
3.13.4. Convenient ApplicationContext instantiation for web applications
3.13.5. Deploying a Spring ApplicationContext as a J2EE RAR file
3.14. The BeanFactory
3.14.1. BeanFactory or ApplicationContext?
3.14.2. Glue code and the evil singleton
4. Resources
4.1. Introduction
4.2. The Resource interface
4.3. Built-in Resource implementations
4.3.1. UrlResource
4.3.2. ClassPathResource
4.3.3. FileSystemResource
4.3.4. ServletContextResource
4.3.5. InputStreamResource
4.3.6. ByteArrayResource
4.4. The ResourceLoader
4.5. The ResourceLoaderAware interface
4.6. Resources as dependencies
4.7. Application contexts and Resource paths
4.7.1. Constructing application contexts
4.7.1.1. Constructing ClassPathXmlApplicationContext instances - shortcuts
4.7.2. Wildcards in application context constructor resource paths
4.7.2.1. Ant-style Patterns
4.7.2.2. The classpath*: prefix
4.7.2.3. Other notes relating to wildcards
4.7.3. FileSystemResource caveats
5. Validation, Data Binding, and Type Conversion
5.1. Introduction
5.2. Validation using Spring's Validator interface
5.3. Resolving codes to error messages
5.4. Bean manipulation and the BeanWrapper
5.4.1. Setting and getting basic and nested properties
5.4.2. Built-in PropertyEditor implementations
5.4.2.1. Registering additional custom PropertyEditors
5.5. Spring 3 Type Conversion
5.5.1. Converter SPI
5.5.2. ConverterFactory
5.5.3. GenericConverter
5.5.3.1. ConditionalGenericConverter
5.5.4. ConversionService API
5.5.5. Configuring a ConversionService
5.5.6. Using a ConversionService programatically
5.6. Spring 3 Field Formatting
5.6.1. Formatter SPI
5.6.2. Annotation-driven Formatting
5.6.2.1. Format Annotation API
5.6.3. FormatterRegistry SPI
5.6.4. Configuring Formatting in Spring MVC
5.7. Spring 3 Validation
5.7.1. Overview of the JSR-303 Bean Validation API
5.7.2. Configuring a Bean Validation Implementation
5.7.2.1. Injecting a Validator
5.7.2.2. Configuring Custom Constraints
5.7.2.3. Additional Configuration Options
5.7.3. Configuring a DataBinder
5.7.4. Spring MVC 3 Validation
5.7.4.1. Triggering @Controller Input Validation
5.7.4.2. Configuring a Validator for use by Spring MVC
5.7.4.3. Configuring a JSR-303 Validator for use by Spring MVC
6. Spring Expression Language (SpEL)
6.1. Introduction
6.2. Feature Overview
6.3. Expression Evaluation using Spring's Expression Interface
6.3.1. The EvaluationContext interface
6.3.1.1. Type Conversion
6.4. Expression support for defining bean definitions
6.4.1. XML based configuration
6.4.2. Annotation-based configuration
6.5. Language Reference
6.5.1. Literal expressions
6.5.2. Properties, Arrays, Lists, Maps, Indexers
6.5.3. Methods
6.5.4. Operators
6.5.4.1. Relational operators
6.5.4.2. Logical operators
6.5.4.3. Mathematical operators
6.5.5. Assignment
6.5.6. Types
6.5.7. Constructors
6.5.8. Variables
6.5.8.1. The #this and #root variables
6.5.9. Functions
6.5.10. Ternary Operator (If-Then-Else)
6.5.11. The Elvis Operator
6.5.12. Safe Navigation operator
6.5.13. Collection Selection
6.5.14. Collection Projection
6.5.15. Expression templating
6.6. Classes used in the examples
7. Aspect Oriented Programming with Spring
7.1. Introduction
7.1.1. AOP concepts
7.1.2. Spring AOP capabilities and goals
7.1.3. AOP Proxies
7.2. @AspectJ support
7.2.1. Enabling @AspectJ Support
7.2.2. Declaring an aspect
7.2.3. Declaring a pointcut
7.2.3.1. Supported Pointcut Designators
7.2.3.2. Combining pointcut expressions
7.2.3.3. Sharing common pointcut definitions
7.2.3.4. Examples
7.2.3.5. Writing good pointcuts
7.2.4. Declaring advice
7.2.4.1. Before advice
7.2.4.2. After returning advice
7.2.4.3. After throwing advice
7.2.4.4. After (finally) advice
7.2.4.5. Around advice
7.2.4.6. Advice parameters
7.2.4.7. Advice ordering
7.2.5. Introductions
7.2.6. Aspect instantiation models
7.2.7. Example
7.3. Schema-based AOP support
7.3.1. Declaring an aspect
7.3.2. Declaring a pointcut
7.3.3. Declaring advice
7.3.3.1. Before advice
7.3.3.2. After returning advice
7.3.3.3. After throwing advice
7.3.3.4. After (finally) advice
7.3.3.5. Around advice
7.3.3.6. Advice parameters
7.3.3.7. Advice ordering
7.3.4. Introductions
7.3.5. Aspect instantiation models
7.3.6. Advisors
7.3.7. Example
7.4. Choosing which AOP declaration style to use
7.4.1. Spring AOP or full AspectJ?
7.4.2. @AspectJ or XML for Spring AOP?
7.5. Mixing aspect types
7.6. Proxying mechanisms
7.6.1. Understanding AOP proxies
7.7. Programmatic creation of @AspectJ Proxies
7.8. Using AspectJ with Spring applications
7.8.1. Using AspectJ to dependency inject domain objects with Spring
7.8.1.1. Unit testing @Configurable objects
7.8.1.2. Working with multiple application contexts
7.8.2. Other Spring aspects for AspectJ
7.8.3. Configuring AspectJ aspects using Spring IoC
7.8.4. Load-time weaving with AspectJ in the Spring Framework
7.8.4.1. A first example
7.8.4.2. Aspects
7.8.4.3. 'META-INF/aop.xml'
7.8.4.4. Required libraries (JARS)
7.8.4.5. Spring configuration
7.8.4.6. Environment-specific configuration
7.9. Further Resources
8. Spring AOP APIs
8.1. Introduction
8.2. Pointcut API in Spring
8.2.1. Concepts
8.2.2. Operations on pointcuts
8.2.3. AspectJ expression pointcuts
8.2.4. Convenience pointcut implementations
8.2.4.1. Static pointcuts
8.2.4.2. Dynamic pointcuts
8.2.5. Pointcut superclasses
8.2.6. Custom pointcuts
8.3. Advice API in Spring
8.3.1. Advice lifecycles
8.3.2. Advice types in Spring
8.3.2.1. Interception around advice
8.3.2.2. Before advice
8.3.2.3. Throws advice
8.3.2.4. After Returning advice
8.3.2.5. Introduction advice
8.4. Advisor API in Spring
8.5. Using the ProxyFactoryBean to create AOP proxies
8.5.1. Basics
8.5.2. JavaBean properties
8.5.3. JDK- and CGLIB-based proxies
8.5.4. Proxying interfaces
8.5.5. Proxying classes
8.5.6. Using 'global' advisors
8.6. Concise proxy definitions
8.7. Creating AOP proxies programmatically with the ProxyFactory
8.8. Manipulating advised objects
8.9. Using the "autoproxy" facility
8.9.1. Autoproxy bean definitions
8.9.1.1. BeanNameAutoProxyCreator
8.9.1.2. DefaultAdvisorAutoProxyCreator
8.9.1.3. AbstractAdvisorAutoProxyCreator
8.9.2. Using metadata-driven auto-proxying
8.10. Using TargetSources
8.10.1. Hot swappable target sources
8.10.2. Pooling target sources
8.10.3. Prototype target sources
8.10.4. ThreadLocal target sources
8.11. Defining new Advice types
8.12. Further resources
9. Testing
9.1. Introduction to testing
9.2. Unit testing
9.2.1. Mock objects
9.2.1.1. JNDI
9.2.1.2. Servlet API
9.2.1.3. Portlet API
9.2.2. Unit testing support classes
9.2.2.1. General utilities
9.2.2.2. Spring MVC
9.3. Integration testing
9.3.1. Overview
9.3.2. Goals of integration testing
9.3.2.1. Context management and caching
9.3.2.2. Dependency Injection of test fixtures
9.3.2.3. Transaction management
9.3.2.4. Support classes for integration testing
9.3.3. JDBC testing support
9.3.4. Annotations
9.3.5. Spring TestContext Framework
9.3.5.1. Key abstractions
9.3.5.2. Context management and caching
9.3.5.3. Dependency Injection of test fixtures
9.3.5.4. Transaction management
9.3.5.5. TestContext support classes
9.3.6. PetClinic example
9.4. Further Resources
IV. Data Access
10. Transaction Management
10.1. Introduction to Spring Framework transaction management
10.2. Advantages of the Spring Framework's transaction support model
10.2.1. Global transactions
10.2.2. Local transactions
10.2.3. Spring Framework's consistent programming model
10.3. Understanding the Spring Framework transaction abstraction
10.4. Synchronizing resources with transactions
10.4.1. High-level synchronization approach
10.4.2. Low-level synchronization approach
10.4.3. TransactionAwareDataSourceProxy
10.5. Declarative transaction management
10.5.1. Understanding the Spring Framework's declarative transaction implementation
10.5.2. Example of declarative transaction implementation
10.5.3. Rolling back a declarative transaction
10.5.4. Configuring different transactional semantics for different beans
10.5.5. <tx:advice/> settings
10.5.6. Using @Transactional
10.5.6.1. @Transactional settings
10.5.6.2. Multiple Transaction Managers with @Transactional
10.5.6.3. Custom shortcut annotations
10.5.7. Transaction propagation
10.5.7.1. Required
10.5.7.2. RequiresNew
10.5.7.3. Nested
10.5.8. Advising transactional operations
10.5.9. Using @Transactional with AspectJ
10.6. Programmatic transaction management
10.6.1. Using the TransactionTemplate
10.6.1.1. Specifying transaction settings
10.6.2. Using the PlatformTransactionManager
10.7. Choosing between programmatic and declarative transaction management
10.8. Application server-specific integration
10.8.1. IBM WebSphere
10.8.2. BEA WebLogic Server
10.8.3. Oracle OC4J
10.9. Solutions to common problems
10.9.1. Use of the wrong transaction manager for a specific DataSource
10.10. Further Resources
11. DAO support
11.1. Introduction
11.2. Consistent exception hierarchy
11.3. Annotations used for configuring DAO or Repository classes
12. Data access with JDBC
12.1. Introduction to Spring Framework JDBC
12.1.1. Choosing an approach for JDBC database access
12.1.2. Package hierarchy
12.2. Using the JDBC core classes to control basic JDBC processing and error handling
12.2.1. JdbcTemplate
12.2.1.1. Examples of JdbcTemplate class usage
12.2.1.2. JdbcTemplate best practices
12.2.2. NamedParameterJdbcTemplate
12.2.3. SimpleJdbcTemplate
12.2.4. SQLExceptionTranslator
12.2.5. Executing statements
12.2.6. Running queries
12.2.7. Updating the database
12.2.8. Retrieving auto-generated keys
12.3. Controlling database connections
12.3.1. DataSource
12.3.2. DataSourceUtils
12.3.3. SmartDataSource
12.3.4. AbstractDataSource
12.3.5. SingleConnectionDataSource
12.3.6. DriverManagerDataSource
12.3.7. TransactionAwareDataSourceProxy
12.3.8. DataSourceTransactionManager
12.3.9. NativeJdbcExtractor
12.4. JDBC batch operations
12.4.1. Batch operations with the JdbcTemplate
12.4.2. Batch operations with the SimpleJdbcTemplate
12.5. Simplifying JDBC operations with the SimpleJdbc classes
12.5.1. Inserting data using SimpleJdbcInsert
12.5.2. Retrieving auto-generated keys using SimpleJdbcInsert
12.5.3. Specifying columns for a SimpleJdbcInsert
12.5.4. Using SqlParameterSource to provide parameter values
12.5.5. Calling a stored procedure with SimpleJdbcCall
12.5.6. Explicitly declaring parameters to use for a SimpleJdbcCall
12.5.7. How to define SqlParameters
12.5.8. Calling a stored function using SimpleJdbcCall
12.5.9. Returning ResultSet/REF Cursor from a SimpleJdbcCall
12.6. Modeling JDBC operations as Java objects
12.6.1. SqlQuery
12.6.2. MappingSqlQuery
12.6.3. SqlUpdate
12.6.4. StoredProcedure
12.7. Common problems with parameter and data value handling
12.7.1. Providing SQL type information for parameters
12.7.2. Handling BLOB and CLOB objects
12.7.3. Passing in lists of values for IN clause
12.7.4. Handling complex types for stored procedure calls
12.8. Embedded database support
12.8.1. Why use an embedded database?
12.8.2. Creating an embedded database instance using Spring XML
12.8.3. Creating an embedded database instance programmatically
12.8.4. Extending the embedded database support
12.8.5. Using HSQL
12.8.6. Using H2
12.8.7. Using Derby
12.8.8. Testing data access logic with an embedded database
12.9. Initializing a DataSource
12.9.1. Initializing a database instance using Spring XML
12.9.1.1. Initialization of Other Components that Depend on the Database
13. Object Relational Mapping (ORM) Data Access
13.1. Introduction to ORM with Spring
13.2. General ORM integration considerations
13.2.1. Resource and transaction management
13.2.2. Exception translation
13.3. Hibernate
13.3.1. SessionFactory setup in a Spring container
13.3.2. Implementing DAOs based on plain Hibernate 3 API
13.3.3. Declarative transaction demarcation
13.3.4. Programmatic transaction demarcation
13.3.5. Transaction management strategies
13.3.6. Comparing container-managed and locally defined resources
13.3.7. Spurious application server warnings with Hibernate
13.4. JDO
13.4.1. PersistenceManagerFactory setup
13.4.2. Implementing DAOs based on the plain JDO API
13.4.3. Transaction management
13.4.4. JdoDialect
13.5. JPA
13.5.1. Three options for JPA setup in a Spring environment
13.5.1.1. LocalEntityManagerFactoryBean
13.5.1.2. Obtaining an EntityManagerFactory from JNDI
13.5.1.3. LocalContainerEntityManagerFactoryBean
13.5.1.4. Dealing with multiple persistence units
13.5.2. Implementing DAOs based on plain JPA
13.5.3. Transaction Management
13.5.4. JpaDialect
13.6. iBATIS SQL Maps
13.6.1. Setting up the SqlMapClient
13.6.2. Using SqlMapClientTemplate and SqlMapClientDaoSupport
13.6.3. Implementing DAOs based on plain iBATIS API
14. Marshalling XML using O/X Mappers
14.1. Introduction
14.2. Marshaller and Unmarshaller
14.2.1. Marshaller
14.2.2. Unmarshaller
14.2.3. XmlMappingException
14.3. Using Marshaller and Unmarshaller
14.4. XML Schema-based Configuration
14.5. JAXB
14.5.1. Jaxb2Marshaller
14.5.1.1. XML Schema-based Configuration
14.6. Castor
14.6.1. CastorMarshaller
14.6.2. Mapping
14.7. XMLBeans
14.7.1. XmlBeansMarshaller
14.7.1.1. XML Schema-based Configuration
14.8. JiBX
14.8.1. JibxMarshaller
14.8.1.1. XML Schema-based Configuration
14.9. XStream
14.9.1. XStreamMarshaller
V. The Web
15. Web MVC framework
15.1. Introduction to Spring Web MVC framework
15.1.1. Features of Spring Web MVC
15.1.2. Pluggability of other MVC implementations
15.2. The DispatcherServlet
15.3. Implementing Controllers
15.3.1. Defining a controller with @Controller
15.3.2. Mapping requests with @RequestMapping
15.3.2.1. URI Templates
15.3.2.2. Advanced @RequestMapping options
15.3.2.3. Supported handler method arguments and return types
15.3.2.4. Binding request parameters to method parameters with @RequestParam
15.3.2.5. Mapping the request body with the @RequestBody annotation
15.3.2.6. Mapping the response body with the @ResponseBody annotation
15.3.2.7. Providing a link to data from the model with @ModelAttribute
15.3.2.8. Specifying attributes to store in a session with @SessionAttributes
15.3.2.9. Mapping cookie values with the @CookieValue annotation
15.3.2.10. Mapping request header attributes with the @RequestHeader annotation
15.3.2.11. Customizing WebDataBinder initialization
15.4. Handler mappings
15.4.1. Intercepting requests - the HandlerInterceptor interface
15.5. Resolving views
15.5.1. Resolving views with the ViewResolver interface
15.5.2. Chaining ViewResolvers
15.5.3. Redirecting to views
15.5.3.1. RedirectView
15.5.3.2. The redirect: prefix
15.5.3.3. The forward: prefix
15.5.4. ContentNegotiatingViewResolver
15.6. Using locales
15.6.1. AcceptHeaderLocaleResolver
15.6.2. CookieLocaleResolver
15.6.3. SessionLocaleResolver
15.6.4. LocaleChangeInterceptor
15.7. Using themes
15.7.1. Overview of themes
15.7.2. Defining themes
15.7.3. Theme resolvers
15.8. Spring's multipart (fileupload) support
15.8.1. Introduction
15.8.2. Using the MultipartResolver
15.8.3. Handling a file upload in a form
15.9. Handling exceptions
15.9.1. HandlerExceptionResolver
15.9.2. @ExceptionHandler
15.10. Convention over configuration support
15.10.1. The Controller ControllerClassNameHandlerMapping
15.10.2. The Model ModelMap (ModelAndView)
15.10.3. The View - RequestToViewNameTranslator
15.11. ETag support
15.12. More Spring Web MVC Resources
16. View technologies
16.1. Introduction
16.2. JSP & JSTL
16.2.1. View resolvers
16.2.2. 'Plain-old' JSPs versus JSTL
16.2.3. Additional tags facilitating development
16.2.4. Using Spring's form tag library
16.2.4.1. Configuration
16.2.4.2. The form tag
16.2.4.3. The input tag
16.2.4.4. The checkbox tag
16.2.4.5. The checkboxes tag
16.2.4.6. The radiobutton tag
16.2.4.7. The radiobuttons tag
16.2.4.8. The password tag
16.2.4.9. The select tag
16.2.4.10. The option tag
16.2.4.11. The options tag
16.2.4.12. The textarea tag
16.2.4.13. The hidden tag
16.2.4.14. The errors tag
16.2.4.15. HTTP Method Conversion
16.3. Tiles
16.3.1. Dependencies
16.3.2. How to integrate Tiles
16.3.2.1. UrlBasedViewResolver
16.3.2.2. ResourceBundleViewResolver
16.3.2.3. SimpleSpringPreparerFactory and SpringBeanPreparerFactory
16.4. Velocity & FreeMarker
16.4.1. Dependencies
16.4.2. Context configuration
16.4.3. Creating templates
16.4.4. Advanced configuration
16.4.4.1. velocity.properties
16.4.4.2. FreeMarker
16.4.5. Bind support and form handling
16.4.5.1. The bind macros
16.4.5.2. Simple binding
16.4.5.3. Form input generation macros
16.4.5.4. HTML escaping and XHTML compliance
16.5. XSLT
16.5.1. My First Words
16.5.1.1. Bean definitions
16.5.1.2. Standard MVC controller code
16.5.1.3. Convert the model data to XML
16.5.1.4. Defining the view properties
16.5.1.5. Document transformation
16.5.2. Summary
16.6. Document views (PDF/Excel)
16.6.1. Introduction
16.6.2. Configuration and setup
16.6.2.1. Document view definitions
16.6.2.2. Controller code
16.6.2.3. Subclassing for Excel views
16.6.2.4. Subclassing for PDF views
16.7. JasperReports
16.7.1. Dependencies
16.7.2. Configuration
16.7.2.1. Configuring the ViewResolver
16.7.2.2. Configuring the Views
16.7.2.3. About Report Files
16.7.2.4. Using JasperReportsMultiFormatView
16.7.3. Populating the ModelAndView
16.7.4. Working with Sub-Reports
16.7.4.1. Configuring Sub-Report Files
16.7.4.2. Configuring Sub-Report Data Sources
16.7.5. Configuring Exporter Parameters
16.8. Feed Views
16.9. XML Marshalling View
16.10. JSON Mapping View
17. Integrating with other web frameworks
17.1. Introduction
17.2. Common configuration
17.3. JavaServer Faces 1.1 and 1.2
17.3.1. DelegatingVariableResolver (JSF 1.1/1.2)
17.3.2. SpringBeanVariableResolver (JSF 1.1/1.2)
17.3.3. SpringBeanFacesELResolver (JSF 1.2+)
17.3.4. FacesContextUtils
17.4. Apache Struts 1.x and 2.x
17.4.1. ContextLoaderPlugin
17.4.1.1. DelegatingRequestProcessor
17.4.1.2. DelegatingActionProxy
17.4.2. ActionSupport Classes
17.5. WebWork 2.x
17.6. Tapestry 3.x and 4.x
17.6.1. Injecting Spring-managed beans
17.6.1.1. Dependency Injecting Spring Beans into Tapestry pages
17.6.1.2. Component definition files
17.6.1.3. Adding abstract accessors
17.6.1.4. Dependency Injecting Spring Beans into Tapestry pages - Tapestry 4.x style
17.7. Further Resources
18. Portlet MVC Framework
18.1. Introduction
18.1.1. Controllers - The C in MVC
18.1.2. Views - The V in MVC
18.1.3. Web-scoped beans
18.2. The DispatcherPortlet
18.3. The ViewRendererServlet
18.4. Controllers
18.4.1. AbstractController and PortletContentGenerator
18.4.2. Other simple controllers
18.4.3. Command Controllers
18.4.4. PortletWrappingController
18.5. Handler mappings
18.5.1. PortletModeHandlerMapping
18.5.2. ParameterHandlerMapping
18.5.3. PortletModeParameterHandlerMapping
18.5.4. Adding HandlerInterceptors
18.5.5. HandlerInterceptorAdapter
18.5.6. ParameterMappingInterceptor
18.6. Views and resolving them
18.7. Multipart (file upload) support
18.7.1. Using the PortletMultipartResolver
18.7.2. Handling a file upload in a form
18.8. Handling exceptions
18.9. Annotation-based controller configuration
18.9.1. Setting up the dispatcher for annotation support
18.9.2. Defining a controller with @Controller
18.9.3. Mapping requests with @RequestMapping
18.9.4. Supported handler method arguments
18.9.5. Binding request parameters to method parameters with @RequestParam
18.9.6. Providing a link to data from the model with @ModelAttribute
18.9.7. Specifying attributes to store in a Session with @SessionAttributes
18.9.8. Customizing WebDataBinder initialization
18.9.8.1. Customizing data binding with @InitBinder
18.9.8.2. Configuring a custom WebBindingInitializer
18.10. Portlet application deployment
VI. Integration
19. Remoting and web services using Spring
19.1. Introduction
19.2. Exposing services using RMI
19.2.1. Exporting the service using the RmiServiceExporter
19.2.2. Linking in the service at the client
19.3. Using Hessian or Burlap to remotely call services via HTTP
19.3.1. Wiring up the DispatcherServlet for Hessian and co.
19.3.2. Exposing your beans by using the HessianServiceExporter
19.3.3. Linking in the service on the client
19.3.4. Using Burlap
19.3.5. Applying HTTP basic authentication to a service exposed through Hessian or Burlap
19.4. Exposing services using HTTP invokers
19.4.1. Exposing the service object
19.4.2. Linking in the service at the client
19.5. Web services
19.5.1. Exposing servlet-based web services using JAX-RPC
19.5.2. Accessing web services using JAX-RPC
19.5.3. Registering JAX-RPC Bean Mappings
19.5.4. Registering your own JAX-RPC Handler
19.5.5. Exposing servlet-based web services using JAX-WS
19.5.6. Exporting standalone web services using JAX-WS
19.5.7. Exporting web services using the JAX-WS RI's Spring support
19.5.8. Accessing web services using JAX-WS
19.5.9. Exposing web services using XFire
19.6. JMS
19.6.1. Server-side configuration
19.6.2. Client-side configuration
19.7. Auto-detection is not implemented for remote interfaces
19.8. Considerations when choosing a technology
19.9. Accessing RESTful services on the Client
19.9.1. RestTemplate
19.9.2. HTTP Message Conversion
19.9.2.1. StringHttpMessageConverter
19.9.2.2. FormHttpMessageConverter
19.9.2.3. ByteArrayMessageConverter
19.9.2.4. MarshallingHttpMessageConverter
19.9.2.5. MappingJacksonHttpMessageConverter
19.9.2.6. SourceHttpMessageConverter
19.9.2.7. BufferedImageHttpMessageConverter
20. Enterprise JavaBeans (EJB) integration
20.1. Introduction
20.2. Accessing EJBs
20.2.1. Concepts
20.2.2. Accessing local SLSBs
20.2.3. Accessing remote SLSBs
20.2.4. Accessing EJB 2.x SLSBs versus EJB 3 SLSBs
20.3. Using Spring's EJB implementation support classes
20.3.1. EJB 2.x base classes
20.3.2. EJB 3 injection interceptor
21. JMS (Java Message Service)
21.1. Introduction
21.2. Using Spring JMS
21.2.1. JmsTemplate
21.2.2. Connections
21.2.2.1. Caching Messaging Resources
21.2.2.2. SingleConnectionFactory
21.2.2.3. CachingConnectionFactory
21.2.3. Destination Management
21.2.4. Message Listener Containers
21.2.4.1. SimpleMessageListenerContainer
21.2.4.2. DefaultMessageListenerContainer
21.2.4.3. ServerSessionMessageListenerContainer
21.2.5. Transaction management
21.3. Sending a Message
21.3.1. Using Message Converters
21.3.2. SessionCallback and ProducerCallback
21.4. Receiving a message
21.4.1. Synchronous Reception
21.4.2. Asynchronous Reception - Message-Driven POJOs
21.4.3. The SessionAwareMessageListener interface
21.4.4. The MessageListenerAdapter
21.4.5. Processing messages within transactions
21.5. Support for JCA Message Endpoints
21.6. JMS Namespace Support
22. JMX
22.1. Introduction
22.2. Exporting your beans to JMX
22.2.1. Creating an MBeanServer
22.2.2. Reusing an existing MBeanServer
22.2.3. Lazy-initialized MBeans
22.2.4. Automatic registration of MBeans
22.2.5. Controlling the registration behavior
22.3. Controlling the management interface of your beans
22.3.1. The MBeanInfoAssembler Interface
22.3.2. Using Source-Level Metadata (JDK 5.0 annotations)
22.3.3. Source-Level Metadata Types
22.3.4. The AutodetectCapableMBeanInfoAssembler interface
22.3.5. Defining management interfaces using Java interfaces
22.3.6. Using MethodNameBasedMBeanInfoAssembler
22.4. Controlling the ObjectNames for your beans
22.4.1. Reading ObjectNames from Properties
22.4.2. Using the MetadataNamingStrategy
22.4.3. The <context:mbean-export/> element
22.5. JSR-160 Connectors
22.5.1. Server-side Connectors
22.5.2. Client-side Connectors
22.5.3. JMX over Burlap/Hessian/SOAP
22.6. Accessing MBeans via Proxies
22.7. Notifications
22.7.1. Registering Listeners for Notifications
22.7.2. Publishing Notifications
22.8. Further Resources
23. JCA CCI
23.1. Introduction
23.2. Configuring CCI
23.2.1. Connector configuration
23.2.2. ConnectionFactory configuration in Spring
23.2.3. Configuring CCI connections
23.2.4. Using a single CCI connection
23.3. Using Spring's CCI access support
23.3.1. Record conversion
23.3.2. The CciTemplate
23.3.3. DAO support
23.3.4. Automatic output record generation
23.3.5. Summary
23.3.6. Using a CCI Connection and Interaction directly
23.3.7. Example for CciTemplate usage
23.4. Modeling CCI access as operation objects
23.4.1. MappingRecordOperation
23.4.2. MappingCommAreaOperation
23.4.3. Automatic output record generation
23.4.4. Summary
23.4.5. Example for MappingRecordOperation usage
23.4.6. Example for MappingCommAreaOperation usage
23.5. Transactions
24. Email
24.1. Introduction
24.2. Usage
24.2.1. Basic MailSender and SimpleMailMessage usage
24.2.2. Using the JavaMailSender and the MimeMessagePreparator
24.3. Using the JavaMail MimeMessageHelper
24.3.1. Sending attachments and inline resources
24.3.1.1. Attachments
24.3.1.2. Inline resources
24.3.2. Creating email content using a templating library
24.3.2.1. A Velocity-based example
25. Task Execution and Scheduling
25.1. Introduction
25.2. The Spring TaskExecutor abstraction
25.2.1. TaskExecutor types
25.2.2. Using a TaskExecutor
25.3. The Spring TaskScheduler abstraction
25.3.1. The Trigger interface
25.3.2. Trigger implementations
25.3.3. TaskScheduler implementations
25.4. The Task Namespace
25.4.1. The 'scheduler' element
25.4.2. The 'executor' element
25.4.3. The 'scheduled-tasks' element
25.5. Annotation Support for Scheduling and Asynchronous Execution
25.5.1. The @Scheduled Annotation
25.5.2. The @Async Annotation
25.5.3. The <annotation-driven> Element
25.6. Using the OpenSymphony Quartz Scheduler
25.6.1. Using the JobDetailBean
25.6.2. Using the MethodInvokingJobDetailFactoryBean
25.6.3. Wiring up jobs using triggers and the SchedulerFactoryBean
25.7. Using JDK Timer support
25.7.1. Creating custom timers
25.7.2. Using the MethodInvokingTimerTaskFactoryBean
25.7.3. Wrapping up: setting up the tasks using the TimerFactoryBean
26. Dynamic language support
26.1. Introduction
26.2. A first example
26.3. Defining beans that are backed by dynamic languages
26.3.1. Common concepts
26.3.1.1. The <lang:language/> element
26.3.1.2. Refreshable beans
26.3.1.3. Inline dynamic language source files
26.3.1.4. Understanding Constructor Injection in the context of dynamic-language-backed beans
26.3.2. JRuby beans
26.3.3. Groovy beans
26.3.3.1. Customising Groovy objects via a callback
26.3.4. BeanShell beans
26.4. Scenarios
26.4.1. Scripted Spring MVC Controllers
26.4.2. Scripted Validators
26.5. Bits and bobs
26.5.1. AOP - advising scripted beans
26.5.2. Scoping
26.6. Further Resources
27. Annotations and Source Level Metadata Support
27.1. Introduction
27.2. Annotations
27.2.1. @Required
27.2.2. Other @Annotations in Spring
VII. Appendices
A. Classic Spring Usage
A.1. Classic ORM usage
A.1.1. Hibernate
A.1.1.1. The HibernateTemplate
A.1.1.2. Implementing Spring-based DAOs without callbacks
A.1.2. JDO
A.1.2.1. JdoTemplate and JdoDaoSupport
A.1.3. JPA
A.1.3.1. JpaTemplate and JpaDaoSupport
A.2. Classic Spring MVC
A.3. JMS Usage
A.3.1. JmsTemplate
A.3.2. Asynchronous Message Reception
A.3.3. Connections
A.3.4. Transaction Management
B. Classic Spring AOP Usage
B.1. Pointcut API in Spring
B.1.1. Concepts
B.1.2. Operations on pointcuts
B.1.3. AspectJ expression pointcuts
B.1.4. Convenience pointcut implementations
B.1.4.1. Static pointcuts
B.1.4.2. Dynamic pointcuts
B.1.5. Pointcut superclasses
B.1.6. Custom pointcuts
B.2. Advice API in Spring
B.2.1. Advice lifecycles
B.2.2. Advice types in Spring
B.2.2.1. Interception around advice
B.2.2.2. Before advice
B.2.2.3. Throws advice
B.2.2.4. After Returning advice
B.2.2.5. Introduction advice
B.3. Advisor API in Spring
B.4. Using the ProxyFactoryBean to create AOP proxies
B.4.1. Basics
B.4.2. JavaBean properties
B.4.3. JDK- and CGLIB-based proxies
B.4.4. Proxying interfaces
B.4.5. Proxying classes
B.4.6. Using 'global' advisors
B.5. Concise proxy definitions
B.6. Creating AOP proxies programmatically with the ProxyFactory
B.7. Manipulating advised objects
B.8. Using the "autoproxy" facility
B.8.1. Autoproxy bean definitions
B.8.1.1. BeanNameAutoProxyCreator
B.8.1.2. DefaultAdvisorAutoProxyCreator
B.8.1.3. AbstractAdvisorAutoProxyCreator
B.8.2. Using metadata-driven auto-proxying
B.9. Using TargetSources
B.9.1. Hot swappable target sources
B.9.2. Pooling target sources
B.9.3. Prototype target sources
B.9.4. ThreadLocal target sources
B.10. Defining new Advice types
B.11. Further resources
C. XML Schema-based configuration
C.1. Introduction
C.2. XML Schema-based configuration
C.2.1. Referencing the schemas
C.2.2. The util schema
C.2.2.1. <util:constant/>
C.2.2.2. <util:property-path/>
C.2.2.3. <util:properties/>
C.2.2.4. <util:list/>
C.2.2.5. <util:map/>
C.2.2.6. <util:set/>
C.2.3. The jee schema
C.2.3.1. <jee:jndi-lookup/> (simple)
C.2.3.2. <jee:jndi-lookup/> (with single JNDI environment setting)
C.2.3.3. <jee:jndi-lookup/> (with multiple JNDI environment settings)
C.2.3.4. <jee:jndi-lookup/> (complex)
C.2.3.5. <jee:local-slsb/> (simple)
C.2.3.6. <jee:local-slsb/> (complex)
C.2.3.7. <jee:remote-slsb/>
C.2.4. The lang schema
C.2.5. The jms schema
C.2.6. The tx (transaction) schema
C.2.7. The aop schema
C.2.8. The context schema
C.2.8.1. <property-placeholder/>
C.2.8.2. <annotation-config/>
C.2.8.3. <component-scan/>
C.2.8.4. <load-time-weaver/>
C.2.8.5. <spring-configured/>
C.2.8.6. <mbean-export/>
C.2.9. The tool schema
C.2.10. The beans schema
C.3. Setting up your IDE
C.3.1. Setting up Eclipse
C.3.2. Setting up IntelliJ IDEA
C.3.3. Integration issues
C.3.3.1. XML parsing errors in the Resin v.3 application server
D. Extensible XML authoring
D.1. Introduction
D.2. Authoring the schema
D.3. Coding a NamespaceHandler
D.4. Coding a BeanDefinitionParser
D.5. Registering the handler and the schema
D.5.1. 'META-INF/spring.handlers'
D.5.2. 'META-INF/spring.schemas'
D.6. Using a custom extension in your Spring XML configuration
D.7. Meatier examples
D.7.1. Nesting custom tags within custom tags
D.7.2. Custom attributes on 'normal' elements
D.8. Further Resources
E. spring-beans-2.0.dtd
F. spring.tld
F.1. Introduction
F.2. The bind tag
F.3. The escapeBody tag
F.4. The hasBindErrors tag
F.5. The htmlEscape tag
F.6. The message tag
F.7. The nestedPath tag
F.8. The theme tag
F.9. The transform tag
G. spring-form.tld
G.1. Introduction
G.2. The checkbox tag
G.3. The checkboxes tag
G.4. The errors tag
G.5. The form tag
G.6. The hidden tag
G.7. The input tag
G.8. The label tag
G.9. The option tag
G.10. The options tag
G.11. The password tag
G.12. The radiobutton tag
G.13. The radiobuttons tag
G.14. The select tag
G.15. The textarea tag
Posted by 1010
반응형
  • Acegi Security 분석 - Authentication과 Authorization에 대한 기본 개념 : Authentication(인증)과 Authorization(인가)에 대한 기본적인 개념에 대하여 다루며, 엔터프라이즈 애플리케이션 개발에 있어서의 Security Concerns을 4가지로 나뉘어 살펴본다.
  • Acegi Security 분석 - Servlet Filter 설정 및 기본 메커니즘 : 웹 애플리케이션의 경우 Acegi Security를 구현하기 위하여 Servlet Filter를 기반으로 구현하고 있다. 따라서 Servlet Filter에 대한 기본적인 설정 및 기본 메커니즘에 대하여 살펴본다.
  • Acegi Security 분석 - Authentication 기본 : Acegi Security가 지원하고 있는 Authentication의 기본적인 기능에 대하여 살펴본다.
  • Acegi Security 분석 - Authorization 기본 : Acegi Security가 지원하고 있는 Authorization의 기본적인 기능에 대하여 살펴본다.
  • Acegi Security 분석 - Acl : Acl 정보를 관리하기 위하여 Acegi Security가 지원하고 있는 기본적인 기능에 대하여 살펴본다.
  • Acegi Security 분석 - Web URL Authorization : 웹 애플리케이션에서 Web URL에 대한 Authorization을 Acegi Security가 어떻게 지원하고 있는지에 대하여 살펴본다.
  • [Acegi Security 분석 - Service Layer Authorization] : 애플리케이션의 서비스 계층에 대한 Authorization을 지원하는 방법에 대하여 살펴본다.
  • Acegi Security 분석 - Domain object instance Authorization : 애플리케이션의 도메인 모델 클래스에 대한 Authorization을 지원하는 방법에 대하여 살펴본다.

  • 출처 : http://www.javajigi.net/display/SFL/Spring+Security

    Posted by 1010
    반응형

    개발 환경 세팅

    사전 준비물 및 버전

    • JDK 5.0
    • Eclipse 3.2.1
    • MySQL 5.0.x
    • Tomcat 5.5.20

    개발 환경 세팅을 위해 필요한 파일들

    예제 소스

    강의 문서


    •Architecture
    •Spring Introduction
    •Spring IoC 이해 및 활용
    •Spring AOP 이해 및 활용
    •Spring Abstract API
    •Spring Transaction
    •Spring Test


    Spring 2.0

    • Aspect Weaver 사용 : Run As.. >> Arguments >> VM Arguments에 -javaagent:lib/aspectjweaver.jar 를 추가해주어야 한다.
    • 모든 spring 프로젝트에 적용 : Window || Preferences || Java || Installed JREs || 사용하는 JDK의 VM Aguments에 -javaagent:lib/aspectjweaver.jar을 추가한다.
      • 이 경우 모든 프로젝트의 lib 디렉토리 하위에 aspectjweaver.jar가 존재해야한다.

    출처 : http://www.javajigi.net/pages/viewpage.action?pageId=7777

    Posted by 1010
    반응형

    Spring - Java/J2EE Application Framework

    Reference Documentation

    Rod Johnson

    Juergen Hoeller

    Alef Arendsen

    Colin Sampaleanu

    Darren Davison

    Dmitriy Kopylenko

    Thomas Risberg

    Mark Pollack

    Rob Harrop

    Version 1.2.2

    Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

    (Work in progress)


    Table of Contents

    서문
    1. 소개
    1.1. 개요
    1.2. 사용 시나리오
    2. 배경 지식
    2.1. 제어의 역행 / 의존 주입(Inversion of Control / Dependency Injection)
    3. Beans, BeanFactory 그리고 ApplicationContext
    3.1. 소개
    3.2. BeanFactory 와 BeanDefinitions - 기초
    3.2.1. BeanFactory
    3.2.2. BeanDefinition
    3.2.3. bean 클래스
    3.2.3.1. 생성자를 통한 Bean 생성
    3.2.3.2. 정적 factory메소드를 통한 Bean 생성
    3.2.3.3. 인스턴스 factory메소드를 통한 Bean 생성
    3.2.4. bean 구분자 (id 와 name)
    3.2.5. 싱글톤이나 비-싱글톤(non-singleton)
    3.3. 프라퍼티, 협력자(collaborators), autowiring 과 의존성 체크
    3.3.1. bean프라퍼티와 협력자(collaborators) 셋팅하기
    3.3.2. 생성자의 인자 분석
    3.3.2.1. 생성자의 인자 타입 대응(match)
    3.3.2.2. 생성자의 인자 인덱스
    3.3.3. bean프라퍼티와 상세화된 생성자의 인자
    3.3.3.1. value와 ref 간략화한(shortcut) 폼
    3.3.4. 메소드 삽입
    3.3.4.1. 룩업(Lookup) 메소드 삽입
    3.3.4.2. 임의의 메소드 교체
    3.3.5. depends-on 사용하기
    3.3.6. Autowiring 협력자
    3.3.7. 의존성을 위한 체크
    3.4. bean의 성질을 커스터마이징하기.
    3.4.1. Lifecycle 인터페이스
    3.4.1.1. InitializingBean / init-method
    3.4.1.2. DisposableBean / destroy-method
    3.4.2. 당신이 누구인지 알고 있다.(Knowing who you are)
    3.4.2.1. BeanFactoryAware
    3.4.2.2. BeanNameAware
    3.4.3. FactoryBean
    3.5. 추상 그리고 자식 bean정의
    3.6. BeanFactory와 상호작동하기
    3.6.1. BeanFactory의 생성물이 아닌 FactoryBean 얻기
    3.7. BeanPostprocessors로 bean 커스터마이징하기
    3.8. BeanFactoryPostprocessors를 가진 bean factory커스터마이징하기
    3.8.1. PropertyPlaceholderConfigurer
    3.8.2. PropertyOverrideConfigurer
    3.9. 추가적인 사용자지정 PropertyEditors 등록하기
    3.10. 존재하는 bean을 위한 별칭을 추가하기 위한 별칭 요소 사용하기.
    3.11. ApplicationContext에 대한 소개
    3.12. ApplicationContext에 추가된 기능
    3.12.1. MessageSource 사용하기
    3.12.2. events 전파하기
    3.12.3. Spring내에서 자원(resources) 사용하기
    3.13. ApplicationContext내에서 사용자정의 행위
    3.13.1. ApplicationContextAware 표시자(marker) 인터페이스
    3.13.2. BeanPostProcessor
    3.13.3. BeanFactoryPostProcessor
    3.13.4. PropertyPlaceholderConfigurer
    3.14. 추가적인 사용자정의 PropertyEditors 등록하기
    3.15. 프라퍼티 표현에서 bean프라퍼티 또는 생성자의 인자를 셋팅하기.
    3.16. 필드값으로부터 bean프라퍼티 또는 생성자의 인자를 셋팅하기.
    3.17. 다른 메소드를 호출하고 선택적으로 반환값을 사용한다.
    3.18. 하나의 파일로부터 다른것으로 bean정의를 끌어오기
    3.19. 웹 애플리케이션으로부터 ApplicationContext생성하기.
    3.20. Glue 코드와 좋지않은 싱글톤
    3.20.1. SingletonBeanFactoryLocator 와 ContextSingletonBeanFactoryLocator을 사용하기
    4. PropertyEditors, data binding, validation and the BeanWrapper
    4.1. 소개
    4.2. Binding data를 사용한DataBinder
    4.3. Bean 조작(manipulation)과 BeanWrapper
    4.3.1. Setting 과 getting 기본과 내포된 설정들
    4.3.2. 내장 PropertyEditors, 변환 타입들(Built-in PropertyEditors, converting types)
    4.3.3. 언급할 가치가 있는 다른 기능들.
    5. Spring AOP: Spring을 이용한 Aspect 지향적인 프로그래밍
    5.1. 개념
    5.1.1. AOP 개념
    5.1.2. Spring AOP의 기능과 대상
    5.1.3. Spring 내 AOP 프록시
    5.2. Spring내 Pointcuts
    5.2.1. 개념
    5.2.2. pointcuts에서의 작업(operation)
    5.2.3. 편리한 pointcut 구현물
    5.2.3.1. 정적 pointcuts
    5.2.3.2. 동적 pointcuts
    5.2.4. Pointcut 수퍼클래스(superclasses)
    5.2.5. 사용자지정 pointcuts
    5.3. Spring 내 Advice타입들
    5.3.1. Advice 생명주기
    5.3.2. Spring내 Advice 타입들
    5.3.2.1. Interception around advice
    5.3.2.2. 전(Before) advice
    5.3.2.3. Throws advice
    5.3.2.4. advice를 반환한 후(after returning advice)
    5.3.2.5. Introduction advice
    5.4. Spring내 Advisors
    5.5. AOP프록시를 생성하기 위한 ProxyFactoryBean사용하기
    5.5.1. 기본
    5.5.2. 자바빈 프라퍼티
    5.5.3. 프록시 인터페이스
    5.5.4. 프록시 클래스
    5.5.5. 'global' advisor 사용하기
    5.6. 편리한 프록시 생성
    5.6.1. TransactionProxyFactoryBean
    5.6.2. EJB 프록시
    5.7. 간결한 프록시 정의
    5.8. ProxyFactory로 프로그램적으로 AOP프록시를 생성하기.
    5.9. advised 객체 조작하기.
    5.10. "autoproxy" 기능 사용하기
    5.10.1. autoproxy bean정의
    5.10.1.1. BeanNameAutoProxyCreator
    5.10.1.2. DefaultAdvisorAutoProxyCreator
    5.10.1.3. AbstractAdvisorAutoProxyCreator
    5.10.2. 메터데이타-지향 자동 프록시 사용하기.
    5.11. TargetSources 사용하기
    5.11.1. 핫 스왑가능한 대상 소스
    5.11.2. 풀링 대상 소스
    5.11.3. 프로토 타입 대상 소스
    5.11.4. ThreadLocal 대상 소스
    5.12. 새로운 Advice 타입을 정의하기
    5.13. 추가적으로 읽을거리와 자원들
    5.14. 로드맵
    6. AspectJ 통합
    6.1. 개요
    6.2. Spring IoC를 사용하여 AspectJ 설정하기.
    6.2.1. "싱글톤" aspects
    6.2.1.1. 예제
    6.2.1.2. 정렬 이슈
    6.2.2. 싱글톤 형식이 아닌 aspect
    6.2.3. Gotchas
    6.3. 목표 Spring advice를 위한 AspectJ 포인트컷(pointcut) 사용하기
    6.4. AspectJ를 위한 Spring aspect
    7. 트랜잭션 관리
    7.1. Spring 트랜잭션 추상화
    7.2. 트랜잭션 전략
    7.3. 프로그래밍적인 트랜잭션 관리
    7.3.1. TransactionTemplate 사용하기
    7.3.2. PlatformTransactionManager 사용하기
    7.4. 선언적 트랜잭션 관리
    7.4.1. BeanNameAutoProxyCreator, 또 다른 선언적 접근방법
    7.5. 프로그래밍적/선언적 트랜잭션 관리 중 선택하기
    7.6. 트랜잭션 관리를 위한 어플리케이션 서버가 필요한가?
    7.7. 공통적인 문제
    8. 소스 레벨 메타데이타 지원
    8.1. 소스-레벨 메타데이타
    8.2. Spring의 메타데이타 지원
    8.3. Jakarta Commons Attributes과 통합
    8.4. 메타데이타와 Spring AOP 자동 프록시
    8.4.1. 기초
    8.4.2. 선언적인 트랜잭션 관리
    8.4.3. 풀링(Pooling)
    8.4.4. 사용자정의 메타데이타
    8.5. MVC 웹티어 설정을 최소화하기 위한 속성 사용하기
    8.6. 메타데이타 속성의 다른 사용
    8.7. 추가적인 메타데이타 API를 위한 지원 추가하기
    9. DAO support
    9.1. 소개
    9.2. 일관된 예외 구조
    9.3. DAO지원을 위한 일관된 추상클래스
    10. JDBC를 사용한 데이터 접근
    10.1. 소개
    10.2. 기본적인 JDBC처리와 에러 처리를 위한 JDBC Core클래스 사용하기
    10.2.1. JdbcTemplate
    10.2.2. DataSource
    10.2.3. SQLExceptionTranslator
    10.2.4. Statements 실행하기
    10.2.5. 쿼리문 실행하기
    10.2.6. 데이터베이스 수정하기
    10.3. 데이터베이스에 연결하는 방법을 제어하기
    10.3.1. DataSourceUtils
    10.3.2. SmartDataSource
    10.3.3. AbstractDataSource
    10.3.4. SingleConnectionDataSource
    10.3.5. DriverManagerDataSource
    10.3.6. DataSourceTransactionManager
    10.4. 자바 객체처럼 JDBC작업을 모델링 하기.
    10.4.1. SqlQuery
    10.4.2. MappingSqlQuery
    10.4.3. SqlUpdate
    10.4.4. StoredProcedure
    10.4.5. SqlFunction
    11. 객체-관계 연결자(O/R Mappers)를 이용한 데이터 접근
    11.1. 소개
    11.2. Hibernate
    11.2.1. 자원 관리
    11.2.2. 애플리케이션 컨텍스트내에서 자원 정의
    11.2.3. Inversion of Control: Template and Callback
    11.2.4. 탬플릿 대신에 AOP인터셉터 적용하기.
    11.2.5. 프로그램의 트랜잭션 구분(Demarcation)
    11.2.6. 선언적인 트랜잭션 구분
    11.2.7. 트랜잭션 관리 전략
    11.2.8. 컨테이너 자원 대 로컬 자원
    11.2.9. 샘플들
    11.3. JDO
    11.4. iBATIS
    11.4.1. 1.3.x and 2.0 사이의 개요와 차이점
    11.4.2. iBATIS 1.3.x
    11.4.2.1. SqlMap을 셋업하기
    11.4.2.2. SqlMapDaoSupport 사용하기
    11.4.2.3. 트랜잭션 관리
    11.4.3. iBATIS 2
    11.4.3.1. SqlMap 셋업하기
    11.4.3.2. SqlMapClientDaoSupport 사용하기
    12. 웹 MVC framework
    12.1. 웹 MVC framework 소개
    12.1.1. 다른 MVC구현물의 플러그인 가능성
    12.1.2. Spring MVC의 특징
    12.2. DispatcherServlet
    12.3. 컨트롤러
    12.3.1. AbstractController 와 WebContentGenerator
    12.3.2. 간단한 다른 컨트롤러
    12.3.3. MultiActionController
    12.3.4. CommandControllers
    12.4. Handler mappings
    12.4.1. BeanNameUrlHandlerMapping
    12.4.2. SimpleUrlHandlerMapping
    12.4.3. HandlerInterceptors 추가하기
    12.5. view와 view결정하기
    12.5.1. ViewResolvers
    12.5.2. ViewResolvers 묶기(Chaining)
    12.6. 로케일 사용하기.
    12.6.1. AcceptHeaderLocaleResolver
    12.6.2. CookieLocaleResolver
    12.6.3. SessionLocaleResolver
    12.6.4. LocaleChangeInterceptor
    12.7. 테마(themes) 사용하기
    12.8. Spring의 multipart (파일업로드) 지원
    12.8.1. 소개
    12.8.2. MultipartResolver 사용하기
    12.8.3. 폼에서 파일업로드를 다루기.
    12.9. 예외 다루기
    13. 통합 뷰 기술들
    13.1. 소개
    13.2. JSP & JSTL
    13.2.1. 뷰 해결자(View resolvers)
    13.2.2. 'Plain-old' JSPs 대(versus) JSTL
    13.2.3. 추가적인 태그들을 쉽게 쓸수 있는 개발
    13.3. Tiles
    13.3.1. 의존물들(Dependencies)
    13.3.2. Tiles를 통합하는 방법
    13.3.2.1. InternalResourceViewResolver
    13.3.2.2. ResourceBundleViewResolver
    13.4. Velocity & FreeMarker
    13.4.1. 의존물들(Dependencies)
    13.4.2. 컨텍스트 구성(Context configuration)
    13.4.3. 생성 템플릿들(Creating templates)
    13.4.4. 진보한 구성(Advanced configuration)
    13.4.4.1. velocity.properties
    13.4.4.2. FreeMarker
    13.4.5. 바인드(Bind) 지원과 폼(form) 핸들링
    13.4.5.1. 바인드(bind) 매크로
    13.4.5.2. 간단한 바인딩
    13.4.5.3. 폼 input 생성 매크로
    13.4.5.4. HTML회피를 오버라이드하고 XHTML호환 태그를 만든다.
    13.5. XSLT
    13.5.1. 나의 첫번째 단어
    13.5.1.1. Bean 정의
    13.5.1.2. 표준적인 MVC 컨트롤러 코드
    13.5.1.3. 모델 데이터를 XML로 변환하기
    13.5.1.4. view프라퍼티 정의하기
    13.5.1.5. 문서 변형
    13.5.2. 요약
    13.6. 문서 views (PDF/Excel)
    13.6.1. 소개
    13.6.2. 설정 그리고 셋업
    13.6.2.1. 문서 view정의
    13.6.2.2. 컨트롤러 코드
    13.6.2.3. Excel view를 위한 하위클래스 만들기
    13.6.2.4. PDF view를 위한 하위클래스 만들기
    13.7. JasperReports
    13.7.1. 의존성
    13.7.2. 설정
    13.7.2.1. ViewResolver 설정하기
    13.7.2.2. View 설정하기
    13.7.2.3. 리포트 파일에 대해
    13.7.2.4. JasperReportsMultiFormatView 사용하기
    13.7.3. ModelAndView 활성화하기
    13.7.4. 하위-리포트로 작동하기
    13.7.4.1. 하위-리포트 파일 설정하기
    13.7.4.2. 하위-리포트 데이터소스 설정하기
    13.7.5. 전파자(Exporter) 파라미터 설정하기
    14. 다른 웹 프레임워크들과의 통합
    14.1. 소개
    14.2. JavaServer Faces
    14.2.1. DelegatingVariableResolver
    14.2.2. FacesContextUtils
    14.3. Struts
    14.3.1. ContextLoaderPlugin
    14.3.1.1. DelegatingRequestProcessor
    14.3.1.2. DelegatingActionProxy
    14.3.2. ActionSupport 클래스들
    14.4. Tapestry
    14.4.1. 아키텍쳐
    14.4.2. 구현체
    14.4.2.1. 샘플 어플리케이션 컨텍스트
    14.4.2.2. Tapestry pages에서 빈들을 얻어오기
    14.4.2.3. 어플리케이션 컨텍스트를 Tapestry에 드러내기
    14.4.2.4. Component 정의 파일들
    14.4.2.5. abstract accessors 추가하기
    14.4.3. 개요
    14.5. WebWork
    15. JMS
    15.1. 소개
    15.2. 도메인 단일화(unification)
    15.3. JmsTemplate
    15.3.1. ConnectionFactory
    15.3.2. 트랜잭션 관리
    15.3.3. 목적지(destination) 관리
    15.4. JmsTemplate 사용하기
    15.4.1. 메시지 보내기
    15.4.2. 동기적으로 받기(Receiving)
    15.4.3. 메시지 변환기(converter) 사용하기
    15.4.4. SessionCallback 과 ProducerCallback
    16. EJB에 접근하고 구현하기
    16.1. EJB에 접근하기
    16.1.1. 개념
    16.1.2. local SLSBs에 접근하기
    16.1.3. remote SLSB에 접근하기
    16.2. Spring의 편리한 EJB구현물 클래스를 사용하기.
    17. Spring을 사용한 원격(Remoting)및 웹서비스
    17.1. 소개
    17.2. RMI를 사용한 서비스 드러내기
    17.2.1. RmiServiceExporter를 사용하여 서비스 내보내기
    17.2.2. 클라이언트에서 서비스 링크하기
    17.3. HTTP를 통해 서비스를 원격으로 호출하기 위한 Hessian 이나 Burlap을 사용하기.
    17.3.1. Hessian을 위해 DispatcherServlet을 묶기.
    17.3.2. HessianServiceExporter를 사용하여 bean을 드러내기
    17.3.3. 클라이언트의 서비스로 링크하기
    17.3.4. Burlap 사용하기
    17.3.5. Hessian 이나 Burlap을 통해 드러나는 서비스를 위한 HTTP 기본 인증 적용하기
    17.4. HTTP호출자를 사용하여 서비스를 드러내기
    17.4.1. 서비스 객체를 드러내기
    17.4.2. 클라이언트에서 서비스 링크하기
    17.5. 웹 서비스
    17.5.1. JAX-RPC를 사용하여 서비스를 드러내기
    17.5.2. 웹 서비스에 접근하기
    17.5.3. Register Bean 맵핑
    17.5.4. 자체적인 핸들러 등록하기
    17.5.5. XFire를 사용하여 웹 서비스를 드러내기
    17.6. 자동-탐지(Auto-detection)는 원격 인터페이스를 위해 구현되지 않는다.
    17.7. 기술을 선택할때 고려사항.
    18. Spring 메일 추상 계층을 사용한 이메일 보내기
    18.1. 소개
    18.2. Spring 메일 추상화 구조
    18.3. Spring 메일 추상화 사용하기
    18.3.1. 플러그인할 수 있는 MailSender 구현클래스들
    18.4. JavaMail MimeMessageHelper 사용하기
    18.4.1. 간단한 MimeMessage 를 생성하고 보내기
    18.4.2. 첨부파일들과 inline 리소스들을 보내기
    19. Quartz 혹은 Timer 를 사용한 스케쥴링
    19.1. 소개
    19.2. OpenSymphony Quartz 스케쥴러 사용하기
    19.2.1. JobDetailBean 사용하기
    19.2.2. MethodInvokingJobDetailFactoryBean 사용하기
    19.2.3. triggers 와 SchedulerFactoryBean을 사용하여 jobs를 묶기
    19.3. JDK Timer support 사용하기
    19.3.1. 임의의 timers 생성하기
    19.3.2. MethodInvokingTimerTaskFactoryBean 사용하기
    19.3.3. 감싸기 : TimerFactoryBean을 사용하여 tasks를 세팅하기
    20. JMX 지원
    20.1. 소개
    20.2. 당신의 bean을 JMX로 내보내기(Exporting)
    20.2.1. MBeanServer 생성하기
    20.2.2. 늦게 초기화되는(Lazy-Initialized) MBeans
    20.2.3. MBean의 자동 등록
    20.3. 당신 bean의 관리 인터페이스를 제어하기
    20.3.1. MBeanInfoAssembler 인터페이스
    20.3.2. 소스레벨 메타데이타(metadata) 사용하기
    20.3.3. JDK 5.0 Annotations 사용하기
    20.3.4. 소스레벨 메타데이타 타입들
    20.3.5. AutodetectCapableMBeanInfoAssembler 인터페이스
    20.3.6. 자바 인터페이스를 사용하여 관리 인터페이스 정의하기
    20.3.7. MethodNameBasedMBeanInfoAssembler 사용하기
    20.4. 당신의 bean을 위한 ObjectName 제어하기
    20.4.1. Properties로 부터 ObjectName 읽기
    20.4.2. MetadataNamingStrategy 사용하기
    20.5. JSR-160 연결자(Connectors)로 당신의 bean을 내보내기
    20.5.1. 서버측 연결자(Connectors)
    20.5.2. 클라이언트측 연결자
    20.5.3. Burlap/Hessian/SOAP 곳곳의 JMX
    20.6. 프록시를 통해서 MBean에 접속하기
    21. Testing
    21.1. 단위 테스팅
    21.2. 통합 테스팅
    21.2.1. 컨텍스트 관리와 캐슁
    21.2.2. 테스트 클래스 인스턴스들의 의존성 주입
    21.2.3. 트랜잭션 관리
    21.2.4. 편리한 변수들
    21.2.5. 예시
    21.2.6. 통합 테스트 실행하기
    A. spring-beans.dtd
    Posted by 1010
    반응형

    출처 : http://www.javajigi.net/display/OSS/Spring+MVC

    Table of Contents

    Spring Introduction

    Spring에 대한 전반적인 내용은 아래를 참조하기 바란다.
    http://wiki.javajigi.net/pages/viewpage.action?pageId=280
    http://martinfowler.com/articles/injection.html

    Spring MVC


    Spring's web MVC framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale and theme resolution as well as support for upload files.
    The default handler is a very simple Controller interface, just offering a ModelAndView handleRequest(request,response) method.
    This can already be used for application controllers, but you will prefer the included implementation hierarchy, consisting of, for example AbstractController, AbstractCommandController and SimpleFormController.
    Application controllers will typically be subclasses of those. Note that you can choose an appropriate base class: If you don't have a form, you don't need a FormController. This is a major difference to Struts.

    A flexible MVC web application framework, built on core Spring functionality.
    This framework is highly configurable via strategy interfaces, and accommodates multiple view technologies like JSP, Velocity, Tiles, iText, and POI. Note that a Spring middle tier can easily be combined with a web tier based on any other web MVC framework, like Struts, WebWork, or Tapestry.

    Spring의 웹 MVC framework는 설정가능한 핸들러 맵핑들, view분석, 로케일과 파일 업로드를 위한 지원같은 테마분석과 함께 핸들러에 요청을 할당하는 DispatcherServlet을 기반으로 디자인되었다.
    디폴트 핸들러는 ModelAndView handleRequest(request,response)메소드를 제공하는 매우 간단한 컨트롤러 인터페이스이다.
    이것은 이미 애플리케이션 컨트롤러를 위해 사용될수 있지만 AbstractController, AbstractCommandController그리고 SimpleFormController같은 것들을 포함한 구현 구조를 포함한것을 더 선호할것이다.
    애플리케이션 컨트롤러들은 전형적으로 이것들의 하위 클래스가 된다. 당신이 선호하는 빈 클래스를 선택하도록 하라.
    만약에 당신이 form을 가지지 않는다면 당신은 FormController가 필요없다. 이것이 Struts와 가장 큰 차이점이다.

    유연한 MVC 웹 어플리케이션 프레임웍은 Core Spring과 상호작용하여 만들어진다.
    이 프레임웍은 전략적인 인터페이스를 거쳐 높은수준으로 만들어진다. 그리고, JSP, Velocity, Tiles, iText, POI처럼 다양한 view 기술을 수용한다.
    Spring middle tier는 Struts, WebWork, Tapestry와 같은 다른 웹 MVC 프레임워크를 기반으로 한 웹티어와 결합할수 있다.
    Spring프레임웍에서 Model-View-Controller를 지원하는 기능이다.

    강좌가 올라와있네요...
    Spring MVC에서 클라이언트 요청의 처리 과정을 먼저 살펴보시면 이해가 되실거 같습니다.
    http://wiki.javajigi.net/pages/viewpage.action?pageId=1133

    참 고 : http://www.springframework.org/docs/reference/
    번역본 : http://openframework.or.kr/framework_reference/spring/ver1.2.2/html/

    설정 방법

    Spring MVC를 사용하기 위해서는

    • web-app/WEB-INF/lib 아래로 spring.jar 또는 spring-webmvc.jar파일을 복사한다.
    • web-app/WEB-INF/web.xml에 DispatcherServlet를 설정한다.
    • <web.xml>
       
      
      <servlet>
      	<servlet-name>action</servlet-name>
      	<servlet-class>
      		org.springframework.web.servlet.DispatcherServlet</servlet-class>
      	<load-on-startup>1</load-on-startup>
      </servlet>	
      <servlet-mapping>
      	<servlet-name>action</servlet-name>
      	<url-pattern>*.do</url-pattern>
      </servlet-mapping>
      <servlet-mapping>
      	<servlet-name>action</servlet-name>
      	<url-pattern>*.html</url-pattern>
      </servlet-mapping>
      

      .do, .html 로 끝나는 모든 요청은 DispatcherServlet에서 담당한다.

    • DispattcherServlet이 초기화 할 때 디폴트로 사용하는 빈 설정파일의 이름은 "서블릿 이름-servlet.xml"이 된다.
    • web-app/WEB-INF/web.xml에서 설정한 <servlet-name>인 action-servlet.xml 파일을 생성한다.

    즉 spring.jar 와 web.xml에 DispatcherServlet 을 사용한다는 선언 그리고 서블릿 이름-servlet.xml 을 작성하면 사용할 수 있다.

    다양한 예제들

    Spring MVC를 사용한 초간단 예제

    링크된 강좌를 살펴보고 대략적인 것을 파악한다면 Spring MVC 사용하기 위해서 개발자들이 만들어야 하는 것은 Controller 라는 것을 알게 될 것이다. 물론 화면에 보여주는 jsp(여기선 jsp 를 연결) 파일, 빈 설정 파일을 만드는 것은 당연한 것이다.

    Controller를 생성하고 빈 설정파일에 맵핑 하는 방법 등의 관계를 보여주기 위한 데이터를 가져와서 List 형태로 보여주는 예제이다

    • Controller
       
      package com.tmax.spring.web;
      
      import java.util.List;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      import org.springframework.web.servlet.ModelAndView;
      import org.springframework.web.servlet.mvc.Controller;
      
      import com.tmax.spring.service.iface.BbsServiceIface;
      
      public class BbsController implements Controller {
      	
      	private BbsServiceIface bbsService;
      	
      	public void setBbsService(BbsServiceIface newBbsService){
      		this.bbsService = newBbsService;
      	}
      	
      	public ModelAndView handleRequest(HttpServletRequest arg0,
      			HttpServletResponse arg1) throws Exception {
      	    
              List list = bbsService.findBbsList();
              
              return new ModelAndView("bbs/list","bbsList", list);
         }
      
      }
      

    위에서 보면 직접적으로 Controller 인터페이스를 구현해서 ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; 를 구현한다. Spring MVC에서 제공하는 다양한 Controller 들은 모두 Controller 인터페이스를 구현하기 때문에 가장 기본적인 구조라고 생각할 수 있다.

    Service 객체는 setter Injection을 통해 생성된 것이다. 이것은 이미 앞에서 다 이해했을 것이다.
    handleRequest(... ) 안에서 실제 메소드를 호출한 뒤 ModelAndView 객체를 통해 리턴을 하였다.

    링크된 Spring MVC의 요청에 대한 생명주기를 보게 되면 비지니스 계층에서 전달된 모델 데이타와 클라이언트에게 보여줄 뷰화면에 대한 정보를 ModelAndView 클래스에 담아서 반환을 하게 되어 있다. 즉 전달 데이타 모델, 화면이름, 화면에서 사용될 객체 이름 등을 담아서 ModelAndView객체를 생성, 리턴한다.

    ModelAndView 객체의 View정보가 논리적인 view이름(String) 인 경우 빈 설정 파일에 정의되어 있는 ViewResolver클래스를 이용하여 클라이언트에세 출력할 화면이름을 만들어 낼 수 있다. ModelAndView의 첫번째 인자인 "bbs/list"가 view 이름이기 때문에 설정파일의 ViewResolver를 만나서 리턴되는 화면의 전체이름이 완성되는 것이다.

    • action-servlet.xml
        
          <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
      	
          <bean name="/bbs/listBbs.do" class="com.tmax.spring.web.BbsController">
      	<property name="bbsService">
      		<ref bean="bbsService" />
      	</property> 
          </bean>
      
          <!-- View Resolver for JSPs -->
          <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
            		<property name="prefix" value="/jsp/"/>
                        <property name="suffix" value=".jsp"/>
          </bean>
      

    설정파일을 보면 ViewResolver로 InternalResourceViewResolver클래스를 사용한다.
    그리고 JSTLView를 사용해서 jsp 로 화면을 출력할 것이라는 것을 알 수 있다. 위에 설정된 것과 ModelAndView객체에 넘긴 view이름을 조합해 보면 /jsp/bbs/list.jsp 가 되는 것이다. (prefix+view 이름+suffix )

    설정파일을 보면 BeanNameUrlHandlerMapping 클래스를 사용해서 Controller와 URL을 맵핑해주는 것을 알 수 있다.
    만일 빈 설정파일에 HandlerMapping이 설정되어 있지 않으면 default 로 BeanNameURLHandlerMapping을 설정한다.
    맵핑 방법에는 BeanNameURLHandlerMapping, SimpleUrlHandlerMapping,CommonsPathMapHandlerMapping 등이 있다.

    • BeanNameURLHandlerMapping
      빈 이름과 URL을 mapping 하는 방법이다.
          <bean name="/bbs/listBbs.do" class="com.tmax.spring.web.BbsController">
      	<property name="bbsService">
      		<ref bean="bbsService" />
      	</property> 
          </bean>
       

    즉 요청이 ..../bbs/listBbs.do가 오면 BbsController 클래스를 호출하겠다는 의미이다. name에는 <bean name="/bbs/listBbs.do /bbs/listBbs2.do".. /> 로 여러 개를 기술할 수 있다.

    • SimpleUrlHandlerMapping
      매핑에 대한 정보를 각각의 Controller에서 설정하는 것이 아니라 하나의 저장소에서 관리하는 것이다.
      Controller를 개발하는 개발자들은 빈을 정의하기만 하고 이 Controller가 어떻게 맵핑되어서 사용하는지에 대해서는 몰라도 된다.
      위의 설정파일을 SimpleUrlHandlerMapping으로 바꾸어 보면
         
          <bean id="bbsController" class="com.tmax.spring.web.BbsController">
      	<property name="bbsService">
      		<ref bean="bbsService" />
      	</property> 
          </bean>
          
         <bean id="urlMapping" 
              class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
              <property name="mappings">
                  <props>
                      <prop key ="/bbs/listBbs.do">bbsController</prop>
      	   </props>
              </property>
          </bean>
      
       

    실제 Controller에 대한 정의는 일반 빈을 정의 할 때와 다르지 않게 된다. 맵핑에 대한 정보는 별도의 빈(urlMapping)에서 관리되고 있다. 만일 더욱 많은 Controller가 생성되고 맵핑을 해 줘야 할 때 모든 매핑 정보는 한곳에서 관리를 할 수 있게 되는 것이다. Controller 마다 맵핑정보까지 나열할 필요가 없게 된다.

    urlMapping 빈에서 Controller와 URL을 맵핑할 때 <props/>를 사용해서 기술할 수 있다. 또 다른 방법은 <props/>를 사용하지 않고 별도의 Properties 파일에서 관리 할 수도 있다.

    • CommonsPathMapHandlerMapping

    Spring MVC를 사용하여 다중요청 처리하기

    개발자들이 Controller 만 만들면 되지만 개발하면서 페이지, 기능이 추가 될 때마다 새로운 Contoller를 만드는 것은 굉장히 짜증이 나는 작업이다. 게다가 점점 관리해야 하는 파일의 수가 많아져서 빈 설정파일도 점점 복잡해질 것이다.이 같은 상황을 해결하기 위하여 Spring MVC는 MultiActionController를 제공한다. MultiActionController은 하나의 Controller를 구현함으로써 다수의 요청을 처리하는 것이 가능하다.

    아래 예는 회원가입,수정을 통해 MultiActionController 구현을 보여준다.

    Controller 객체인 MemberController.java

    MemberController.java
    package net.javajigi.member.web;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import net.javajigi.member.model.Member;
    import net.javajigi.member.service.MemberService;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
    
    public class MemberController extends MultiActionController{
    	
    	private MemberService memberService = null;
    	public void setMemberService(MemberService memberService){
    		this.memberService = memberService;
    	}
    	
    	public ModelAndView add(HttpServletRequest request, HttpServletResponse reponse) throws Exception{
    		
    		Member command = new Member();
    		bind(request,command);
    		
                      memberService.addMember(command);
    		
    		return new ModelAndView("main");		
    		
    	}
    	
    	public ModelAndView update(HttpServletRequest request, HttpServletResponse reponse) throws Exception{
    		
    		Member command = new Member();
                      bind(request,command);
    		memberService.updateMember(command);
    		
    		return new ModelAndView("list");
    		
    		
    	}
    
    }
    

    위의 예제는 add(가입), update(수정)을 구현하기 위해 MultiActionController를 상속받은 Controller 이다.
    각각의 메소드에 로직이나 서비스 호출 부분들의 소스를 구현하다.
    add 메소드를 보면 화면에서 입력받은 내용을 bind()를 사용해 domain 객체에 넘기고 있다.(이 내용은 나중에..)
    그 뒤 서비스의 addMember()를 호출한다.
    처리 후 리턴될 페이지를 설정한다.(add->main.jsp, update->list.jsp)

    Controller를 작성 후 webapps/WEB-INF/action-servlet.xml 파일에 빈을 매핑한다.

    action-servlet.xml
    <bean id="memberService" class="net.javajigi.member.service.MemberService"/>
    
    <bean name="/member/addMember.do /member/updateMember.do" class="net.javajigi.member.web.MemberController">
    	<property name="methodNameResolver">
    		<ref local="memberControllerMethodNameResolver"/>
    	</property>	
    	<property name="memberService">
    		<ref local="memberService"/>
    	</property>			
    </bean>	
    
    <bean id="memberControllerMethodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
    	<property name="mappings"> 
    		<props>
    			<prop key="/member/addMember.do">add</prop>
    			<prop key="/member/updateMember.do">update</prop>
    		</props>
    	</property>
    </bean>
    

    MemberController는 두 개의 URL(/member/addMember.do,/member/updateMember.do)에 맵핑이 되었다.각각의 URL이 호출될 때 MemberController 안의 어떤 메소드가 호출될 지는 "memberControllerMethodNameResolver"빈에서 PropertiesMethodNameResolver를 통해 매핑이 된다. 결국 /member/addMember.do 가 호출되면 MemberController의 add 메소드가 /member/updateMember.do가 호출되면 update 메소드가 호출된다.

    결국 위의 예는 빈 설정파일에서 URL을 다르게 함으로써 메소드에 접근할 수 있도록 설정한 것이다. (PropertiesMethodNameResolver 클래스를 통해서)

    <bean name="/member/member.do" class="net.javajigi.member.web.MemberController">
    	<property name="methodNameResolver">
    		<ref local="methodNameResolver"/>
    	</property>
    </bean>
    
    <bean id="methodNameResolver" class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver">
    	<property name="paramName"> 
    		<value>method</value>
    	</property>
    	<property name="defaultMethodName"> 
    		<value>add</value>
    	</property>
    </bean>
    

    위의 예를 보면 MemberController는 하나의 URL에 맵핑이 되었다. 그럼 어떻게 MemberController의 다른 메소드를 호출할 수 있을까? 그것은 "methodNameResolver" 빈의 "ParameterMethodNameResolver" 를 이용해서 호출이 가능하다.
    URL을 통해 전달되는 인자중에 "paramName"인 method라는 이름을 가지는 인자의 값에 해당하는 메소드를 호출해 주는 것이다.
    즉 /member/member.do?method=add 를 하게 되면 MemberController의 add()를 호출하는 것이다. /member/member.do?method=update를 하면 update()를 호출하게 될 것이다. 만일 아무 값도 없다면 default 으로 add()를 호출하게 된다. 호출하는 URL은 같지만 뒤 인자의 인자값을 변경해 줌으로써 다른 메소드를 호출할 수 있다.

    ParameterMethodNameResolver를 이용하면 MultiActionController를 상속받은 Controller 에 메소드가 추가되더라도 설정파일에 추가적인 설정이 필요 없을 것이다. 하지만 호출할 때 추가된 메소드 이름을 알아야 한다.

    데이터 바인딩(Data Binding)
    웹 애플리케이션 개발에서 입력, 수정과 관련된 기능을 구현하기 위해서 입력하는 값에 대한 유효성 체크와 입력 값을 도메인 모델로 바인딩 하는 기능이 필요하다.그 동안 개발자들은 유효성 체크를 위해서 화면단에는 많은 자바스크립트를 데이타 바인딩을 위해서는 HttpServletRequest에 담긴 인자를 꺼내서 다시 도메인 모델로 set하는 코드들이 난무했을 것이다.

    Spring MVC 에서는 입력화면(또는 수정화면)에서 전달되는 입력 데이터를 비즈니스 계층에 전달하기 위하여 HttpServletRequest에 담긴 인자와 도메인 모델의 속성을 자동적으로 연결하는 데이터 바이팅을 지원하고 있다.

    위에 나온 bind()가 그 역할을 하는 것이다.

    bind(request,command);

    Bind request parameters onto the given command bean
    @param request request from which parameters will be bound
    @param command command object, that must be a JavaBean
    @throws Exception in case of invalid state or arguments

    대신 클라이언트가 입력한 데이터를 도메인 모델과 데이터 바인딩 하기 위해서는 입력 화면에서 사용한 속성이름과 도메인 모델의 속성이름이 같아야 한다.

    • 회원가입 입력폼인 memberEntry.jsp
      <%--
      작성자 : 난다
      일자 : 2006.02.13
      설명: 회원가입
      --%> 
      <%@page contentType="text/html; charset=euc-kr"%>
      <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
      
      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
      <html>
        <head>    
          <title>사용자 관리 - 회원 가입</title>     
          <link rel="stylesheet" type="text/css" href="../css/style.css" media="screen">
        </head>  
       <body>
       <div id="member">
       <form name="thisform" method="post" action="${pageContext.request.contextPath}/member/addMember.do">   
      	<table summary="회원가입">
      		<caption>회원 정보 </caption>
      		<tr>
      			<th id="id">* 아이디</th>
      			<td headers="id">
      				<input type="text" id="id" name="id" />
      			</td>
      		</tr>
      		<tr>
      			<th id="password">* 비밀번호</th>
      			<td headers="password">
      				<input type="text" id="password" name="password" />
      			</td>
      		</tr>
      		<tr>
      			<th id="passwordRepeat">* 비밀번호</th>
      			<td headers="passwordRepeat">
      				<input type="text" id="passwordRepeat" name="passwordRepeat" />
      			</td>
      		</tr>	
      		<tr>
      			<th id="password_hint">* 비밀번호 힌트 </th>
      			<td headers="passwordHint">
      				<input type="text" id="passwordHint" name="passwordHint" />
      			</td>
      		</tr>
      		<tr>
      			<th id="passwordAnswer">* 비밀번호 답 </th>
      			<td headers="passwordAnswer">
      				<input type="text" id="passwordAnswer" name="passwordAnswer" />
      			</td>
      		</tr>
      		<tr>
      			<th id="email">* e-mail</th>
      			<td headers="email">
      				<input type="text" id="email" name="email" />
      			</td>
      		</tr>						
      	</table>
      	<center>	
      		<input type="submit" value="확인" tabindex="4" />
      	</center>
      </form>
      </div>
      </body>
      </html>
      
      
    • 도메인 모델 Member.java
    Member.java
    package net.javajigi.member.model;
    
    import org.apache.commons.lang.builder.ToStringBuilder;
    
    public class Member {
    	private String userId = null;
    	private String password = null;
    	private String passwordRepeat = null;
    	private String passwordHint = null;
    	private String passwordAnswer = null;
    	private String email = null;
    	
             변수들에 대한 getter,setter 메소드...
                  .
                  .
                  .
            public String toString() {
              return ToStringBuilder.reflectionToString(this);
            }
    	
    }
    

    memberEntry.jsp의 입력필드의 이름은 id,password,passwordRepeat,passwordHint,passwordAnswer,email로 정의 되어 있다.
    그리고 바인딩되는 도메인 모델 Member도 id,password,passwordRepeat,passwordHint,passwordAnswer,email 가 정의되어 있다.
    즉 같은 속성이름에 대해서는 가져올 수 있지만 만일 다르다면 가져오지 못하게 된다.

    위의 예제에서 사용한 MultiActionController는 입력데이터에 대한 유효성 체크 기능을 지원하지 않는다. 만일 유효성 체크도 원한다면 MultiActionContorller를 상속받아서는 사용할 수 없다.
    유효성 체크와 바인딩 기능까지 모든 기능을 사용하길 원한다면 AbstractCommandControllder 클래스를 상속받아야 한다.

    AbstractCommandControllder를 상속받은 Controller

    MemberAbstractCommandController.java
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.validation.BindException;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.AbstractCommandController;
    
    import com.tmax.spring.domain.Member;
    import com.tmax.spring.service.app.MemberService;
    
    public class MemberAbstractCommandController extends AbstractCommandController{
        private static transient Log log = LogFactory.getLog(MemberAbstractCommandController.class);
    	
        private MemberService memberService = null;
    	
        public void setMemberService(MemberService memberService){
    	this.memberService = memberService;
        }
    	
        public MemberAbstractCommandController(){
    	setCommandClass(Member.class);
        }
    	
        protected ModelAndView handle(HttpServletRequest request, 
    		HttpServletResponse response, Object command, BindException errors) 
                     		throws Exception {
    	Member member = (Member)command;
    		
    	//memberService.addBbs(command);
    	log.error("가져오는지..."+member.toString());
    		
    	return new ModelAndView("../index");	
         }
     }
    

    AbstractCommandController를 상속받아 구현 하려면 바인딩하는 클래스를 디폴트 생성자 안에 setCommandClass()를 통해서 설정해야 한다. 위의 예를 보면

    public MemberAbstractCommandController()

    Unknown macro: { setCommandClass(Member.class); }

    그러면 들어오는 입력 값들과 설정한 클래스와의 바인딩이 이전에 보았던 bind() 라는 메소드 없이 Casting 만으로 이루어 진다.

    하지만 AbstractCommandController는 요청에 대해 각각의 Controller를 만들어야 한다.결국 데이터 바인딩 및 데이터에 대한 유효성 체크 기능을 지원하고 있지만 AbstractCommandController를 상속받은 Controller로는 다수의 요청을 처리 할 수 없는 것이다.즉 Controller 를 이용하면 요청에 따라 추가되고 빈 설정 정보또한 수정해 주어야 한다.

    결국 큰 주제로 했던 다수의 요청을 한 Controller에서 처리하는 방법은 아니지만 좀 더 손쉽게 유효성체크와 데이터 바인딩을 할 수 있다는 점에서는 유용하다. 간단한 CRUD 같은 작업은 MultiController를 구현해 한 Controller에서 사용할 수도 있고( 물론 유효성체크는 스크립트나 다른 방법을 통해) 복잡하고 많은 입력 필드를 가지고 있는 화면이라면 AbstractCommandController를 사용해 좀 더 손쉽게 처리 할 수 있을 것이다.

    이것은 각각의 프로젝트, 요구사항등에 맞추어 결정하면 될 것이다.

    action-servlet.xml 추가사항
    	<bean name="/member/addMemberWithAbstract.do"   class="com.tmax.spring.web.MemberAbstractCommandController">
    	  <property name="memberService">
    		<ref bean="memberService" />
    	 </property>	
            </bean>
    
    • 실제로직을 담당하는 비지니스 객체인 MemberService.java 파일을 생성한다.
    MemberService.java
    package net.javajigi.member.service;
    
    import net.javajigi.member.model.Member;
    
    public class MemberService {
    	
    	public void addMember(Member member){
    		//로직처리
    	}
    	
    	public void updateMember(Member member){
    		//로직처리
    	}
    }
    
    

    하나의 Controller만으로 입력,수정처리하기

    <ation-servlet.xml>
     
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
        "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    	<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    	<property name="mappings">
    			<props>				
    				<prop key="/login.do">loginFormController</prop>				
    			</props>
    		</property>
    	</bean>	
    	
    	<bean id="loginFormController" class="spring.LoginFormController">
    		<property name="formView"><value>/login</value></property>
    		<property name="successView"><value>/main</value></property>
    	</bean>
    		
    	<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    	<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    		<property name="viewClass">
    			<value>org.springframework.web.servlet.view.JstlView</value>
    		</property>
    		<property name="cache"> 
    			<value>false</value>
    		</property>
    		<property name="prefix">
    			<value>/</value>
    		</property>
    		<property name="suffix">
    			<value>.jsp</value>
    		</property>		
    	</bean>
    </beans>

    Controller를 URL과 매핑해주어야 한다.
    login.do에 대한 요청을 loginFormController가 담당하며, 이 Controller는 로그인을 위한 로직처리를 포함하고, 사용자가 직접 작성해야한다.
    입력화면은 /login 이며, 로직을 무사히 처리한후에는 /main으로 이동한다.
    JSTL pages , jsp Page를 사용하기위해 JstlView를 선언하였다.

    • web-app/login.jsp 로그인 페이지를 생성한다.
      <login.jsp>
      <%@page contentType="text/html; charset=euc-kr"%>
      <%@ page language="java" import="java.util.*" %>
      <html>
        <head>
          <title>My JSP 'login.jsp' starting page</title>    
        </head>  
        <body>
        <form name="myForm" method="post" action="/spring/login.do">
         	<table>
         		<tr>
         			<td>
         				ID	: <input type="text" name="userId" value="1">   				
         			</td>
         		</tr>
         		<tr>
         			<td>
         				PASSWORD	: <input type="password" name="password" value="2">   				
         			</td>
         		</tr>
         		<tr>
         			<td>
         				<input type="submit" name="login" value="로그인"/>
         			</td>
         		</tr>
         	</table>
         </form>
        </body>
      </html>
      
    • web-app/main.jsp 로그인성공후 이동할 메인 페이지를 생성한다.
      <main.jsp>
      <%@page contentType="text/html; charset=euc-kr"%>
      <%@ page language="java" import="java.util.*" %>
      
      <html>
        <head>
          <title>My JSP 'main.jsp' starting page</title>   
        </head>  
        <body>
        	Login Success~!!
        </body>
      </html>
      
    • web-app/WEB-INF/net.javajigi.user.web.LoginFormController.java 파일을 생성한다.
    LoginFormController.java
    package net.javajigi.user.web;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import net.javajigi.user.model.Authenticate;
    import org.springframework.validation.BindException;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.SimpleFormController;
    
    public class LoginFormController extends SimpleFormController {	
    	
    	public LoginFormController() {
                      // bean 객체
    		setCommandName("authenticate");
    		setCommandClass(Authenticate.class);
    	}
    	
    	public ModelAndView onSubmit(HttpServletRequest request,
    			HttpServletResponse response, Object command, BindException errors)
    			throws Exception {
    		
    		if (request.getParameter("login") != null) {
    			Authenticate auth = (Authenticate) command;
    
    			try {
    				//로직처리 
                                              //boolean bResult = UserLoginProc.login(auth);
    
                                        //View 객체 리턴 
    				return new ModelAndView(getSuccessView());
    				
    			} catch (Exception e) {
    				request.setAttribute("errorMessage", e.getMessage());
    
    				return new ModelAndView(getFormView());
    			} 
    			
    		} else {
    			HttpSession session = request.getSession();
    
    			if (session.getAttribute("loginUser") != null) {
    				return new ModelAndView("decorators/logout");
    			} else {
    				return new ModelAndView("decorators/login");
    			}
    		}
    	}
    }
    

    SimpleFormController 을 상속받은 LoginFormController.java 파일을 생성한다.

    • web-app/WEB-INF/net.javajigi.user.model.Authenticate.java 파일을 생성한다.
    Authenticate .java
    package net.javajigi.user.model;
    
    
    public class Authenticate {
        private String userId = null;
    
        private String password = null;
        
    	public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public String getUserId() {
            return userId;
        }
    
        public void setUserId(String userId) {
            this.userId = userId;
        }
    }
    
    

    LoginFormController.java에서 호출되는 bean객체를 생성한다.

    참고문헌

    Posted by 1010
    반응형
  • jpetstore 설치하기 : Spring2.0 샘플에 포함된 jpetstore를 설치하고 테스트 해 본다.
  • jpetstore 에서의 Spring 초기 설정과 IoC : jpetstore 에서의 웹 애플리케이션을 위한 Spring 초기 설정과 간단한 IoC에 대한 설명
  • jpetstore 에서의 Spring MVC 설정 : jpetstore 에서의 Spring MVC 설정을 어떻게 하는지 알아본다. 그리고 간략하게 Spring MVC에서 HTTP Request를 처리하는 과정에 대해서 셋팅하는 것과 함께 살펴본다.
  • jpetstore 에서의 HandlerMapping : Spring MVC에서 HandlerMapping을 어떻게 사용하는지 jpetstore을 통해서 알아본다.
  • jpetstore 에서의 InternalResourceViewResolver : Spring MVC에서 InternalResourceViewResolver을 어떻게 사용하는지 jpetstore을 통해서 알아본다.
  • jpetstore 예제로 살펴보는 Spring MVC와 iBatis 연동 : "shop/viewCategory.do" 요청을 예제로 하여 Spring MVC(Controller)의 전체적인 처리 과정과 iBatis를 사용하여 연동한 Spring framework의 과정을 살펴본다.
  • 참고문헌

  • 출처 : http://www.javajigi.net/pages/viewpage.action?pageId=6962

    Posted by 1010
    반응형

    출처 : http://dogfeet.tistory.com/159

    Ioc.java

    01.import java.util.Calendar;
    02.import org.springframework.beans.factory.BeanFactory;
    03.import org.springframework.beans.factory.xml.XmlBeanFactory;
    04.import org.springframework.core.io.ClassPathResource;
    05.  
    06.public class Ioc {
    07.  
    08.    private Calendar myCalendar;
    09.      
    10.    public void setMyCalendar(Calendar cal) {
    11.        myCalendar = cal;
    12.    }
    13.      
    14.    @Override
    15.    public String toString() {
    16.        return "Ioc Class with " + myCalendar;
    17.    }
    18.      
    19.    public static void main(String[] args) {
    20.        System.out.println("Spring Study!");
    21.        BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml"));
    22.        System.out.println(bf.getBean("ioc"));
    23.        System.out.println(bf.getBean("date"));
    24.    }
    25.}

    beans.xml

    01.<?xml version="1.0" encoding="UTF-8"?>
    04.       xsi:schemaLocation="
    07.  
    08.  
    09.    <bean id="calendar" class="java.util.GregorianCalendar" />
    10.      
    11.    <bean id="date" class="java.util.Date" />
    12.  
    13.    <bean id="ioc" class="Ioc">
    14.        <property name="myCalendar" ref="calendar" />
    15.    </bean>
    16.      
    17.</beans>

    library files

    • commons-logging.jar
    • org.springframework.beans-3.0.0.RC1.jar
    • org.springframework.core-3.0.0.RC1.jar

    execution

    1.$ javac Ioc.java -cp org.springframework.beans-3.0.0.RC1.jar:org.springframework.core-3.0.0.RC1.jar
    2.$ java -cp .:org.springframework.core-3.0.0.RC1.jar:org.springframework.beans-3.0.0.RC1.jar:commons-logging.jar Ioc
    3.Spring Study!
    4.Nov 10, 2009 5:49:14 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
    5.INFO: Loading XML bean definitions from class path resource [beans.xml]
    6.Ioc Class with java.util.GregorianCalendar[time=1257842954691,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Seoul",offset=32400000,dstSavings=0,useDaylight=false,transitions=14,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2009,MONTH=10,WEEK_OF_YEAR=46,WEEK_OF_MONTH=2,DAY_OF_MONTH=10,DAY_OF_YEAR=314,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=2,AM_PM=1,HOUR=5,HOUR_OF_DAY=17,MINUTE=49,SECOND=14,MILLISECOND=691,ZONE_OFFSET=32400000,DST_OFFSET=0]
    7.Tue Nov 10 17:49:14 KST 2009
    Posted by 1010
    반응형
    Spring 3.0 가 곧 출시(4월경) 될 것이라는 이야기 속에, Spring 2.5 버전에 대한 이야기를 해야겠다.
    Spring 2.1로 줄곧 개발하고 있었는데, 항상 느끼던게 지나치도록 복잡한 설정이었다. 진입장벽이 높다고 했던가? 아마도 SpringFramework의 초기 진입시 가장 어려운 점은 무엇보다도 설정 이 아닌가 생각되었다.
    Spring 2.1에서의 복잡한 설정(물론 몇 번 해보면 익숙해지면 금방 copy & paste 가 가능하다.)은 Spring 2.5에서 어노테이션이라는 강력한 기능으로 중앙집중적인 설정에서 지방분권적인 설정을 가능하게 만들었다.
    (xml에 의한 설정이 아닌 개별 java 파일로 설정이 옮겨감)


    Hello World를 찍어보라구!!

    공실장 : 어이 김과장, 내가 이번에 Spring을 공부 해보고 싶은데 어떤 책을 봐야하지? Spring in Action은 한번 죽 훓어 봤는데, 다른 좀 쉬운 책 그런거 없나?
    김과장 : ... Hello World는 찍어보셨나요?
    공실장 : 허허허, 나보고 Hello World나 찍어보라는건가? 내가 그런걸... 허허허

    (며칠 뒤)
    김과장 : 공실장님, Spring 은 어떻게 잘 되고 계세요?
    공실장 : 나 Spring은 포기 했네, 설정이 어려워서 그런지 Hello World가 도무지 실행이 안되던데...
    어이없이 들릴지 몰라도, 처음 Spring을 접하는 사람이라면 Hello World 조차도 왠지 멀게만 느껴질 것이다. 위의 예처럼 설정만 누군가 해준다면, 자바 소스는 짤 수 있겠는데... 라는 말이 나오겠지만, 무엇보다도 설정을 할 줄 아는게 중요하겠다.

    하지만, 개발기간이 초초 단기라면, 한달 이내에 분석부터 구현까지 모두 끝내야하는 것이라면 Step by Step으로 설정부터 배워나가서 Spring을 구성하기가 어려울 것이다. 아마, Spring은 배제될 것이 뻔하다. 단순히 jsp 페이지만으로 구성해서 빨리 마치고자 노력할 것이다. 온통 Copy & Paste 범벅이 될 게 자명하다. 누군가 Copy & Paste도 실력이라는 진리를 이야기 해 준적이 있지만, 생각없는 Copy & Paste는 내 프로그램의 사명이라고 생각되지 않는다면 다른 길을 찾아야 하지 않을까?


    Utility를 만들어서 사용하는건 어때?

    몇 번의 Copy & Paste 로 만든 사이트가 지긋지긋하다면, 혹은 비즈니스 로직에만 집중하라는 만고의 진리를 느껴보고 싶다면, Utility를 만들어 보는 것도 나쁜 생각은 아닌 것 같다. 초기 진입을 낮춰 주는 무언가가 있다면 난 정말 비즈니스 로직은 환상적으로 짤 수 있을거야라는 정말 Lazy 한 생각이라면 Utility로 간단히 해결하자.

    PCube(회사 내 서브 프로젝트명으로 Spring 2.5 기반 설정 파일 자동화 Utility의 이름) 의 시작은 프로젝트 시간이 촉박한 개발자를 위해 시작한 사내 프로젝트이다. Spring을 이용하되, 설정을 쉽게 도와줌으로써 "시작" 할 수 있는 여건을 마련해 준다. 이제 한 컷 씩 따라하면서 "Spring을 누군가 설정만 해준다면..." 라는 Lazy한 개발자가 되보자.

     
    위의 2개 파일을 일단 다운로드 받도록 한다.

    각 파일의 설명은 아래와 같다.

     pcube-1.0.1.jar Generate 해주는 클래스 실행 파일 모음

     project.pcube Generate 해야 할 각족 설정이 담긴Seed 파일

    project.pcube 에 설정된 값들을 가지고, pcube-1.0.1.jar 파일로 설정 파일을 Generate 한다. 특별히, project.pcube 파일은 JSON 파일 형식으로 되어 있어서 실제로 열어보면 간단한 중첩 구조로 되어 있음을 알 수 있다.

    샘플을 보면서 실제로 어떤 설정을 해야 하는지 살펴보자.
    {
        project:
        {
            name:'Test Project generate by Utility(PCube)',
            directory:'C:/eclipse_spring/workspace/TestProject/WebContent',
            package:'com.company.project.package',
            author:'Hyoseok Kim',
            email:'toriworks@gmail.com',
            comment:'프로젝트의 설명을 넣어주세요.',
            schema:
            {
                kind:'MySql',
                version:'5.0',
                connects:
                [
                    {id:'jdbc/JNDIName', url:'localhost', db:'testDB', uid:'root', pwd:'123456'}
                ],
                tables:
                [
                    {
                        name:'User',
                        lang:'UTF8',
                        fields:
                        [
                            {fname:'user_id',type:'varchar(20)',jtype:'String',auto:'no',key:'yes',nil:'no',default:''},
                            {fname:'user_name',type:'varchar(20)',jtype:'String',auto:'no',key:'no',nil:'no',default:''},
                            {fname:'auth_code',type:'int',jtype:'int',auto:'no',key:'no',nil:'yes',default:''},
                            {fname:'last_login',type:'datetime',jtype:'String',auto:'no',key:'no',nil:'no',default:'now()'},
                            {fname:'login_count',type:'int',jtype:'int',auto:'no',key:'no',nil:'no',default:'0'},
                            {fname:'email_address',type:'varchar(150)',jtype:'String',auto:'no',key:'no',nil:'no',default:''},
                            {fname:'contact1',type:'varchar(50)',jtype:'String',auto:'no',key:'no',nil:'no',default:''},
                            {fname:'contact2',type:'varchar(50)',jtype:'String',auto:'no',key:'no',nil:'yes',default:''},           
                            {fname:'title_code',type:'varchar(20)',jtype:'String',auto:'no',key:'no',nil:'yes',default:''},
                            {fname:'title',type:'varchar(20)',jtype:'String',auto:'no',key:'no',nil:'no',default:''},
                        ]
                    },
                    {
                        name:'Manager',
                        lang:'UTF8',
                        fields:
                        [
                            {fname:'manager_id',type:'varchar(20)',jtype:'String',auto:'no',key:'yes',nil:'no',default:''},
                            {fname:'manager_name',type:'varchar(20)',jtype:'String',auto:'no',key:'no',nil:'no',default:''},
                            {fname:'last_login',type:'datetime',jtype:'String',auto:'no',key:'no',nil:'no',default:'now()'},
                            {fname:'login_count',type:'int',jtype:'int',auto:'no',key:'no',nil:'no',default:'0'},
                            {fname:'email_address',type:'varchar(150)',jtype:'String',auto:'no',key:'no',nil:'no',default:''},
                        ]
                    }               
                ]
            }
        }
    }

    데이터베이스에 대한 설계가 모두 끝난 다음에 PCube를 이용하는게 가장 best practice라고 생각이 되는데, 왜냐하면 위의 Seed 파일의 내용을 보면 데이터베이스의 엔티티 정보가 그대로 들어가 있기 때문이다. 이 정보를 바탕으로 iBatis 쿼리를 만들어 내도록 되어 있기 때문임을 이해해 줬으면 한다.

    Seed 파일의 첫 문장은 project: 로 시작하고, 기본적인 프로젝트 정보를 담는 부분과 데이터베이스 엔티티 부분으로 크게 나눌 수가 있다. 기본적인 프로젝트 정보를 담는 부분은 나중에 Java 소스의 주석부분으로 옮겨지게 된다. 중요한 점은 package: 인데, package: 부분에 쓰인 대로 Java 소스에 Package 정보가 부여된다.


    패키지명이 Seed 파일에 기술된 대로(com.company.project.package) 구성되었음을 확인 해 볼 수 있다.

    옆에 보여지는 이미지가 자동생성 된 이후에 구성된 Java package 인데, 실제로 controller, dao, domain, service, test, utils, validation 총 7가지의 파일을 자동적으로 생성해 낸다.





    domain package 의 파일을 살펴보면 아래와 같다.

    대강 살펴보면, Seed 파일에서 작성한 내용이 그대로 옮겨졌다는 걸 알 수가 있다.

    domain 클래스만 살펴보았지만, 기타 다른 package를 보면 규칙에 따라서 파일이 생성됨을 알 수가 있다.



















    본격적으로 실습을 해보자!!!



    File을 누르고, Dynamic Web Project 를 선택하자.


    Project 명으로 TestProject 라고 입력하고, 하단의 Finish 버튼을 누른다.(Project contents, Target Runtime, Dynamic Web Module version, Configuration 등의 설정은 필요에 따라 설정하자.)



    lib 폴더를 펼친 후에, 아까 다운로드 받아놓은 pcube-1.0.1.jar 파일을 lib 폴더에 복사한다.

    그리고, 2개의 폴더를 새로 만든다.
    input 과 output 폴더를 만든다.

    위 과정까지 마친 후의 폴더 모습은 다음과 같다.



    input 폴더와 output 폴더가 추가되었고, WEB-INF 밑의 lib 폴더에 pcube-1.0.1.jar 파일이 추가되었음을 알 수가 있다.

    그리고, input 폴더에 위에서 받아 놓은 Seed 파일인 project.pcube 파일을 복사 해 둔다.
    input 폴더에 project.pcube 파일을 읽어 들여서, output 폴더로 파일들이 생성될 예정이다.








    이제 파일들을 Generate 하기 위해서, 클래스 파일 하나를 만들어 보자.


    클래스명에 GenerateFilesByAutomation 이라 입력하고, main 함수를 만들 예정이므로 public static void main(String[] args) 에 체크를 했다. Finish 버튼을 누르면, 파일 작성을 하기 위해 파일이 열린다.

    GenerateFilesByAutomation.java 에 이렇게 내용을 기록하자.

    import java.io.IOException;
    import org.json.JSONException;

    import com.cs.pcube.repository.start.GenerateFiles;

    /**
     * @author Hyoseok Kim(
    toriworks@gmail.com)
     *
     */

    public class GenerateFilesByAutomation {

     /**
      * @param args
      * @throws JSONException
      * @throws IOException
      */
     public static void main(String[] args) throws IOException, JSONException {
     
      // 파일을 자동으로 만들어보자.
      GenerateFiles generator = new GenerateFiles();  
      generator.generateAllFiles(0); 

     
     }
    }

    수기로 작성해야 하는 부분은 위에처럼 파란색 굵은 글씨로 표시를 했다. 이렇게 작성을 했으면, Java 파일을 실행 시켜보자.

    Shift + Alt + X 누른 후에, J 버튼을 눌러서 Java application을 실행 시켜보자.

     
    실행 후에, output 폴더를 새로 고침을 눌러서 갱신을 해보자.(처음에는 생성된게 잘 보이지 않는다. 꼭 새로 고침을 눌러보자.)

    새로 고침을 누르면, 크게 2부분으로 파일들이 나눠져 있음을 알 수가 있다.
    첫째는 .java 파일들이고, 둘째는 Tomcat 쪽 파일과 .xml 파일들이다. 생성되어진 모든 파일을 한번 살펴보자.




    생성된 디렉토리와 파일들을 Full로 펼쳐 보이면 이 정도 구조를 가지는 파일들이 생성되었음을 알수 있다.

    com 밑의 폴더들은 기본적인 .java 파일과 iBATIS 용 .xml 파일들이 보이고, etc 밑의 폴더에서는 tomcat 용 설정파일과 WebContent에 위치해야 할 .jsp 파일과 .xml 파일들이 있음을 볼 수가 있다.

    또한 SiteMesh를 이용하고 있기 때문에 이에 따르는 decorator.xml 이 폴더 구조에서 보여지고 있다.

    정리하자면, Spring 2.5 + iBATIS + SiteMesh 를 사용하는 프로젝트를 자동생성해 준다.

    이후 작업은 단순히 폴더를 복사해서, 폴더 구조에 맞게끔 복사를 해주면 그만이다. 예를 들어서, WebContent 폴더는 프로젝트의 WebContent 밑에 통째로 복사해 주면 된다.


    그렇다면, 생성된 Spring 설정 파일은?

    일단 Spring 2.5에서 사용할 설정 파일에 대해 살펴보자. 3개의 파일을 생성해 주고 있는데, 프로젝트명-data.xml, 프로젝트명-service.xml, 프로젝트명-servlet.xml 이다. 각 각의 파일에 대해 살펴보자.

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 이 파일은 XML Schema 기반으로 Spring 설정을 구성합니다. -->

    <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:tx="http://www.springframework.org/schema/tx"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

      <!-- dataSource 설정 설정 -->
      <!-- (주의) dataSource가 여러개 일 경우 bean id 값을 조정하셔야 합니다. -->
      <!-- 1번째 dataSource 설정 -->

      <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"
       p:jndiName="java:comp/env/jdbc/JNDIName" />

      <!-- 트랜잭션 부분 코드를 삽입한다. -->
      <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
      </bean>

      <tx:annotation-driven transaction-manager="txManager"/>

      <!-- SqlMap와 dataSource를 연결해준다. -->
      <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"
       p:configLocation="/WEB-INF/resources/ibatis/sql-map-config.xml"
       p:dataSource-ref="dataSource" />

      <!-- 이 곳에 하위로 사용자와 관리자용 Spring 설정 파일 설정 부분 import 태그를 이용해 입력합니다.
      또는 이 예시에 주어진 대로 파일을 생성 하고 아래 내용을 복사하세요.
      예시
      <import resource="resources/operation/sample-manager-data.xml" />
      <import resource="resources/operation/sample-user-data.xml" /> -->

      <!-- User DAO 선언 -->
      <bean id="userDao" class="com.company.project.package.dao.ibatis.UserDaoImpl">
       <property name="sqlMapClient" ref="sqlMapClient" />
      </bean>

      <!-- Manager DAO 선언 -->
      <bean id="managerDao" class="com.company.project.package.dao.ibatis.ManagerDaoImpl">
       <property name="sqlMapClient" ref="sqlMapClient" />
      </bean>

    </beans>



    수작업으로 할일은 거의 없다. 자동으로 파일을 만들었으니, 그냥 설정이 이렇게 구성되는구나 라고 알고 있으면 되겠다. 하지만, 설정을 보면서 Spring 책을 꼭 찾아서 봄 직하다.

    다음은 프로젝트명-service.xml을 살펴보자. 크게 어렵지 않다.

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 이 파일은 XML Schema 기반으로 Spring 설정을 구성합니다. -->

    <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-2.5.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

      <!-- 빈 스캐닝 부분을 추가한다. -->
      <context:annotation-config />
      <context:component-scan base-package="com.company.project.package" />

      <!-- 이 곳에 하위로 사용자와 관리자용 Spring 설정 파일 설정 부분 import 태그를 이용해 입력합니다. -->
      <!-- 예시 -->
      <!-- <import resource="resources/operation/sample-manager-service.xml" /> -->
      <!-- <import resource="resources/operation/sample-user-service.xml" /> -->

    </beans>

    service 는 더 간단하다. 왜냐하면, 빈 스캐닝을 통해서 굳이 .xml 파일에 설정을 넣을 필요가 없어졌기 때문이다. 그렇다면, Service 레이어를 담당하는 .java 파일을 함께 살펴보자.(좀 길게 되어 있으나, 한번 쯤은 꼭 봐야 하기에 전체 소스를 옮긴다.)

    /**
     * Test Project generate by Utility(PCube)
     *  This file is generate by PCUBE system automatically.
     *  Generated by Sun Feb 01 18:52:46 KST 2009
     */
    package com.company.project.package.service.impl;

    // << Please import packages >>
    // TODO : import package name if you want to add

    import org.springframework.stereotype.Service;

    import org.springframework.dao.DataAccessException;
    import com.company.project.package.dao.IManagerDao;
    import com.company.project.package.domain.Manager;
    import com.company.project.package.service.IManagerService;

    import org.springframework.beans.factory.annotation.Autowired;

    import java.util.*;

    // << Please insert import packages (for example, import java.util.List; ) >>


    /**
     * @author Hyoseok Kim (
    toriworks@gmail.com)
     *
     */

    @Service
    public class ManagerServiceImpl implements IManagerService {

     /**
      * Constructor
      */

     public ManagerServiceImpl() {}

     // Setter injection 대신에 @Autowired와 @Qualifier annotation 사용으로 injection 수행
     @Autowired
     private IManagerDao managerDao = null;

     public void setManagerDao(IManagerDao managerDao) {
      this.managerDao = managerDao;
     }

     /**
     * C of CRUD - 새로운 정보를 데이터베이스에 입력하기 위해서 DAO 객체에 역할을 위임합니다.
     */

     @Override
     public void create(Manager manager) throws DataAccessException {
      managerDao.create(manager);
     }

     /**
     * U of CRUD - 기존에 등록된 정보를 수정하기 위해서 DAO 객체에 역할을 위임합니다.
     */
     @Override
     public void update(Manager manager) throws DataAccessException {
      managerDao.update(manager);
     }

     /**
     * D of CRUD - 기존에 등록된 정보를 삭제하기 위해서 DAO 객체에 역할을 위임합니다.
     */
     @Override
     public void delete(Object parameter) throws DataAccessException {
      managerDao.delete(parameter);
     }

     /**
     * R of CRUD - DAO 객체에 위임하여 요청받은 목록을 반환합니다.
     */

     @SuppressWarnings("unchecked")
     @Override
     public List<Manager> lists(Object parameter) throws DataAccessException {
      return managerDao.lists(parameter);
     }

     /**
     * DAO 객체에 위임하여 목록 개수를 반환합니다.
     */
     @Override
     public Integer listsCount(Object parameter) throws DataAccessException {
      return (Integer) managerDao.listsCount(parameter);
     }

     /**
     * R of CRUD - DAO 객체에 위임하여 요청받은 자료의 상세정보를 반환합니다.
     */
     @Override
     public Manager detail(Object parameter) throws DataAccessException {
      return managerDao.detail(parameter);
     }

    } // class end



    CRUD 에 관한 메소드가 선언되어 있고, 특이점으로 파일 상단에 @Service 라고 어노테이션이 선언되어 있다. @Service 어노테이션 덕분에 빈 스캐닝을 통해 자동적으로 빈이 컨테이너에 올라갈 수 있다.

    마지막으로 프로젝트명-servlet.xml 파일을 살펴보자.

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- 이 파일은 XML Schema 기반으로 Spring 설정을 구성합니다. -->

    <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-2.5.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

      <!-- @RequestMapping 을 사용하기 위한 빈 설정 2개 추가한다. -->
      <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
      <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

      <!-- 빈 스캐닝 부분을 추가한다. -->
      <context:annotation-config />
      <context:component-scan base-package="com.company.project.package" />

      <!-- 뷰 리졸버를 선언합니다. -->
      <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
       p:prefix="/WEB-INF/jsp" p:suffix=".jsp">
      </bean>

      <!-- 에러 뷰 리졸버를 선언합니다. -->
      <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
       <property name="exceptionMappings">
        <props>
         <prop key="java.lang.Exception">errorPage</prop>
         <prop key="org.springframework.dao.DataAccessException">errorPage</prop>
        </props>
       </property>
      </bean>

      <!-- 업로드 리졸버를 선언합니다. -->
      <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
       <property name="maxUploadSize" value="100000000" />
       <property name="defaultEncoding" value="utf-8" />
       <property name="uploadTempDir" ref="uploadDirResource" />
      </bean>

      <!-- 업로드 임시 공간을 선언합니다. -->
      <bean id="uploadDirResource" class="org.springframework.core.io.FileSystemResource">
       <constructor-arg>
        <value>C:/eclipse_spring/workspace/TestProject/WebContent/respository_temp/</value>
       </constructor-arg>
      </bean>

      <!-- 이 곳에 하위로 사용자와 관리자용 Spring 설정 파일 설정 부분 import 태그를 이용해 입력합니다. -->
      <!-- 예시 -->
      <!-- <import resource="resources/operation/sample-manager-servlet.xml" /> -->
      <!-- <import resource="resources/operation/sample-user-servlet.xml" /> -->

    </beans>



    @RequestMapping 을 사용하기 위해서 추가된 2개의 태그가 있다는 것만 기억하면 될 것 같다.
    그렇다면, 실제로 Controller에서 @RequestMapping이 사용되는 걸 보자.

    package com.company.project.package.controller;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.servlet.ModelAndView;


    /**
     * @author Hyoseok Kim (
    toriworks@gmail.com)
     *
     */

    @Controller
    public class IndexController {

     @RequestMapping("/index.html")
     public ModelAndView viewDefaultIndex() {

      ModelAndView mav = new ModelAndView();
      mav.setViewName("index");

      return mav;
     }
    }



    @RequestMapping 어노테이션의 사용법에 대해서는 차후 좋은 블로그를 찾아서 소개를 하던, 아니면 다뤄보도록 하겠다.


    이제 결론을 내보자!!!

    작은 귀찮음에서 시작한 거였지만, 그럭저럭 설정에 애 먹고 있는 사람들에게 요긴했으면 한다. 혹 SpringFramework 3.0에는 이런 기능도 함께 있으면 싶기도 하다. 부족하나마, 잘 따라해 주기만 한다면, 데이터베이스 엔티티가 많으면 많은 수록 프로젝트의 기간은 더 많이 단축할 수 있으리라 생각된다. 위의 예에서는 단순히 2개의 엔티티만을 대상으로 했지만, 그 수가 많으면 많을 수록 빛을 발하리라 기대해 본다.


    출처 : http://toriworks.tistory.com/106

    Posted by 1010
    반응형
    Spring 프레임워크 참고문서
    Spring 프레임워크 강의
    Spring 프레임워크 기반의 테스트
    Spring 프레임워크 Batch
    SpringOne2008America
    Spring 프레임워크 워크북 부연설명
    • 4장-예제 4-12 applicationContext.xml 부연설명 : 4장 Spring JDBC p.244에 있는 (예제 4-12)applicationContext.xml에 대한 부연설명
    • Spring MVC를 활용한 정적인 페이지 개발 : 6장의 Spring MVC를 단계적으로 추가적인 설명과 처음 Spring MVC를 이용하여 구현하고자 하는 개발자에게 도움이 될 만한 내용이 추가된다. 또한 책에서는 메인페이지를 처음으로 구현하고 있는데, 실제 예제소스에서는 이미 완성된 소스를 보이고 있음으로 단계적으로 개발하기에 부족함이 있는 듯 하여 직접 하나씩 구현해 보면서 예제소스를 만들어 가는 것으로 한다.
    Spring 프레임워크 참고자료
    • Spring 프레임워크 참고 문서 : Spring 프레임워크를 공부하기 위한 참고문서를 정리해 놓은 페이지
    • unitils : Spring, Hiberate 프레임워크, Easymock 프레임워크를 Annotation 기반으로 테스트가 가능하도록 지원하는 프레임워크이다.
    Posted by 1010
    반응형
    Spring 프레임워크 참고문서
    Spring 프레임워크 강의
    Spring 프레임워크 기반의 테스트
    Spring 프레임워크 Batch
    Acegi Security 문서
    Spring 프레임워크 워크북
    Spring 프레임워크 워크북 관련 문서 및 자료들
    Spring 프레임워크 워크북 추가 문서

    Spring 프레임워크는 상당히 방대한 기능을 제공하고 있으며, 빠른 속도로 발전하고 있다. 따라서 한권의 책에서 Spring 프레임워크가 가지고 있는 모든 기능을 다루기는 힘든 것이 사실이다. 따라서 이 위키를 통하여 Spring 프레임워크 워크북에서 다루지 못한 내용들을 하나씩 채워나갈 생각이다.

    독자들 중 Spring 프레임워크의 기능 중 책에서 다루지 않고 있는 기능에 대하여 알고 싶은 부분이 있다면 게시판을 통하여 제안을 해주기 바란다. 시간이 허락하는 한도내에서 문서를 작성하고 정보를 제공할 예정이다.

    Spring 프레임워크 워크북 부연설명
    • 4장-예제 4-12 applicationContext.xml 부연설명 : 4장 Spring JDBC p.244에 있는 (예제 4-12)applicationContext.xml에 대한 부연설명
    • Spring MVC를 활용한 정적인 페이지 개발 : 6장의 Spring MVC를 단계적으로 추가적인 설명과 처음 Spring MVC를 이용하여 구현하고자 하는 개발자에게 도움이 될 만한 내용이 추가된다. 또한 책에서는 메인페이지를 처음으로 구현하고 있는데, 실제 예제소스에서는 이미 완성된 소스를 보이고 있음으로 단계적으로 개발하기에 부족함이 있는 듯 하여 직접 하나씩 구현해 보면서 예제소스를 만들어 가는 것으로 한다.
    Spring 프레임워크 참고자료
    • Spring 프레임워크 참고 문서 : Spring 프레임워크를 공부하기 위한 참고문서를 정리해 놓은 페이지
    • unitils : Spring, Hiberate 프레임워크, Easymock 프레임워크를 Annotation 기반으로 테스트가 가능하도록 지원하는 프레임워크이다.
    Posted by 1010
    반응형
    2006년 한빛 미디어 강의 (Spring Framework)
    2007년 두번째 한빛 미디어 강의 (Spring Framework)
    2007년 한빛 미디어 강의 문서 (Spring Framework)
    2008년 한빛 교육 센터 강의 자료 (Spring Framework)
    4장-예제 4-12 applicationContext.xml 부연설명 (Spring Framework)
    Acegi Security 분석 - Acl (Spring Framework)
    Acegi Security 분석 - Authentication 기본 (Spring Framework)
    Acegi Security 분석 - Authentication과 Authorization에 대한 기본 개념 (Spring Framework)
    Acegi Security 분석 - Authorization 기본 (Spring Framework)
    Acegi Security 분석 - Domain object instance Authorization (Spring Framework)
    Acegi Security 분석 - Servlet Filter 설정 및 기본 메커니즘 (Spring Framework)
    Acegi Security 분석 - Web URL Authorization (Spring Framework)
    N사 신입 사원 교육을 위한 Spring 프레임워크 강의 (Spring Framework)
    ReloadableResourceBundleMessageSource 클래스를 이용하여 MessageSource 자동 로딩하기 (Spring Framework)
    Spring MVC에 HandlerInterceptor 사용하기 (Spring Framework)
    Spring 프레임워크 기반하에서 컴포넌트 관리 전략 (Spring Framework)
    Spring 프레임워크 빈 설정 파일에 대한 기본적인 설명 (Spring Framework)
    Spring 프레임워크 샘플 애플리케이션 (Spring Framework)
    Spring 프레임워크 워크북 2장 1강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 2장 2강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 2장 3강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 2장 4강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 2장 5강 - Spring IoC (Spring Framework)
    Spring 프레임워크 워크북 3장 1강 - Spring AOP (Spring Framework)
    Spring 프레임워크 워크북 3장 2강 - Spring AOP (Spring Framework)
    Spring 프레임워크 워크북 3장 3강 - Spring AOP (Spring Framework)
    Spring 프레임워크 워크북 3장 4강 - Spring AOP (Spring Framework)
    Spring 프레임워크 워크북 6장 1강 - Spring MVC (Spring Framework)
    Spring 프레임워크 워크북 6장 2강 - Spring MVC (Spring Framework)
    Spring 프레임워크 워크북 원고 준비문서 (Spring Framework)
    Spring 프레임워크 워크북을 위한 로드맵 (Spring Framework)
    Spring 프레임워크 워크북을 위한 참고문서 (Spring Framework)
    Spring 프레임워크 참고 문서 (Spring Framework)
    Unitils를 이용한 단위 테스트 (Spring Framework)
    샘플 예제를 위한 환경 세팅시 참고사항 (Spring Framework)
    자바지기 Spring 프레임워크 강의 (Spring Framework)
    Posted by 1010
    반응형
    출처 : http://www.jakartaproject.com/

    Spring 설치- 서론 spring 설치

    서론...

    작년에 한 것도 없고 요즘 재미도 없고 해서

    올해는 spring을 알아보려 합니다.

    아는 것도 없는데 저의 취미생활로 하니까 돌은 던지기 말아주시면 감사합니다. ^^*

    얼마 전에 회사에서 Spring in action 이라는 책을 신청해서 나와서

    책보면서 작성해 보려 합니다.


    spring 설치


    전 myeclipse를 사용합니다.

     Visit http://www.myeclipseide.com/

    여기서 받으시면 됩니다.

    이 툴을 사용하는 이유는 플러그가 자동으로 설치되고

    spring 개발을 쉽게 도와주기 때문입니다.


     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    그림에서 Add Spring Capabilities… 클릭하시면

    (주의 : 반드시 프로젝트를 하나 만들고 해야 합니다.)






































    그림이 나타나고 finish 하죠


    그런 다음 http://www.springframework.org 에서 spring 최신 버젼을 받습니다.

    책에서는 1.2.7 기준이라고 하는데  홈페이지에서는 벌써

    2.0.2 이네요 걍~~~ 2.0.2로 하려 합니다.

    다운을 하려고 보면 파일이 두 입니다.














    파일은 큰것으로 보아 서드파티 라이브러리가 포함된것이고 아래는 코아만 들어 있다고 하네요

    윗것으로 다운 받습니다

    물론 myeclipse에서 1.2 버전 지원하는데 2.0 다운 받아 수동으로 라이브러리 추가하려고 합니다.

    근데 아마도 책은 1.2 버전 기준이기때문에 나중 2.0 사용하지 않을까 하네요 ^^*



    Spring 라이브러리


    Jar 파일

    용도

    의존대상

    Spring-core.jar

    스프링 핵심 컨테이너와 유틸리티

    커먼스 로깅, 선택사항:Log4J

    Spring-aop.jar

    스프링 aop 프레임워크 메타데이터 지원

    Spring-core.jar, AOP 연맹

    선택사항:cglib, 커먼스 어트리뷰츠

    Spring-context.jar

    애플리케이션 컨텍스트, 유효성 검증 프레임워크, 템를릿 지원

    (벨로시티, 프리마커), 리모팅(jax-rpc, hessian, burlap), EJB 지원,

    스케쥴링

    Spring-core.jar

    선택사항:벨로시티,프리마커,javamail,

    Ejb,jax-rpc,hessian, burlap,쿼츠

    Spring-dao.jar

    Jdbc dao 지원 , 트랜잭션 기반구조

    Spring-core.jar

    선택사항:spring-aop.jar, jta

    Spring-orm.jar

    하이버네이트, jdo, 아이바티스를 포함한 orm 프레임워크 지원

    Spring-core.jar

    선택사항:하이버네이트,jdo,아이바티스

    Spring-web.jar

    애플리케이션 컨텍스트 유틸리티, 멀티파트 파일 업로드지원

    Spring-cotext.jar, 서블릿

    선택사항:커먼스 파일업로드,cos

    Spring-webmvc.jar

    스프링 mvc 프레이워크

    Spring-web.jar

    선택사항:jsp,jstl,타일즈,itext, poi

    Spring.jar

    다른 jar 파일들을 포함한 스프링 프레임워크 전체

    위의 모든 사항들을 포함


    사실 나도 모르는 용어들이 많다 사용해본 것도 개정도 있다.

    앞으로 알아가면 재미(?) 같다. ㅎㅎㅎㅎㅎ


    ------------------------------------------------------------------------------------------------------------------------


    Spring 로드존슨이 만든 오픈소스 프레임 워크이며, 그의 책인 Expert one-on-one : j2ee design and development 처음 소개 되었다 한다. 어라... 이책 봤는데..왜 몰랐지…

    책에서 스프링의 원래 이름은 interface21 었다.


    스프링에 대한 감을 잡기 위한 설명은…


    1.경량

    크기와 부하의 측면에서 경량이고 1MB 크기의 jar파일로 배포된다. 그리고 스프링은 침입적이지 않다고 한다. 무슨말인지.. 스프링을 도입한 애플리케이션의 객체가 보통의 경우 스프링의 특정 클래스에 대한 의존성을 갖지 않는다는 의미라고 한다. 그냥 ejb 비해 의존성이 없다는 얘기로 이해하고 넘어가야 겠다


    2.제어역행

    제어역행(IoC, Inversion of Control)이라는 기술을 통해 애플리케이션의 느슨한 결합을 도모한다.

    말은 기본개념은 객체를 생성하거나 찾는 대신, 구현되는 방법을 기술하는 것이다. 컴포넌트와 서비스들을 코드에 직접 연결하지는 않지만, 설정 파일에서 어떤 컴포넌트가 어떤 서비스를 요구하는지를 기술한다. 컨테이너(이 경우, Spring 프레임웍, IOC 컨테이너)는 이 모든 것을 연결한다.


    3.관점지향

    관점지향 프로그래밍(AOP, Aspect-Oriented Programming) 위한 풍부한 지원을 한다. 여기서 관점지향 프로그래밍이란 비즈니스 로직을 프로그램밍하게만 한다는 것이다. 트랜잭션과 시스템 감시같은 것은  관련 모듈을 이용하면 된다.

    http://aopalliance.soureforge.net 참고하면 된다.


    4.컨네이너

    어플리케이션 객체의 생명주기와 설정을 포함하고 관리한다는 점에서 스프링은 일종의 컨테이너이고, 빈을 생성, 빈의 연관 설정등 있다고 한다.


    5.프레임워크

    스프링에서는 파일내에 선언적으로 구성하여 애플리케이션 객체를 생성하며 어플리케이션 로직 개발은 개발자에게 맡기고 이외는 기능은 모듈로서 제공한다.


























    Spring 프레임웍을 구성하는 각 모듈(또는 컴포넌트)은 독립적이거나, 다른 모듈들과 함께 구현된다. 각 컴포넌트의 기능은 다음과 같다.

    • 코어 컨테이너(core container): Spring 프레임웍의 핵심 기능을 제공한다. 코어 컨테이너의 주요 컴포넌트는 BeanFactory(Factory 패턴의 구현)이다. BeanFactoryInversion of Control (IOC) 패턴을 사용하여 애플리케이션의 설정 및 의존성 스팩을 실제 애플리케이션 코드에서 분리시킨다.
    • Spring 컨텍스트(Spring context): Spring 프레임웍에 컨텍스트 정보를 제공하는 설정 파일이다. Spring 컨텍스트에는 JNDI, EJB, 국제화, 밸리데이션, 스케줄링 같은 엔터프라이즈 서비스들이 포함된다.
    • Spring AOP 모듈(Spring AOP): 설정 관리 기능을 통해 aspect 지향 프로그래밍 기능을 Spring 프레임웍과 직접 통합시킨다. 따라서 Spring 프레임웍에서 관리되는 모든 객체에서 AOP가 가능하다. Spring AOP 모듈은 Spring 기반 애플리케이션에서 객체에 트랜잭션 관리 서비스를 제공한다. Spring AOP에서는 EJB 컴포넌트에 의존하지 않고도 선언적 트랜잭션 관리를 애플리케이션과 결합할 수 있다.
    • Spring DAO: Spring JDBC DAO 추상 레이어는 다른 데이터베이스 벤더들의 예외 핸들링과 오류 메시지를 관리하는 중요한 예외 계층을 제공한다. 이 예외 계층은 오류 핸들링을 간소화하고, 예외 코드의 양도 줄여준다. Spring DAO의 JDBC 예외는 일반 DAO 예외 계층에 순응한다.
    • Spring ORM: 프레임웍은 여러 ORM 프레임웍에 플러그인 되어, Object Relational 툴 (JDO, Hibernate, iBatis SQL Map)을 제공한다. 이 모든 것은 Spring의 일반 트랜잭션과 DAO 예외 계층에 순응한다.
    • Spring Web module: 웹 컨텍스트 모듈은 애플리케이션 컨텍스트 모듈의 상단에 구현되어, 웹 기반 애플리케이션에 컨텍스트를 제공한다. Spring 프레임웍은 Jakarta Struts와의 통합을 지원한다. 웹 모듈은 다중 요청을 핸들링하고, 요청 매개변수를 도메인 객체로 바인딩하는 작업을 수월하게 한다.
    • Spring MVC framework: MVC 프레임웍은 완전한 기능을 갖춘 MVC 구현이다. MVC 프레임웍은 전략 인터페이스를 통해 설정할 수 있으며, JSP, Velocity, Tiles, iText, POI 같은 다양한 뷰 기술을 허용한다.

    Pasted from <http://www-128.ibm.com/developerworks/kr/library/wa-spring1/>



    ------------------------------------------------------------------------------------------------------------------------


    먼저 Hello World 라는 예제를 한번 해본다.



    GreetingService.java
    인터페이스

     : 여기에서 인터페이스로 만들어서 구현하는 이유는 특별히 없다.


























    GreeringServiceImpl.java

    : 인터페이스를 구현한 인사말을 출력하는 클래스이다.

    여기서 주목할것은 setGreeting() 있다는 것이다. 소스를 보게되면 setGreeting() 누가 호출했는지 자세히 알아보는것이다 이것이 spring 힘이 아닐까 한다.


















    applicationContext.xml

    : 파일은 spring 컨테이너에게 클래스에 대한 정보와 설정방법을 알려준다.

    <bean> </beam> 내용은  아래의 코드와 같다.


    GreetingServiceImpl greetingService = new GreetingServiceImpl();

    greetingService.setGreeting("Buenos Dias!");





     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    HelloApp.java 메인 클래스

    : BeanFactory 클래스가 스프링 컨테이너이다.












    그림은 출력물이다.


    아주 간단한 예제 프로그램이다. 생각은 spring 힘은 ejb에서 lookup으로 찾아서 실행하는 것이 아니라

    Xml 이용하여 빈들을 실행하고 제어 , 컨트롤을 하는 것이다. 이것이 역행제어를 말하는 같다.

    Ejb보다는 쉽게(?) 있고 Ejb에서 트랜잭션을 보장 해주는 것을 spring에서도 해주고 비즈니스 로직만 프로그래밍하면 이외 기능은 기타의 모듈로서 해결할 있을 거라 생각한다.


    그럼 앞으로 알아야 것은 빈들을 어떻게 컨트롤하는가 spring 다른 기능의 모듈과 어떻게 연결하는가만

    배우면 같다.



    ------------------------------------------------------------------------------------------------------------------------


    Spring EJB에서 구현되는 것을 좀더 쉽게 구현 있다.(spring 다른 것과 결합하여) 제어 역행이라는 개념을 이용하여 인터페이스를 통해 서로 협업하는 간단한 자바 객체들() 사용해 엔터프라이즈 애플리케이션을 개발할 있다. 이들 빈은 런타임에 spring 컨테이너에 의해 함께 묶인다.

    결국 spring 적은 비용으로 느슨하게 결합된 코드를 유지할 있게 해준다.


    spring 제어 역행의 상부에 AOP 제공한다. AOP 애플리케이션 전체에  걸쳐 흩어져 있는 코드를 한곳(aspect) 위치하도록 준다. 런타임에 빈들이 함께 묶이면 애스펙트들이 그에 엮임으로써, 빈은 새로운 작동을 하게 된다.


    엔터프라이즈 개발을 지원하다는 명목하에, spring 다양한 퍼시스턴스 기술과 통합할 있다. JDBC, 하이버네이트, JDO, 어떤 것을 사용하든, 스프링의 DAO 프레임워크는 퍼시스턴스 프레임워크에 있어서 에러 처리와 자원관리를 위한 일관된 모델을 제공함으로써 개발 편의성을 도모한다.


    퍼시스턴스 기술과의 동합을 보와하는것은 spring 트랜잭션 지원 기능이다. EJB 사용하지 않고도 AOP 통해 애플리케이션에 선언적으로 트랜잭션 지원을 추가할수 있다.또한 분산 트랜잭션을 위한 JTA 트랜잭션과의 통합을 포함하여 다양한 트랜잭션을 지원한다.


    Spring 여러지원 가능한 기능으로 메일,EJB, 서비스, JNDI 다양한 J2EE 서비스와의 통합기능도 제공한다. 제어역행을 통해 이들 서비스를 쉽게 설정하며, 애플리케이션 객체에 단순한 인터페이스를 제공한다.


    Spring 프리젠테이션 계층을 우해 다중 뷰기술을 지원한다. 벨로시티나 JSP 같은 프리젠테이션 기술뿐만 아니라 마이크로소프트 엑셀, PDF 파일을 생성할 있다. 프리젠테이션의 상부에는 내장된 MVC 프레임워크가 존재하고 스트럿츠나 웹워크(webwork) 같은 다른 웹프레임워크의 대안을 제시하며, spring 모든 서비스와 좀더 쉽게 통합 있다.


    생각을 덧붙이자면,

    Spring CBD 개발된 컴포넌트인 같다. spring 트랜잭션을 지원하기 위해 AOP 컴포넌트를 붙이고, 데이터 베이스와의 연결은 JDBC, 하이버네이트, JDO 등의 컴포넌트로 사용하면 되고, 프리젠테이션은 태피스트리나 벨로시티 컴포넌트를 이용하면 같다.

    , 내가 구현 가능한 기능이 있으면 가져다가 spring 붙이고 사용하면 가능하다는 뜻이다.

    근데 그러기 위해서는 많은 컴포넌트를 알아야 하고 spring 붙이기 위해서는 붙이는 방법 등을 학습해야 한다. 알고 나면 쉬운 것이지만 처음부터 알기란 매우 힘들 같다.

    만약 이것을 학습하고 있는데 다른 좋은 무엇가가 나타나면 어떻게 되는 것일까…..




    ------------------------------------------------------------------------------------------------------------------------


    Spring에서는 어떤 컴포넌트가 다른 컴포넌트와의 연관관계를 관리할 책임이 없다.

    어떤 컴포넌트에서 다른 컴포넌트를 호출하는 것을 말하는 같다.

    대신, 컨테이너에 의해 컴포넌트 간의 협업을 위한 참조가 주어진다.

    이렇듯  애플리케이션 컴포넌트 간의 연관관계를 만드는 행위를 묶기(wiring)라고 하며, 이번 장에서 다룰 주제이다.

    Spring 묶기는 단순히 객체간의 연관관계를 성립시키는 작업 이상의 것이다.

    또한 spring 사용해 빈의 특성을 설정하는 방법, 별도 파일로 배치(deployment) 설정을 외부화 하는 방법, 빈의 생명주기를 관리하는 방법 등을 알게 된다.

    비즈니스를 묶는 것과 관련된 내용이 spring 핵심이 아닐까 한다.


    Spring 프레임워크를 사용하기 위해 빈을 설정 때에는, 항상 spring 컨테이너에 어떤 지시사항을 기술해야 한다. 따라서 컨테이너를 알면, 빈이 어떻게 관리되는지를 이해하는데 도움이 되기때문에 컨테이너를 알아보자.

     컨테이너는 spring 프레임워크의 핵심이다. Spring 컨테이너는 제어역행(IOC) 사용해 애플리케이션을 구성하는 컴포넌트를 관리한다. 여기서 협력하는 컴포넌트간의 연관관계를 생성하는 것을 포함한다. 이는 객체는 좀더 명확하게 이행 있고, 재사용이 가능해지며, 단위 테스트가 쉬워진다고 한다.


    Spring 기본 컨테이너 2

       org.springframework.beans.factory.BeanFactory 인터네이스로 정의된 빈팩토리

       : 기본적인 의존성 주입 지원

      org.springframework.context.ApplicationContext 인터네이스로 정의되는 애플리케이션 컨텍스트

       : 프로퍼티 파일의 텍스트 메시지 해석, 애플리케이션 프레임워크 서비스 제공


    외에 다수 존재한다. 그리고 빈팩토리와 애플리케이션 컨텍스트를 모두 컨테이너 용어 한다.


    1. 팩토리

     - 빈을 생성하고 분배하는 책임을 지는 클래스이고 팩토리 디자인 패턴을 구현한 것이다.

        ( 팩토리 패턴은 객체를 생성하고 분배하는 패턴이고 자세한것은 스스로…^^)

     

       빈팩토리는 애플리케이션내의 많은 객체에 대해 알고 있기 때문에 객체들을 인스턴스화할때 협업하는 객체 간의 연관관계를 생성시키는 것이 가능하다.( 말은 객체들간의 관계를 정의하는 무엇가가 필요하는 말이다.)

      이렇게 하는 이유는 자신과 빈의 클라이언트로부터 설정이라는 작업이  없다. 그로 인하여 빈팩토리가 객체를 분배하는 시점에서는 객체들이 완전히 설정된 상태이며, 협업하는 객체들끼리 인식하고 있고 곧바로  사용할 있는 상태인 것이다.  빈팩토리는 커스텀 초기화 메소드와 소멸 메소드를 호출함으로써 빈의 생명주기에 개입할 있다.


    Spring 다양한 BeanFactory 구현 클래스 가장 유용한 것은 org.springframework.beans.factory.xml.XmlBeanFactory 로서 xml 파일에 기술되어 있는 정의를 바탕으로 빈을 로딩한다.


     BeanFactory factory = new XmlBeanFactory(new FileSystemResource("beans.xml"));


    코드는 팩토리에게 XML 파일로부터 빈에 대한 정의를 읽어오라고 알려준다. 그러나 팩토리가 빈을 인스턴스화하는 것은 아니다. 빈은 팩토리에 "늦게 로딩"(lazy loading)되는데, 말은 팩토리가 빈의 정의(빈과 특성에 대한 설명) 즉시 로딩하는 반면, 자체가 필요하기 되기 전까지는 인스턴스화하지 않는다는 의미다.

      팩토리로부터 빈을 언어오기 위해서는 아래와 같다.


      MyBean myBean = (MyBean) factory.getBean("myBean");


    getBean(); 호출되면, 팩토리는 의존성 주입을 이용해 빈을 인스턴스화하고 빈의 특성(빈의 set 메소드) 설정하기 시작한다. 이렇게 해서 스프링컨테이너 안에서의 빈의 일생이 시작된다.


     

    2. 애플리케이션 컨텍스트

     

     표면적으로는 ApplicationContext BeanFactory 상당히 비슷하다. 빈을 로딩하고 빈들을 묶어주며, 요청에 따라 빈을 분배한다. , ApplicationContext 인터페이스가 BeanFactory 인터페이스를 확장한 것이다. 또한 getBean() 메소드를 사용해 ApplicationContext 로부터 빈을 얻을 있다.

    차이점은 팩토리는 모든 빈을 늦게 로딩(lazy loading)하는데, 애플리케이션 컨텍스트는  컨텍스트를 시작시킬때 모든 싱클톤 빈을 미리 로딩(preloading)함으로써 빈이 필요할때 즉시 사용될수 있도록 보장해 준다. , 빈이 생성되기를 기다릴 필요가 없다는 것이다.

    하지만 모든 객체를 싱클톤으로 생성해 놓는다지만 객체가 많아지면 무거울꺼라는 생각이 든다. 해보지 않아서 추측임.

    (싱클톤은 디자인패턴의 일종이다 스스로…. ^^)

     

     ApplicationContext 다음과 같은 추가기능을 제공한다.

     -.국제화(I18N) 지원을 포함해 텍스트 메시지를 해석하는 수단 제공

     -.이미지 등과 같은 자원을 로딩하는 범용적인 방법 제공

     -.리스너로 등록되어있는 빈에 이벤트를 발생할 있음.


     BeanFactory 자원이 제약이 따르는 이동통신기기 같은 상황이 아니면 ApplicationContext 사용한다고 한다.

     

    ApplicationContext 다양한 구현 클래스 일반적인

     -.ClasspathXmlApplicationContext : 클래스 패스에 있는 XML 파일로부터 컨텍스트 정의를 로딩하며, 컨텍스트 정의를 클래스 패스에 있는 자원으로 취급한다.

     -.FileSystemXmlApplicationContext : 파일 시스템에 있는 XML 파일로부터 컨텍스트 정의를 로딩한다.

     -.XmlWebApplicationContext : 애플리케이션에 포함되어있는 XML 파일로부터 컨텍스트 정의를 로딩한다.


    ) ApplicationContext context = new FileSystemXmlApplicationContext("c:/foo.xml");

        : 특정한 위치에서 foo.xml 찾음.

         ApplicationContext context = new ClassPathXmlApplicationContext("c:/foo.xml");

        : 클래스 패스에 있는 foo.xml 찾음.




    ------------------------------------------------------------------------------------------------------------------------


      1. 빈의 일생

      전통적인 자바 애플리케이션에서의 빈의 생명주기는 new 키워드를 통해 인스턴스화 되거나 역직렬화(deserializatino) 되며, 사용할 있는 상태가 된다.




      여기서 잠깐

      직렬화는 객체의 현재 상태(attribute value)를 네트웍상의 다른곳(remote)또는 local의 file등에

      전달/보존하기 위해 byte stream으로 내보내는 작업(상태 copy)을 말합니다.

      예제)

      public class Person implements java.io.Serializable{

          

            public String name = "홍길동";

            public int    age  = "20";

            public static String address = "서울시";

            transient String post ="111";

      }


      위의 예제의 Person이라는 class의 인스턴스가 현재의 상태 를 네트웍으로 전송하기 위해 상태값을 직렬화 하면

      이 직렬화된 데이터는 네트웍으로 전송될것이고, 전달받는 상태를 복원하는 작업을 역직렬화
      (deserialized)라고

      합니다. 물론 전달받는 곳에는 Person이라는 class가 존재해야 합니다.

      그래서 역직렬화하는 과정에서 Person인스턴스가 생성되면서 전달받은 데이터로 상태를 복원합니다.


      RMI(Remote Method Invocation)기술을 사용할 경우 이 직렬화/역직렬화를 통해 remote간에 객체의

      상태를 전달받습니다.


      네트웍 전달뿐 아니라 파일에도 객체의 상태를 저장하기 위해 직렬화와 역직렬화를 사용합니다.

      직렬화는 ObjectOuputStream의 writeObject() 메소드를 사용하며, 역직렬화는 ObjectInputStream의

      readObject()를 사용합니다.


      그리고 직렬화 대상 class는 java.io.Serializable interface를 implements해야 합니다.

      Serializable interface내부는 비어져 있습니다.


      출처 : http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=1117764219&p=3&s=t



      Spring 빈의 생성방법을 커스터마이징 있는 기회를 제공하기 때문에 spring 컨테이너에서의 빈의 생명주기를 이해하는 것이 중요하다.


      다음 그림은 spring 애플리케이션 컨텍스트 안에서의 빈의 생명주기이다.




      빈의 생성


      a. 컨테이너는 빈의 정의(빈에 대한 정보를 어딘가에 있겠죠) 알아낸 다음, 빈을 인스턴스화 한다.

      b. 의존성 주입을 이용해 빈의 정의에 지정되어 있는 모든 특성에 값을 채운다.

      c. 빈이 BeanNameAware 인터페이스를 구현한다면, 팩토리는 빈의 ID setBeanName() 전달하여 호출한다.

      d. 빈이 BeanFactoryAware 인터페이스를 구현한다면, 팩토리는 자신의 인스턴스를 setBeanFactory() 전달하여 호출한다.

      e. 빈이 ApplicationContextAware 인터페이스를 구현했다면 setApplicationContext() 메소드가 호출된다.

      f. 빈과 연관되어 있는 하나 이상의 BeanPostProcessor 존재한다면, 각각의 모든 postProcessBeforeInitialization() 메소드가 호출된다.

      g. 빈이 재정되어 있는 init-method 있으면 호출된다.

      h. 마지막으로 빈과 연관되어 있는 하나 이상의 BeanPostProcessor 존재하다면, 모든 postProcessAfterInitialization()메소드가 호출된다.
      i.
      빈은 애플리케이션에 의해 즉시 사용 있는 상태이며, 이상 필요하지 않을때까지 팩토리 안에 남아 있게 된다.


      빈의 소멸


      a. 빈이 DisposableBean 인터페이스를 구현하고 있다면, destroy() 메소드가 호출된다.

      b. 커스텀 destroy-method 지정되어 있다면 메소드가 호출된다.




    ------------------------------------------------------------------------------------------------------------------------


    Spring 컨테이너 안에서 빈들(컴포넌트)끼리 사용하거나 사용되어지는 것을 묶기라고 한다. 빈을 묶을 에는 어떤 빈들이 사용 지와 빈들을 묶기 위해서 어떻게 의존성 주입을 사용할지를 컨테이너에게 알려줘야 한다. 이때 컨테이너에게 알려 때는 xml 이용하여 알려준다.


    간단한 샘플을 보면서 진행해 보자

    여러분이 스프링 트레이닝 주식회사라는 기술교육단체와 계약된 상태라고 가정하자 스프링 트레이닝사는 수강생이 온라인으로 교과 과정을 등록할 있도록 하는 애플리케이션을 만들어 달라고 요청했다.

    먼저 애플리케이션의 서비스 계층부터 만들어 보자

    아래의 그림은 스프링 트레이닝 애플리케이션에서 서비스 계층을 구성하는 계층을 보여준다.

     


     

    서비스 계층에는 수강생 서비스(Student Service) 교과과정 서비스 (Course Service) 라는 개의 서비스 컴포넌트가 있다.

     -.수강생 서비스(Student Service) : 수강생과 관련된 모든 것을 처리

     -.교과과정 서비스 (Course Service) : 교과과정과 관련된 모든 기능을 처리


    지금은 어떻게 구현되어 있는지 코드를 구현 부분만 살펴보고 나중에

    완전한 소스를 알아보자 궁금하면 www.acornpub.co.kr 에서 소스를 다운 받아 확인해 보세요

     

     

    Student Service.java

     

    public interface StudentService {

      public Student getStudent(String login);

      public void registerStudent(Student student);

      public java.util.Set getCompletedCourses(Student student);

      public String getCurrentUserId();

    }




    CourseService.java

     

     

    public interface CourseService {

      public Course getCourse(int id);

      public Course getCourse(Integer id);

      public void saveCourse(Course course);

      public java.util.Set getAllCourses();

      public void enrollStudentInCourse(Course course, Student student) throws CourseException;

      public void sendCourseEnrollmentReport();

    }



    StudentServiceImpl


     

    public class StudentServiceImpl implements StudentService {

      Logger LOGGER = Logger.getLogger(StudentServiceImpl.class);

     

      private StudentDao studentDao;

     

      // 생성자에 의한 주입

      public StudentServiceImpl(StudentDao dao) {

        studentDao = dao;

      }

      // setter 의한 주입

      // 참고: 소스에는 없다. 다만, 원리를 설명하기 위함.

      public setStudentDao(StudentDao dao) {

        studentDao = dao;

      }



      public Student getStudent(String login) {

        return studentDao.findById(login);

      }


      public void registerStudent(Student student) {

        HashSet authorities = new HashSet();

        authorities.add(new Authority("ROLE_STUDENT"));

        authorities.add(new Authority("ROLE_USER"));

        student.setAuthorities(authorities);


        studentDao.create(student);

      }


      public java.util.Set getCompletedCourses(Student student) {

        return studentDao.getCompletedCourses(student);

      }

     

      /*

       * TODO - This could be used more generically for Instructors as well.

       * But since it's only used for Students right now and because it would

       * be silly to create a UserService for this simple method, we'll just

       * put it here for now.

       */

      public String getCurrentUserId() {

        return ((SecureContext)ContextHolder.getContext()).getAuthentication().getPrincipal().toString();

      }

    }



    StudentServiceImpl 학생을 찾고, 생성하고 수강하는 작업등을 StudentDao 위임하여 StudentDao 데이터베이스와 상호작용하여 처리한다. StudentDao 객체의 실제 구현내용은 나중에 알아보기로 한다.

    그리고 StudentDao 구현한 클래스는 StudentDaoImpl 클래스이다.

    StudentDaoImpl 위의 소스에서 것과 같이 생성자와 setStudentDao() 메소드를 이용하여 StudentDao 대한 참조를 얻을 있다.


    CourseServiceImpl.java

     

     

     

    public class CourseServiceImpl implements CourseService {

     

      private static Logger LOGGER = Logger.getLogger(CourseServiceImpl.class);

      // 생성자 주입을 통한 CourseDao 설정

      public CourseServiceImpl(CourseDao dao) {

        this.courseDao = dao;

      }



      public Course getCourse(int id) {

        return getCourse(new Integer(id));

      }

     

      public Course getCourse(Integer id) {

        return courseDao.findById(id);

      }


      public void saveCourse(Course course) {

        courseDao.save(course);

      }


      public void enrollStudentInCourse(Course course,

          Student student) throws CourseException {


        // TODO:    enforcePrerequisites(course, student);


        // TODO:    Check for schedule conflicts


        course.getStudents().add(student);

        courseDao.save(course);

      }

     

      public Set getAllCourses() {

        return courseDao.findAll();

      }


      public void sendCourseEnrollmentReport() {

        Set courseList = courseDao.findAll();


        SimpleMailMessage message =

            new SimpleMailMessage(this.mailMessage);


        StringBuffer messageText = new StringBuffer();

        messageText.append("Current enrollment data is as follows:\n\n");

       

        for(Iterator iter = courseList.iterator(); iter.hasNext(); ) {

          Course course = (Course) iter.next();

          messageText.append(course.getId() + "    ");

          messageText.append(course.getName() + "    ");

          int enrollment = courseDao.getEnrollment(course);

          messageText.append(enrollment);

        }


        message.setText(messageText.toString());


        try {

          mailSender.send(message);

        } catch (MailException e) {

          LOGGER.error(e.getMessage());

        }

      }

     

      private void enforcePrerequisites(Course course,

          Student student) throws CourseException {

       

        Set completed = studentService.getCompletedCourses(student);


        // Check for prerequesites

        Set prereqs = course.getPrerequisites();

        for(Iterator iter = prereqs.iterator(); iter.hasNext(); ) {

          if(!completed.contains(iter.next())) {

            throw new CourseException("Prerequisites are not met.");

          }

        }

       

        // Check for scheduling clash

        for(Iterator iter = completed.iterator(); iter.hasNext(); ) {

          Course completedCourse = (Course) iter.next();

         

        }

      }

     

      // COLLABORATORS

      private CourseDao courseDao;

      public void setCourseDao(CourseDao courseDao) {

        this.courseDao = courseDao;

      }

     

      private StudentService studentService;

      public void setStudentService(StudentService service) {

        this.studentService = service;

      }


      private MailSender mailSender;

      public void setMailSender(MailSender mailSender) {

        this.mailSender = mailSender;

      }

     

      private SimpleMailMessage mailMessage;

      public void setMailMessage(SimpleMailMessage mailMessage) {

        this.mailMessage = mailMessage;

      }

    }



    CourseServiceImpl 생성자를 통해 CourseDao 참조를 있다. enrollStudentInCourse() 메소드는 수강생을 교과과정에 추가하기 전에 enforcePrerequisites() 먼저 호출한다. 만약 수강생이 선수과목을 이수하지 않았다면 enforcePrerequisites() CourseException 던질 것이며, 이를 다시 enrollStudentInCourse() 던질 것이다.

    enforcePrerequisites() StudentService 참조를 사용해 수강생이 수료한 모든 과목을 얻어온다. 이는 선수과목이라는 비즈니스 요구사항을 충목시키기 위해 CourseDao 뿐만 아니라, CourseServiceImpl과도 참조한다는 의미다.

    CourseServiceImpl CourseDao 생성자를 통해 얻는 것과 달리, StudentService 참조는 setStudentService() 메소드를 통해 얻는다. 이런 이유는 courseDao 이용하여 과목을 찾는데 사용되기 되고,  속성 값을 설정하지 않으면 인스턴스를 생성할수 없기때문에  private CourseDao courseDao 속성을 설정했다.

    다음은 xml 빈을 묶는 것에 대해 알아 보자


    Posted by 1010
    반응형
     
    사용자 삽입 이미지
    Spring Framework 개요

    작성자 : 김문규
    최초 작성일 : 2008. 6.25


    1. 한마디로
    엔터프라이즈 급의 웹 애플리케이션 개발을 위한 여러 기능(라이브러리)을 제공하는 프레임워크입니다.


    2. 특징, 장점
     - Lightweight Container
     - Dependency Injection Pattern 지원
     - AOP(Aspect Oriented Programming) 지원
     - POJO(Plain Old Java Object) 지원
     - 일관된 Transaction 처리 방법 제공
     - 다양한 Persistance 관련 API 제공 (JDBC, iBATIS, Hibernate, JPA, JDO...)
     - Restlet과 연동 가능 (요고는 내 입장에서 특징임)


    3. 관련 Web Site
    http://springframework.org/


    4. 맺음말
    Spring 프레임워크는 최근 가장 많이 사용되고 있는 웹 프레임워크 입니다. 제공하는 기능이 방대하면서도 그 사용은 용이하기 때문입니다. Spring이 제공하는 기능 중에 가장 강력한 기능은 물론 IoC(DI)와 AOP 입니다. 이는 이어지는 포스트에서 아주 자세하게 다루어질 예정입니다.

    그리고 최근 주목받고 있는 REST 아키텍쳐의 자바 구현체인 Restlet과 같이 사용할 수 있기도 합니다. Restlet 만으로는 모든 웹서비스를 구현하기에는 불편한 것이 사실입니다. 하지만 다행이도 Spring과 결합 모델로 구현이 가능하다고 합니다. (이 부분은 관련 포스트에서 다룰 수 있을지 잘 모르겠습니다.)

    프레임워크라는 것은 일종의 트렌드입니다. 최근에 가장 인기 있는 프레임워크에 대해서 공부해 두는 것 쯤은 개발자에게 도움이 되지 않을 까 생각됩니다. 이후의 포스트들이 지적 호기심 충족에 도움이 되길 바랍니다.

    그럼 이만! 쓩!

    -----------------------------------------------------------------------------------------------------


    Inversion of Control in Spring Framework

    작성자 : 김문규 (MyMK)
    최초 작성일 : 2008. 7.10

    IoC는 Spring Framework의 장점을 꼽으라면 가장 먼저 언급되는 개념입니다. 한국어로 변역하면 '제어의 역행'! 한국어가 더 어려워 보입니다. 비슷한 말로 Dependency Injection(DI)라는 말고 있습니다. 한국말로는 의존성 삽입! 아하~ 조금 이해가 되시나요?

    간단하게 이해하기 쉽게 같이 알아보지요.

    1. 개념
     객체 간의 의존관계를 객체 내부에 선언 또는 정의하지 않고, 외부의 조립기를 이용하여 의존 관계를 설명한다는 것

    2. 예제
    객체간의 연관성을 선언하는 3가지 방법을 보고, 문제점이 어떻게 IoC(DI)를 이용해서 해결되는 지 알아보지요.

    1) 직접 선언하는 방법

    public class WriteArticleServiceImpl {
        private ArticleDao articleDao = new MysqlArticleDao();
        ...
    }

     - 쉽습니다.
     - 하지만, 이를 테스트하기 위해서는 MySqlArticleDao가 정상적으로 동작해야 합니다. 어렵습니다.
     - 또한, OracleArticleDao를 사용하기로 바뀌었다면 코드를 고쳐야 하지요~ 물론 컴파일도 다시요. 귀찮습니다.

    2) Factory 패턴, JNDI를 이용하는 방법

    public class WriteArticleServiceImpl {
        private ArticleDao articleDao = ArticleDaoFactory.create();
        ...
    }

     - 조금 나아졌나요? 최소한 Oracle로 바뀌어도 코드 수정은 안해도 되겠네요~ ^^
     - 근데 테스트 측면에서는 전혀! 나아진게 없어 보입니다. 올바르게 동작하는 Factory와 JNDI에 등록된 객체가 필요합니다.

    3) 외부 조립자를 이용하는 방법

    public class WriteArticleServiceImpl {
        private ArticleDao articleDao;
        public WriteArticleServiceImpl(ArticleDao articleDao) {
            this.articleDao = articleDao;
        }
        ...
    }

    외부 설정 파일 (applicationContext.xml)
    <bean name="writeArticleService" class="com.sec.service.WriteArticleServiceImpl">
        <constructor-arg><ref-bean="articleDao" /></constructor-arg>
    </bean>

     - 외부 설정(applicationContext.xml)에서 객체간의 의존성을 설명하고 있다는 감이 오시지요? 바로 이겁니다. 외부에서 객체 의존성을 정의하고 있는 것이지요. 책에서는 조립한다고 설명하더군요. (Nice!)
     - 여기서는 생성자를 이용한 방법을 사용하지만 setter를 이용하는 방법도 있습니다. 요건 나중에 차차..
     - 이제 위에서 말한 2가지 문제점이 다 해결되어 보이지요? 아하~ 굳입니다. ^^

    3. 참조
    1) 웹 개발자를 위한 스프링 2.5 프로그래밍
    2)
    http://martinfowler.com/articles/injection.html
     - 다음은 IoC 설명의 정석으로 불려지는 Martin Fowler의 글입니다. 시간이 되시면 읽어보세요. 위의 설명이 좀 더 잘 이해되실 겁니다.


    -----------------------------------------------------------------------------------------------

    Aspect Oriented Programming (AOP) in Spring Framework

    작성자 : 김문규
    최초 작성일 : 2008. 7.10

    1. 정의
    AOP는 Spring Framework의 중요한 특징 중 하나입니다.
    AOP란, 기능을 핵심 비지니스 로직과과 공통 모듈로 구분하고, 핵심 로직에 영향을 미치지 않고 사이사이에 공통 모듈을 효과적으로 잘 끼워넣도록 하는 개발 방법입니다.
    공통 모듈은 보안 인증, 로깅 같은 요소들이 해당됩니다.

    예를 들어 다시 설명하면, 로그를 찍기위해 로그 출력 모듈을 만들어서 직접 코드 사이사이에 집어 넣을 수 있겠지요? 이런건 AOP 적이지 않은 개발입니다.
    반면에 로그 출력 모듈을 만든 후에 코드 밖에서 이 모듈을 비지니스 로직에 삽입하는 것이 바로 AOP 적인 개발입니다. 코드 밖에서 설정된다는 것이 핵심입니다.

    2. 예제
    1) AOP스럽지 못한 코드

    public class InventoryController implements Controller {
        protected final Log logger = LogFactory.getLog(getClass());
        private ProductManager productManager;
        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            String now = (new java.util.Date()).toString();
            logger.info("returning hello view with " + now);
            Map<String, Object> myModel = new HashMap<String, Object>();
            myModel.put("now", now);
            myModel.put("products", this.productManager.getProducts());
            return new ModelAndView("hello", "model", myModel);
        }
        public void setProductManager(ProductManager productManager) {
            this.productManager = productManager;
        }
    }

    로그를 찍고 싶은 지점에 로깅 코드를 직접 삽입하는 방법입니다. 물론 이렇게 하는 것이 효율적일 수도 있습니다. 하지만, 클래스 진입 시점마다 로그를 찍는 것과 같이 동일한 패턴이 있는 경우에는 rule을 정의하고 여기에 따라서 동일한 모듈이 호출된다고 하면 매우 직관적이고 간결하면서 유지 보수가 편하게 구현이 될것으로 생각됩니다. 이것을 지원하는 것이 바로  AOP이며 spring에서는 이를 지원하고 있습니다.

    2) Spring에서 AOP를 사용하는 방법
    Spring에서는 크게
     - Spring API를 이용하는 방법
     - XML schema을 이용하는 방법
     - Annotation 기능을 이용한 방법
    이 있습니다.
    여기서는 2번째 XML schema를 이용하는 방법의 예제를 소개합니다.
    전체 소스를 첨부합니다.


    loggingaspect.java

    package springapp.common;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.aspectj.lang.JoinPoint;
    public class LoggingAspect {
     protected final Log logger = LogFactory.getLog(getClass());
    // pointcut method 호출 전에 실행 시킬 로깅 함수
    public String beforeLogging(JoinPoint joinPoint) {
      String methodName = joinPoint.getSignature().getName();
      logger.info("calling: " + methodName);
      return methodName;
     }

    // pointcut method 결과 리턴 후에 실행 시킬 로깅 함수
     public void returningLogging(JoinPoint joinPoint, Object ret) {
      String methodName = joinPoint.getSignature().getName();
      logger.info("called successfully: " + methodName + " returns " + ret);
     }

    // pointcut method에서 예외 발생시에 실행 시킬 로깅 함수
     public void throwingLogging(JoinPoint joinPoint, Throwable ex) {
      String methodName = joinPoint.getSignature().getName();
      logger.info("exception occured: " + methodName + " throws "
         + ex.getClass().getName());
     }

    // pointcut method 종료 후에 실행 시킬 로깅 함수
     public void afterLogging(JoinPoint joinPoint) {
      String methodName = joinPoint.getSignature().getName();
      logger.info("finish call: " + methodName);
     }
    }


    applicationContext.xml

    .... (다른 설정 생략)

    <bean id="logging" class="springapp.common.LoggingAspect" />
     
    <aop:config>
      <aop:pointcut id="publicMethod" expression="execution(public * springapp.service..*.*(..))" />
      <aop:aspect id="loggingAspect" ref="logging">
          <aop:before pointcut-ref="publicMethod" method="beforeLogging" />
          <aop:after-returning pointcut-ref="publicMethod" method="returningLogging" returning="ret" />
          <aop:after-throwing pointcut-ref="publicMethod" method="throwingLogging" throwing="ex" />
          <aop:after pointcut-ref="publicMethod" method="afterLogging" />
      </aop:aspect>
    </aop:config>

    .... (다른 설정 생략)


    먼저 AOP 관련 용어를 설명하도록 하겠습니다.
     - advice : 언제 어떤 기능을 적용할 지에 대한 정의
     - joinpoint : 공통 기능 적용 가능 지점 (spring에서는 메서드 호출만이 가능합니다.)
     - pointcut : joinpoint 중에 실제로 적용할 지점
     - weaving : 핵심 로직에 공통 로직을 삽입하는
     - aspect : 여러 객체에 공통으로 적용되는 공통 관심 사항

    설정 정보는 아래의 구조로 생성합니다.
    <aop:config>
        <aop:aspect> : aspect를 설정
            <aop:before> : method 실행 전
            <aop:after-returning> : method 정상 실행 후
            <aop:after-throwing> : method 예외 발생 시
            <aop:after> : method 실행 후 (예외 발생 예부 상관 없음)
            <aop:around> : 모든 시점 적용 가능
        <aop:pointcut> : pointcut 설정

    pointcut 설정은 AspectJ의 표현식에 따릅니다.

    execution( 수식어패턴 리턴타입패턴 패키지패턴.메소드이름패턴(파라미터패턴) )


    이제 모든 설정은 끝났습니다. 그닥 힘들이지 않고 설정할 수 있습니다. 하지만 joinpoint에 해당되는 지점만 적용이 가능하기 때문에 사용해야 할 시점을 잘 선택해야 할 듯 합니다.

    3. 맺음말
    지금까지 필터같은 기술을 이용해서 이와 비슷한 기능을 구현 했었지요. 실제 대형 과제를 할 경우에는 더욱더 필요한 기능이 아닌가 싶습니다. 아무리 모듈화를 하고 그 모듈을 적용한다 하더라도 일일이 코드에 적용하는 것은 정말 귀찮은 일일뿐 아니라 오류의 소지도 많아지게 됩니다. 그렇기 때문에 AOP는 정말 유용한 기능이 아닐 수 없습니다. 개인적인 생각으로는 이거 하나만으로도 spring을 써야할 이유가 아닐까 합니다. 제가 드린 예제를 기반으로 꼭 여러분의 과제에 적용해 보시길 바랍니다. 감사합니다.

    4. 참조
    마지막으로 AOP를 이해하는 것에 많은 도움을 준 칼럼을 소개하고자 합니다. 한빛미디어에 김대곤님이 기고하신 글입니다.

    less (닫기)..

    AOP 개요

    저자:
    김대곤(private@roadtohome.com)

    본 기사는 Aspect Oriented Programming에 대해 간략한 소개글이다. 아직까지는 생소한 분야일 수 있겠지만, 점점 더 많이 듣게 되리라 생각된다. AOP를 설명하는데 있어서 자주 등장하는 네 개의 용어들(Aspect, Cross-cutting concern, Point-cut, Advice)를 설명함으로서 AOP가 왜 등장하게 되었으며, AOP가 제시하는 해결책에 대해 살펴볼 것이다. 먼저 "Aspect", "Oriented", "Programming"에서 생소한 단어는 단연 "Aspect"일 것이다. 야후 사전의 정의에 따르면, "Aspect"은 "사물의 면, 국면, 관점"으로 정의되어 있다. 소프트웨어 시스템은 여러가지 관점에서 바라볼 수 있다, 또는 여러 가지 단면을 가지고 있고 있다. 예를 들어, 자금 이체를 하는 프로그램을 작성한다고 생각해 보자. 출금계좌와 입금계좌, 그리고 이체금액을 입력받아 SQL 문장 또는 함수 한 번 돌리는 것으로 끝나는가? 절대 아니다. 먼저, 해킹을 방지하기 위해 사용자가 적절한 보안 프로그램을 설치했는지 점검하는 코드도 있어야 하고, 사용자가 인증되었는지 점검하는 코드도 써야 하고, 상대방 은행에서 적절하게 처리되었는지도 점점해야 하고, 혹시 사용자가 이체버튼을 두 번 누른 것은 아닌가 체크해야 하고, 시스템 로그도 남겨야 한다. 즉, 구현하려고 하는 기능 뿐 아니라 보안, 인증, 로그, 성능와 같은 다른 기능들도 녹아 있어야 한다. 어쩌면 이체를 위한 코드보다 잡다한 다른 측면의 문제들을 다루는 코드가 더 길어질 수 있다. 이런 코드들은 입금이나 출금 같은 다른 곳에서 들어가야 한다. 구현하려고 하는 비즈니스 기능들을 Primary(Core) Concern, 보안, 로그, 인증과 같이 시스템 전반적으로 산재된 기능들을 Cross-cutting concern이라고 부른다. AOP는 Cross-cutting concern를 어떻게 다룰 것인가에 대한 새로운 패러다임이라고 할 수 있다.

    AOP는 구조적 방법론에서 객체지향 방법론으로 전환처럼 시스템 개발에 관한 전체적인 변화는 아니다. Object-Oriented Programming이 Aspect-Oriented Programming으로 대체되는 일은 없을 것이다. AOP는 구조적 방법론에도 적용될 수 있고, 다른 방법론에도 다 적용될 수 있지만, 주로 객체지향방법론이 가지는 단점을 보완하는 것으로 묘사되고 있다. 그러면 객체지향 프로그래밍이 또는 다른 이전의 프로그래밍 기법들이 Cross-cutting Concern를 어떻게 다루는지 알아보자. 매우 간단하다. Primary Concern를 구현한 프로그램에 함께 포함시켰다. 그것이 단 한 줄의 메소드 호출이라 하더라도. 많은 프로그래머들은 거의 모든 프로그램에 산재된 로그하는 단 한 줄의 코드를 찾아서 바꾸어 본 경험이 있을 것이다. 또는 간단하게 생각하고 프로그램을 수정하려고 했는데, 도데체 어디를 수정해야 되는지 모르게 코드가 길고, 알 수 없는 코드들이 자리를 차지하고 있을 때의 난감함. Primary concern, Cross-cutting concern이 하나의 프로그램 안에 들어가게 되면, 프로그램을 이해하기가 힘들고, Cross-cutting concern 코드가 여기저기에 산재되어 수정하기 힘들게 된다. 당연히 생산성 떨어지고, 품질 떨어지고, 유지보수 비용 많이 들게 된다.

    그럼 AOP는 Cross-cutting concern를 어떻게 처리하는가? 이것도 매우 간단하다. 새로운 아이디어라고 할 수도 없다. Primary Concern 구현하는 코드 따로, Cross-cutting concern 구현하는 코드 따로 쓰고, 나중에 두 개 조합하게 완벽한 어플리케이션 만들겠다는 것이다. 기술 용어로 쓰면, Advice(Cross-cutting concern 구현한 코드)와 Primary concern 구현한 코드를 Point-cut 정보를 이용해서 Weaving(조합)하는 것이 AOP가 이 문제를 다루는 방법이다.

    사용자 삽입 이미지


    기술적 용어로서의 "Aspect"은 "Advice"와 "Point-cut"을 함께 지칭하는 단어이다. Point-cut은 어떤 Advice를 Code 어느 위치에 둘 것인가 하는 것이다. 예를 들면, 로그 기능을 구현한 Advice는 Code 속에 있는 모든 public 메소드가 수행되고 나면, 그 마지막에 실행되어라 라고 지정한 것이라 할 수 있다.

    이전까지의 객체지향 프로그래밍은 Cross-cutting concern을 정적으로 어플리케이션에 결합시킨 반면 AOP는 동적으로 Cross-cutting concern를 다룬다고 표현하기도 합니다. 용어에서도 알 수 있듯이 AOP는 소프트웨어 엔지니어링 원칙 중에 하나인 "Separation of concern"를 구현하려고 하고 있습니다. 이러한 문제들을 다루고 있는 분야 중에 하나는 디자인 패턴할 수 있고, 예를 들어, Visitor 패턴은 정적인 구조를 동적으로 바꾸려고 합니다. AOP가 현재까지 나온 방법들 중에서 Cross-cutting concern를 다루는 가장 좋은 방법인가 하는 질문엔 아직 답하긴 힘들 것 같습니다. 그럼에도 분명 언제가는 책상 위에 관련 서적 한 권 있어야 할 것 같은 분야가 될 것 같습니다.

    출처 : http://network.hanb.co.kr/view.php?bi_id=968


    --------------------------------------------------------------------------------------------------
    Developing a Spring Framework MVC application step-by-step

    작성자 : 김문규 (MyMK)
    최초 작성일 : 2008.07.22

    다음의 내용은 spring framework 공식 홈페이지(www.springframework.org)의 튜토리얼을 정리한 것입니다. 저와 같은 spring 초보자를 위해서 개인적인 방식으로 재구성하였습니다. 처음 spring을 접하는 분들이 학습에 참고 자료로 활용하시길 바랍니다.  

    코드 자체의 세세한 부분은 다루지 않을 것입니다. spring 프레임워크을 이용해서 기본적인 웹 서비스를 구현하는 방법을 보여주는 것에 주력할 것입니다. 따라서, 이 글에서는 설정 파일의 작성에 대해서 중점적으로 논의하겠습니다.

    (이 글은 기본적인 자바 웹 개발에 경험이 있는 분을 기준으로 작성합니다.)

    1. Source Structure


    사용자 삽입 이미지

    튜토리얼치고는 소스와 설정파일이 많은 편입니다. 이는 Spring 자체가 MVC를 지원하는 프레임워크이고 설정 파일도 논리적으로 분리하는 것을 권장하기 때문입니다. 튜토리얼에서도 이 개념은 유지하고 있습니다. 이는 차차 설명하기로 합니다.

    1) 소스 구조
     최상위 폴더는 아래와 같습니다.
    ┌ bin - 컴파일된 class 저장
    ├ db - DB와 관련된 스크립트
    ├ src - 말 그대로 소스들
    └ war - WAS에 deploy될 것들
    을 가리킵니다.

    각각의 폴더에 대해 조금 더 자세히 알아보도록 하겠습니다. 조금 생소한 부분만 설명합니다. 너무 쉬운 부분은 넘어가도록 하겠습니다.

    /src
    springapp은 최상위 package이고 여기에 domain, repository, service, web이라는 하위 package가 존재합니다. 아래의 기준으로 클래스 모듈을 위치 시킵니다.
    . domain - data 모델링 객체
    . repository - DAO 과 관련된 객체
    . service - 비지니스 로직
    . web - web 프리젠테이션 관련 객체

    □ /war
    . /WEB-INF/tld/spring-form.tld
    spring에서 사용할 수 있는 tag library의 정의입니다. tag library는 기존 jsp에서 사용하던 tag를 spring에서 재정의 한 것으로 좀 더 사용이 편합니다.
    . /WEB-INF/*.xml
    설정 파일들입니다. 실질적으로 spring을 사용함에 있어 가장 먼저 이해하여야 할 부분입니다. 뒤에서 자세히 설명하도록 하겠습니다.




















    2. Configuration 관련
    1) web.xml
      □ org.springframework.web.context.ContextLoaderListener
         . 계층별로 나눈 xml 설정파일을 web.xml에서 load되도록 등록할 때 사용.
         . 기본값으로 applicationContext.xml을 사용함
       예시)
       <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
       </listener>
       <context-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>
                  /WEB-INF/mars-ibatis.xml
                  /WEB-INF/mars-service.xml
             </param-value>
       </context-param>

      □ <taglib>
         . spring tag library의 위치 설정

    2) applicationContext.xml
      □ ContextLoaderListener(또는 ContextLoaderServlet)가 ApplicationContext를 만들 때 사용

      □ ApplicationContext란, 웹에 독립적인 애플리케이션의 모든 영역(dao, service, manager, 기타 등등) 에 대한 정의를 말합니다.

      □ connection pool에 대한 정의
         . org.apache.commons.dbcp.BasicDataSource
            : datasource를 정의, db driver, 보안 정보등을 알림
         . org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
            : 읽어들일 property 파일에 대한 정보를 알림

      □ transaction에 대한 지원을 위한 한 방법
      이 부분은 나를 비롯한 spring 초보자의 입장에서 아직 이해하기 어려운 부분이기 때문에 아래의 코드가 AOP 기법을 활용하여 transaction을 지원하고 있다는 것만 알아두기로 하자.

    transaction 지원 코드 보기


    3) springapp-servlet.xml
      □ DispatcherServlet이 WebApplicationContext를 만들 때 사용

      □ WebApplicationContext이란, 특정 웹(DispatcherServlet)에 종속되는 영역(Controller 및 각종 웹관련 설정 bean들)에 대한 정의를 말합니다. (ApplicationContext과의 차이를 확인하세요.)

      □ 주로 웹 페이지 리소스에 대한 핸들러 클래스 빈의 정의가 존재합니다.

      □ org.springframework.context.support.ResourceBundleMessageSource
         . message.properties 파일에 정의된 속성 값을 사용할 수 있도록 함
         . 국제화에 응용되면 아주 좋아 보입니다~

      □ org.springframework.web.servlet.view.InternalResourceViewResolver
         . prefix와 suffix 값의 정의에 따라 리소스 표현을 축약할 수 있도록 함.
         예시)
        <bean id="viewResolver"
         class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="viewClass"
              value="org.springframework.web.servlet.view.JstlView"></property>
            <property name="prefix" value="/WEB-INF/jsp/"></property>
            <property name="suffix" value=".jsp"></property>       
        </bean>
         라고 선언하면 /WEB-INF/jsp/testfile.jsp은 비지니스 로직 내부에서 testfile로 간단하게 표현 가능 함



    3. Inside Code
    spring web service는 아래의 흐름을 가집니다.
    ①(사용자) jsp 호출
    ②(웹서버) 설정 검색
    ③(웹서버) 요청 처리 클래스 호출
    ④(웹서버) 비지니스 로직 수행
    ⑤(웹서버) view로 결과 반환
    ⑥(사용자) jsp 화면


    1) Controller
      □ 사용자에게 jsp가 호출되면 xxxxx-servlet.xml에 정의된 클래스 빈을 호출합니다.

      □ 예제에는 DB에 있는 정보를 가져다가 저장하는 페이지와 원하는 값을 저장하는 페이지 두가지가 있습니다. hello.jsp가 전자에 해당하고 priceincrease.jsp는 후자에 해당합니다. 각 페이지를 확인하시고 각 페이지의 핸들러 클래스 빈을 springapp-servlet.xml에서 추적해 보시길 바랍니다.

      □ 일반적인 controller의 사용 예 : springapp.web.InventoryController.java
         . 일단 기본적인 Controller의 예시로 Controller interface를 구현합니다. 대부분의 경우에 사용하면 되고, request에 대한 처리를 위해 handleRequest()로 진입합니다.
         . 여기서 비지니스 로직을 처리한 후, ModelAndView 객체를 반환합니다. 이 때 넘기고자 하는 데이터를 파라미터에 담아서 보낼 수 있습니다.

        public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String now = (new java.util.Date()).toString();
            logger.info("returning hello view with " + now);
            Map<String, Object> myModel = new HashMap<String, Object>();
            myModel.put("now", now);
            myModel.put("products", this.productManager.getProducts());
            return new ModelAndView("hello", "model", myModel);
        }

      □ 폼을 처리하는 controller의 예 : springapp.web.PriceIncreaseFormController.java
         .  jsp가 폼을 다루고 있을 경우에는 SimpleFormController Class를 상속하여 확장합니다.
         . onSubmit()은 jsp에서 버튼이 눌려진 경우 콜백 지점을 가리키도 formBacjingObject는 폼에 기본값을 저장하고자 할 때 사용합니다.
         . formBackngObject는 domain에 존재하는 모델링 객체를 이용하고 있음에 주의합니다.

        public ModelAndView onSubmit(Object command)
                throws ServletException {
            int increase = ((PriceIncrease) command).getPercentage();
            logger.info("Increasing prices by " + increase + "%.");
            productManager.increasePrice(increase);
            logger.info("returning from PriceIncreaseForm view to " + getSuccessView());
            return new ModelAndView(new RedirectView(getSuccessView()));
        }
        protected Object formBackingObject(HttpServletRequest request) throws ServletException {
            PriceIncrease priceIncrease = new PriceIncrease();
            priceIncrease.setPercentage(20);
            return priceIncrease;
        }

    2) Model
      □ 비지니스 로직을 수행 중에 데이터를 읽고 쓰고 수정하고 삭제하기 위해 데이터 베이스에 대한 접근을 수행합니다. 해당 예제는 HSQLDB를 사용하고 있으며 이를 JDBC로 연결하고 있습니다.

      □ JDBC와 관련하여 몇개의 클래스를 추가 지원하고 있지만 미미합니다. 실제로는 iBatis, Hibernate를 지원하고 있다는 것이 더 큰 특징이라 하겠습니다.

      □ 간단한 예제를 확인하시길 바라며, 이 부분은 설명하지 않겠습니다.

    3) View
      □ 예제에서는 JSTL(Java Server page Standard Tag Library)이라는 것을 사용하고 있기에 약간의 공부가 필요합니다.
         . 예를 들면, <input type='text' /> 이렇게 사용하던 것을 <form:input path="percentage"/> 이런식으로 사용합니다.
         . iteration, print 같은 주요 제어문과 폼이 tag로 정리되어 있습니다.
         . 더 자세한 내용은 http://java.sun.com/products/jsp/jstl/ 참조 바랍니다.



    4. Ending
    이 문서의 목적은 spring을 자세하게 설명하는 것이 아니라, 튜토리얼데로 spring MVC를 간단하게 한번 만들어 보는 것에 그 의의를 두고 있습니다. 튜토리얼과의 차별화를 위해 Top-down 방식을 취했으며 전체의 구성을 설명하고자 했습니다.
    저도 약 2주전부터 틈틈히 공부하면서 알게된 것을 정리한 것이라 부족한 면이 많습니다. 시간이 나는데로 잘못된 부분은 수정하고 필요한 내용은 추가하겠습니다.
    긍적적이고 발전적인 커멘트를 기다립니다. 감사합니다.

    ----------------------------------------------------------------------------------------------------
    Persistance Support of Spring Framework

    작성자 : 김문규
    최초 작성일 : 2008. 8. 1

    Spring에서 DB에 연결하는 방법에 대해서 알아보려 합니다. 다음과 같이 세가지 방법이 있습니다.
     - JDBC를 이용하는 방법
     - JdbcDaoSupport 클래스를 이용해서 JDBC를 조금 편하게 템플릿화하는 방법
     - SqlMapClientDaoSupport 클래스를 이용해서 iBatis Framework를 연동하는 방법

    각 방법이 어떻게 다른지를 설명드리고 튜토리얼 수준의 예제를 보여드리겠습니다.

    1. JDBC
    일반적으로 JDBC를 이용할 경우 아래의 순서를 따라서 구현됩니다. 
     - DriverManager에 해당 DBMS Driver를 등록
     - 해당 Driver로 부터 Connection 객체 획득
     - Connection 객체로부터 Statement 객체 획득
     - Statement의 method를 이용하여 SQL실행
     - ResultSet 으로 받아서 처리(executeUpdate 의 경우엔 제외)
     - 객체 close() (ResultSet, Statement, Connection)

    여기서 실제로 개발자의 비지니스 로직이 들어가는 부분은 4번과 5번의 일부입니다. 나머지는 음... 기계적인 코드입니다. 실제 코드를 예로 들면 아래와 같습니다.

    Connection.java

    .........
    Connection con = null;
    Statement st = null;
    ResultSet rs = null;
     
    try {
        con=DriverManager("jdbc:mysql://localhost/dbname","root","1234");  // dbname에는 사용하는 database 이름, root 계정, 패스워드는 1234

        Statement st=con.createStatement();
        ResultSet rs=st1.executeQuery("select * form names");
     
        if( rs.next() ) {
            do {
                // result set을 잘 정리합니다. 물론 일일이
                ...
                // 비지니스 로직을 수행합니다.
                ...
            } while( rs.next() )
        }
    } catch (Exception ex) {
        // 적절한 예외 처리
    } finally {
        if( rs != null) rs.close();
        if( st != null ) st.close();
        if( con != null ) conn.close();
    }
    .........

    의외로 너무 많은 부분인 반복되는 느낌입니다. 이런 부분은 Spring을 사용하면 많이 줄어들게 됩니다. 다음절에서 확인해 보지요.

    2. DaoSupport in Spring Framework
    Spring에는 Template이라는 것을 이용해서 이 과정을 알아서 처리해 줍니다.
     - JdbcTemplate
     - NamedParameterJdbcTemplate
     - SimpleJdbcTemplate
     
    이를 편하게 사용하기 위해서 주로 아래의 Dao 클래스를 이용합니다.
     - JdbcDaoSupport
     - NamedParameterJdbcDaoSupport
     - SimpleJdbcDaoSupport

    여기서 가장 다양한 기능을 가진 SimpleJdbcDaoSupport 클래스의 사용법을 코드에서 확인해 보도록 하겠습니다. (전체 코드는 이전 4번 튜토리얼 분석 포스트에서 사용했던 예제를 보시면 됩니다.)

    applicationContext.xml

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
     
    <bean id="productDao" class="springapp.repository.JdbcProductDao">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    ProductDao.java

    public class JdbcProductDao extends SimpleJdbcDaoSupport implements ProductDao {
        public int selectCount() {
            return getSimpleJdbcTemplate().queryForInt(
                    "select count(*) from products");
        }
    }

    음..확실히 간편해 진것 같습니다.

    하지만, 쿼리문이 코드 내부에 하드코딩되어 있다는 점이 약간 아쉬운 부분입니다. 쿼리문 변경 시에 코드도 같이 변경되고 재컴파일도 되어야 합니다. (뭐 솔직히 컴파일 하면 되긴 해요... ^^) 그리고 따로 설명하지 않겠지만 ParameterizeRowMapper 인터페이스를 이용해서 domain 객체와 결과를 연결하는 방법이 있는데 이 역시 하드코딩 되기 때문에 유지 보수의 유연성이 떨어지는 면이 없지 않습니다.

    그래서, 쿼리 및 쿼리의 결과와 객체간의 연결을 외부에서 따로 관리하는 기능을 요구하게 되고 이런 기능을 가진 iBatis가 최근 많이 사용되고 있습니다. 다음 절에서 iBatis의 연동에 대해 알아보겠습니다.
     
    3. Spring Framework와 iBatis 연동
    1) iBatis
    국내 SI 현실에 가장 잘 어울린다고 평가받고 있는 persistance framework 입니다. 그 이유는 SQL 쿼리문을 그대로 사용하기 때문에 기존 JDBC 개발자들에게 거부감이 덜하고  경험적(?)인 시스템의 이해가 빠르기 때문일 겁니다. 여타의 ORM framwork는 배우기도 힘들고 또는 제 3자 입장에서 이해하기도 힘들어서 우리나라 SI 업계처럼 개발팀이 이합집산하는 문화에는 어울리지 않는다는 군요.

    각설하고 간단한 예를 보도록 하지요. iBatis에 대해서는 제가 따로 설명하지 않을 것입니다. 아래의 자료들을 보시면 간단하게 이해하실 수 있을 겁니다. 그리고, googling 하시면 JPetStore라는 예제가 있습니다. 이를 한번 실행시켜 보시는 것도 도움이 될 것입니다.

    다음은 iBatis 관련해서 많은 자료를 만드신 이동국님의 Tutorial 변역본입니다. 8장밖에 안되지만 iBatis의 동작에 대해서 이해하는데 많은 도움이 되실 것입니다.
    별첨으로 하나 더 첨부합니다. 이 파일은 sqlMap의 사용법에 대해 설명된 문서이고 역시 이동국님이 번역하신 자료입니다.

    자~! 이제 iBatis의 사용법에 대해서 간단히 감을 잡으셨나요?
    앞 절에서 언급한 것처럼 제 생각에는 2가지의 큰 장점을 가진다고 생각됩니다. (다른 장점이 있으시면 사알짝 좀 알려주세요~~)
     - SQL 쿼리문이 외부 설정 파일에서 관리된다.
     - domain 객체와 SQL result set의 연결이 외부 설정 파일에서 관리된다.

    그런데 JDBC와 마찬가지로 iBatis에도 단순하게 기계적으로 반복되는 코드가 있습니다. 바로 SqlMapClient를 선언하는 부분이지요! (아래 코드 참조)
    public MyAppSqlConfig {
        private static final SqlMapClient sqlMap;
        static {
            try {
                String resource = “com/ibatis/example/sqlMap-config.xml”;
                Reader reader = Resources.getResourceAsReader (resource);
                sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
            } catch (Exception e) {
                .......
        }
    }
     
    JDBC를 지원하기 위해 JdbcDaoSupport 클래스가 있지요? 그럼 SqlMapClient를 지원해 주는 것은? 네! SqlMapClientDaoSupport 클래스 입니다. 그럼 드디어 sqlMapClientDaoSupport 클래스에 대해 알아보겠습니다.

    2) sqlMapClientDaoSupport
    앞서 알아본 JdbcDaoSupport와 이름도 비슷하지만 사용법도 매우 유사합니다. 설정 파일과 관련된 자바 코드를 보시지요.
    applicationContext.xml - sqlMapConfig.xml - Product.xml 이 연쇄적인 관계를 가지고 있습니다. 여기서 applicationContext.xml은 spring 설정 파일이고 나머지는 iBatis 설정 파일입니다.
    비지니스 로직 수행 중 iBatisMessageDao의 함수들이 요청될 것이고 이떄 Product.xml에 있는 쿼리문들을 호출해서 수행하게 됩니다.
    주석문을 달고 색깔로 구분해 놓았습니다. 연관 관계에 유의하시면서 주의 깊게 확인하시길 바랍니다. (오랜만에 칼라풀한 포스트 입니다. ^^)

    applicationContext.xml

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
    </bean>

    // sqlMapClient를 선언해야 합니다.
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
         <property name="dataSource" ref="dataSource" />
         <property name="configLocation" value="WEB-INF/sqlmap/sqlMapConfig.xml" />
    </bean>

    // DAO 입니다.
    <bean id="productDao" class="springapp.repository.iBatisProductDao">
         <property name="sqlMapClient" ref="sqlMapClient" />
    </bean>

    <bean id="productManager" class="springapp.service.SimpleProductManager">
            <property name="productDao" ref="productDao" />
    </bean>

    sqlMapConfig.xml

    // classpath가 root 디렉토리 임에 유의 하셔야 합니다.
    <sqlMapConfig>
        <sqlMap resource="../sqlmap/Product.xml" />
    </sqlMapConfig>

    Product.xml

    <sqlMap namespace="ProductNamespace">
        // Product 도메인 객체 입니다. 여기저기서 사용되게 됩니다.
        <typeAlias alias="Product" type="springapp.domain.Product" />

        <!-- getProductList -->
        // getProductList와 관련된 결과 값의 형태를 정의합니다.
        <resultMap id="getProductResultMap" class="Product">
            <result property="id" column="id" />
            <result property="description" column="description" />
            <result property="price" column="price" />
        </resultMap>
     
        <select id="getProductList" resultMap="getProductResultMap" resultClass="Product">
            select id, description, price from products
        </select>

        <!-- saveProduct -->
        // saveProduct와 관련된 입력 값의 형태를 정의합니다.
        <parameterMap id="saveProductParamMap" class="Product">
            <parameter property="id"  />
            <parameter property="description"  />
            <parameter property="price"  />
        </parameterMap>
     
        // parameter를 지정하는 방식에 유의 하시길 바랍니다.
        <update id="saveProduct" parameterClass="Product" >
            update products set description = (#description#), price = (#price#) where id = (#id#)
        </update>
    </sqlMap>

    iBatisMessageDao.java
     
    // 코드 내부에는 전혀 쿼리 문이 없습니다!! 여기에 별 5개 꼭 확인하세요!
    public class iBatisProductDao extends SqlMapClientDaoSupport implements ProductDao {
        @SuppressWarnings("unchecked")
        public List<Product> getProductList() {
            List<Product> products = getSqlMapClientTemplate().queryForList("getProductList");
            return products;
        }

        public void saveProduct(Product prod) {
            getSqlMapClientTemplate().update("saveProduct", prod);
        }
    }

    2절에서 설명한 JdbcDaoSupport 클래스의 사용법과 비교했을 때 iBatis는 이를 한단계 더 추상화 되어 있음을 알 수 있습니다. 모든 쿼리문과 해당 쿼리 결과와 객체간의 관계를 XML 외부 설정으로 따로 관리하고 있다는 것이죠.
    그렇기 때문에, 앞에서 말씀드린데로 이 방법은 java 코드내에 하드코딩하는 것보다 개발의 유연성을 높이고 추후 관리시에 유리한 장점을 가집니다. (추상화란 처음에는 귀찮고 나중에는 편리한 것이지요!)

    다음은 이전 4번째 spring 프레임워크 포스트에서 사용했던 튜토리얼을 iBatis로 연동시킨 예제입니다. 위에 설명드린 내용이 어떻게 적용되어 있는지 한번 더 확인하실 수 있을 것으로 생각됩니다.


    4. 맺음말
    휴..정말 긴 튜토리얼 리뷰였습니다. 매일 생각없이 JDBC로 개발하던 저에게는 참으로 재미난 것이 아닐 수 없었습니다.
    물론 때에 따라서는 JDBC 자체가 빛나는 순간이 있겠지만, 대부분의 경우에는 iBatis가 우리를 편하게 해주지 않을까 생각됩니다. iBatis 관련 공부 중 이런 글이 있었습니다.
    "iBatis는 80%의 JDBC 기능을 20%의 자바 코드로 구현한 간단한 프레임워크이다."
    맞습니다. 이는 만병통치약이 아닙니다. 다만 대부분에 경우에 잘 먹히는 고마운 녀석인 것이지요. 뭐 종합감기약 정도 ^^;
    그렇기 때문에 여러분도 한번 직접 경험해 보시고 개인적인 판단을 세우시길 바랍니다. 여러분에게 맞지 않을 수 있으니까요. 이상입니다. 감사합니다.

    5. 참고자료
    1) 웹 개발자를 위한 스프링 2.5 프로그래밍 (가메출판사, 최범균 지음)





    출처 : http://www.iamcorean.net/
    Posted by 1010
    반응형
    사용자 삽입 이미지

    사용자 삽입 이미지

    Posted by 1010
    반응형
    1. 스프링 프레임워크 소개

      * 스프링이란?
        - 스프링(Spring)은 간단히 말하면 엔터프라이즈 어플리케이션에서 필요로 하는 기능을 제공
          하는 프레임워크이다. 스프링은 J2EE가 제공하는 다수의 기능을 지원하고 있기 때문에,
          J2EE를 대체하는 프레임워크로 자리 잡고 있다.

      * 스프링 프레임워크 특징
        - 스프링은 경량 컨테이너이다. 스프링은 자바 객체를 담고 있는 컨테이너이다. 스프링은 이들
          자바 객체의 생성, 소멸과 같은 라이프 사이클을 관리하며, 스프링으로부터 필요한 객체를
          가져와 사용 할 수 있다.
        - 스프링은 DI(Dependency Injection) 패턴을 지원한다. 스프링은 설정 파일을 통해서 객체 간의
          의존 관계를 설정할 수 있도록 하고 있다. 따라서 객체는 직접 의존하고 있는 객체를 생성 하거
          나 검색할 필요가 없다.
        - 스프링은 AOP(Aspect Oriented Programming)를 지원한다.스프링은 자체적으로 AOP를 지원
          하고 있기 때문에 트랜잭션이나 로깅, 보안과 같이 여러 모듈에서 공통으로 필요로 하지만 실제
          모듈의 핵심은 아닌 기능들을 분리해서 각 모듈에 적용할 수 있습니다.
        - 스프링은 POJO(Plain Old Java Object)를 지원한다. 스프링 컨테이너에 저장되는 자바 객체
          는 특정한 인터페이스를 구현하거나 클래스를 상속받지 않아도 된다. 따라서 기존에 작성한
          코드를 수정할 필요 없이 스프링에서 사용할 수 있다.
        - 트랙젝션 처리를 위한 일관된 방법을 제공한다. JDBC를 사용하든, JTA를 사용하든, 또는 컨테
          이너가 제공하는 트랙잭션을 사용하든, 설정 파일을 통해 트랜잭션 관련 정보를 입력 하기
          때문에 트랙잭션 구현에 상관없이 동일한 코드를 여러 환경에서 사용 할 수 있다.
        - 영속성과 관련된 다양한 API를 지원한다. 스프링은 JDBC를 비롯하여 iBATIS, Hibernate,
          JPA, JDO등 데이터베이스 처리와 관련하여 널리 사용되는 라이브러리와의 연동을 지원하고
          있다.
        - 다양한 API에 대한 연동을 지원한다. 스프링은 JMS, 메일, 스케쥴링 등 엔터프라이즈 어플리
          케이션을 개발하는데 필요한 다양한 API를 설정 파일을 통해서 손쉽게 사용할 수 있도록 하고
          있다.

      * IoC(Inversion of Control)란?
        - Spring 프레임워크가 가지는 가장 핵심적인 기능은 IoC(Inversion of Control)이다.자바가
          등장한 최초에는 객체 생성 및 의존관계에 대한 모든 제어권이 개발자에 있었다. 그러나,
          서블릿, EJB가 등장하면서 제어권이 서블릿과 EJB를 관리하는 서블릿컨테이너 및 EJB 컨테이
          너에게 넘어가게 되었다. Spring 프레임워크도 객체에 대한 생성 및 생명주기를 관리할 수
          있는 기능을 제공하고 있다. 이와 같은 이유때문에 Spring 프레임워크를 Spring  컨테이너,
          IoC컨테이너와 같은 용어로 부르기도 한다.
         : 물론 모든 객체에 대한 제어권을 컨테이너에게 넘겨버린 것은 아니다. 서블릿 컨테이너와 EJB
           컨테이너에서도 서블릿과 EJB에 대한 제어권만 컨테이너가  담당하고 나머지 객체에 대한 제
           어권은 개발자들이 직접 담당하고 있다. 이처럼 Spring 컨테이너 일부 POJO(Plain Old Java
           Object)에 대한 제어권을 가진다. Spring컨테이너에서 관리되는 POJO는 각 계층의 인터페이
           스를  담당하는 클래스들에 대한 제어권을 가지는 것이 대부분이다.

      * Dependency Injection
        - DI는 Spring 프레임워크에서 지원하는 IoC의 한 형태이다. DI를 간단히 말하면, 객체 사이의
          의존관계를 객체 자신이 아닌 외부의 조립기(assembler)가  수행한다는 개념이다.

          일반적인 웹어플리케이션의 경우 클라이언트의 요청을 받아주는 컨트롤러 객체, 비지니스
         로직을 수행하는 서비스 객체, 데이터에 접근을 수행하는 DAO객체 등으로 구성된다.
         만약, WriteArticleServiceImple클래스가 ArticleDao 인터페이스에  의존하고 있을 경우를 생각
         해보자

         의존관계를 형성하는 방법에는 아래와 같은 경우들이 있다.

         첫번째,  코드에 직접 의존 객체를 명시하는 경우.
       
         public class WriteArticleServiceImpl{

            private Article articleDao = new MysqlArticleDao();

         ....

         }

         이 경우 의존하는 클래스가 변경되는 경우 코드를 변경해야 하는 문제가 있다.

         두번째, Factory 패턴이나 JNDI등을 사용해서 의존 클래스를 검색하는 방법이 있다.

         public class WriteArticleServiceImpl{

            private ArticleDao articleDao = ArticleDaoFactory.create();

            ...

         }

         Factory나 JNDI를 사용하면 첫번째문제(즉,  의존  클래스가 변경되면 코드를 변경해야 하는
         문제)를 없앨 수는 있지만, WriteArticleServiceImpl클래스를  테스트하려면 올바르게 동작하는
         Factory또는 JNDI에 등록된 객체를 필요로 한다는 문제점이 있다.

         세번째, DI패턴이다. 이 방식에서는 의존관계에 있는 객체가 아닌 외부 조립기(assembler)가 
         각 객체 사이의 의존 관계를 설정해준다.
         WriteArticleServiceImpl클래스의  코드는 MysqlArticleDao 객체를 생성하거나 검색하기 위한
         코드가  포함되어  있지 않다. 대신 조립기의 역할을 하는 Assembler가 MysqlArticleDao
         객체를 생성한 뒤 WriteArticleServiceImpl객체에 전달해 주게  된다.
     
         DI패턴을 적용할 경우 WriteArticleServiceImpl클래스는 의존하는 객체를  전달받기  위한 설정
         메서드(setter method)나 생성자를  제공할 뿐 WriteArticleServiceImpl에서  직접 의존하는
         클래스를 찾지 않는다.

      * AOP(Aspect Oriented Programming)
        - 로깅, 트랙잭션처리, 보안과  같은 핵심 로직이  아닌 cross-cutting concern(공통관심사항)을
          객체 지향기법(상속이나 패턴등)을 사용해서 여러 모듈에 효과적으로 적용하는데 한계가 있었
          으며, 이런 한계를 극복하기 위해 AOP라는 기법이 소개되었다.

          공통관심사항(cross-cutting concern)을  별도의 모듈로 구현한 뒤, 각  기능을 필요로 하는 곳
          에서 사용하게 될 경우, 각 모듈과 공통 모듈 사이의 의존관계는 복잡한 의존 관계를 맺게 된다.

          AOP에서는  각클래스에서 공통 관심사항을 구현한 모듈에 대한  의존  관계를갖기보다는,
          Aspect를  이용하여 핵심 로직을 구현한 각 클래스에 공통 기능을 적용하게 된다.

          AOP에서는 핵심  로직을 구현한 클래스를 실행하기 전 후에 Aspect를 적용하고, 그 결과로
          핵심 로직을 수행하면 그에 앞서 공통 모듈을 실행하거나 또는 로직  수행 이후에 공통 모듈을 
          수행하는 방식으로 공통 모듈을 적용하게 된다.

          AOP에서 중요한 점은 Aspect가 핵심 로직 구현 클래스에 의존하지 않는다는 점이다. AOP에
          서는 설정 파일이나  설정클래스  등을 이용하여 Aspect를 여러 클래스에  적용할 수 있도록
          하고 있다.
    Posted by 1010
    반응형

    스프링 공부를  하는데.. 아무리 이론을 봐도 무슨 말인지 하나도 모르겠고,
    자바 처음 배우던 때를 떠올리며.. Helloworld를 찍어봐야겠다고 판단.
    네이버에서 찾아 낸 Spring으로 Helloworld찍기.. 시킨대로 하니 화면에 뜨네. 휴.
    자 그럼, 안 잊어 먹기 위해 나도 정리를 해놓자.

    내 컴퓨터 환경은...
    JDK1.5 + Tomcat 5.5 + SpringFramework 2.5.6 아 차,, +  Eclipse는 Europa.

    우선 스프링 프레임워크를 받으려면..
    http://www.springframework.org/download 로 가서
    spring-framework-2.5.6-with-dependencies  파일을 받는다.
    뭘 받을지 모르겠음 멜 남기시고..

    자 그럼 Eclipse 실행 시키고~ Dynamic Web Project를 만든다.
    프로젝트 이름은 SpringHelloWorld 라고 지정.
    Target Runtime은 Tomcat으로 하고 Finish.

    lib폴더에 lib파일들을  넣는다.(제일 중요!!)
    스프링 다운 받아서 압축 푼 폴더에 보면
    spring-framework-2.5.6\dist\spring.jar 파일
    : (스프링  프레임워크 lib파일)
    spring-framework-2.5.6\lib\jakarta-commons\commons-logging.jar 파일
    : (로그 사용을 위한 lib파일)
    spring-framework-2.5.6\dist\modules\spring-webmvc.jar
    : (컨트롤러 상속받기 위해서)

    그리고 태그라이브러리 사용을 위한 jar파일을 받는다.
    http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi
    압축풀어 보면 standard.jarjstl.jar파일이있다. 

    이 아이들까지전부 lib폴더에넣는다..

    그럼 web.xml파일부터 수정해보자.

     <display-name>SpringHelloWorld</display-name>
       <servlet>
         <servlet-name>springapp</servlet-name>
         <servlet-class>
          org.springframework.web.servlet.DispatcherServlet
         </servlet-class>
       </servlet>
     
      <servlet-mapping>
        <servlet-name>springapp</servlet-name>
        <url-pattern>*.htm</url-pattern>
     </servlet-mapping>
     <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
     </welcome-file-list>
    </web-app>

    *.htm으로 들어오는 요청에 대해서 springapp서블릿을 사용하겠다는 얘기.
    서블릿은 스프링 프레임워크에 있는 DispatcherServlet 이다.

    DispatcherServlet의  제어 행위를 처리하는 설정 파일이 필요한데, 이것은 서블릿 이름에
    -servlet.xml 을 붙여 작성한다. 그러니까 여기서는 springapp-servlet.xml  이겠지.
    /WEB-INF/springapp-servlet.xml을  작성한다.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
     "http://www.springframework.org/dtd/spring-beans.dtd">

    <beans>
        <bean id="springappController" class="web.SpringappController"/>
     
        <bean id="urlMapping"
         class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
         <property name="mappings">
          <props>
           <prop key="/hello.htm">springappController</prop>
          </props>
         </property>
        </bean>
     
        <bean id="viewResolver"
         class="org.springframework.web.servlet.view.InternalResourceViewResolver">
         <property name="viewClass">
          <value>org.springframework.web.servlet.view.JstlView</value>
         </property>
         <property name="prefix">
          <value>/WEB-INF/jsp/</value>
         </property>
         <property name="suffix">
          <value>.jsp</value>
         </property>
        </bean>
    </beans>

    잘 보면 beans태그안에 bean태그가  3개가있다.
    첫 번째는 Controller인거 같고,,
    두 번째는 urlMapping 시켜주는데 /hello.htm으로 요청이 들어오면 springappController에게
    넘기라는 거 같다.
    세 번째꺼는 선행자 후위자를 정한거네.
    미리  써놓으면 항상 쓰이는 WEB-INF/jsp/파일이름.jsp 에서 앞 뒤 없이 이름만 써서 가능.

    그럼 SpringappController.java를 작성해 보자.
    이놈은 Controller라는  인터페이스를 상속받습니다.
    web이라는 패키지를 생성 후 거기에다가 class를 생성합니다.
    생성할 때 New Java Class화면에서 Interface부분에 Add버튼을 클릭해서
    Controller를선택하고 추가 해주면 자동으로 생성됨.

    package web;

    import java.util.Date;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;

    public class SpringappController implements Controller {
     protected final Log logger = LogFactory.getLog(getClass());
     
     public ModelAndView handleRequest(HttpServletRequest arg0,
       HttpServletResponse arg1) throws Exception {
      // TODO Auto-generated method stub
      String now = (new Date()).toString();
      logger.info("returning hello view with " + now);
     
      return new ModelAndView("hello", "now", now);
     }
    }

    시간을 리턴한다. new Date()로 시간을 생성해서 그걸 String변환 후 now 변수에  넣고,
    로그를 기록한 후, return할때 now라는 변수는 now라는 이름으로 쓸 수 있게 hello파일에 넘김.
    이  hello는 앞  뒤  합쳐서 /WEB-INF/hello.jsp 가 됨.

    이제 /WEB-INF/jsp/hello.jsp 파일을  보자.

    <%@ page language="java" contentType="text/html; charset=windows-31j"
        pageEncoding="windows-31j"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=windows-31j">
    <title>Hello :: Spring Application</title>
    </head>
    <body>
     <h1>Hello World! - Spring Application</h1>
     <p>Greetings, it is now <c:out value="${now}" /></p>
    </body>
    </html>

    now라는 변수를 c:out태그로 출력한다. Controller에서시간을담아서 넘겨 받았다.
    그리고 jsp파일을 WEB-INF폴더에 넣는 이유는 JSP파일접근에대한권한이 아마 우리한테 없고 Spring에게 넘겨서라고 내가 찾아 본 블로그 주인장은 말했다..
    즉, 직접적으로 접근하지 못하도록 하려고?

    was/SpringHelloWorld로 접속시 index.jsp를 실행하는데 파일이 없으니  하나 만들자.
    폴더 위치 : WebContent/index.jsp
    <head>와  </head> 사이에
    <script>
     location.href="hello.htm"
    </script>
    이렇게만 입력하면 자동으로 hello.htm으로 포워드 해준다.
    그럼 실행!

    사용자 삽입 이미지

    이상.. Helloworld찍을 수 있게 도움 받은 블로그 Kyle's Home님께 감사드린다.
    이 글의 내용도 Kyle's Home님의 글을 거의 그대로 옮겨 놓았다.
    Posted by 1010