[Spring] @Autowired의 Before/After

Posted at 2009/04/29 15:35// Posted in 나만의 작업/Spring


알고있는 내용이기에~ 그냥 가볍게 Before 와 After code

Before - @Autowired annotation이 없었을 때 

applicationContext.xml에서 설정
<bean id="empDao" class="EmpDao" /> <bean id="empManager" class="EmpManager"> <property name="empDao" ref="empDao" /> </bean>

EmpDao의 bean을 inject
public class EmpManager { private EmpDao empDao; public EmpDao getEmpDao() { return empDao; } public void setEmpDao(EmpDao empDao) { this.empDao = empDao; } ... }

이랬던 코드가~ 바뀐다. 

After

applicationContext.xml에서 설정

<context:annotation-config /> <!-- 요거 꼭 빼먹지 말것 --> <bean id="empManager" class="autowiredexample.EmpManager" /> <bean id="empDao" class="autowiredexample.EmpDao" />

import org.springframework.beans.factory.annotation.Autowired; public class EmpManager { @Autowired private EmpDao empDao; }

Annotation을 써서 훨씬 보기에 간결한 코드를 짤 수 있다.


출처 - http://flyburi.com/446

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

특정 Bean의 기능 수행을 위해 다른 Bean을 참조해야 하는 경우 사용하는 Annotation으로는 @Autowired 또는 @Resource가 있다.

  • @Autowired

  • Spring Framework에서 지원하는 Dependency 정의 용도의 Annotation으로, Spring Framework에 종속적이긴 하지만 정밀한 Dependency Injection이 필요한 경우에 유용하다.
  • @Resource

  • JSR-250 표준 Annotation으로 Spring Framework 2.5.* 부터 지원 가능한 Annotation이다. Annotation 사용으로 인해 특정 Framework에 종속적인 어플리케이션을 구성하지 않기 위해서는 @Resource를 사용할 것을 권장한다. @Resource를 사용하기 위해서는 클래스패스 내에 jsr250-api.jar 파일이 추가되어야 함에 유의해야 한다.
다음은 @Resource를 사용한 예이다.
@Service
public UserServiceImpl implements UserService {
    @Resource
    private UserDAO userDAO;
}

@Autowired와 @Resource를 사용할 수 있는 위치는 다음과 같이 약간의 차이가 있으므로 필요에 따라 적절히 사용하면 된다.
  • @Autowired : 필드, 생성자, 입력파라미터가 여러개인 메소드(@Qualifier는 메소드의 파라미터)에 적용 가능
  • @Resource : 필드, 입력 파라미터가 한 개인 빈 프로퍼티 setter 메소드에 적용가능
@Autowired나 @Resource를 필드에 직접 정의하는 경우 별도 setter 메소드는 정의하지 않아도 된다.

@Autowired

@Autowired는 기본적으로 type-driven injection 이다. 타입으로 참조할 빈을 찾았을 때 같은 타입의 빈이 여러 개 검색되었을 경우, @Qualifier annotation을 사용하여 구분할 수 있도록 해준다. 

다음은 @Qualifier를 사용한 예이다.
@Service
public ProductService {
    @Autowired
    @Qualifier("electronics")
    private ProductCategory productCategory;
}
Qualifier를 정의하는 방법에는 다음과 같이 두가지가 있다.
  • XML을 사용한 정의
  • <bean class="anyframe.sample.springmvc.annotation.web.SimpleProductCategory">
        <qualifier value="electronics"/>
        <!-- inject any dependencies required by this bean -->
    </bean>
    
    <bean class="anyframe.sample.springmvc.annotation.web.SimpleProductCategory">
        <qualifier value="cosmetics"/>
        <!-- inject any dependencies required by this bean -->
    </bean>
    		
  • Annotation을 사용한 정의
  • @Component
    @Qualifier("electronics")
    public class ElectronicsProductCategory implements ProductCategory {
    	//...
    }

기본적으로 @Autowired가 적용된 프로퍼티는 필수이기 때문에 반드시 해당 빈이 존재해야 하지만, required 속성을 false로 설정하는 경우에는 해당되는 Bean을 찾지 못하더라도 에러가 발생하지 않는다.
@Service
public UserService implements UserService {
    @Autowired(required=false)
    private UserDAO userDAO;
}

@Resource

@Resource annotation은 다음과 같은 경우에 사용한다.
  • Bean name으로 Dependency Injection을 하고자 하는 경우
  • Type-driven injection을 할 수 없는 Collection이나 Map 타입의 빈
@Resource를 사용하는 경우 참조되는 Bean은 변수명을 Bean Name으로 하여 Spring 컨테이너에 의해 자동으로 인지되는데, 변수명을 이용하여 참조 관계에 놓인 Bean을 찾았는데 해당 Bean이 없는 경우에는 클래스 타입을 이용하게 된다. 참조 관계에 놓인 Bean의 Name을 직접 명시하고자 하는 경우에는 다음과 같이 'name' 속성을 부여할 수 있다.
@Service
public UserServiceImpl implements UserService {
    @Resource (name="uDAO")
    private UserDAO userDAO;
}

Custom Qualifier

@Autowired annotation을 이용하여 보다 정밀하게 Dependency Injection을 수행할 필요가 있을 경우, Spring Framework의 Custom Qualifier를 사용할 수 있다.
1.Custom Qualifier 정의
다음은 @UserDAOQualifier라는 Custom Qualifier 코드의 일부로 @UserDAOQualifier는 dbtype이라는 속성 정보를 갖고 있다. dbtype 따라 참조해야 할 Bean이 달라지는 경우 즉, DBMS 유형에 따라 다른 데이터 접근 로직이 실행되어야 하는 경우에 사용될 수 있다.
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention (RetentionPolicy.RUNTIME)
@Qualifier
public @interface UserDAOQualifier {
    String dbtype();
}
java.lang.annotation.Target은 사용한 Annotation이 어디에 적용될 것인지 Annotation을 선언할 때 정의한다. (예를 들면 클래스나 메소드, 필드 등.) 만약 Target이 정해지지 않으면 어디에서나 사용가능한 Annotation이 된다. java.lang.annotation.Retention은 Annotation을 선언할때 쓰이는데, 다음과 같은 Parameter들이 사용 가능하다.
  • RetentionPolicy.SOURCE : 해당 Annotation이 소스레벨이라는 사실을 알려줌 (default)
  • RetentionPolicy.CLASS : 소스코드와 컴파일된 클래스 파일에는 사용 가능하지만, 런타임에는 사용 불가.
  • RetentionPolicy.RUNTIME : 소스코드, 컴파일된 코드, 런타임에도 사용 가능

2.XML 정의
속성 정의 XML 파일 내에 Custom Qualifer로써 UserDAOQualifier 클래스에 대해 정의하여 특정 클래스에서 해당 Annotation을 사용할 수 있도록 한다.
<bean id="customAutowireConfigurer"
    class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
    <property name="customQualifierTypes">
        <set><value>…UserDAOQualifier</value></set>
    </property>
</bean>
다음은 dbtype에 따라 어떤 Bean이 할당될 것인지를 명시하는 부분으로 dbtype이 Oracle일 경우 UserDAOWIthOracle의 인스턴스가 컨테이너에 의해 인지되도록 정의되어 있음을 알 수 있다.
<bean class="...UserDAOWIthOracle">
    <meta key="dbtype" value="oracle"/>
</bean>

3.활용
앞서 정의한 Custom Qualifier @UserDAOQualifier는 다음과 같이 사용한다. 다음의 코드에서 dbtype을 oracle로 정의하고 있으므로, xml 정의에 따라 userDAO라는 변수에는 UserDAOWIthOracle의 인스턴스가 할당될 것이다.
public class UserServiceImpl implements UserService {
    @Autowired
    @UserDAOQualifier(dbtype="oracle")
    private UserDAO userDAO;
    // ...
}


출처 - http://dev.anyframejava.org/anyframe/doc/core/3.2.1/corefw/guide/annotation-dependencies.html


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


@Autowired 이라는 단어를 그대로 풀이하자면 자동으로 와이어링을 해주겠다는 것이다.

이는 XML(ApplicationContext)에 따로 빈을 작성해서 DI해주는 작업을 하는 대신에 DI받고자 하는 객체나 메소드에 Autowired 어노테이션을 명시적으로 작성한다. Autowired 어노테이션을 객체나 메소드의 앞에 명시적으로 작성했다면 해당 객체가 DI를 받기 위해 setter 메소드를 작성해서 사용할 필요가 없다. DI를 받기 위해 존재해야 하는 것이 setter 메소드라는 존재인데 이는 XML에서 DI를 해서 빈을 넘겨주면 *.java 파일에서 빈을 받기 위해 생성자나 setter 메소드가 필요한 것인데 Autowired 어노테이션이 이 작업을 하지 않아도 된다고 알려주는 것이다.

단, Autowired 어노테이션을 사용하기 위해 XML에 별도의 설정이 필요하다.
XML(ApplicationContext)에 <context:annotation-config/> 작성


--Before--

public class BoardController {
private BoardService boardService;
public void setBoardService(BoardService boardService) {
this.boardService = boardService;
}

public BoardService getBoardService() {
return boardService;
}
...
}

<bean id="boardController" class="board.controller.BoardController">
<property name="boardService" ref="boardServiceImpl" />
</bean>
프로퍼티에 boardServiceImpl로 등록이 되어 있는데 이는 빈으로 따로 작성



--After--

public class BoardController {
@Autowired
private BoardService boardService;
...
}

<context:annotation-config/>    
<bean id="boardController" class="board.controller.BoardController"/>

DI를 받으려고 하는 bean도 작성
작성해주지 않으면 DI받으려고 할때 bean을 찾지 못한다고 에러메시지 출력
<bean id="boardServiceImpl" class="board.service.BoardServiceImpl"/>


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


<context:component-scan bace-package="패키지">
  <context:exclude-filter type="annotation" 
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

이와 같은 설정을 통해 @Autowired하려는 모든 빈을 등록해줄 수 있다. 이 설정은 base-package로 등록해 놓은
패키지에 존재하는 어노테이션을 명시해 놓은 클래스나 메소드, 변수 등을 별도의 설정이나 등록 없이 사용해 줄 수 있다. 
예를 들어 @Autowired하려는 변수에 객체를 빈을 통해 주입하는 방식을 위의 설정을 통해 조금은 설정이 편리해진다.
빈을 DI 받기 위한 여타의 작업들을 할 수고를 덜어준다.


출처 - http://luckychips.egloos.com/1629118


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


어노테이션은 자바 1.5부터 지원한다.

스프링2는 어노테이션을 이용하여 빈과 관련된 정보를 설정할 수 있다.

 

@Required

org.springframework.beans.factory.annotaion패키지에 에 있으며 스프링 2부터 제공되고 필수 프로퍼티를 명시할때 사용된다.

 

@Required를 이용하여 필수 프로퍼티를 설정

1단계 : 코드내에 프로퍼티 설정 메서드에 @Required 어노테이션을 붙인다.

 

import org.springframework.beans.factory.annotation.Required

public class TestBean {

    private TestDao testDao;

 

    @Required

    public void setTestDao(TestDao testDao) {

        this.testDao = testDao;

    }

}

 

2단계 : 스프링 설정 파일에 RequiredAnnotationBeanPostProcessor클래스를 빈으로 등록

 

<bean class="org.springframework.beans.factory.annotaion.RequiredAnnotationBeanpostProcessor"/>

<bean name="testBean"  class="lja.test.TestBean">

    <property name="testDao" ref="testDao"/> <!--@Required 어노테이션을 적용하였으므로 설정하지 않으면 예외를 발생시킨다.-->

</bean>

 

RequiredAnnotationBeanPostProcessor 클래스는 스프링 컨테이너에 등록된 bean객체를 조사하여 @Required 어노테이션으로 설정되어 있는 프로퍼티의 값이 설정되어 있는지 검사하고 설정되어있지 않으면 bean생성시 예외를 발생시킨다.

 

 

RequiredAnnotationBeanPostProcessor 클래스를 빈으로 등록하지 않고

<context:annotation-config>태그를 이용할 수도 있다.

 

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

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:context="http://www.springframework.org/schema/context"

    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.5xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:annotation-config/>

</beans>

 

@Autowired

의존관계를 자동으로 설정해주는 어노테이션

: 타입을 이용하여 의존객체를 자동으로 설정한다.

: 생성자, 필드, 메서드 세곳에 적용 가능하다.

 

예) 프로퍼티 설정 메서드 이용

import org.springframework.beans.factory.annotation.Required

public class TestBean {

    private TestDao testDao;

 

    @Autowired

    public void setTestDao(TestDao testDao) {

        this.testDao = testDao;

    }

}

위와 같은 경우 testDao프로퍼티에 TestDao 타입의 bean객체를 전달한다.

AutowiredAnnotationBeanPostProcessor 클래스를 설정파일에 빈객체로 등록하면 @Autowired 어노테이션을 이용한 자동설정이 적용된다.

 

또한 Required어노테이션과 마찬가지로 <context:annotation-config>태그를 사용하면 된다.

@Autowired 어노테이션은 프로퍼티 설정 메서드 뿐 아니라 일반 메서드나 멤퍼필드 자체에 적용시킬수도 있다.

제너릭이 적용된 컬렉션 타입에도 적용시킬 수 있다.

 

@Autowired 어노테이션은 타입을 이용하여 자동으로 빈객체를 전달하기 때문에 타입에 맞는 객체가 없거나 두개 이상일 경우 예외를 발생시킨다.

 

@Autowired 어노테이션을 적용한 프로퍼티를 반드시 설정할 필요가 없는 경우에는 @Autowired 어노테이션의 required속성 값을 false로 지정한다.

@Autowired(required=false)

 

이렇게 지정된 경우에는 해당 타입에 맞는 빈객체가 존재하지 않는다고 해도 예외를 발생시키지는 않는다.

 

@Qualifier

: 동일한 타입의 빈객체들 중 특정 빈 객체를 자동으로 설정하도록 한다.

: @Autowired 어노테이션과 함께 사용되며 자동 연결될 빈객체의 수식어를 값으로 갖는다.

 

public class TestBean {

    @Autowired

    @Qualifier("main")

    private TestDao testDao;

}

위 코드에서 수식어구는 main 이고

이러한 수식어는 설정파일에서 <qualifer> 태그를 이용하여 설정한다.

 

<bean id="testDao" class="lja.test.TestDao">

    <qualifier value="main"/>

</bean>

 

@Resource

:javax.annotation 패키지에 위치한 어노테이션

:자바 6버전 및 JEE5 버전에 추가된 것으로 어플리케이션에서 필요로 하는 자원을 자동 연결할 때 사용

:스프링 2.5 부터 지원하는 어노테이션으로 스프링에서는 의존하는 빈객체를 전달할 때 사용하다.

:name속성에 자동으로 연결될 빈객체의 이름을 입력한다 @Resource(name="testDao")

:CommonAnnotationBeanPostProcessor 클래스를 설정파일에 빈객체로 등록하여 어노테이션을 적용시킨다.

<bean class="org.springframework.beans.factory.annotation.CommonAnnotationBeanPostProcessor"/>

혹은 <context:annotation-config>태그를 이용한다.

 

 

[출처] http://blog.naver.com/dikalee/140088444257 

Posted by linuxism
,


2012/02/20 17:57

MyBatis + 커넥션풀 + 트랜잭션 1장 Spring MVC

이제 대충 스프링이 뭐하는 프레임워크인지 감을 잡았다면 개발에 필요한 라이브러리와 환경을 구축해보자. 혹시나 MyBatis와 커넥션풀에 대해 잘 모르는 사람이라도 일단 속는 셈치고 함께 설치해주는 것이 좋다. 이 두가지 라이브러리와 더불어 스프링 시큐리티가 포함되면 정말 금상첨화지만 일단 스프링 시큐리티까지 설치하면은 설명이 길어지므로 시큐리티는 차후에 사용 포스트를 작성하도록 하겠다.

먼저 개발환경 구축에 필요한 라이브러리 또는 프레임워크 목록은 위 URL에 버전과 함께 상세히 나열되어있다. 혹시나 이 라이브러리들의 의존 라이브러리들이 포함되지 않았을 수도 있으므로 안전하게 메이븐을 이용해 라이브러리를 설치하도록 하자. 메이븐이란 훌륭한 라이브러리 관리 기술이 있음에도 이를 귀찮다고 이용하지 않는다면 프로그래머의 자격이 없다.


메이븐을 아직 설치하지 않았다면 위의 URL을 통해 메이븐을 설치하길 바란다. 더욱이 상기의 개발환경을 기본으로 구축했다는 전제하에 쓰는 글이므로 진행 중에 오류나 이해가 가지 않는 부분이 생길 수도 있다.




먼저 MyBatis에 대해 잘 모르는 사람을 위해 이 라이브러리를 설명하고자 한다. 사실 꼭 MyBatis를 써야하는 이유는 없지만 분명한 사실은 MyBatis와 같은 라이브러리를 활용하여 데이터 퍼시스턴스(Data Persistence)계층을 효율적으로 관리해야 한다는 점은 이미 웹개발자들에 있어서 일반적인 상식이 됬기 때문이다.

위의 문장에서 데이터 퍼시스턴스는 간단히 DB에서 정보를 꺼내오는 역할을 담당하는 계층을 말한다. 현대의 비지니스 객체 모델은 프리젠테이션 계층, 비지니스 로직 계층, 퍼시스턴스 계층으로 나눌 수 있는데 프리젠테이션은 우리가 현재 보고 있는 페이지 또는 브라우져로 출력되는 페이지들을 말하며 비지니스 로직은 퍼시스턴스와 프리젠테이션 사이에서 조율하는 계층을 일컫는다.


실제로 웹 어플리케이션은 위와같은 구도로 돌아간다. 스프링을 사용한다면 사용자가 보낸 요청을 스프링 컨테이너가 가로챈 뒤에 처리하므로 위의 그림보다 조금 복잡하게 돌아가긴 하지만 원리는 동일하다. 그러므로 상기의 계층 구도는 웹 개발에 있어서 매우 일반적인 구성이라고 할 수 있겠다.

사실 우리가 반드시 위의 구도를 지켜가며 코드를 칠 필요는 없다. 필자도 스프링을 알기 전에는 필자 맘대로 계층을 분류하고 메서드를 만들어대고 패키지를 찍어댔었다. 그런 의미에서 당신도 위와 같이 복잡한 계층을 무시하고 마음대로 코드를 쳐댈 수도 있지만 굳이 많은 사람들이 위와같은 계층을 지키려 노력하며, 보이지 않는 약속을 하는 이유는 어디까지나 효율적인 분업이 가능하게 하기 위해서다.

뜬금없는 소리지만 로마 군대가 강력했던 이유는 효율적인 징병제와 어디를 가더라도 로마 군인이라면 이해할 수 있는 진지를 구축했다는 점인데 이런 시대를 가로지르는 진리는 어느 분야에서든 통하기 마련이다. 로마 군대를 비유로 계층별 분류의 가장 큰 이유를 꼽자면 과거 선배 웹프로그래머들의 많은 시행착오 끝에 나온 결과이며 이런 계층별 분류가 웹프로그래머들만의 강력한 진지라고 할 수 있기 때문이다.

그러므로 퍼시스턴스 계층을 분리하는 것을 당연스럽게 받아들이자. 실제로 MyBatis 개발진들이 발표한 결과로 퍼시스턴스 계층을 분리하면 약 30~40% 정도의 엄청난 양의 코드가 절약된다고 한다. 게다가 양만 줄어드는게 아니라 코드자체가 매우 직관적인 표현으로 변하여 가독성까지 높일 수 있다.


먼저 위의 그림과 같은 순서로 하나의 프로젝트를 만들어보자. 프로젝트를 만들었다면 초기에 pom.xml에 구성되있는 라이브러리들이 조금 낡았다는 생각이 들 것이다. 일단 pom.xml파일을 열어 현재 문서의 가장 상단에 있는 POM.XML의 설정을 위한 라이브러리 최신 버전에 있는 모든 라이브러리들을 설치하도록 하자. 라이브러리 교체는 직접 pom.xml을 수정해도 되고 이클립스의 플러그인 기능을 이용하여 수정해도 되는데 이 과정은 매우 쉬우므로 따로 설명하지는 않겠다.

라이브러리들을 최신버전으로 바꾸어 주었다면 먼저 MyBatis와 스프링을 연동시켜야 한다. MyBatis와 스프링이 연동되려면 2가지 XML 파일들이 필요한데 매퍼 설정파일과 매퍼 XML파일이 바로 그 것이다. 이름은 어떤 것으로 해도 상관없지만 현재과정을 따라가는 만큼 이 문서에서 제시된 파일명으로 통일하는 게 좋겠다.

먼저 두가지 XML파일 중에 매퍼 설정파일을 보도록 하자. MyBatis가 별도의 매퍼 설정파일을 필요로 하는 이유는 매퍼 설정을 통해 퍼시스턴스 계층(MySQL, Oracle 등)의 옵션을 세부적으로 조절할 수 있으며 DB의 테이블 관리를 위해 만들어진 자바빈 파일들을 MyBatis만의 타입으로 매핑시키고 SQL구문이 들어있는 매퍼 XML파일을 등록시킬 수 있기 때문이다. 아래 이미지처럼 프로젝트 패키지에 XML파일을 만들고 위의 DTD를 선언하도록 해보자.


XML파일 이름은 아무래도 mybatis의 설정파일이니 mybatis-config.xml 정도가 괜찮겠다. XML파일을 만들었다면 아래의 DTD를 선언하고 본격적으로 설정을 해주도록 하자.

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "HTTP://mybatis.org/dtd/mybatis-3-config.dtd">

설정에는 다양한 옵션이 존재하는데 옵션에 관한 부분만 해도 어마어마하므로 MyBatis 한글 레퍼런스 문서를 참조해가며 직접 구현해보는 것을 추천한다. 다행히 iBatis때부터 열성적인 한국 사용자들이 존재해서 그런지 MyBatis 프로젝트 페이지에 한글 번역 레퍼런스 문서가 당당히 존재하고 있다. 

잡담이지만 MyBatis는 다른 인기 프레임워크와 다르게 별도의 관련 서적이 없는데, 그 이유는 서적이 필요 없을 정도로 쉽고 간편하기 때문이라고 생각한다. 여하튼 필자의 설정방법은 그냥 예제 정도로만 인식하고 실제 개발에 착수할 때는 각각의 옵션이 어떤 역할을 하고 있는지 정확히 인식하고 사용하여야 한다.

mybatis-config.xml의 예제

MyBatis 한글 레퍼런스 문서와 상기 이미지를 참조해가며 <configuration> 요소를 완성시키도록 하자. 위의 <settings>요소만 채웠다해서 매퍼 설정파일이 완성됬다고 할 수는 없지만 중요한 것은 과정을 밟아가며 이해하는 것이므로 이 정도에서 다음 과정으로 넘어가도록 하자.

매퍼 설정파일의 <settings>요소를 작성하였다면 이젠 매퍼 XML파일을 만들 차례다. 매퍼 XML파일은 직접 데이터베이스에서 동작하는 SQL구문을 작성하는 공간인데 이 역시 MyBatis 한글 레퍼런스 문서에 자세한 설명이 있으니 꼭 한번 정독해보기를 권장한다.

개인적으로 매퍼 XML파일을 배치시키는 가장 좋은 방법은 매퍼 XML파일 하나로 모든 쿼리를 처리하는 것보다 테이블에 맞춰 잘게 자른 다음에 패키지 별로 분류 시키는 것이 유리하다. 만약 이런 패키지 분류에 대해 잘 모르겠다면 아래의 문서를 읽기를 권장한다. 패키지 분류는 매우 중요한 상식이므로 이런 분류법에 대해 잘 모른다면 문서를 읽는데 큰 어려움이 있을 것이다.

필자의 매퍼 XML파일 배치방법


현재까지의 과정을 잘 따라왔다면 상단의 이미지처럼 MyBatis 설정 파일들이 위치되었을 것이다. 상단의 이미지에서 users 패키지는 앞으로 우리가 users란 테이블을 만든 뒤 이 테이블을 이용해 테스트할 예정이므로 미리 선행으로 패키지를 분류해둔 것이다. users-sql.xml란 이름으로 매퍼 XML파일을 만들었다면 다음과 같은 DTD를 선언해 주도록 하자.

<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

users-sql.xml의 예제

상단의 이미지는 필자가 작성한 매퍼 XML파일의 모습이다. <mapper>요소 안에 별다른 내용이 없는 이유는 아직 users 테이블도 만들지 않은 데다 마땅히 넣을 만한 SQL구문도 없으므로 향후 작성을 위해 가장 기본적인 형태만 유지한 것이다. 여기서 주의 할 점은 namespace 속성은 Java 코드에서 <mapper>요소를 구분하는데 쓰이는 중요 속성이므로 반드시 설정하도록 한다.(테이블 명과는 아무 관계가 없다. 다만 통일감을 위해 동일명으로 지었을 뿐이다.)


MyBatis의 두 설정파일은 위와 같은 의존관계를 갖고 있다. 매퍼설정파일은 하나 이상의 매퍼XML파일을 매핑할 수 있으며 스프링 컨텍스트 또한 하나 이상의 매퍼설정파일을 매핑할 수 있다. 이제 만들어 둔 users-sql.xml을 mybatis-config.xml에 매핑시켜보도록 하자. mybatis-config파일은 퍼시스턴스 계층의 세부옵션과 더불어 이처럼 세부적인 매퍼 XML파일들을 묶어주는 큰 역할을 담당하고 있다.

mybatis-config.xml파일에 user-sql.xml을 매핑시키는 예제

위의 예제처럼 설정했다면 이제 매퍼XML파일은 매퍼설정파일을 통해 스프링컨텍스트와 연동될 수 있게 되었다. 간단한 테스트를 위해 users테이블을 만들고 mybatis-config.xml파일을 스프링 컨텍스트와 연결시켜보자.

위의 예제와 같이 users란 테이블을 만들고 필드 또한 일치시키도록 한다. 그리고 스프링 컨텍스트 파일은 mybatis만 독자적으로 관리할 수 있게끔 mybatis-context.xml이란 새 컨텍스트 파일을 만들기로 하자. 모든 준비가 끝났다면 mybatis-context.xml에 커넥션풀을 활용한 dataSource 빈을 만들 차례다.

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/데이터베이스명" />
<property name="username" value="DB접속 아이디" />
<property name="password" value="DB접속 비밀번호" />
</bean>

위의 dataSource 빈은 apache에서 제공하는 커넥션풀을 이용하여 생성한 것이다. 커넥션풀이란 매번 접속 때마다 커넥션을 생성하는 게 아닌, 미리 일정량의 커넥션을 구현해 놓은 뒤 커넥션 요청이 올 때마다 이 커넥션을 제공하는 방식을 일컫는다. 커넥션풀은 단순히 좋아서 쓰는게 아니라 반드시 구현해야 하는 기술이다. 당신의 DB가 요청마다 커넥션을 생성하는데는 어느 정도 한계가 있으며 오라클 같이 DB를 만들어 파는 회사는 이런 커넥션 양에 값을 매기기도 한다.

물론 상업용 WAS(Web Application Server)같은 경우는 서버 단에서 DB 풀서비스를 제공해주기도 하지만 우리처럼 가난한 영세민이 사용하는 톰캣에서는 그런 기능을 기대할 수 없다. (그렇다고 톰캣을 폄하하는 것은 절대 아니다.) 우리는 커넥션풀 라이브러리를 적극 활용하여 현재 확보된 커넥션을 최대한 효율적으로 사용 함과 동시에 대량의 커넥션 요청에도 유연하게 대처할 수 있도록 반드시 커넥션풀을 이용해 dataSource를 구현해야만 한다.

여담이지만 오픈소스 커넥션풀에는 dbcp와 c3p0 두가지가 있다. 위의 예제에서는 dbcp를 이용하여 구현하였지만 둘 중에 어느 것을 활용한다 해도 상관은 없다. 어디까지나 선택은 독자의 몫이다.

위의 빈을 mybatis-context.xml에 입력한 다음 부족한 세부 설정을 맞추어서 자신의 DB와 연동시키도록 하자. 모든 설정을 끝마쳤다면 이제 당신은 커넥션 풀을 이용한 데이터베이스와 스프링 프로젝트의 연동에 성공한 셈이다. 이제 dataSource빈을 MyBatis에 주입시키기만 하면 끝이므로 다음과 같이 mybatis-context.xml파일에서 아래와 같은 빈을 만들어 보도록 하자.

<context:annotation-config />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:org/danzka/vodka/mybatis-config.xml" />
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory" />
</bean>


MyBatis를 처음 접한 사람이라면 위의 설정만으론 이해가 가지 않을 것이다. 자세한 설명을 위해 먼저 SqlSessionFactoryBean을보도록 하자. 이 빈은 mybatis의 SqlSessionFactory 객체를 스프링의 팩토리빈을 활용하여 생성시켜준다. 팩토리빈이란 다이나믹 프록시를 스프링에서 쉽게 구현할 수 있도록 한 것인데 자세한 것은 토비의 스프링 - 트랜잭션 부분을 보면서 심화학습하도록 하고 중요한 것은 이 빈이 SqlSessionFactory 빈을 만들어 준다는 것이다.

SqlSessionFactory 클래스는 데이터베이스 정보와 설정파일을 필요로 하는데 dataSource와 mybatis-config.xml파일을 각각 매핑시키면 된다. 여기서 주의할 점은 SqlSessionFactoryBean 클래스는 setConfigLocation란 메서드를 통해 해당 XML을 resource객체로 변환시키기 때문에 보통 경로설정 문제로 난처한 상황을 겪는 경우가 많다. 하지만 우리는 mybatis-config.xml파일을 클래스가 위치하는 폴더에 존재하기 때문에 'classpath:'를 통해 쉽게 설정파일을 가져올 수가 있게 되었다. 지금은 잘 느끼지 못하겠지만 이런 파일배치의 장점은 향후 JUnit을 이용한 테스트와 웹으로 직접 배포하는 테스트를 동시에 하려고 하는 과정에서 그 편리함을 깨닫게 될 것이다.

여기까지 완료되었다면 SqlSessionTemplate 클래스 생성자에 작성한 sqlSessionFactory를 주입시켜주면 되는데 <constructor-arg>라는 요소를 활용하여 해당 클래스의 생성자에 주입해주도록 하자. SqlSessionTemplate 클래스는 디테일한 설정을 위해 3가지 방법으로 생성자를 주입받을 수 있는데 위와 같이 단순히 sqlSessionFactory만 주입하면 기본설정을 따르게 된다는 사실을 기억해야 한다.

이렇게 mybatis-context.xml파일의 설정이 모두 끝났으므로 마지막으로 컨테이너가 인식할 수 있도록 web.xml에 만든 컨텍스트 파일을 등록시키기만 하면 된다. 상기의 이미지와 같이 입력하면 자동으로 컨테이너가 시작될 때 mybatis-context.xml 파일도 함께 읽어 들일 것이다.



이제 본격적으로 MyBatis의 기능을 이용해 보도록 하자. MyBatis를 이용하기 위해선 위에서 만든 SqlSessionFactory와 SqlSessionTemplate를 주입받아 직접적으로 MyBatis의 기능을 이용할 수 있는 클래스 파일을 만들어야 하는데 정말 감사하게도 mybatis는 스프링을 이용하는 유저를 위해 이런 역할을 담당하는 클래스를 만들어 두었다. 그 클래스는 바로 라이브러리 중 mybatis-spring-1.0.2.jar 파일에 들어있는 SqlSessionDaoSupport란 클래스인데 우리는 이 클래스를 상속받아 쓰거나 주입받아 쓰기만 하면 된다.

SqlSessionDaoSupport클래스의 주입부분

위의 소스 중에 설명도 없이 스리슬쩍 넘어간 부분이 있는데 바로 <context:annotation-config />란 설정이다. 이 설정을 해두면 코드에서 @Autowired를 활용하여 주입없이도 바로 xml 객체를 불러올 수 있게 된다. 아직 한번도 @Autowired를 사용한 기억이 없는데 왜 설정이 필요하냐면 바로 위의 이미지처럼 우리가 활용할 SqlSessionDaoSupport 클래스가 이 설정을 요구하기 때문이었다. 만약에 위의 설정이 없다면 우리는 직접 SqlSessionDaoSupport빈을 만들고 직접 주입시켜주어야만 한다. 대신 이 설정이 있다면 별도의 주입없이도 자연스럽게 필요한 객체를 불러들일 수 있게 된다.

1장의 내용은 여기까지로 하고 다음 장에서는 SqlSessionDaoSupport클래스를 활용하여 직접 users 테이블에서 데이터를 불러오고 테스트하는 방법을 다루도록 하겠다. 참고로 이 클래스를 이용하는 방법은 주입방법과 상속방법이 있는데 우리는 상속을 통한 방법으로 접근 하고자 한다. 더불어 스프링을 활용한 트랜잭션 설정까지 함께 다루도록 하겠다.


출처 - http://springmvc.egloos.com/492767


이제 거의 막바지에 왔다. 미리 만들어둔 users 테이블의 자바빈 클래스를 만들어 보자.


users테이블에 대한 자바빈 클래스를 만들었다면 이 자바빈 클래스을 MyBatis가 객체로 인식할 수 있게끔 mybatis-config.xml에 등록시켜야 한다. MyBatis 한글 레퍼런스 문서를 보면 이에 대한 상세한 설명이 담겨져 있으므로 꼭 한번 정독해보길 바란다.

위의 이미지처럼 한다면 User 클래스를 MyBatis에 매핑시키는 작업은 일단락 된 것이나 마찬가지다. 이제 users테이블에서 사용할 쿼리문을 users-sql.xml에 매핑시킬 차례다.

매퍼 XML파일을 이용해 쿼리를 생성하는 방법은 매우 쉽고 다양하다. 이에 대한 자세한 설명 역시레퍼런스 문서를 참조하며 공부하는 것이 제일일 것이다. 그러므로 쿼리문을 생성하는 과정에 대해서는 깊게 설명하지 않겠다. 대신 users 테이블에 대한 기능을 만들기 전에 생각해야될 사항에 대해 잠시 말하고자 한다.

먼저 1장을 유심히 읽어보았다면 스프링이란 프레임워크를 고안해낸 창시자 로드 존슨이 "항상 프레임워크 기반으로 접근하라"라는 말을 했다고 했다. 이 말은 곧 자신이 만들 서비스에 대한 프레임워크를 만드는 방식으로 개발해야 한다는 말이다. 그렇다면 어떻게 해야 제대로된 프레임워크 기반으로 제작하는 것이 될까?

우리는 일단 프레임워크의 표준모델을 인식해야만 한다. java의 가장 표준적인 프레임워크는 map과 list같은 컬렉션 프레임워크를 들 수 있는데 우리가 만들 프레임워크 또한 이런 표준 프레임워크의 설계를 어느 정도 따라야 앞으로 만들어질 프레임워크를 제 3자가 사용하는데 있어서 쓸데없는 혼동과 학습과정을 줄일 수 있을 것이다. 예를 들어 map에는 데이터를 넣기 위해 'put'이란 메서드를 사용하는데 우리가 만들 프레임워크에는 동일한 기능의 메서드를 'vodkazzangzzangjoa'와 같이 지었다면 어떻게 될까?

사람들은 혼란에 빠지게 될 것이고 업무는 쓸데없는 창작 메서드명을 기억하는데 할애될 수 밖에 없을 것이다. 게다가 다른 모델을 본받지 않는 독자적인 설계모델이 올바르다고 보장할 수 있겠는가? 이런 기초가 없는 프레임워크 모델은 얼마 못가 한계를 보이게 되고 부득이한 수정과정을 거쳐야만 할 것이다. 그렇다면 우리가 이런 불필요한 문제를 해결하고 훌륭한 단독 프레임워크를 만들기 위해서는 어떻게 해야 할까?

이런 문제를 해결하기 위해선 우리가 처음 프레임워크를 개발할 때에 어느 정도 기존의 성공적인 모델을 본받고 이 표준프레임워크 모델을 모방하는 과정을 거쳐야 한다는 것이다. 그리고 점차 개발해 나가면서 표준프레임워크 모델로는 불충분 하다면 거기서 확장하는 방식으로 접근해 나가면 된다. 이런 접근법을 위해선 우리는 가급적 구체적으로 표준모델을 정하고 메서드 명 뿐만 아니라 리턴타입까지 일치시킬 수 있도록 노력 해야 한다.

이런 관점에서 예를 들어보자면 방금 전 예를 들었던 Map 인터페이스의 'put' 메서드는 데이터를 프레임워크에 저장하는 역할을 수행하며 저장의 성공여부를 불리언 값으로 리턴 해주고 있다. 혹시나 독자는 이런 불리언 리턴값이 불필요하다 생각들어도 만에 하나 이런 기능을 유용하게 사용하고 있는 사람들을 고려하여 최대한 비슷한 구조로 프레임워크를 만들 수 있도록 해야 한다.

다시 본론으로 돌아와 1부에서 예고 했던대로 SqlSessionDaoSupport를 상속받아 나만의 UserDao를 만들어 보도록 하자. 위의 예제는 users 테이블의 데이터를 삽입하는 'put'메서드인데 SqlSessionDaoSupport에 내장되 있는 getSqlSession()메서드를 활용해 users-sql.xml에 매핑되있는 SQL구문을 불러오고 있다.

원리는 다음과 같다. getSqlSession 메서드는 첫번째 인자로 users-sql.xml의 <mapper>요소 중 namespace속성(이 예제에서는 users)을 통해 어느 <mapper>를 불러올지 판단하고 그 세부 요소의 id를 선택하여 쿼리문장을 불러오는 형태이다. 2번째는 인자값으로 DB에 넘겨줄 파라미터가 된다. 만약에 레퍼런스 문서를 잘 읽었다면 굳이 설명이 필요 없을 정도로 잘 알고 있을테니 이런 부수적인 설명은 생략하기로 하겠다.

이제 만든 MyBatisUserDao를 빈에 등록시킨 다음 잘 동작하는지 JUnit테스트를 해보자. JUnit테스트를 위해서는 작성된 스프링 컨텍스트 XML파일이 target/classes폴더에 들어가야 하는데 이에 대한 자세한 설명은 다음의 포스트를 읽으면 감이 올 것이다.

테스트 결과는 성공이다. 이제 스프링과 MyBatis가 성공적으로 연결되었고 마지막으로 스프링 트랜잭션만 설정하면 끝이다.

<aop:config>
<aop:advisor pointcut="bean(*)" advice-ref="transactionAdvice" />
</aop:config>
<tx:advice id="transactionAdvice">
<tx:attributes>
<tx:method name="*" />
</tx:attributes>
</tx:advice>

위의 코드를 root-context.xml에 넣도록 하자. 그리고 네임스페이스에 'tx'와 'aop'를 추가해주면 트랜잭션에 대한 설정은 모두 끝난다! 정말 이걸로 되나 싶을 정도로 간단한 몇줄의 코드이긴 하지만 트랜잭션을 만만히 봤다간 큰코다칠 것이다. 과거에 트랜잭션은 피땀과 눈물을 흘려가며 구현했어야 마땅한 것이었다. 굉장히 고급기술이었고 일반인은 상상도 할 수 없는 기술이었다. 하지만 지금은 스프링같이 발달한 프레임워크 덕분에 우리는 간단한 설정만으로도 트랜잭션을 이용할 수 있게 되었다 :D

사실 위의 코드 몇줄로 트랜잭션의 설정이 모두 끝났다고는 할 수 없지만 여기서는 디테일한 설명보단 방법론에 대한 이야기만 하고 있으므로 나중에 좀 더 시간을 내어 설명하도록 하겠다. 


이것으로 모든 설정이 끝났고 이제는 개발만 남았다. 사실 중간 과정에서 MyBatisUserDao를 제대로 구현해 보려면 userDao 인터페이스를 구현한 뒤 이를 MyBatisUserDao가 상속받도록 설정하는 것이 마땅하지만 이런 부분은 토비의 스프링에서도 잘 설명해주고 있고 괜히 여기서 중복 설명해봤자 쓸데없는 참견이라 생각되어 생략하였다. 정말 별것도 아닌 걸로 질질 끌어댄 문서를 읽어주느라 수고했고 다음에 더 좋은 주제로 돌아오도록 하겠다.


출처 - http://springmvc.egloos.com/493015




'Framework & Platform > Spring' 카테고리의 다른 글

Spring - singleton과 스프링  (0) 2012.05.24
spring - @Autowired  (0) 2012.05.24
Spring - @RequestBody , @ResponseBody  (0) 2012.05.24
Spring - Expression Language와 @Value  (0) 2012.05.24
spring - 메소드 파라미터  (0) 2012.05.24
Posted by linuxism
,


흐음... 요즘은.. 서비스가 하나의 형태로 나타나지 않는다. 똑같은 데이터를 가지고 이런서비스 저런 서비스에서 똑같이 사용할 수 있단 말이지.
방법이야 게이트단에서 각 단에 맞게 보내주면되긴하다. 

스프링에서도 제공하는기술이 데이터 를 XML이나 JSON 형태로 주고 받을 수 있다. 그거지. 많은 추가 작업없이 가능하다.
예로 .htm 으로 호출하면 html이 오고 .json 으로 호출하면 그 결과가 json으로 오면된다. 주로 키맵의 리턴이 되는 형태 (여러 서비스들간의 통신에서?)
로 주고 받을 수 있다는거다.

@RequestBody 와 @ResponseBody 어노테이션은 각각 HTTP 요청 몸체를 자바 객체로 변환하고 자바 객체를 HTTP 응답 몸체로 변환해주는데 사용된다. 

@RequestBody 어노테이션을 이용하면 HTTP 요청 몸체를 자바 객체로 전달받을 수 있다.
@ResponseBody 어노테이션을 이용하면 자바 객체를 HTTP 응답 몸체로 전송할 수 있다. 

@RequestMapping( method = RequestMethod.POST)
@ResponseBody
public String simpleTest(@RequestBody String body) {
return body;
}

@RequestBody 어노테이션은 @RequestMapping에 의해 POST 방식으로 전송된 HTTP 요청 데이터를 String 타입의 body 파라미터로 전달한다.
@ResponseBody 어노테이션은 @RequestMapping 메서드에 적용되면 해당 메소드의 리턴값을 HTTP 응답데이터로 사용한다.

위의 경우 리턴값이 String 이므로 String 데이터를 HTTP 응답 데이터로 전송한다.

응답데이터.. 결과는 아래처럼 간다...
만약 폼에 값들이 들어있었다면

name=John&age=22

위의 경우 요청 몸체 데이터를 body 파라미터에 전달받고 있으며, body 파라미터를 그대로 결과값으로 리턴한다.
그런데 @ResponseBody 어노테이션이 적용되어 있어서 .. HTTP 요청데이터가 그대로 응답데이터로 간다.

해서 html... 웹브라우저 뷰에 

name=John&age=22

나올것이다.

이런것을.. 스프링MVC는 HttpMessageConverter를 이용해서 자바객체와 HTTP 요청/응답 몸체 사이의 변환을 처리한다.



HttpMessageConverter를 이용한 처리

AnnotationMethodHandlerAdapter 클래스는 @RequestBody 어노테이션이 적용된 파라미터나 @ResponseBody 어노테이션이 적용된 메소드에 대해 HttpMessageConverter 를 사용해서 변환처리한다. (정확히는 HttpMessageConveter의 구현클래스를 사용)

이 HttpMessageConverter를 구현한 몇가지 클래스들이 있다.
ByteArrayHttpMessageConveter 등.. 찾아봐라

만약 다른 messageConverter를 사용하겠다면 명시적 정의 가가능하다. (아래처럼...)
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="cacheSeconds" value="0" />
<property name="alwaysUseFullPath" value="true" />
<property name="webBindingInitializer">
<bean class="madvirus.spring.chap06.binder.CustomWebBindingInitializer" />
</property>
<property name="messageConverters">
<list>
<ref bean="byteArrayHttpMessageConverter" />
<ref bean="stringHttpMessageConverter" />
<ref bean="formHttpMessageConverter" />
<ref bean="sourceHttpMessageConverter" />
<ref bean="marshallingHttpMessageConverter" />
<ref bean="jsonHttpMessageConverter" />
</list>
</property>
</bean>


출처 - http://ezsnote.tistory.com/entry/RequestBody-ResponseBody




'Framework & Platform > Spring' 카테고리의 다른 글

spring - @Autowired  (0) 2012.05.24
Spring - MyBatis + 커넥션풀 + 트랜잭션  (0) 2012.05.24
Spring - Expression Language와 @Value  (0) 2012.05.24
spring - 메소드 파라미터  (0) 2012.05.24
spring - cookie 설정  (0) 2012.05.24
Posted by linuxism
,