Spring 에서 객체변수의 인스턴스를 얻어오는 방식은 3가지 방법이 있다.


1. 자동묶기 방식
 - 해당 bean을 컨테이너에 등록시 autowire 지시자를 이용하여 자동묶기를 한다.
   일반적으로 xml파일과 java파일에 아래와 같이 코딩한다.
   해당 java소스에는 반드시 set메소드가 존재해야 한다.
   단, constructor 방식을 사용할 경우에는 생성자가 주입을 담당하기 때문에
   굳이 set메소드가 존재하지 않아도 된다.
  
  1.1. application-context.xml
    ...생략...
    <bean id="sqlMapClientUtil" class="com.local.util.SqlMapClientUtil" autowire="byName">
    </bean>
    ...생략...

  1.2. SqlMapClientUtil.java
    ...생략...
    private SqlMapClient sqlMapClient;
   
    public void setSqlMapClient(SqlMapClient sqlMapClient) {
    this.sqlMapClient = sqlMapClient;
    }
    ...생략...

*   기타 autowire 옵션 

   1) byName : 프로퍼티의 이름과 같은 이름을 갖는 빈 객체를 설정
   2) byType : 프로퍼티의 타입과 같은 타입을 갖는 빈객체를 설정
   3) constructor : 생성자 파라미터 타입과 같은 타입을 갖는 빈객체를 생성자에 전달

   4) autodetect : constructor 방식을 먼저 적용하고, byType 방식을 적용하여 의존 객체를 설정
    

byName를 제외한 방식의 경우 2개 이상의 bean이 검색될 경우 아래와 같은 Exception을 발생시키게 된다.
org.springframework.beans.factory.UnsatisfiedDependencyException                    

이러한 경우 autowire 방식이 아닌 직접지정 방식을 사용해야 한다.
      
타입에 의한 자동와이어링은 타입이 같은 빈이 두 개 이상 존재하는 경우에는 적용되지 못한다.

이름에 의한 자동와이어링은 프로퍼티 이름과 빈의 아이디만 비교하면 되기 때문에 상대적으로 빠르다.

하지만 타입을 비교하는 것은 String으로 된 이름을 비교할 때보다 느리다. 

게다가 자동와이어링은 빈의 모든 프로퍼티에 일괄 적용되어 대상이 아니어도 비교하는 작업이 일어나게 된다.​

 



2. 직접 지정방식

 - 해당 bean을 컨테이너에 등록시 주입할 property를 아래와 같이 지정한

.

  2.1. application-context.xml
    ...생략...
    <bean id="sqlMapClientUtil" class="com.local.util.SqlMapClientUtil">
      <property name="sqlMapClient" ref="sqlMapClient"/>
    </bean>
    ...생략...

  2.2. SqlMapClientUtil.java
    ...생략...
    private SqlMapClient sqlMapClient;
   
    public void setSqlMapClient(SqlMapClient sqlMapClient) {
    this.sqlMapClient = sqlMapClient;
    }
    ...생략...
    
 * 생성자 방식을 사용할 경우 아래와 같이 지정한다.
    <constructor-arg type="java.lang.String" index="0">
        <value>config/service/serviceContext-config.xml</value>
    </constructor-arg>
    인자의 순서와 타입에 주의하도록 한다.
    java 다형성에 의해 생성자가 여러개 정의되어 있는 경우
    인자의 순서와 타입에 의해 생성자가 결정된다.

 


 

 

3. bean으로 등록되지 않은 클래스에서 동적 얻어오는 방식
 - 컨테이너에 등록된 bean에 객체를 주입할때 위와 같은 방식으로 할 수도 있지만
   bean으로 등록하지 않은 유틸클래스에서 해당 객체를 주입할 필요가 있을때가 있다.
   유틸클래스로 사용될 클래스를 모두 bean 컨테이너에 명시하고, autowire를 하던지
   직접 지정하는 것도 한 방법이다.
   하지만 지금부터 소개하는 아래의 방식이 더 깔끔한 방식 임을 알고 있으면 된다.
   아래의 방식은 application-context 자체를 객체로 얻어서 해당 객체에서 관리되는

   bean객체를 얻어올 수 있다는 점을 이용한다.
  
   그렇기 때문에 프록시 역할을 하게될 application-context 객체를 얻어오는 클래스를

   구현하고 최소한 해당 클래스는 bean으로 등록되어 있어야 한다.
  
  3.1. application-context 객체를 얻어올 클래스 구현.
    - ApplicationContextAware 인터페이스를 구현한 클래스여야 한다.
      해당 인터페이스에는 아래와 같은 1개의 메소드가 존재한다.
     
      public abstract interface ApplicationContextAware
      {
        public abstract void setApplicationContext(ApplicationContext paramApplicationContext)
          throws BeansException;
      }
     
      해당 메소드를 구현해서 application-context 객체를 구하면 된다.
      아래는 해당 인터페이스를 구현한 실제 클래스 예이다.
    
      ...생략...
      public class ApplicationContextUtil implements ApplicationContextAware{
    
        private static ApplicationContext context;
       
        public void setApplicationContext(ApplicationContext ac)throws BeansException {
        ApplicationContextUtil.context = ac;
       }
       
       public static ApplicationContext getContext(){
        return ApplicationContextUtil.context;
       }
      ...생략...

  3.2. 컨테이너 등록
    - 위에서 구현한 ApplicationContextUtil 을 bean으로 등록한다.
    
     <bean id="applicationContextUtil" class="com.local.util.ApplicationContextUtil" autowire="byName">
     </bean>
     
  3.3. 유틸 클래스에서 ApplicationContextUtil 을 통해 객체를 주입한다.
    - 아래와 같이 application-context 객체에서 getBean()메소드를 통해 컨테이너에 존재하는 bean 객체를 얻어올 수 있다.
  
      localeResolver = (LocaleResolver)ApplicationContextUtil.getContext().getBean( "localeResolver"); 

 

Posted by mypiece
,

스프링에서는 모든 서블릿에서 사용될 공유자원을 DI개념으로 사용하기 위해
일반적으로 ContextLoader를 구현한 위 두개의 클래스를 제공한다.
이는 일반적으로 web.xml에 ContextLoaderListener 또는 ContextLoaderServlet 중 하나를
선택적으로 정의해서 사용한다.


목적과 기능은 동일하나 Servlet 2.2 나 2.3 에서는 ContextLoderServlet을,

Servlet 2.4 이상부터는 ContextLoaderListener를 사용하는 것이 권장된다.

백워드 호환을 위해 ContextLoaderServlet를 사용하는게 좋다고 하지만
상황에 따라 명시적으로 선택해서 사용하는 것이 좋을 듯 하다.

WAS에서 지원하는 Servlet버전을 확인하여 그에 맞게 사용하도록 하자.

참고로 Tomcat5.5에서 제공하는 Servlet버전은 2.4 이므로

ContextLoaderListener를 사용하면 되겠다.


각각 선언방법은 아래와 같다.

ContextLoaderListener

 <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>


ContextLoderServlet

 <servlet>
  <servlet-name>ContextLoaderServlet</servlet-name>
  <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>


주의할 점은 ContextLoaderServlet를 선언하여 사용할 경우에는
반드시 <load-on-startup>을 기타 명시적인 서블릿보다 적게 지정하여
제일먼저 로드되도록 해야 한다. ContextLoaderListener는 listener로 지정되기 때문에

굳이 지정하지 않아도 서버가 구동될때 제일먼저 로드된다.


ContextLoader에서 사용될 자원의 경우 일반적인 Servlet과 마찬가지로
web.xml에서 param 지정을 통해 명시할 수 있다.
일반적인 param이 아닌 Context param으로 아래와 같이 명시한다.


<context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>


위에서 <param-value>/WEB-INF/applicationContext.xml</param-value> 절은

생략할수도 있는데 이럴 경우 /WEB-INF/applicationContext.xml파일을

기본 설정파일로 사용하게 된다.

<param-value>을 지정할 경우 xml파일의 경로는 물론이고, 파일명도 원하는대로

선언할 수 있다. 프로젝트 성격에 따라 경로는 달라질 수도 있고, 추후 유지보수를 위해서도

위와 같이 명시적으로 선언하는 것을 추천한다.


이렇게 구현된 ContextLoader는 기타 DispatcherServlet의 부모 역할을 하게 되며
ContextLoader의 모든 자원은 DispatcherServlet에서 활용이 가능하게 된다.
단, ContextLoader에서 자식인 DispatcherServlet의 자원을 이용하는 것은 불가능하다.

Servlet에서의 initParam 자원 취득방식은 다음과 같다.
기본적으로 해당 DispatcherServlet에서 자원을 검색하게 되고
없다면 부모인 ContextLoader에서 자원을 검색하게 된다.
만약 ContextLoader에도 해당 자원이 없다면 Exception을 발생시키게 된다.

Posted by mypiece
,

XML파일의 활용

밥벌이/Java 2015. 9. 18. 22:21

1. xml 파일 생성하기

- 일반적으로 xml 파일은 정적인 설정정보를 저장하기 위한 용도로 사용되며 

어플리케이션에서 해당 정보를 읽어서 사용한다. 

이러한 이점으로 인해 자주 변경되야 하는 설정정보의 경우 xml파일로 작성하여 

수정이 있을 때 별도의 소스 컴파일 과정없이 확장이 가능하게 된다.

다수의 설정정보를 저장하는 만큼 파일 내에서 필요한 데이터를 

효율적으로 파싱하기 위해 기본적인 구조는 트리구조를 가지고 있다.

 

기본적인 작성형식은 아래와 같다.

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

<movieList>

<movie>

<name>신의한수</name>

<creator>조범구</creator>

<genre>액션</genre>

</movie>

<movie name = “트랜스포머”>

<option name = “creator">마이클베이</option>

<option name = “genre”>SF</option>

</movie>

</movieList>

  

- <?xml version=”1.0” encoding=”UTF-8”?> 의 경우 해당 파일이 xml 파일임을 

명시하는 것으로 반드시 최상단에 들어가야 한다. encoding 값 설정의 경우 선택적이긴 

하지만 한글을 사용할 것이라면 위와 같이 UTF-8로 지정해 주는 것이 좋고, 대부분 그렇게 사용한다.

* 주의할 점은 XML 파일에서는 대소문자를 철저히 구분하므로 

위 예시처럼 대소문자를 가리지 않거나 “”를 생략한다면 에러가 발생한다.

 

- 아랫 줄 부터는 실제 사용할 정보를 기술한다. 

<movieList></movieList>와 같이 반드시 종료태그를 기술해야 한다.

 

- <movieList> 와 같이 가장 바깥쪽에 있는 태그를 Root Element라고 하며 

반드시 1개만 존재할 수 있다. 1개 이상 존재한다면 읽어올 때 에러가 발생한다.

 

-  아래로 보이는 <movie> 태그로 2개의 영화에 대한 정보를 기술하였다.

정보 기술의 경우 첫번째 영화(신의한수)처럼 단순노드만으로도 구성이 가능하며 

두번째 영화(트랜스포머)처럼 속성을 포함한 노드로도 구성이 가능하다.

 

- 처음에 말했듯이 XML은 기본적으로 트리구조를 가지고 있기 때문에 

각 정보에 접근하는 방식은 node와 element로서 접근하게 된다. 

node는 단순히 해당 경로의 접점을 의미하며 element의 경우 경로와 

요소를 포함한 객체를 의미한다. 

이러한 이유로 위 xml에서 '신의한수' 라는 정보를 얻고자 한다면 

<move> -> <name> 와 같이 node만으로 접근하고 해당 텍스트를 얻어오면 되지만 

'트랜스포머'라는 정보를 얻고자 한다면 <movie> -> <option> node로 접근한 뒤 

element로 형변환한뒤에 name 속성에 해당하는 을 얻어와야 한다.

 

 



2. xml 파일 읽기
- 기본적으로 XML 파일을 읽기 위해서는 아래의 과정을 거친다.

 모든 방식의 접근방식을 기술한 것이 아니라 본인이 활용하거나 습득한 정보만

나열되어 있기 때문에 하나의 예시로써 활용하고 추가로 필요한 부분은 검색 또는 API를 참고해야 할 것 같다.


읽어올 XML 파일의 내용은 아래와 같다.

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

<confList>

<confResource name = “ExcelCompare”>

<option name = “basePath”>d:/업무/요청자료/</option>

<option name = “target_fileName”>전체 사용자목록.xls</option>

<option name = “standard_fileName”>A팀 현재 사용자목록.xls</option>

<option name = “result_fileName”>A팀 정상 사용자목록.xls</option>

</confResource>

<confResource name = “...”>

...

</confResource>

</confList>


해당 설정파일의 경우 ‘A팀 현재 사용자목록’ 중 ‘전체 사용자목록’에 포함되지 않는 사용자들을 비교하여

삭제한 뒤 결과 엑셀파일을 새로 생성하는 자바어플리케이션 개발시 사용되었다.
추후 <confResource> 태그를 하단에 추가하여 name 속성에 각각의 프로세스명을 기입하여

다른 자바어플리케이션 개발시에도 함께 활용할 계획이었다.



2.1. Document 객체 생성


DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();


// classpath 상에 존재하는 설정파일을 읽어올 때
InputStream is = getClass().getResourceAsStream(“/conf/DestinyConf.xml”);
Document d = db.parse(is);


/*
classpath 상에 존재하지 않는 설정파일을 읽어올 때
File confFile = new File(“d:/conf/DestinyConf.xml”);
Document d = db.parse(confFile);
보이는 것 처럼 DocumentBuild의 parse() 메소드는 오버로딩 되어있어 InputStream, File 중 아무거나 받을 수 있다.
*/


d.getDocumentElement().normalize();



2.2. Root Element 값 얻기


d.getDocumentElement().getNodeName();



2.3. 설정파일 리스트 얻어오기


NodeList confList = d. getElementsByTagName (“confResource”);

- confResource 라는 태그명을 가지는 모든 node 리스트를 얻어온다.



2.4. 설정파일 리스트에서 현재 프로세스에서 사용할 설정파일에 맞는 자원 찾기

for(int i = 0; i < confList.getLength(); i++){
/* confResource라는 태그명을 가진 node를 순차적으로 돌면서 해당 프로세스의 설정정보로 지정된 node를 찾는다. */


 Element confResource = (Element)confList.item(i);
 /* 단순히 node로 접근하는 것이 아닌 name속성을 체크해야 하므로 Element 로 변환한다. */


 if(confResource.getAttribute(“name”).equals(“ExcelCompare”)){
 /* name 속성이 해당 프로세스인 것을 찾는다. */


  NodeList confResourceList = confResource.getElementsByTagName(“option”);
  /* 해당 노드 안에는 프로세스에서 사용할 option 값들이 여러 노드로 분배되어 있기 때문에

      option 태그를 가지는 node들로 리스트를 생성한다. */


  for(int j = 0; j < confResourceList.getLength(); j++){
   /* 해당 node 리스트를 돌면서 필요한 설정값을 읽어서 설정한다.*/


   Element option = (Element)confResourceList.item( j);
   String optionName = option.getAttribute( "name");


   if(optionName.equals( "basePath")){
    basePath = option.getTextContent(); //해당 node의 텍스트 자원을 얻어온다.
   }else if(optionName.equals("target_fileName")){
    target_fileName = option.getTextContent();
   }else if(optionName.equals( "standard_fileName")){
    standard_fileName = option.getTextContent();
   }else if(optionName.equals("result_fileName")){
    result_fileName = option.getTextContent();
   }
  }
 }
}

'밥벌이 > Java' 카테고리의 다른 글

VO, DTO 구분해서 사용해야 할까?  (0) 2020.07.16
BufferedStream 팁  (0) 2015.09.18
Properties 파일 읽기  (0) 2015.09.18
Posted by mypiece
,