ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [toby의스프링] 10장 - IoC 컨테이너와 DI
    먹고 사는 이야기/프로그래밍 2011. 11. 8. 22:23

    이번 장의 분량은 꽤 많다. 약 140p - 장수로는 70장 - 소설처럼 술술 읽히지 않는 특성 상 글자를 읽는데만 서너시간씩 걸린다.
    눈꺼풀은 어찌나 무겁던지.. 

    처음 이 책을 들었을 때 철저히 숙독해야겠다고 마음 먹었다.
    먹고 살기 위한 공부가 아니라 공부를 위한 공부를 해 보겠다고 말이지.
    사실 이미 아는 내용이거나, 실무에서 빈번하게 쓰이지 않는 부분은 그냥 대충 읽고 넘어가도 좋을텐데.. 얼마간은 바보 같은 짓인것 같기도 하다.

    이 책은 한번 읽고 따라해보는 스타일이 아니라 원리를 이해하고 계속해 두고 보는 바이블 스타일이다.
    그래서 단시간에 읽고 이해하기도 어렵지만 정리하기는 더욱 어려운 것 같다.



    이번 장에서는 스프링 컨테이너에서 Bean을 DI하는 법에 대해 말하고 있다.
    사실 실무에서 늘 하던거지만, 이렇게 체계적으로 파고 들어 본 적은 없었다.
    그저 기계적인 접근(Ctrl + C, Ctrl + V)만이 있었을 뿐.

    그래서 이번 장은 참 따분하기도 했고[각주:1], 또 참 흥미로운 내용[각주:2]이기도 했다.
     
    70페이지나 되는 방대한 분량을 게시물 하나로 정리하기는 좀 어렵겠고..
    하나 하나 세세하게 짚어야 할 이유도 별로 없을 것 같다.


    IoC 컨테이너를 통해 애플리케이션이 만들어지는 방식 (본문 p772)



    IoC에 대해서는 이미 앞선 1부에서 충분히 이야기 했던 내용이니 이론적인 이야기는 그림을 통해 설명하고 넘어가자.
    귀찮아서라기보다, 딱히 더 설명할 것이 없다.

    대신 실무에서 빈번하게 사용되는 방식인 XML을 이용한 applicationContext 설정을 가져다 review 하는 방식으로 이 장을 정리하고자 한다.
    예제로 제시되는 applicationContext 는 얼마 전에 실제로 개발한 프로젝트에서 사용한 것 중 일부이다.
    원본 applicationContext는 분량이 꽤 방대해 그 중 일부를 발췌했다.
    따라서 정상 동작하는 소스가 아니므로, 어떻게 하는지만 대충 살펴보기 바란다.


    applicationContext.xml


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

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

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

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

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

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

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


            <!-- 동적 URL Mapping : 각 url에 오브젝트를 연결한다. -->

    <bean id="defaultUrlMapping" 

    class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

    <property name="order" value="1"/>

    <property name="interceptors"> //정의된 url에 인터셉트를 적용한다.

                        <list>

                            <ref bean="serviceSessionInterceptor"/> //인터셉터의 예. list 로 복수를 지정할 수 있다.

                            <ref bean="menuNodeInterceptor"/>

                        </list>

                    </property>

    <property name="mappings">

    <props>

    <prop key="/serviceName#1.do">serviceController#1</prop> //서블릿(do)에 연결될 오브젝트(Controller)

    <prop key="/serviceName#2.do">serviceController#2</prop>
                                    ... (연결될 수 만큼) ...
    </props> 

    </property>
    </bean>
     

            <!-- 정적 URL Mapping : 각 url에 staticUrlController를 연결한다. 단순 html 페이지에서 사용 -->

    <bean id="staticUrlMapping" 

    class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

    <property name="alwaysUseFullPath" value="true" />

    <property name="order" value="6"/>

    <property name="interceptors"> //정의된 url에 인터셉트를 적용한다.

                        <list>

                            <ref bean="serviceSessionInterceptor"/> //인터셉터의 예. list 로 복수를 지정할 수 있다.

                            <ref bean="menuNodeInterceptor"/>

                        </list>

                    </property>

    <property name="mappings">

    <props>

    <prop key="/serviceName#1.do">staticUrlController</prop> //서블릿(do)에 연결될 staticUrlController

    <prop key="/serviceName#2.do">staticUrlController</prop>
                                    ... (연결될 수 만큼) ...
    </props> 

    </property>
    </bean>

     

    <bean id="staticUrlController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/> // 위에서 호출한 정적(static) 페이지를 보여주는 서블릿 컨트롤러


            <!--JSP View Mapping : JSP페이지를 view로 연결 -->

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 

    p:prefix="/WEB-INF/jsp/"

    p:suffix=".jsp">

    <property name="viewClass"

               value="org.springframework.web.servlet.view.JstlView" />

    </bean>

     

            <!--트랜잭션 적용을 위한 AOP 설정 -->

    <aop:config>

    <aop:pointcut id="defaultServiceOperation"

    expression="execution(* classpathRoot.*.service.*Service.*(..))" />

    <aop:advisor pointcut-ref="defaultServiceOperation"

    advice-ref="defaultTxAdvice" order="2"/>

    </aop:config>


    <!-- 실제 메소드에 트랜잭션 적용하기 -->

    <tx:advice id="defaultTxAdvice" transaction-manager="transactionManager">

    <tx:attributes>

    <tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/> //update로 시작하는 이름을 가진 모든 메소드에 적용한다. 이하 동일.

    <tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>

    <tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>

    <tx:method name="process*" propagation="REQUIRED" rollback-for="Exception"/>

    <tx:method name="perform*" propagation="REQUIRED" rollback-for="Exception"/>

    <tx:method name="get*" propagation="REQUIRED" read-only="true" />

    <tx:method name="set*" propagation="REQUIRED" read-only="true" />

    <tx:method name="find*" propagation="REQUIRED" read-only="true"/>

    <tx:method name="*" propagation="REQUIRED" read-only="true"/>

    </tx:attributes>

    </tx:advice> 

     

    <!-- Bean을 등록하고 해당 Bean에 DI 하기 -->

    <bean id="codeService" class="mest.eks.common.service.CodeService">

    <property name="zipCodeService" ref="zipCodeService"/> // 아래 오렌지색 처리된 zipCodeService를 참조함

    </bean>

    <bean id="zipCodeService" class="projectName.common.service.ZipCodeService">

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

    </bean>


    <!-- DB 연결 설정 -->

    <bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor" lazy-init="true"/>


    <bean id="oracleLobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true">

      <property name="nativeJdbcExtractor"><ref local="nativeJdbcExtractor"/></property>

      </bean>

    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean" >

    <property name="configLocation">

    <value>/WEB-INF/config/sqlmap-config.xml</value> //iBatis Sql Mapping 연결설정

    </property>

    <property name="dataSource">

    <ref bean="dataSource" />

    </property>

    <property name="lobHandler" ref="oracleLobHandler" />

    </bean>

    <!-- jndi -->

    <bean id="jnditemplate" class="org.springframework.jndi.JndiTemplate" >

      <property name="environment">

          <props>

            <prop key="java.naming.factory.initial">jeus.jndi.JNSContextFactory</prop>

            <prop key="java.naming.provider.url">localhost:9736</prop>

          </props>

      </property>

    </bean>

    <!-- Jeus Server use ConnectionPoolingDataSource -->

    <bean id="transactionManager"

    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"

    p:dataSource-ref="dataSource" />

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">

    <property name="jndiName" value="JNDI_NAME"/>

    <property name="jndiTemplate" ref="jnditemplate"/>

    </bean> 

    </beans>



    sqlmap-config.xml

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


    <!DOCTYPE sqlMapConfig

    PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"

    "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">


    <sqlMapConfig>

    <settings enhancementEnabled="true" useStatementNamespaces="true" />


    <!-- common sqlMap -->

    <sqlMap resource="xmlfile.xml" />

    ... (필요한 수 만큼 반복) ... 

    </sqlMapConfig> 




    xmlfile.xml

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


    <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"

        "http://ibatis.apache.org/dtd/sql-map-2.dtd">

    <sqlMap namespace="BoardFile">

    <typeAlias alias="fileVo" type="projectName.model.FileVo"/>

    <typeAlias alias="map" type="java.util.Map"/>

    <resultMap id="_resultMap" class="fileVo" >

    <result column="FILE_NM" property="fileNm" />

    <result column="FILE_PATH" property="filePath" />

    </resultMap>

    <select id="fileInfo" resultMap="_resultMap" parameterClass="fileVo">

    SELECT *

    FROM

    TABLE_NAME

    WHERE

    SEQ = #seq#

    <isNotEmpty property="requestParameter"> //넘겨 받은 값에 의한 판별

    AND MENU_CD = #requestParameter#

    </isNotEmpty>

    </select>

    </sqlMap> 



    applicationContext 를 설명할 때, 단순히 기술 방식만 언급하는게 아니라 이를 클래스나 메소드에서 어떻게 활용하는지를 함께 언급해야 할 것이다.


    위에서 보여준 그림을 다시 보면서 얘기하자면..

    IoC 컨테이너를 통해 애플리케이션이 만들어지는 방식 (본문 p772)



    위에서 잔뜩 나열한 xml 파일들은 모두 메타정보리소스에 해당하는 내용들이다. 이를 스프링의 메타정보리더가 읽어들여 설정메타정보로 바꾼다. 이 정보가 IoC 컨테이너가 되려면 POJO 클래스와 함께 사용되어야 한다. 즉, applicationContext에서 설정된 내용들을 POJO 클래스에서 불러와 활용해야 한다.

    하지만 POJO 클래스에서 메타정보를 활용하는 방법에 대해서는 이미 1부에서 학습했기 때문에[각주:3] 실제 적용된 applicationContext 파일을 소개하는 선에서 마무리하자.



    마지막으로, 이 장에서 언급된 개념 중 특히 중요한 항목들을 나열해보자.


    1. 스프링의 빈 등록 방법: XML, 빈 자동인식, 자바 코드에서 등록(XML + 자동인식이 자주 쓰인다)

    2. 스프링의 빈 의존관계 설정 방법: XML, 애노테이션, 자바 코드에서 등록(XML + 애노테이션이 자주 쓰인다)

    3. 스코프: 빈의 존재 범위. 싱글톤, 프로토타입, 기타 스코프(요청request, 세션session, 글로벌세션globalSession, 애플리케이션application : 모두 웹 전용이다)
     




    아.. 졸리다.


    1. 대부분 아는 내용이었으니까 [본문으로]
    2. 스프링의 작동 원리에 대해 이야기하고 있었으므로 [본문으로]
    3. 더구나 자바 클래스 파일의 작성법을 다룰 수는 없지 않은가? 이것은 어디까지나 스프링에 대한 학습서 내용을 간추린 것이니까. ㅎㅎ;; [본문으로]

    댓글

Kunner.com since 2000.