봄싹2009.03.20 17:00
실험 1.
메세지 소스에다가 ... 두개의 메세지 파일을 등록을 해주었습니다.

 <bean id=" messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basenames">
            <list>
                <value>/WEB-INF/messages/MessageResources_a_ko</value>
                <value>/WEB-INF/messages/MessageResources_b_ko</value>
            </list>
        </property>
        <property name="cacheSeconds" value="5" />
    </bean>

그런데 해당 파일에.. 내용은 같은 키파일들이 한파일에 중복되고.. 또 두 파일에 중복이 되어있습니다..

MessageResources_a_ko.properties 파일내용.
error.what=가
error.what=나
error.what=다

MessageResources_b_ko.properties 파일내용.
error.what=라
error.what=마
error.what=바

이렇게 했을때..
메세지 소스에서.. getMessage("error.what"....) ;

이런식으로 메세지를 꺼냇을때... 어떤것이... 찍힐까요?

실험결과 !

너무나 당현한건가 할지는 모르겟지만.. 정답은 "다" 이네용;; ㅋ

org.springframework.context.support.ReloadableResourceBundleMessageSource 264 line
    protected String resolveCodeWithoutArguments(String code, Locale locale) {
        if (this.cacheMillis < 0) {
            PropertiesHolder propHolder = getMergedProperties(locale);
            String result = propHolder.getProperty(code);
            if (result != null) {
                return result;
            }
        }
        else {
            for (int i = 0; i < this.basenames.length; i++) {
                List filenames = calculateAllFilenames(this.basenames[i], locale);
                for (int j = 0; j < filenames.size(); j++) {
                    String filename = (String) filenames.get(j);
                    PropertiesHolder propHolder = getProperties(filename);
                    String result = propHolder.getProperty(code);
                    if (result != null) {
                        return result;
                    }
                }
            }
        }
        return null;
    }

대충 보면 ... 빈의 이름으로 루프를 돌다가 프로퍼티에서 코드로 값을 꺼내고 있으면 ~
바로 리턴해버리니까 .. 첫번째 파일의 메세지중에서 나올껏이고..
프로퍼티도.. key 형태이니.. 마지막에 들어간 값 하나만 존재 하니..
결국 첫번째 파일의 마지막 키값이 들어가네요~

역시 긴가 민가 할때는 소스를... 한번쯤 봐주면... 명확한 답이.. 음트트트트.. ㅋ
맞겠지 아마.. ?ㅋ 이유가.. ?ㅋ
Posted by is윤군

댓글을 달아 주세요

봄싹2009.02.27 11:47
음.. web Application 을 만들다 보면 컨트럴 단의 가장 심한 반복 중 하나는 request객체에서 화면에서 넘긴 파리미터 값을..
꺼내 오는 작업이다..
하지만.. 스프링 MVC를 쓴다면....... 놀라운 마법을 경험할 수 있다.
@RequestParam 어노테이션을 이용해서 값을 바인딩 할 수도 있고 .. 그냥 인자값으로 적어도 값을 받을 수 있고..
객체를 인자값으로 적어 줘도 값을 받을 수 잇다..
컨트럴의 메소드중...
public String money(Money money,String id){
}
Money.java
...........
private Strind id;
...........

이렇게 한다면 화면에서 id를 파라미터로 넘기면 id도 바인딩되고 ... money.id 에도 똑같은 값이 바인딩이 된다..
이건.. 스프링을 사용해본 사람이라면 다들 경험하는 일일껏이다..
하지만 가끔은 혼동이 온다.. 다 자동으로 바인딩한다면;;
흠.. 무튼.. 이러한 마법을 부려 주는 곳이 어딜까 뒤져뒤져 찾아 보았다..

스프링 2.5.6 버전 기준으로 HandlerMethodInvoker.java 144 line..


소스를 자세히 들여다 보면 JAVA reflect 을 활용해서 ... 열심히 바인딩 해주는 모습을 볼수가 있다..
스프링의 마법은 어쩌면 JAVA reflect을 자유 자제로 다루는 스프링 개발팀이 일구어낸 작품일 것이다.
물론 스프링이 최고아 ! 라는 흑백논리보단.. 이러한 것들을 생각하고 만들어낸 스프링 개발팀이 멋있을 따름이다..

아직도 스프링의 마법같은 기능들에 대해서 10/1도 알지는 못하지만...
로그 레벨을 Trace로 걸고 스프링 소스들을 살펴보는건 나에겐 무척 즐거운 놀이감이다!!

스프링 마법 만쉐이~~~~~~~~!!
Posted by is윤군

댓글을 달아 주세요

봄싹2009.02.05 13:46
음...
스프링으로 만들어진 데몬이 있다;;
그러고 ;;

그 데몬과 다르게 ;; 일 배치로 작업을 돌리것이 있어서 ;; quartz를 또다시 한번 사용했다;;

몇달전에 사용했던 기억을 더듬어서 ;; 하는데;;
음.. 처음 막힌건;;
quartz의 jobClass로 등록된 놈에서 스프링 컨테이너에서 살고잇는 빈들을 가져다 쓰고 싶었다;;

처음 시도는 @Autowired 음.. '' ;;
안된다.. 보아 하니;;
<bean id="xxxxx"    class="org.springframework.scheduling.quartz.JobDetailBean"
        p:jobClass="여긴 클랙스가 들어가야 된다;;"/>

대충 보니;; 저놈이 클래스를 가져 가서 그때 그떄 만들어서 쓰던지;; 무튼;; 해당놈이 스프링 컨테이너 생명주기에 들어가는 놈이 아니라는 것이다..

그래서 ;; JobDetailBean.java 소스를 보니~
public class JobDetailBean extends JobDetail
    implements BeanNameAware, ApplicationContextAware, InitializingBean {
    private ApplicationContext applicationContext;
    private String applicationContextJobDataKey;

두놈이 보인다..
음 그리고 ~ JobDetailBean 이놈이 스프링 컨테이너의 생명주기에 들어가고 .. Aware들을 이용해서 ;;
자신에게 ApplicationContext를 가지고 있는것.. ;; 그리고 쭈욱~보니..

if (this.applicationContextJobDataKey != null) {
            if (this.applicationContext == null) {
                throw new IllegalStateException(
                    "JobDetailBean needs to be set up in an ApplicationContext " +
                    "to be able to handle an 'applicationContextJobDataKey'");
            }
            getJobDataMap().put(this.applicationContextJobDataKey, this.application    ApplicationContext ctx;
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        ctx = (ApplicationContext)context.getJobDetail().getJobDataMap().get("applicationContext");
        for(String name : ctx.getBeanDefinitionNames()){
            System.out.println(name);
        }
Context);
        }
대충 이것도 발견~!!
음.. JobDataMap에 applicationContextJobDataKey 이름으로 applicationContext를 넣어주는것;;

오홋 빙고~~~
그래서..
      <bean id="xxxJobDetail"    class="org.springframework.scheduling.quartz.JobDetailBean"
        p:jobClass="org.xxx.xxx.aaaa"
        p:applicationContextJobDataKey="applicationContext">
    </bean>

이라고 설정해서..
    ApplicationContext ctx;
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        ctx = (ApplicationContext)context.getJobDetail().getJobDataMap().get("applicationContext");
        for(String name : ctx.getBeanDefinitionNames()){
            System.out.println(name);
        }

이렇게 찍어보니.~~ 다 나온다~~ 스프링 컨테이너에서 살고 있는 놈들이~ 유후~~~~;;
물론...
      <bean id="xxxJoquartzbDetail"    class="org.springframework.scheduling.quartz.JobDetailBean"
        p:jobClass="xyz.aa">
        <property name="jobDataAsMap">
            <map>
                <entry key="bean key" value-ref="넣고 싶은 bean"/>
            </map>
        </property>
    </bean>

    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
(Xxx)context.getJobDetail().getJobDataMap().get("xxx");
        }

이러게 하여서 빈을 JobDataMap 이라는 놈에게 담아서 꺼내 쓸수도 있다~!!


Posted by is윤군

댓글을 달아 주세요

봄싹2009.01.20 17:04

Posted by is윤군

댓글을 달아 주세요

봄싹2009.01.15 13:15
음.. 스프링에 DataAccessException 이라는 놈이 있다.
그런데 그놈은 디비관련 에러코드를 어느정도 맵핑하여 ;;
정보들을 돌려준다;; badSqlGrammarCode 나 .. dataIntegrityViolationCode 뭐 이런것들의 정보;;
그래서 한번 살펴보았다;;
사실은 에러코드매핑하는걸 하나 만들고자 하였는데 스프링에선 어떻게 했나 궁금하기도 하고.
전에 한번 봤었는데 기억이 가물가물 하여 ~
점심시간에 짬을 내어본다..

spring-jdbc.jar을 보면
org/springframework/jdbc/support/sql-error-codes.xml  파일이 존재 한다 .
여기에 보면 ;;
<bean id="Oracle" class="org.springframework.jdbc.support.SQLErrorCodes">
        <property name="badSqlGrammarCodes">
            <value>900,903,904,917,936,942,17006</value>
        </property>
        <property name="invalidResultSetAccessCodes">
            <value>17003</value>
        </property>
        <property name="dataIntegrityViolationCodes">
            <value>1,1400,1722,2291,2292</value>
        </property>
----------------------------------

이러한 정보들이 있다..  그럼 이놈들을 사용가능하게 만들어주는 놈은 누구인가 ?

org.springframework.jdbc.support.SQLErrorCodesFactory (해당 API) 놈인데;...
대충 살펴보면 ;; spring에서 정의한 sqlErrorCode이와 사용자가 직접 정의해서 사용할 수 있도록 되어 있다;;
역시나 대박이다~ 언제나 프레임웍에 국한되어 사용하지 않고.. 사용자 측면도 생각해주는 Spring.. good!!
대충의 코드는 이러하다..

코드를 보면.. 우선 처음에 SQL_ERROR_CODE_DEFAULT_PATH 기본으로 정의된 xml을 읽고 (org/springframework/jdbc/support/sql-error-codes.xml) 다음에는 ..
SQL_ERROR_CODE_OVERRIDE_PATH 사용자 정의 xml 을 한번더 읽는다.. 이름은 sql-error-codes.xml 로 classpath 에 잡혀 있으면 될듯하다.. ;;

여기서 보면서 보면서 .. 아리까리 했던건..
  DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
  XmlBeanDefinitionReader bdr = new XmlBeanDefinitionReader(lbf);
이부분이다 ..

DefaultListableBeanFactory (해당 API)이름으로 유추해보아..
기본 빈들을(타입이 같은?) 리스트 형태로 만들어주는 팩토리? 이렇게 되나 ? xml에서 읽은 빈들을 리스트 형태로 만들어주는 것 같기도 하고.. 한데;; 소스를 보면 타입이 같은 빈들을 Map에 담아서 빈이름 key 빈 value 형태로 돌려준다..
xml을 보면 ...
<bean id="Oracle" class="org.springframework.jdbc.support.SQLErrorCodes">
<bean id="MySQL" class="org.springframework.jdbc.support.SQLErrorCodes">
<bean id="MS-SQL" class="org.springframework.jdbc.support.SQLErrorCodes">

스프링에선 많은 BeanFactory를 제공하고.. 그중에 몇개만 사용해본지라..
다음에 DefaultListableBeanFactory 이놈에 대해서 살펴봐야 겠다..ㅋㅋ

무튼 정리 하자면 ;;; xml에 정의된 빈들을 생성하여 Map  형태로 .. 돌려 줌을 알수 있다..
그리고 에러코드를 꺼내쓸때에는 .. 디비 이름으로 찾아내는데.. xml에서도 볼수 있듯이 빈의 id가 db이름인걸 알수 있다..
그래서 에러가 났을때;; 디비이름을 가지고... sqlErrorCodes 라는 놈을 거내와서 사용하는듯하다~

일단 에러가 났을때.. 이 팩토리를 이용해서 뭐 어떻게 꺼내서 쓰는지 보지는 못했지만.. 시간날때 틈틈히 스프링 소스까보기를 진행해봐야 겠다~;; 대박나라~ Spring~ㅋㅋ

이 내용은 100% 나의 추측들로 작성된 글이다.. ;; 100% 맞게 설명했다고는 볼수 없을 것 같다..;; 기억보존용 POST이기도 하고~
아~ 그냥 이런놈들이 있구나~ 하면서 눈팅용으로 작성된 글임을... 잊지 말길 ..
어여 하루 빨리 ~ 완벽 소스 이해를 하는 날이 왓으면........ 하면서~~~~~~~~~ !!
Posted by is윤군

댓글을 달아 주세요