교재 실습/자바 웹 개발 워크북

151. 스프링 IoC 컨테이너 적용 (1)

Jint 2022. 11. 14. 23:18

이번 절에서는 실습 웹 프로젝트 'SPMS'에 스프링 IoC 컨테이너를 적용해 본다. 지금까지는 우리가 만든 ApplicationContext 클래스를 사용하여 자바 객체를 관리하였지만, 이제 스프링 IoC 컨테이너를 사용하여 mybatis나 Dao, 페이지 컨트롤러 등의 자바 객체를 관리해 본다.

 

1. mybatis 관련 의존 라이브러리 추가

스프링 IoC 컨테이너에서 mybatis를 사용하려면 mybatis를 스프링과 연결해 주는 라이브러리가 필요하다. 또한, JDBC와 관련된 스프링 라이브러리를 추가해야 한다.

Gradle 설정 파일(web08/build.gradle)을 열고 다음과 같이 'mybatis-spring' 및 'spring-jdbc' 라이브러리를 추가한다.

dependencies {
    compile 'org.mybatis:mybatis-spring:1.2.2'
    compile 'org.springframework:spring-jdbc:4.0.3.RELEASE'
    providedCompile 'javax.servlet:javax.servlet-api:3.0.1'
    compile 'jstl:jstl:1.2'
    compile 'org.reflections:reflections:0.9.9-RC1'
    compile 'log4j:log4j:1.2.17'
    compile 'mysql:mysql-connector-java:5.1.30'
    compile 'org.mybatis:mybatis:3.2.6'
    compile 'org.springframework:spring-context:4.0.3.RELEASE'
    compile group: 'commons-collections', 
        name: 'commons-collections', 
        version: '3.2'
    testCompile group: 'junit', 
            name: 'junit', 
            version: '4.+'
}

 

compile 'org.mybatis:mybatis-spring:1.2.2'

이 설정을 추가하면 mybatis-spring-1.2.2.jar 파일이 추가된다. 이 라이브러리에는 SqlSessionFactory 객체를 스프링 IoC 컨테이너에서 생성할 수 있도록 팩토리 빈 클래스가 들어 있다. 이 라이브러리가 있어야만 스프링 IoC 컨테이너에서 mybatis와 관련된 객체를 생성할 수 있다.

compile 'org.springframework:spring-jdbc:4.0.3.RELEASE'

이 설정을 추가하면 'spring-jdbc-4.0.3.RELEASE.jar 파일과 spring-tx-4.0.3.RELEASE.jar 파일이 추가된다. spring-jdbc에는 DB 커넥션 풀을 다루는 클래스들이 들어있고, spring-tx에는 트랜잭션을 다루는 클래스들이 들어 있다.

 

2. Gradle 빌드

Gradle 설정 파일(build.gradle)을 바꾸면 항상 Gradle 빌드를 다시 수행해야 한다. 그래야만 메이븐 중앙 서버(http://repol.maven.org/maven2/)로부터 추가된 의존 라이브러리를 내려받는다.

프로젝트 컨텍스트 메뉴에서 Gradle - Refresh Gradle Project를 클릭한다.

Gradle 빌드 작업은 다음 순서로 지정한 다음, 빌드를 실행한다.

:clean

:eclipse

이번에는 'build' 작업을 제외하였다. 'eclipse' 작업만 실행해도 의존 라이브러리를 자동으로 내려받기 한다. 또한, 프로젝트의 클래스 경로 설정 파일(.classpath)에 해당 라이브러리를 추가한다. 따라서 소스 코드를 작성하고, 웹 애플리케이션을 실행하는데 아무런 문제가 없다. 'build' 작업은 개발이 완료된 다음 배포 파일을 만들 때 포함하면 된다.

 

# 빈 설정 파일 준비

스프링 IoC 컨테이너에서 관리할 객체는 빈 설정 파일에 선언해야 한다.

src/main/resources 폴더에 beans.xml 파일을 생성하고, 다음과 같이 편집한다.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:c="http://www.springframework.org/schema/c"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jee="http://www.springframework.org/schema/jee"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jee 
		http://www.springframework.org/schema/jee/spring-jee.xsd">

	<context:component-scan base-package="spms"/>

	<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/studydb"/>
	
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="typeAliasesPackage" value="spms.vo"/>
		<property name="mapperLocations" value="classpath*:spms/dao/*Dao.xml"/>
	</bean>

</beans>

 

- 컴포넌트 자동 스캔

클래스 경로를 뒤져서 @Component 어노테이션이 붙은 클래스를 찾아 객체를 생성하게 하려면 다음의 태그를 선언해야 한다.

<context:component-scan base-package="spms"/>

클래스를 조사할 폴더는 spms 패키지이다. 스프링 IoC 컨테이너는 spms 패키지를 포함하여 하위 패키지까지 모두 조사한다. 그리고 @Component 어노테이션이 붙은 클래스에 대해 인스턴스를 생성한다.

 

- 톰캣 서버에 등록된 DataSource 객체 가져오기

톰캣 서버와 같은 애플리케이션 서버에 등록된 객체는 JNDI 이름으로 찾아서 가져온다. 스프링을 사용하기 전에는 다음과 같이 직접 InitialContext 객체를 생성하여 JNDI 객체를 찾았습니다.

InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/studydb");

스프링 IoC 컨테이너에서는 jee:jndi-lookup 태그를 사용하여 간단히 처리할 수 있다.

<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/studydb"/>

jee:jndi-lookup 태그의 jndi-name 속성에 찾고자 하는 객체의 JNDI 이름을 지정한다. 이 태그는 jee 네임스페이스에 소속되어 있기 때문에, 다음과 같이 네임스페이스를 선언해야 사용할 수 있다.

xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/jee 
    http://www.springframework.org/schema/jee/spring-jee.xsd"

 

- 트랜잭션 관리자 설정

아직 SPMS 프로젝트에 트랜잭션을 적용하지 않았지만, 다음 절에서 다룰 것이다. 그 때 자세히 설명하도록 하고 일단, 데이터베이스 트랜잭션 관리를 담당할 객체를 선언한다.

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

DB 커넥션에 대해 트랜잭션 관리를 수행하는 것이기 때문에, dataSource 프로퍼티를 설정해야 한다. 톰캣에서 가져온 DataSource 객체를 할당한다.

 

- 스프링에서 mybatis의 SqlSessionFactory 객체 생성

mybatis의 핵심은 SqlSessionFactory 객체이다. 이 객체가 있어야만 다음 코드와 같이 SqlSession 객체를 얻을 수 있고, SqlSession 객체를 통해 SQL문을 실행할 수 있다.

SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.selectList("spms.dao.MemberDao.selectList", paramMap);

다음은 SqlSessionFactory 객체를 생성하기 위해 작성했던 코드이다. SqlSessionFactoryBuilder 객체를 통해 생성했다.

String resource = "spms/dao/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

문제는, 스프링에서는 이런 방식으로 처리할 수 없다는 것이다. 스프링 IoC 컨테이너에서 SqlSessionFactory 객체를 생성하려면 팩토리 빈 클래스가 있어야 한다. 다행히 mybatis에서는 별도의 라이브러리로 이 클래스를 제공하고 있다. Gradle 설정 파일(build.gradle)에 추가했던 설정이 바로 이 라이브러리를 가져오는 명령어이다.

compile 'org.mybatis:mybatis-spring:1.2.2'

이 라이브러리 안에 팩토리 빈 클래스(SqlSessionFactoryBean)가 들어 있다. 다음은 이 팩토리 빈 클래스를 사용하는 코드이다.

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="typeAliasesPackage" value="spms.vo"/>
    <property name="mapperLocations" value="classpath*:spms/dao/*Dao.xml"/>
</bean>

org.mybatis.spring.SqlSessionFactoryBean 클래스는 SqlSessionFactory 객체를 만들어주는 스프링 IoC 컨테이너용 클래스이다. 클래스 이름(xxxFactoryBean)을 보면 인스턴스를 만들어 주는 팩토리 빈 임을 짐작할 수 있다.

 

※ SqlSessionFactoryBean 클래스의 이름만 보면 xxxFactoryBean이기 때문에 꼭 SqlSession 객체를 생성할 것 같지만, 실제는 SqlSessionFactory 객체를 생성한다. 그렇다고 클래스 이름을 SqlSessionFactoryFactoryBean이라고 짓기엔 'Factory'가 두 번 들어가서 보기가 흉했을 것이다. 개발자의 고심이 느껴진다!

 

- mybatis 설정

이전에는 SqlSessionFactory 객체를 생성하는데 필요한 정보를 다음과 같이 별도의 설정 파일에 두었다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- <properties resource="spms/dao/db.properties"/> -->
	<settings>
		<setting name="logImpl" value="LOG4J"/>
	</settings>

	<typeAliases>
		<typeAlias type="spms.vo.Project" alias="project"/>
		<typeAlias type="spms.vo.Member" alias="member"/>
	</typeAliases>

	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC"/>
			<!-- 
			<dataSource type="POOLED">
				<property name="driver" value="${driver}"/>
				<property name="url" value="${url}"/>
				<property name="username" value="${username}"/>
				<property name="password" value="${password}"/>
			</dataSource>
			-->
			<dataSource type="JNDI">
				<property name="data_source" value="java:comp/env/jdbc/studydb"/>
			</dataSource>
		</environment>
	</environments>

	<mappers>
		<mapper resource="spms/dao/MySqlProjectDao.xml"/>
		<mapper resource="spms/dao/MySqlMemberDao.xml"/>
	</mappers>
</configuration>

스프링 IoC 컨테이너에서는 따로 mysql 설정 파일을 작성하지 않는다. SqlSessionFactoryBean 객체의 프로퍼티 설정으로 대신한다. <environments> 태그의 데이터 소스와 트랜잭션 정보는 다음과 같이 dataSource 프로퍼티 값으로 대신한다.

<property name="dataSource" ref="dataSource"/>

<typeAliases> 태그의 값 객체(Value Object) 정보는 다음과 같이 typeAliasesPackage 프로퍼티 값으로 대신한다. 프로퍼티 값 spms.vo는 값 객체가 들어있는 패키지 이름이다.

<property name="typeAliasesPackage" value="spms.vo"/>

<mappers> 태그의 SQL 맵퍼 파일 정보는 다음과 같이 mapperLocations 프로퍼티 값으로 대신한다.

<property name="mapperLocations" value="classpath*:spms/dao/*Dao.xml"/>

SQL 맵퍼 파일의 경로를 보면 'classpath*:'로 시작한다. 이것은 클래스 경로에서 파일을 찾으라는 뜻이다. spms/dao/*Dao.xml은 spms.dao 패키지에서 파일 이름이 Dao.xml로 끝나는 맵퍼 파일을 가리킨다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr