Whiteship's Note

'Spring'에 해당되는 글 355건

  1. 2010.08.10 [스프링 테스트] 웹 테스트용 WebApplicationContext에 request, session 스코프 빈 등록하기 (2)
  2. 2010.07.16 [토비의 스프링 3] 2차 완독 후기 (8)
  3. 2010.07.13 [스프링 3.0] 상속구조에서 @RequestMapping 퀴즈
  4. 2010.07.13 [스프링 3.0] 클래스-메서드 레벨 @RequestMapping 퀴즈
  5. 2010.06.04 [스프링 퀴즈] @Autowired (4)
  6. 2010.05.20 Toby's 스프링 3.0 완독
  7. 2010.05.19 Toby's 스프링 3.0 @MVC를 읽고서 (3)
  8. 2010.05.16 Toby's 스프링 3.0 스프링 웹 기술과 스프링 MVC를 읽고서...
  9. 2010.05.14 Toby's 스프링 3.0 스프링 웹 기술과 스프링 MVC를 읽으며..
  10. 2010.05.14 Toby's 스프링 3.0 데이터 액세스 기술을 읽고서
  11. 2010.05.13 Toby's 스프링 3.0 10장 IoC 컨테이너와 DI를 읽고서 (8)
  12. 2010.05.11 Toby's 스프링 8, 9장을 읽고서
  13. 2010.05.10 Toby's 스프링 핵심기술 응용을 읽고서.. (2)
  14. 2010.05.09 Toby's 스프링 AOP 마무리 (4)
  15. 2010.05.07 Toby's 스프링 AOP 등장 배경을 읽으며...
  16. 2010.04.13 [스프링 3.0] 소녀시대와 함께하는 스프링 @MVC (14)
  17. 2010.03.16 [스프링 3.0] @Value 이용해서 기본값 설정하기
  18. 2010.03.04 [스프링 3.0] @Async 테스트
  19. 2010.02.28 [스프링 3.0] FormattingConversionServiceFactoryBean에 들어있는 Converter와 Formatter (2)
  20. 2010.02.19 Spring Framework 3.0.1 나왔구나
  21. 2010.02.18 [스프링 3.0] PropertyEditorRegistry가 이길까 ConversionService가 이길까 (2)
  22. 2010.01.28 [스프링 3.0] 로깅은 SLF4J를 이용한 Log4J로 (1)
  23. 2010.01.12 [스프링 3.0] JSR-330 Provider 인터페이스 이용해서 싱글톤이 아닌 빈을 싱글톤 빈에 주입하기 (2)
  24. 2009.12.23 스프링 3.0의 설정 간편화
  25. 2009.12.22 스프링 3.0의 MVC 간편화 (2)
  26. 2009.12.17 스프링 프레임워크 3.0이 GA 됐다. (4)
  27. 2009.12.11 [스프링 3.0] 스프링 bean과 일반 자바 객체가 호출하는 @Bean 메서드의 차이
  28. 2009.12.10 [스프링 3.0] @Async 사용하기
  29. 2009.12.08 스프링의 로깅 의존성 (2)
  30. 2009.11.16 [스프링 3.0] RC2 릴리즈~

[스프링 테스트] 웹 테스트용 WebApplicationContext에 request, session 스코프 빈 등록하기

Spring/3.0 : 2010.08.10 12:23


Spring, Junit 에서 session, request scope bean 을 사용하기 

오리대마왕님께서 올려주신 글을 보니 '토비의 스프링 3'에 나오는 웹 테스트 코드가 생각나서 그냥 session 스코프 빈을 한번 등록해 봤습니다. 오리대마왕님 테스트의 주 목적도 이거였을 텐데.. 컨트롤러에 AOP를 등록하고, 현재 request를 담고 있는 HttpRequestHolder를 request 스코프로 만든 걸 봤는데... 테스트 목적에 비해 코드가 다소 장황한 것 같습니다. (사실 올려주신 코드에서 HttpRequestHolder를 request 스코프로 등록하지 않고 그냥 singleton으로 써도 결과는 똑같더군요. 현재 요청을 가로 채고 있던 녀석은 HttpRequestHolder를 request 스코프로 했기 떄문이 아니라 RequestContextHolder 때문인것 같더군요. 그리고 컨트롤러에 설정한 AOP도 그냥 스프링 인터셉터를 쓰시면 더 간단하게.. 처리가 가능한.. 암튼..)

public class SimpleRequestScopeTest {

    @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
    static class RequestScopedBean {}

    @Test
    public void simpleReqestScopeBean() throws ServletException {
        ConfigurableDispatcherServlet dispatcherServlet = new ConfigurableDispatcherServlet();
        dispatcherServlet.setClasses(RequestScopedBean.class);
        dispatcherServlet.init(new MockServletConfig("spring"));

        WebApplicationContext wac = dispatcherServlet.getWebApplicationContext();
        RequestScopedBean rsb = wac.getBean(RequestScopedBean.class);
        assertThat(rsb, is(notNullValue()));
    }
}

이게 다입니다. RequestScopedBean이라는 클래스를 만들고, 스코프를 설정해준 다음 ConfigurableDispatcherSevlet에 전달해서 빈으로 등록해주었고, DS에서 WAC를 가져온다음 RSB를 getBean 해서 null이 아닌지 확인하면 끝입니다.

단순 빈 등록만 확인하려면 WebApplicationContext를 직접 만들어 쓰면 되겠지만, 여기서는 웹 테스트 용도로 작성하는 테스트라고 가정하면, 요청 매핑이나, 실행 결과 ModelAndView등을 확인하려면 역시 DispatcherServlet을 써먹어야 합니다. 하지만 DispatcherServlet은 ModelAndVIew를 밖으로노출해주지 않죠. 그럴 필요도 없구요. 그래서 테스트용으로 DispatcherServlet을 확장한 것이 바로 저기에 보이는 ConfigurableDS입니다. 이 CDS를 사용한 초단단 웹 @MVC 테스트는 다음과 같습니다.

public class HelloControllerTest {
@Test
public void helloController() throws ServletException, IOException {
ConfigurableDispatcherServlet servlet = new ConfigurableDispatcherServlet();
servlet.setRelativeLocations(getClass(), "spring-servlet.xml");
servlet.setClasses(HelloSpring.class);
servlet.init(new MockServletConfig("spring"));
MockHttpServletRequest req = new MockHttpServletRequest("GET", "/hello");
req.addParameter("name", "Spring");
MockHttpServletResponse res = new MockHttpServletResponse();
servlet.service(req, res);
ModelAndView mav = servlet.getModelAndView();
assertThat(mav.getViewName(), is("/WEB-INF/view/hello.jsp"));
assertThat((String)mav.getModel().get("message"), is("Hello Spring"));
}
}
(출처, 토비의 스프링 3 예제 코드)

그러나. 이것 마저도 복잡합니다. ConfigurableDispatcherServlet 를 만들고 설정한 다음, Request에 URL을 설정하고 그 결과로 나오는 MAV를 확인하는 패턴이 고정적입니다. 따라서 이걸 더 간편하게 사용할 수 있도록 만든 클래스가 있는데.. 그게 AbstractDispatcherServletTest 입니다. ConfigurableDispatcherServlet과 AbstractDispatcherServletTest는 토비의 스프링 3에서 직접 보실 수도 있고, 예제 코드를 다운 받아 보실 수도 있는데 설명이 들어있는 책으로 보시는걸 추천해 드립니다.

여기서는 간단한 사용법만 보여드리자면;;

public class SimpleMVCTest extends AbstractDispatcherServletTest {
@Test
public void simpleHandler() throws ServletException, IOException {
this.setClasses(SimpleHandler.class, SimpleViewHandler.class)
.runService("/hi");
assertThat(this.response.getContentAsString(), is("hi"));
this.runService("/view");
assertThat(this.getModelAndView().getViewName(), is("view.jsp"));
}

@Controller static class SimpleHandler {
@RequestMapping("/hi") @ResponseBody 
public String hi() { return "hi"; }
}
@Controller static class SimpleViewHandler {
@RequestMapping("/view")
public String view() { return "view.jsp"; }
}
}
(출처, 토비의 스프링 3 예제 코드)

이런식으로 초간단 MVC 테스트를 만들 수 있습니다.
top


[토비의 스프링 3] 2차 완독 후기



결국 추천사를 쓸 수 있게 됐습니다. 캬캬캬.
배려해주신 출판사 부사장님과 토비님께 감사드립니다. 잘 쓸께요. 아.. 부담 100배!!!!



----------------지난 이야기-----------------

사실 완독이라고 할 수는 없다. 이번엔 강의 준비를 하면서 다시 봤는데 강의할 부분만 읽었기 때문에 모든 글을 다시 읽진 않았다. 읽으면서 중간 중간 코드와 API 설명이 나온 부분을 유심히 보면서 혹시 소스 코드에 잘못된 것은 없는지 검증하는 작업을 했다. 내가 빅뱅의 쉘든 같은 사람이었다면 완벽하게 리뷰를 할 수 있었을텐데 난 레너드 수준에서 리뷰를 마감했다. 1차 때 보다 훨씬 적운 수의 리뷰를 드렸지만 품질 만큼은 1차때 보다 더 나은 리뷰를 해드린것 같아 다행으로 생각하고 있다. 그래도 분명히 놓친 부분이 있을텐데... 그건 개정판을 내실 때 수정할 수 있도록 출판사에 제보해주면 좋겠다. 아마 별도의 페이지가 있을지 싶다.

한 가지 아쉬움이 남는다. 난 추천사를 못 썼다. 근래 가장 많은 시간과 애정을 쏟아가며 리뷰한 책이고 그 누구보다도 여러 개발자에 이 책을 추천해주고 싶었는데 그럴 수 없었다. 여태까지 본 책 중에 이렇게까지 자발적으로 추천사를 쓰고 싶은 책은 없었는데 조금 안타깝다. 

내가 이 책을 리뷰 한 것은 이 책에 내가 적은 글을 남기고 싶어서도 아니고 돈을 받고 싶어서도 아니고 강의를 하고 싶어서도 아니었다. 돈은 애초에 생각도 안했고, 강의는 이미 리뷰를 시작한 다음에 계획 됐다. 추천사는 이 책을 읽다보니 너무도 쓰고 싶어졌지만 이젠 상관없다. 난 그냥 이 책 내용이 정말 좋았고 많은 도움을 받았다. 나도 어떻게든 이 책이 조금 더 반짝거리게 해주고 싶었다. 또 기나긴 여정에서 고군분투 하시는 토비님에게 조금이나마 힘이 되고 싶었다. 그것으로 됐다.
top


[스프링 3.0] 상속구조에서 @RequestMapping 퀴즈

Spring/3.0 : 2010.07.13 15:09


아무도 안 풀 것 같지만... 자신이 @RM을 얼마나 이해했는지 측정해보기 위해서는 좋은 방법이니까 시간나면 꼭 해보시기 바랍니다.

@RequestMapping("/hier")
public class SuperController {
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier super list");
return "/WEB-INF/views/hello.jsp";
}

}

@Controller
public class SubController extends SuperController {
}

1. 이때 /hier/list 요청을 하면 처리가 될까? 

@Controller
public class SubController extends SuperController {
@Override
public String list(Model model){
model.addAttribute("message", "hier sub! list");
return "/WEB-INF/views/hello.jsp";
}
}

SubController 코드를 이렇게 바꿨다. 

2. 이때 /hier/list를 요청했을 때 화면에 찍히는 ${message}의 값은 무엇인가?

@RequestMapping("/hier2")
public class Super2Controller {
}

@Controller
public class Sub2Controller extends Super2Controller {
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier list");
return "/WEB-INF/views/hello.jsp";
}

}

3. 이때 /hier2/list를 요청했을 때 핸들러가 실행될까?

public class Super3Controller {
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier2 super list");
return "/WEB-INF/views/hello.jsp";
}
}

@Controller
@RequestMapping("/hier3")
public class Sub3Controller extends Super3Controller {

}

4. 이때 /hier3/list 요청이 처리 될까?

@Controller
@RequestMapping("/hier3")
public class Sub3Controller extends Super3Controller {
@Override
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier2 sub~! list");
return "/WEB-INF/views/hello.jsp";
}

}

5. 구현체를 이렇게 바꾸면 에러가 날까? 

6. 그렇치 않다면? ${message}의 값은 어떻게 될까?

@RequestMapping("/hier4super")
public class Super4Controller {
@RequestMapping("/all")
public String list(Model model){
model.addAttribute("message", "hier4 super list");
return "/WEB-INF/views/hello.jsp";
}
}

@Controller
@RequestMapping("/hier4")
public class Sub4Controller extends Super4Controller {
@Override
@RequestMapping("/list")
public String list(Model model){
model.addAttribute("message", "hier4 sub list");
return "/WEB-INF/views/hello.jsp";
}

}

7. /hier4suprer/all 이라는 요청은 처리 될까?

8. /hier4/list 라는 요청은 처리 될까?

정답은 토비님 책 또는 이번주 강의에서..

오늘의 퀴즈 2종 세트를 다 맞추시는 분은 @ReuqestMapping 마스터!

top


[스프링 3.0] 클래스-메서드 레벨 @RequestMapping 퀴즈

Spring/3.0 : 2010.07.13 12:12


이미 2.5부터 추가된 기능이고 가장 자주 사용하고 있는 애노테이션 @ReqeustMapping.. 과연 얼마나 알고 있을까? 

public class Book2Controller {
@RequestMapping("/book2/add")
public String book2Add(Model model){
model.addAttribute("message", "book2 add");
return "/WEB-INF/views/hello.jsp";
}

@RequestMapping("/book2/get")
public String book2Get(Model model){
model.addAttribute("message", "book2 get");
return "/WEB-INF/views/hello.jsp";
}
}

1. 이 클래스를 <bean />을 사용해서 빈으로 등록하면 /book2/add 이나 /book2/get 요청 핸들러가 동작할까?

2. 만약 1번에서 false를 선택했다면 위 코드의 @RM이 동작하게 만드는 방법 두가지는 무엇일까?

@Controller
@RequestMapping("/book3")
public class Book3Controller {
@RequestMapping
public String add(Model model){
model.addAttribute("message", "book3 add");
return "/WEB-INF/views/hello.jsp";
}

@RequestMapping
public String get(Model model){
model.addAttribute("message", "book3 get");
return "/WEB-INF/views/hello.jsp";
}
}

3. 이렇게 매핑 했을 때 /book3/add 와 /book3/get은 동작할까?

4. 만약 3번에서 false를 선택했다면 Book3Controller의 @RequestMapping 설정을 어떻게 고치면 동작하게 할 수 있을까? (역시 두가지)

@Controller
@RequestMapping("/book3/*")
public class Book3Controller {
@RequestMapping
public String add(Model model){
model.addAttribute("message", "book3 add");
return "/WEB-INF/views/hello.jsp";
}

@RequestMapping
public String get(Model model){
model.addAttribute("message", "book3 get");
return "/WEB-INF/views/hello.jsp";
}
}

5. 위와 같이 설정했을 때 /book3/a/b/c/add 는 동작할까 안할까?

6. 만약 5번에서 동작하지 않는다고 대답했다면.. Book3Controller의 @RM 설정을 어떻게 고치면 동작하게 될지 적어보자.

정답은.. 토비님 책 또는 이번주 강의에서...
top


[스프링 퀴즈] @Autowired

Spring/etc : 2010.06.04 17:30


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AutowiredTest {

    @Autowired DataSource dataSource;

    @Test
    public void notNull(){
        assertThat(dataSource, is(notNullValue()));
    }

}

위와 같은 테스트가 있다. 위 코드가 있는 곳과 같은 패키지에 AutowiredTest-context.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.springframework.jdbc.datasource.SimpleDriverDataSource"/>

</beans>

1. 이렇게 설정했을 때 테스트는 어떻게 될까? 깨질까? 성공할까?

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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.xsd">

    <bean class="org.springframework.jdbc.datasource.SimpleDriverDataSource"/>

    <bean class="org.springframework.jdbc.datasource.SimpleDriverDataSource"/>

</beans>

2. 이렇게 설정했다면 어떻게 될까?

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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.xsd">

    <bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"/>

    <bean id="testDataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"/>

</beans>

3. 그럼 이건 어떨까?

셋다 맞춘 분에게 물어보고 싶은 질문이 있는데;; 맞추시는 분께 댓글로 질문 드리겠습니다.
top


Toby's 스프링 3.0 완독



아직 나오지도 않은 책을 벌써 완독했다. 캬.. 이런 특권이 있나...

열심히 봤는데 10일정도 걸린것 같다. 읽는 족족 소화되는 내용들이 아니라서 좀 힘들다. 마지막에 읽은 내용들은 AOP과 LTW, 콘텍스트 테스트 프레임워크, 스프링 기타 기슬과 스프링 학습법 이었다. 마지막 부분인데 지면 관계상인지 이 책의 앞부분에 비하면 다소 함축적이었다.

나도 그동안 감상을 수도 없이 적었으니 이번엔 생략이다.ㅋ

이 책의 앞에서는 열심히 테스트로 내용 검증도 해주고 그랬었는데 2부에 들어서면서부터 방식이 바뀌었다. 그렇다고 해당 내용 검증을 하지 않을 분이 아니다. 저자인 Toby님은 완벽주의자다. 이 책과 같이 제공될 소스 코드에 보면 책에서 언급한 내용을 일일히 테스트 코드로 작성한 것을 볼 수 있다. 

다음 봄싹 스터디에서는 해당 테스트 코드를 가지고 스터디를 해볼까 생각중이다. 또 해당 테스트 코드를 오픈 소스로 계속 다듬어 볼 생각도 있다. 학습 테스트를 오픈소스로 만들어 두면 차후에 스프링을 처음 쓰거나 해당 기능을 처음 쓰는 사람들에게 도움이 될 것 같다.

top


Toby's 스프링 3.0 @MVC를 읽고서



하악하악... 무려 워드로 130p 가까운 걸 읽었더니 머리가 혼란스럽다. 내가 잘모르던 것이 가장 많이 등장한 챕터인것 같다. 챕터 제목이 @MVC 라고 스프링 레퍼런스에 있는 @MVC 부분과 대응해서 생각했다간...
ㅎㅎㅎㅎ.......

이번 장의 겉모습은 @MVC 이지만 난 왠지 스프링 MVC 확장 프레임워크에 대한 힌트를 얻은 것 같다. 솔직히 말해서 대충 쓰려면 몰라도 되는 것들이 제법 있지만 그건 정말 대충 쓸 때의 이야기이고 스프링 강의를 한다던지, 스프링 @MVC 기반 프레임워크를 만들거라든지, 제대로 알고 쓰고 싶다는 경우에는 반드시 알아야 하는 내용들이 가득 들어있다. 지면 관계상 다소 함축적으로 설명한 부분도 없자나 있다. 그런 부분은 API나 레퍼런스를 보면 쉽게 알 수 있을 것이고 저자가 권하듯이 소스 코드를 보면 더 확실하게 알 수 있을 것 같다.

이번 장은 맨 처음부터 끝까지 긴장하면서 봐야했다. 그래서 좀 힘들었다. 아 마지막에 form 태그 설명할떈 조금 쉬엄쉬엄 봤는데 그전까진 정말 뇌가 100m 달리기를 하는듯한 기분이다. 그중에서 틀린 설명이나 잘못된걸 집어내라고 하니 머리가 빠질것 같은 기분이다. 번데기에서 주름 없는 곳을 찾으란 얘기랑 같은 거다.

이 장에서 제일 재밌게 본 부분은 상속 관계에서 @RequestMapping 동작 방식이다. 그밖에도 스프링 3에 추가된 컨버터와 포매터 그리고 모델의 일생, 메시지 컨버터, @MVC 확장 포인트 등을 설명해준다. @MVC를 사용해온지 꽤 됐는데도 모르는 기능이 많았다. 마지막 부분에는 핸들러 메서드의 인자로 받을 수 있는 것을 확장하는 방법도 나오는데 정말 대박이었다.. 뭐 이런 기능들까지 있나 싶을정도로.. 까오..

이제 나머지를 달려야겠다.
top


Toby's 스프링 3.0 스프링 웹 기술과 스프링 MVC를 읽고서...



난 그동안 스프링 MVC를 잘 몰랐다. 다행이다. 이제는 좀 알것같다. 대충 머리속에 그려진다. 지난번 소녀시대 스프링 @MVC를 발표할 때 아주 중요한 스프링 MVC 구성요소 몇가지를 소개했었다. 물론 그것들 보다는 소녀시대 이름만 외우고 가신 분들이 많겠지만;; 이 책에서는 모든 구성요소를 다 설명해주며 그 구성요소군에 속해있는 여러 요소들의 특징과 사용법도 설명해주고 있다. 물론 지면 관계상 뷰 기술중 대부분은 생략이지만 간단한 확장 방법을 통해서 충분히 다른 것들의 사용법도 예상할 수 있었다.

가장 기가막힌 부분은 스프링 Controller(API)와 핸들러 어댑터를 확장해서 새로운 형태의 컨트롤러를 만들어 사용하는 방법이었다. 이 부분은 정말 스프링 MVC 정복기라고 부르고 싶은 부분이다. 물론 호불호가 갈릴만한 부분이기도 하다.

하지만 그런 지식과 실천은 꼭 필요하다. 왜냐면 스프링이 제공하는 웹 계층 구현방법이 너무 다양하기 때문이다. 그것을 정책으로 통일시킬 수 있겠지만 더 좋은건 그 정책을 코드에 반영하는 것이 필요하고 그것을 기반으로 코드를 작성하면 규모가 큰 프로젝트라 하더라도 코드의 일관성을 유지할 수 있을 것이다. 프레임워크 개발이 필요한 이유가 바로 이때문이 아닐까? 프레임워크를 사용하는 이유는 둘 중 하나인것 같다. 프레임워크가 강요하는 정책을 따르기로 결정했거나, 그 프레임워크를 기반으로 프로젝트 고유의 정책과 특성을 반영한 추상계층을 만들어 낼 수 있기 때문인 것 같다. 스프링은 기본적인 방법들은 제공하지만 어떤 방법을 강요하지는 않는다. 따라서 개발자마다 코딩 스타일이 많이 다를 수 있는데 그것을 해결할 수 있는 방법을 소개해준다.

또한 스프링 3.0 최신 기술 중에 컨텐츠 네고 기법을 사용한 뷰 리졸버 설명이 기가 막힌다. 전혀 간단하지 않은 동작 원리를 쉽게 풀어 설명해주어 이해하기 편했다. 이 부분은 레퍼런스나 API에서도 설명이 빈약해서 직접 소스코드를 분석해가며 쓰셨다고 하니 그저 감사할 따름이다.

마지막으로 이 책에서 정의한 프레임워크 개발에 대한 글을 (저자의 허락-'살짝쿵 스포일러를 넣어도 대
'-하에) 옮겨본다. 
"프레임워크 개발이란 이미 있는 기술을 조합해서 어떻게 쓸지 결정하고 툴이나 공통 모듈 정도를 만들어 놓는 것이 아니다. 프레임워크란 애플리케이션의 코드가 효율적인 방법으로 개발되서 사용될 수 있도록 새로운 틀(framework)을 만드는 작업이다."

ps: 이번 장을 보면서 새로운 형태의 OSAF 3.0을 개발해야겠고 다짐했다.
top


Toby's 스프링 3.0 스프링 웹 기술과 스프링 MVC를 읽으며..



아직 다 못봣다. 1/3정도 봤는데 기본적인 스프링 MVC 구성요소와 흐름을 상세하게 설명해주고 있다. 지난 KSUG 발표때 소녀시대 이용해서 설명했던 바로 그 부분이다. 그 뒤에는 스프링 MVC 테스트가 나온다. 캬.. 이 부분이 대박이다.

스프링 웹 계층 테스트는 잘 만들질 않는다. 귀찮다. Mock머시기 클래스의 객체들 만들고 컨트롤러 실행시켜서 ModelAndView 받아서 확인하는 아주 간단한 테스트 조차 너무 귀찮다. 그런데 그런 것 말고 어떤 URL을 스프링이 처리하는지.. 안하는지 보려면 DispatcherServlet까지도 테스트 범위에 들어가야 한다. 그리고 그 결과를 가져와야 하는데 그게 막상 해보면 알겠지만 좀 귀찮은 것이 아니다. 그래서 그냥 스프링이 알아서 테스트 했겠지 생각하고 넘어가기 일수다.

하지만 그러면 안된다. 제대로 확인도 안해보고 막연한 상상에 기대어 살면 안된다. @RequestMapping 같이 생소한 방법을 사용할 땐 더더욱 그렇다. 그래서 테스트가 필요하다. 단, 편하게 할 수 있어야 되는데 바로 그 "편하게 MVC 테스트하는 방법"을 제공해 준다. 여기서 소개한 방법을 응용해서 얼마든지 자신이 확인하고 싶은 것을 확인할 수 있다. 그럼 더이상 막연한 상상에 기대어 코딩하는 일은 없어질 것이다. 좀 더 확신을 가지고 마음 편한 상태에서 코딩할 수 있게 될 것 같다.

물론,, 테스트 안해도 맘편하다면.. 어쩔 수 없다.ㅋ
top


Toby's 스프링 3.0 데이터 액세스 기술을 읽고서



JDBC, iBatis, JPA, Hibernate를 사용하여 DAO를 사용하는 방법과 로컬 트랜잭션, JTA 트랜잭션 사용법까지 나와있다. 특히 이중에서 내가 자주 안써본 JDBC, iBatis, JPA 내용과 JTA 트랜잭션이 꽤 재미있었다.

JDBC 코딩 하시는 분들께는 거의 필수인 JdbcTemplate이나 SImpleJdbcTemplate은 대부분 익숙할 것이다. 하지만 그게 끝이 아니었다. 이것들을 기반으로 객체 지향 API로 쿼리 객체를 만들고 재사용할 수 있는 API도 제공한다. 스프링 빨간책에서 커맨드 패턴과 JdbcTemplate을 이용해서 한단계 더 추상화 시킨 SqlUpdate와 SqlQuery를 볼 수 있었는데 그보다 더 개선된 새로운 API인것 같다. (SimpleJdbcInsert API를 보니 2.5에 추가된 것인가 보다.)

iBatis보다는 JPA 내용이 더 재미있었다. 최근 JPA 2.0이 발표되면서 Hibernate를 JPA 프로파이더로 사용하고 JPA를 사용해볼까 고민하던 중이었기 때문인지도 모르겠다. 하지만 그 뿐이 아니다 JPA를 사용하는 방법에는 어떤 것들이 있고 각각의 장단점 비교가 나온다. 특히 이부분에서 CDI와 관련된 스코프 관련 내용도 나오는데 예전에 토비님 블로그에서 이상한 공격(?)을 받았던 글이 떠올랐다. 그 둘을 연관지어서 읽어보면 더 재미있을 것 같다.

Hibernate는 JPA에 비해 적용방법은 간단해 보였다. 이유는 JPA 처럼 좀 더 쉽게 쓰게 해주려는 마술을 적용하지 않았기 때문일 것이다. 그래서 매번 .getCurrentSession()을 해줘야하지만 머.. 결국은 선택이다.

또 KSUG 포럼에 종종 올라오는 질문 중에 하나인 트랜잭션 관련 내용이 알차다. 데이터소스 하나 트랜잭션 매니저 하나인 아주 간단한 경우부터 JTA까지 다룬다. 톰캣에서 JTA를 사용하는 예제까지 보여주고 있으며 하나의 데이터베이스에 대한 JDBC, 하이버 DAO가 있을떄 어떻게 그리고 어떤 트랜잭션 매니저를 왜 사용하면 되는지 설명해준다. 그밖에 여러 경우를 전부다..

오늘도 별로 꼬투리 잡을께 없다. +_+
top


Toby's 스프링 3.0 10장 IoC 컨테이너와 DI를 읽고서



결론은 다시보고 있다. 다시 보고 싶은 부분이 생겼다. 들어있는 내용이 꽤 많기 때문에 그 중에서 더 자세히 살펴보고 싶은 부분이 분명히 생길것이다. 그래서 결국 나처럼 다시 보게 될 것이다.

이 책을 쓰느라 얼마나 많은 노력과 학습을 하셨는지 이 장에 잘 묻어나온다. 특히 모든 빈 등록 방법 설명, 장단점 비교, 전략 분석. 모든 빈 의존관계 설정 방법 설명, 장단점 비교, 전략 분석은 토비님의 완벽주의 성향을 잘 드러낸다.

스프링 웹 애플리케이션은 거의 대부분 애플리케이션 콘텍스트 상속 구조로 되어있다. 이것을 잘 이해하지 못하면 AOP가 적용되지 않는다느니 트랜잭션이 되지 않느다느니 원인을 찾기도 힘든 문제들을 만들어 낼 수도 있고 그 문제를 해결하지도 못한다. 그것에 대한 아주 명확한 이해와 해결책을 제시하는 내용이 들어있다. 이걸 모르고 스프링 개발을 하고 있다면 무늬만 스프링 개발자라고 하고 싶을 정도로 중요한 내용이기 때문에 반드시 숙지하도록 하자.

프로토타입과 관련된 내용도 주옥같다. 아주 오래전 KSUG 포럼에 프로토타입 스코프 빈을 참조하는 싱글톤 스코프 빈이 있을때 프로토타입 스코프가 제대로 동작하도록하는 여러가지 방법을 논의한적이 있었다. 사실은 Toby님의 일방적인 퀴즈에 불과했지만 그때 메신저를 통해 들었던 정답과 그 이후에 추가된 해결책까지도 설명하고 있다. 레퍼런스나 API 문서를 빠듯하게 뒤져도 안나올만한 방법이 소개되고 있다. 싱글톤 빈만 사용하면 되지 머.. 라고 안일한 생각을 가진 스프링 사용자가 아니라면 꼭 살펴보고 학습해야 한다.

이밖에도 레퍼런스나 API 문서에서 조차 찾기 힘든 내용이 한둘이 아니다. 그런 내용을 이렇게 친절하게 설명해주는.. 그것도 한글로 된 책을 볼 수 있다는 것은 가히 축복이다.

아. 마지막으로 꼬투리 하나만 잡자면 왠지 10장부터는 약간 토비님 블로그를 읽는 느낌이 난다. 그런데 어쩔 수 없다 저 많은 내용을 더 쉬운 말투와 구체적인 설명과 그림으로 다 풀어내려면 200~300p짜리 책한권이 될 것 같다. 내용자체가 쉽지 않기 때문일 수도 있다. 그렇다고 무슨 안드로메다 수학공식도 아니니까 겁먹진 말자.
top


Toby's 스프링 8, 9장을 읽고서



8장은 스프링에 관한 이야기이다. 스프링이란 무엇인가? 스프링의 목적? 그 목적을 달성하는 방법을 설명하며 스프링 삼각형이라 불리는 IoC/DI, AOP, PSA를 자세히 설명해준다. 또한 POJO 프로그래밍에 대해 설명한다. 

그 중에서 기억에 남는 부분은 "복잡함을 해결한다."라는 부분인데 이 이야기는 2008년 Spring One Ameria 컨퍼런스에 참석하여 직접 들은 이야기라 감회가 새로웠다. 여기서는 그 복잡함이란 무엇인지 또 스프링은 그것을 어떻게 제압하겠다는 것인지 잘 설명해주고 있다.

POJO 프로그래밍에 대한 내용역시 유익하다. POJO라는 단어가 난무하는 요즘 POJO에 대해 제대로 집고 넘어갈 필요가 있는데 그런 간지러움을 잘 긁어주고 있다. 특히 평소 JPA를 사용한 도메인 클래스가 POJO인건지 아닌건지 고민이었는데 그런 고민을 해결하는데 실마리를 제공해주는 기준을 제시하고 있다.

이외에 스프링 삼각형에 대해서는 이미 1부에서 찐하게 살펴본 내용을 이론적으로 점검하는 기분이 들었다. 원래는 8장이 제일 처음이었다고 했는데 1부 맨 뒤로 돌리신건 100번 잘하신것 같다. 아무래도 8장부터 읽기 시작했다면 지레겁먹고 책을 덮었을 수도 있겠다.

9장은 본격적인 2부 시작으로 뭔가 코딩을 할 것 같은 기세이다. 프로젝트를 세팅하고 아키텍처를 논한다. 프로젝트는 세팅이야 스프링 책인지라 STS와 Eclipse+Spring IDE 이야기가 주를 이룬다. 가장 흥미롭우며 9장의 주를 이루는 부분은 사실 그 뒤에 있는 아키텍처 이야기다. 이 부분 토비님 세미나에서 다룰 주제이기도 하고 개인적으로 가장 궁금한 부분이기도 했다. 이 부분을 읽으면서 예전에 시도하다 말았던 아키텍처를 다시 한번 시도해보고 싶다는 생각이 들었다.

자.. 이제 드뎌 10장을 달릴 차례다.
top


Toby's 스프링 핵심기술 응용을 읽고서..



먼저 이 장의 제목은 별로다. 이 장에 들어있는 내용을 다 나타내고 있지 못하다. 흠.. 읽고나서 다시 제목을 생각해보니 이번 장은 "스프링 스타일 코딩"이 더 좋을지도 모르겠다.

이번 장에서 살펴볼 수 있었던 스프링의 기능은 내가 미처 몰랐던 기능인 내장 DB 지원 기능을 비롯해서 3.0에 새로 추가된 OXM 그리고 필수로 알아야 하는 리소스 지원 등을 다루고 있다. 이 기능들을 유기적으로 연결하여 SqlService라는 것을 개발하는데 그 과정이 정말이지 놀랍다.

왜 놀라운지는 읽어보는 사람들만이 알수 있겠지만, 이 장을 읽으면서 내내 느낀점은 이 책은 스프링 사용자 뿐 아니라 모든 개발자가 읽어야 한다는 것이다. 이번장에서 SOLID라 일컫는 객체 지향 기본 원칙들을 아무런 거부감 없이 자연스럽게 살펴볼 수 있으며 사용하기 좋은 API 설계 방법이나 점진적인 변화, 진화를 통한 개발 방법을 살펴볼 수 있다. 이런것들은 상당히 딱딱하고 탁상공론 적이며 형이상학적인 주제가 될 수 있는데 반해 이 책에서는 그것들을 항상 눈에 보이는 코드와 테스트를 가지고 증명해 보인다. 또한 이 책의 스타일인 '차분차분'과 '단계적'인 방법을 통해서 살펴보기 때문에 체할 걱정은 없으며 굉장히 실용적이다.

지난주 봄싹 디자인패턴 스터디에서 SOLID 원칙을 학습했지만 탁상공론에 그칠만한 내용밖에 못다뤄 굉장히 아쉬웠다. 물론 이론적인 내용도 필요하다지만 사실 그런 이론은 시험 볼때나 필요한 것이지 코딩에는 별 도움이 안 된다. 코딩에 도움이 되지 않는 이론이 무슨 소용이란 말인가. 순수 이론으로써의 가치를 가지고자 정리된 원칙들이 아닐 것이다. 그러한 원칙들을 고려하여 코딩하는 것이 어떤 것인지... 그로인해 어떤 스타일의 코드가 탄생되는지 보고 싶다면 이 책을 꼭 보시라권하고 싶다.

나 역시 백날 스프링을 쓰면 머하나.. 스프링에 어울릴만한 코드는 한 줄도 작성하지 못하는데.. 라며 자괴감에 빠졌던 적이 있다. 머 그렇다고 지금도 크게 달라진건 없지만 이 챕터가 그러한 자괴감에서 나를 꺼내주는데 한몫 할것 같다. 역시 이 책은 스프링을 발판삼아 더 나은 개발자가 되고 싶은 분들을 위한 책이다. 자 이제 2부를 달려보자.
top


Toby's 스프링 AOP 마무리



지금은 스프링 AOP를 넘어서 스프링 활용 부분을 읽고 있는데 기억에서 떠나기 전에 정리해둬야겠다.

 AOP 챕터가 다소 크다는 느낌을 받았다. 스프링 AOP 기능 뿐 아니라 트랜잭션에 관한 설명도 상당 부분이 나온다. 하지만 스프링 트랜잭션을 이해하려면 스프링 AOP에 대한 이해가 필수적이고 스프링 AOP를 이해하기 위한 좋은 예로 스프링 트랜잭션 만큰 좋은 것도 없다고 생각이 든다. 따라서 스프링 AOP를 설명한 굉장한 분량만큼이나 그 노고를 느낄 수 있었다. 특히 중간 중간 텍스트로 인해 지칠 수 있는 독자들을 위한 그림들은 상당히 단순 명료하면서도 배려적이다.

그 중에서 가장 눈길을 끈 곳은 포인트컷 테스트였다. 정말 대박이었다. 예전에 읽었을 때에도 감명을 받고 사실 그 내용을 바탕으로 포인트컷 표현식 테스트에 대한 블로깅까지 했던적이 있다. AOP를 사용할 때 항상 고민하던 부분이었다. 도대체 이 포인트컷이 어디에 적용이 되는건지 확실하게 알 수 없을까... 이클립스나 인텔리J 도움 없이 조인포인트를 확인할 수 있는 아주 간단한 방법이 있다는 것을 보고는 새삼 놀랐다. 내가 너무 모른다는 사실도 한몫했다.

또한 execution 포인트컷에서 사용하는 표현식을 분석한 표역시 대단하다는 생각밖에 들지 않는다. AOP 용어도 이해하고 대강의 사용법까지 익히더라도 항상 막막한 부분은 바로 표현식 작성이었다. 지금 읽은 부분에서는 아직 args나 this, type같은 건 등장하지 않았지만 아마 2부에서 더 자세하게 다룰 것 같다.

AOP 용어 정의를 거의 맨 뒤에서 설명하는 이럭 획기적인 방식으로도 AOP를 더 쉽고 알차게 학습 할 수 있다는 사실에 여러분도 놀랐것이다.  



top


Toby's 스프링 AOP 등장 배경을 읽으며...



이번 기회에 스프링 AOP를 새로운 방식으로 학습하고 있다. 지금은 일단 스프링 AOP의 용어나 기능보다는 그 근본이 되는 ProxyFactoryBean의 유용함, 등장 배경을 이해하는데 초점을 맞추고 있다. 굉장히 중요한 부분이지만 도무지 레퍼런스에서는 이런 내용을 습득할 수 없었다. 빨간책(몇번째 시리즈인지는 까먹음. with Spring이던가..)에서는 언급이 되어 있지만 왠지 어렵고 잘 머리에 들어오지 않는다.

그 등장 배경을 이해하는 것은 결코 쉽지 않다. 그만큼 간단한 절차가 아니기 때문이다. 조금 길다 싶을 정도로 여러 단계의 정제 과정을 걸치게 된다. 그 과정 하나 하나에 문제, 해결책, 장점, 단점 또 그 단점을 극복하는 해결책 그 결과의 장점과 단점. 다시 또 그 단점을 극복하는 .. 이런식으로 꼬리에 꼬리를 물고 가다가 결국에 스프링 AOP의 핵심 요소들이 등장하게 된다. 

마침내 어드바이스, 포인트컷, 어드바이져, 포인트컷 표현식등 AOP 용어가 들려오더라도 전혀 당황스럽지 않다. 그 용어들의 느낌과 개념이 자연스럽게 이해된다. 어드바이스는 머다. 포인트컷은 머다. 라는 식으로 AOP 무식하게 학습했던 과거가 부끄럽게 느껴진다. 이런 학습방법으로 접근했다면 천천히 가는 것처럼 느껴졌겠지만 오히려 더 확실하게 개념을 익힐 수 있었을텐데 라는 생각이 많이 들었다.

이 부분에서 꼭 집중해서 살펴봐야 할 부분이 있다면 BeanPostProcessor가 언급된 부분이다. 그 부분을 자세히 읽으면 AOP 적용시 흔히 실수하는 상황을 잘 캐치할 수 있을 것이다. 보통 빈 설정을 잘못해서 AOP가 적용되지 않는 경우가 많은데 왜 그런지 그 이유와 원인을 이해할 수 있을 것이다.

이제 본격적으로 스프링 AOP에 대한 내용을 읽어야 하는데 시간이 늦어서 집에가서 봐야겠다..
그 작업을 target으로 집안청소 Advice와 퇴근 후 일정 Pointcut을 조합한 Advisor를 감지하여 프록시를 만들어 주는 DefaultAdvisorAutoProxyCreator를 설정해야겠다.
top


[스프링 3.0] 소녀시대와 함께하는 스프링 @MVC

Spring/3.0 : 2010.04.13 23:49


아우 귀찮아 ㅠ.ㅠ 아우 귀찮아... ㅠ_ㅠ 
괜히 만들었어~~ 괜히 소녀시대로 장난쳤어~~

퀴즈1) 태연은 뭐다??
퀴즈2) HandleAdapter는 뉴~규?

정답은 이번주 토요일 KSUG 세미나에서...
top


[스프링 3.0] @Value 이용해서 기본값 설정하기

Spring/3.0 : 2010.03.16 18:04


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class ValueTest {

    @Autowired Whiteship whiteship;

    @Test
    public void defaultName(){
        assertThat(whiteship.getName(), is("keesun"));
    }

}

@Component
public class Whiteship {

    @Value("keesun")
    private String name;

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

악.. 저녁 약속이 있어서 길게 정리는 못하겠네요. 


이것도 DI용 애노테이션이라는거..


top


[스프링 3.0] @Async 테스트

Spring/3.0 : 2010.03.04 17:55


@Async 애노테이션을 사용한 메서드의 반환 타입은 둘 중 하나여야 합니다. void 거나.. java.util.concurrent.Future 타입이어야 한다네요. 뭐.. 그러니까 사실상 하나나 마찬가지이죠. 그렇다고 해서 다른 리턴타입으로 설정하면 비동기로 동작하지 않는 건 아닙니다. 다만;; 해당 메서드의 클라이언트 입장에서 보면 리턴값이 전부 null 이기 때문에 황당한 경우가 발생할테지만 말이죠.

스프링에서 Future 인터페이스의 구현체로 AsyncResult를 제공해줍니다. 이걸 이용해서 간단하게 Thread를 반환하는 비동기 메서드를 만들었습니다.

    @Async
    public Future<Thread> more() {
        return new AsyncResult<Thread>(Thread.currentThread());
    }

@Async는 @Transaction과 비슷하게 타입에 선언할 수도 있습니다. 그러면 해당 클래스의 모든 메서드가 비동기 메서드로 처리되겠죠.

<task:executor id="myExecutor" pool-size="5"/>

쓰레드 풀 갯수를 5개로 설정해 놓고 다음과 같이 테스트를 해봤습니다.

    @Test
    public void async() throws Exception {
        assertThat(beanService, is(notNullValue()));
        Set<Thread> threads = new HashSet<Thread>();

        for(int i = 0 ; i < 200 ; i++){
            collectThreadInfo(beanService.more(), threads);
        }
        assertThat(threads.size(), is(5));
        assertThat(threads.contains(beanService.more().get()), is(true));
    }

    private void collectThreadInfo(Future<Thread> future, Set<Thread> threads) throws Exception {
        threads.add(future.get());
    }

200번까지 안돌려도 상관없지만. 그냥.. 충분히 돌려서 쓰레드 풀에 있는 모든 쓰레드를 컬렉션에 모아둔 다음에 쓰레드 풀에서 만든 쓰레드 갯수를 확인하고 마지막으로 한 번 더 호출해서 반환 받은 Thread가 현재까지 사용한 쓰레드 중 하나인지 확인합니다.


top

TAG @Async, Spring

[스프링 3.0] FormattingConversionServiceFactoryBean에 들어있는 Converter와 Formatter

Spring/3.0 : 2010.02.28 08:47


3.0에 새로 추가된  <mvc:annotation-driven>을 등록할 때 자동으로 등록되는 FormattingConversionServiceFactoryBean이 있을 때 기본으로 사용할 수 있는 Converter와 Formatter를 살펴보겠습니다.

사실 이 클래스는 프로젝트에서 확장할 가능성이 높은 클래스입니다. 사용하는 도메인에 대한 포매터를 제공할 가능성이 높기 떄문이죠. 그렇지 않고 그냥 쓴다는 건... 글쎄요. 스프링의 바인딩 기능을 제대로 활용하지 않고 있는 프로젝트일 가능성이 높습니다. 3.0 이전이었다면 모든 요청에 걸쳐 전역적으로 바인딩에 활용할 녀석을 PropertyEditorRegistrar(PER)를 구현한 클래스를 만들어  ConfigurableWebBindingInitializer에 빈으로 끼워넣었습니다.

그래서인지 <mvc:annotation-driven> 에도 conversion-service 속성을 제공하여 자신이 확장한 FCSFB를 끼워넣을 수 있게 해줍니다.

그런데.. 그냥 기본으로 사용할 때 대체 어떤 것들이 들어있는지는 API에도 명확히 나와있지 않습니다.
A factory for a FormattingConversionService that installs default formatters for common types such as numbers and datetimes.

Subclasses may override installFormatters(FormatterRegistry) to register custom formatters.

숫자나 날짜 같은 흔히 사용하는 타입에 대한 기본 포매터를 설치해준다. 확장할 땐 머시기 메서드를 써라.

흠.. 뭐가 있는지 알아야 쓰지 말입니다. @_@; 그래서 FCSFB 객체를 콘솔에 출력해봤습니다.

ConversionService converters =
    @org.springframework.format.annotation.DateTimeFormat java.lang.Long -> java.lang.String: org.springframework.format.support.FormattingConversionServiceFactoryBean$NoJodaDateTimeFormatAnnotationFormatterFactory@794e113b, @org.springframework.format.annotation.NumberFormat java.lang.Long -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    @org.springframework.format.annotation.DateTimeFormat java.util.Calendar -> java.lang.String: org.springframework.format.support.FormattingConversionServiceFactoryBean$NoJodaDateTimeFormatAnnotationFormatterFactory@794e113b
    @org.springframework.format.annotation.DateTimeFormat java.util.Date -> java.lang.String: org.springframework.format.support.FormattingConversionServiceFactoryBean$NoJodaDateTimeFormatAnnotationFormatterFactory@794e113b
    @org.springframework.format.annotation.NumberFormat java.lang.Double -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    @org.springframework.format.annotation.NumberFormat java.lang.Float -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    @org.springframework.format.annotation.NumberFormat java.lang.Integer -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    @org.springframework.format.annotation.NumberFormat java.lang.Short -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    @org.springframework.format.annotation.NumberFormat java.math.BigDecimal -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    @org.springframework.format.annotation.NumberFormat java.math.BigInteger -> java.lang.String: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    java.lang.Character -> java.lang.Number : org.springframework.core.convert.support.CharacterToNumberFactory@69236cd5
    java.lang.Number -> java.lang.Character : org.springframework.core.convert.support.NumberToCharacterConverter@552c8fa8
    java.lang.Number -> java.lang.Number : org.springframework.core.convert.support.NumberToNumberConverterFactory@1cee1ede
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.lang.Long: org.springframework.format.support.FormattingConversionServiceFactoryBean$NoJodaDateTimeFormatAnnotationFormatterFactory@794e113b, java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Long: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Calendar: org.springframework.format.support.FormattingConversionServiceFactoryBean$NoJodaDateTimeFormatAnnotationFormatterFactory@794e113b
    java.lang.String -> @org.springframework.format.annotation.DateTimeFormat java.util.Date: org.springframework.format.support.FormattingConversionServiceFactoryBean$NoJodaDateTimeFormatAnnotationFormatterFactory@794e113b
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Double: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Float: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Integer: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.lang.Short: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigDecimal: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    java.lang.String -> @org.springframework.format.annotation.NumberFormat java.math.BigInteger: org.springframework.format.number.NumberFormatAnnotationFormatterFactory@47ad6b4b
    java.lang.String -> java.lang.Boolean : org.springframework.core.convert.support.StringToBooleanConverter@4dbb9a58
    java.lang.String -> java.lang.Character : org.springframework.core.convert.support.StringToCharacterConverter@57922f46
    java.lang.String -> java.lang.Enum : org.springframework.core.convert.support.StringToEnumConverterFactory@67dacccc
    java.lang.String -> java.lang.Number : org.springframework.core.convert.support.StringToNumberConverterFactory@3fe2670b
    java.lang.String -> java.util.Locale : org.springframework.core.convert.support.StringToLocaleConverter@28db23f1
    java.lang.String -> java.util.Properties : org.springframework.core.convert.support.PropertiesToStringConverter@14be49e0
    org.springframework.core.convert.support.ArrayToArrayConverter@53f78b68
    org.springframework.core.convert.support.ArrayToCollectionConverter@9ac5f13
    org.springframework.core.convert.support.ArrayToObjectConverter@744d76b4
    org.springframework.core.convert.support.ArrayToStringConverter@1395dd5b
    org.springframework.core.convert.support.CollectionToArrayConverter@5e6214f5
    org.springframework.core.convert.support.CollectionToCollectionConverter@2eb0a3f5
    org.springframework.core.convert.support.CollectionToObjectConverter@4a5f2db0
    org.springframework.core.convert.support.CollectionToStringConverter@4edc41c5
    org.springframework.core.convert.support.IdToEntityConverter@20e183e9, org.springframework.core.convert.support.ObjectToObjectConverter@359b46dc
    org.springframework.core.convert.support.MapToMapConverter@4b14b82b
    org.springframework.core.convert.support.ObjectToArrayConverter@14004204
    org.springframework.core.convert.support.ObjectToCollectionConverter@65493102
    org.springframework.core.convert.support.ObjectToStringConverter@2830ae41
    org.springframework.core.convert.support.StringToArrayConverter@3e5dc994
    org.springframework.core.convert.support.StringToCollectionConverter@58e41bc3

흠.. 저런 것들이 있군요.
top


Spring Framework 3.0.1 나왔구나

Spring/3.0 : 2010.02.19 11:52


http://blog.springsource.com/2010/02/18/spring-framework-3-0-1-released/

번역하기는 귀찮아서 짧게 요약만 합니다.

- 의존성 포함한 다운로드까지 제공해달라는 사람들이 좀 있어서 메이븐과 Ivy로 가져온 써드파티 라이브러리들까지 압축한 뭉탱이 파일 다운로드도 제공함.

- 3.0.1에서 새롭게 지원하는 라이브러리: 타일즈 2.2, 하이버네이트 3.5(CR1, JPA 2.0 구현체)

- ApplicationListener 감지 기능 향상: 프록시, 팩토리 메서드, Generic 정보를 확ㄹ용한 이벤트 선언 감지 등

- 포인트컷-기반 프록시, EntityManager 프록시, @Transactional 프록시를 직렬화할 수 있게 되었다. 특히 웹 애플리케이션 환경에서 유용할 듯

- FactroryBean과 HttpMessageConverter 같은 Generic 인터페이스의 Class 매개변수 선언을 좀 더 완하했다.

- JdbcTemplate의 쿼리 메서드에 가변인자를 사용했다.

- 보너스: 스프링의 JSP 태그라이브러리에 <spring:eval> 패그를 추가했다. JSP에서 SpEL 표현식을 사용할 수 있으며 스프링 3.0의 포매팅 시스템을 사용하여 포매팅된 결과를 보여줄 수도 있다. 기본적으로 JSTL의 <c:out>과 <fmt:*> 기능을 통합한 것으로 볼 수 있겠다.


top


[스프링 3.0] PropertyEditorRegistry가 이길까 ConversionService가 이길까

Spring/3.0 : 2010.02.18 17:24


public class Whiteship {

    String name;

    public Whiteship(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


public class SpringSprout {

    private Whiteship whiteship;

    private int num;

    public Whiteship getWhiteship() {
        return whiteship;
    }

    public void setWhiteship(Whiteship whiteship) {
        this.whiteship = whiteship;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

이렇게 두 개의 클래스가 있을 때 Whiteship을 SpringSprout로 주입하는 빈 설정을 다음과 같이 했다.

    <bean class="sandbox.convert.SpringSprout">
        <property name="whiteship" value="keesun"/>
        <property name="num" value="1"/>
    </bean>

동작할까 안할까?

당연히 안한다. Whiteship 타입이 필요한데 keesun이라는 문자열을 던져주다니 장난 하는게냐 라고 예외를 던질꺼다.

하지만 되게 만들 수 있다. 이전까지는 PropertyEditorSupport 클래스를 확장하여 아주 손쉽게 구현할 수 있었다.

public class WhiteshipPE extends PropertyEditorSupport {

    @Override
    public String getAsText() {
        return ((Whiteship)getValue()).getName();
    }

    @Override
    public void setAsText(String name) throws IllegalArgumentException {
        setValue(new Whiteship(name));
    }
}

이런 코드는 특히 MVC에서 바인딩 할때 매우 유용하다. 이걸 안쓰면 request에서 일일히 꺼내서 노가다를 해야하는데 난 절대로 그러고 싶지 않다.

그런데 PE의 문제는 쓰레드-세이프하지 않다는 것이다. 그래서 PE를 등록할 때 조심해야 한다. 특히 전역 변수를 가지는 PE를 싱글톤으로 바인더에 등록해버리지 않았나 조심해야 한다. 반드시 그때 그때 new를 사용해서 등록해 주도록 하고 PE 생성자에 필요한 레퍼런스를 전달해주는게 안전하다. 아마 이 내용도 사부님 책에 들어갈 것 같으니 자세한 내용은 그 책을 참고하도록 하자.

PE의 대안이자 좀 더 범용적인 변환기 역할로 3.0에 도입된 것이 ConversionService이고 ConversionServiceFactoryBean에 Converter, ConveterFacrtory, Formatter 등을 등록해놓고 변환을 부탁할 수 있게 되었다.

public class WhiteshipConverter implements Converter<String, Whiteship> {
    public Whiteship convert(String source) {
        return new Whiteship(source);
    }
}

그런데;; PE와 역할이 중복되는데;;; 둘 다 빈으로 등록해둘수 있다.


    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="propertyEditorRegistrars">
            <bean class="sandbox.convert.CustomPERegister">
            </bean>
        </property>
    </bean>

    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <bean class="sandbox.convert.WhiteshipConverter"/>
            </set>
        </property>
    </bean>

결국 문제는 대체 String을 Whiteship으로 바꿔야 할 때 PE를 쓸 것이냐 CS를 쓸 것이냐이다. 무엇을 쓰게 될지... 그게 궁금했었다. 그런데 마침 오늘 사부님이 퀴즈를 냈고;; 난 스케줄을 팽개치고 매달렸다;;

1차 문제 상황

CS와 PE가 등록되어 있을 때 PE가 이겼다.
하지만 난 CS가 이기는 경우를 예제에서 본적이 있었다. 그래서 도무지 납득이 되지 않았고 어떻게 설정해야 가능한지 궁금했다.

CS만 등록했을 땐 CS로 동작했다.
PE만 등록했을 땐 PE로 동작했다.
둘다 등록했을 땐 PE가 동작헀다.

2차 문제 상황

Whiteship만 가지고 테스트를 했었는데 기본 타입도 추가해봤다. int num이 그것이다. 이제 더 큰 문제가 생겼다.
Whiteship만 PE가 이기고 나머지 기본 타입은 CS가 이겼다.
정말 깜놀이었다. 이사실을 발견하지 않았다면 난 그냥 클래스로더를 공부했을 것이다.

어디선가 기본 PE를 덮어쓰거나 없애버린 거 아닌지 궁금했고 소스 코드를 뒤져보기 시작했다.
시간이 손살같이 지난간다.
우울해지기 시작한다.

3차 결론

소스 코드를 뒤적거리다가 ConvertingPropertyEditorAdapter 클래스를 찾았다.
이 클래스는 ConversionService를 사용해서 PE를 노출시켜주는 어댑터다.
겉으로는 PE를 등록한것 같지만 실제로는 ConvesionService를 사용하는 것이다.ㅋ

스프링에서 내부에서 저걸 사용해서 기본 PE들을 등록하도록 바꿨는지는 확인하지 않았다.
왠지 답이 아닌것 같다. OTL..
내가 졌다.

트릭같은 기법을 써서라도 PE를 누르고 아니 속이고 CS가 동작하게 했으니...그만 놓아줄 수 있을 것 같다.
어서 사부님 책이 나와주길....

ps: 위에 나온 코드는 급조한 코드오니 조심할 부분을 건너뛴 곳도 있습니다. 주의하세요.
top


[스프링 3.0] 로깅은 SLF4J를 이용한 Log4J로

Spring/3.0 : 2010.01.28 17:08


스프링을 사용하면 기본적으로 JCL(자카르타 커먼스 로깅)을 사용하게 되는데 JCL이 실제 로거를 선택하는 시점이 런타임이라 클래스로더 문제라던가 런타임시 오버헤드가 생길 수 있는데 이것을 개선한 구현체 SLF4J로 갈아타면 그러너 문제 걱정을 덜 수 있겠습니다. 보너스로 문자열 연결로 발생하는 오버 헤드도 개선할 수 있으며 귀찮은 if문 추가하는 코딩에서 벗어날 수 있는 문법? API?를 제공해줍니다. 그래서인지 스프링도 3.0부터는 본격적으로 spring-core가 의존하는 JCL을 SLF4J로 교체하고 사용하는 예제 및 레퍼런스 설명을 보여주고 있습니다..

갈아타는 방법은 다음과 같습니다.

1. 스프링에서 참조하는 JCL 라이브러리를 빼버리기.
2. JCL-over-SLF4J 추가하기.
3. SLF4J API 추가.
4. SLF4J-log4j 추가.
5. log4j 추가.

이전에 스프링소스 블로그와 레퍼런스에 올라온 방법은 이걸 그대로 메이븐 의존성으로 옮겨적고 있지요.

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                 </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${org.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.mail</groupId>
                    <artifactId>mail</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
            </exclusions>
            <scope>runtime</scope>
        </dependency>

1번은 단순히 JCL 라이브러리에 의존할 생각이 없기 때문에 빼버리는 것이고, 그때 JCL에 의존하는 클래스들이 깨질 텐데 그것을 JCL-over-SLF4J를 이용해서 겉은 JCL 같지만 내부에서는 SLF4J API를 호출하도록 일종의 어댑터 나 다리 역할을 해주는 라이브러리를 추가하고, 인터페이스 격인 3. SLF4J API를 추가한 뒤 실제 사용할 로거로 SL4J를 구현한 4. SLF4J-Log4J 라이브러리를 추가합니다. 마지막으로 최종적으로 사용할 로거 5. Log4J를 추가한 것입니다. 이때 Log4J가 불필요하게 참조하는 jmx, mail, jms 등의 라이브러리를 제외시켜줍니다.

만약에 Log4J가 아니라 다른 로거를 사용할 거라면 4번과 5번을 교체하면 될테고 JCL 뿐 아니라 log4j에 대한 직접 호출도 slf4j로 오게 하려면 2번 대신 log4j-over-slf4j을 추가하면 되겠습니다.

굉장히 장황니다. @_@;; 하지만 뭐.. 좋다는데.. 갈아타긴 해야겠죠.

출처: SLF4J in 10 slides, by Ceki Gülcü

위와 같은 설정은 스프링 3.0 예제와 레퍼런스에서 사용하고 있습니다. 정말 장황합니다. 일단 여기서 1, 2번은 필수다 하지만 레퍼런스와 블로그 글에도 나와있듯이 그 이하 3, 4, 5는 Logback이라는 SLF4J API 구현체로 한방 설정이 가능합니다.

logback은 크게 세 가지 모듈로 나뉘는데 그 중에 SLF4J API 구현체인 classic 모듈이 있고, Log4J를 개선한 core 모듈이 있습니다. logback 모듈을 가져오면 위에서처럼 log4j가 끌고오는 부가적인 라이브러리도 없고 깔끔하게 log4j API를 사용할 수 있습니다.

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework.version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                 </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>com.springsource.ch.qos.logback.classic</artifactId>
            <version>0.9.9</version>
        </dependency>

따라서 굳이 Log4J가 아닌 다른 로거로 갈아탈 계획이 없다면 이렇게만 설정해도 되겠습니다. 로깅 설정만 거의 1/3로 줄어듭니다.

사용법은

1. SLF4J API를 이용해서 로거를 만들고..

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Logger log = LoggerFactory.getLogger(AccountJsonBindingTests.class);

2. 다음과 같이 로깅하면 됩니다.

log.info("name is {}", name);

+를 사용해서 문자열 연산을 할 필요도 없고, if문을 줘서 문자열 연산을 막을 필요도 없습니다.

3. log4j 설정은 프로퍼티 파일이나 XML로 할 수 있는데 스프링 3.0 예제는 보통 XML을 사용하더군요. src와 test 소스 폴더 하위의 클래스패스 루트에 각각 다음과 같은 log4j.xml 파일을 둡니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

    <!-- Appenders -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p: %c - %m%n" />
        </layout>
    </appender>

    <!-- 3rdparty Loggers -->
    <logger name="org.springframework.core">
        <level value="info" />
    </logger>

    <logger name="org.springframework.beans">
        <level value="info" />
    </logger>
   
    <logger name="org.springframework.context">
        <level value="info" />
    </logger>

    <logger name="org.springframework.web">
        <level value="info" />
    </logger>

    <!-- Root Logger -->
    <root>
        <priority value="warn" />
        <appender-ref ref="console" />
    </root>
   
</log4j:configuration>

src와 test 간의 차이는 마지막 부분의 <root> 안의 <priority>가 src에서는 warn이고 test에서는 info라는 것 뿐이 없습니다. 이 설정에서는 Log4J를 사용하고 있는데.. logback API를 이용해서 설정해도 당근 잘 동작합니다.

top


[스프링 3.0] JSR-330 Provider 인터페이스 이용해서 싱글톤이 아닌 빈을 싱글톤 빈에 주입하기

Spring/3.0 : 2010.01.12 22:26


결국 어디선가는 룩업을 해야합니다. 그냥 주입해서 될 일이 아닙니다. getter injection을 사용할 수도 있지만 AOP, Proxy 등 다소 장황해집니다. 룩업은 하되 가장 표준적이면서, 편리하고, 써드파티 라이브러리 의존성을 낮추는 방법을 얼마전 사부님 블로그를 통해 알게 되었습니다. 그 방법은 바로 구글 쥬스에 있던 걸 표준화 한 JSR-330의 Provider.

먼저 빈 설정입니다. 빈 두개. White는 싱글톤, Ship은 프로토타입 스코프로 설정합니다.

@Configuration
public class ProviderTestAppConfig {

    @Bean White getWhite(){
        return new White();
    }

    @Bean @Scope("prototype") Ship getShip(){
        return new Ship();
    }
}

다음은 위에 등록한 실제 빈 클래스들..

public class Ship {
}

public class White {

    @Autowired
    private Provider<Ship> shipProvider;

    public void hi(){
        System.out.println(shipProvider.get());
    }

}

바로 이 부분이 가장 눈여겨 봐야 할 코드입니다. @Autowired 대신에 @Inject를 써도 됩니다. 그게 중요한게 아니라 스프링이 Provider 인터페이스 구현체를 자동으로 만들어 주입해준다는 것이 중요합니다. 어떤 클래스가 어떻게 해주는지는 귀찮아서 찾아보지 않았습니다. 사부님 책에는 자세한 설명이 나올지도?! +_+

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationContextLoader.class)
public class ProviderTest {

    @Autowired White white;

    @Test
    public void get(){
        white.hi();
        white.hi();
        white.hi();
        white.hi();
    }

}


이건 뭐 기본적으로 자바5는 사용한다는 전제하에 제공되는 것이기 때문에 자바5 도입이 불가능하거나 거부감이 있으신 분들은 스프링의 ObjectFactroy나 ServiceLocatorFactoryBean를 검토해보시는게 좋겠습니다.

ps1: 빈 설정에서 @Scope("prototype")을 때어내도 같은 결과가 나오지 않을까 궁금하다는 생각을 하신 분이 계신가요?? 정답은... 안갈챠드려요. 직접 해보세요. 캬캬캬캬.

ps2: AnnotationContextLoader 이 클래스는 저랑 성윤이가 만든거고 스프링에 없습니다. http://jira.springframework.org/browse/SPR-6567 여기서 소스 및 테스트를 받으실 수 있답니다. vote 좀;;

ps3: 근데 이런 글에 누가 관심이나 있나요.. 싱글톤만 쓰는데;; @_@; 싱글톤이 아닌 빈을 어떻게 왜 써먹는걸까요..? 논의는 봄싹 그룹스에서. 음하핫
top


스프링 3.0의 설정 간편화

Spring/etc : 2009.12.23 11:15


원문: http://blog.springsource.com/2009/12/22/configuration-simplifications-in-spring-3-0/

케이스가 시작한 "스프링 3 간편화" 시리즈 두번째로 스프링의 새로운 @Configuration 애노테이션 및 그와 관련된 지원 기능을 매우 간략하게 소개하고자 한다.

스프링 JavaConfig 프로젝트에 관심을 가지고 있던 사람들이라면 @Configuration 애노테이션 클래스가 스프링 XML 파일과 매우 비슷한 역할을 한다는 것을 알 것이다. 메서드와 애노테이션을 사용하여 스프링 빈을 정의를 선언하는 코드-중심적인 방법을 제공한다. 이러한 것을 POC(Plain Old Configruation)이라고 부를 수도 있겠다. :) XML이 하나도 필요없는 간단한 상활을 일컷는 말이다.

시작해보자. @Configuration 기능을 설명하려고 새로운 스프링 샘플 SVN 저장소에 매우 간단한 프로젝트를 만들어두었다. 그것을 받아서 바로 빌드하고 싶을것이다. 서브버전 클라이언트와 최신 버전 메이븐이 필요하다.

svn co https://src.springframework.org/svn/spring-samples/configuration-basic/trunk configuration-basic
cd configuration-basic
mvn clean test
[...]
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ————————————————————————
[INFO] BUILD SUCCESSFUL
[INFO] ————————————————————————

이클립스 .classpath와 .project 메타데이터도 체크아웃에 포함되어 있기 때문에 프로젝트를 SpringSprout Tool Suite 또는 m2eclipse 플러그인을 설치한 최신버전 이클립스에서 import 할 수 있을 것이다. 두 경우 모두 File -> Import -> Existing Projects into Workspace를 하면 된다.

AppConfig.java 클래스부터 살펴보자. @Configuration 애노테이션을 추가한 클래스는 XML 설정 기반 스프링 애플리케이션의 app-config.xml과 같은 역할을 한다. 실행시점에 어떻게 객체 인스턴스들이 연관되어 있으며 관리되는지에 대한 "청사진"을 제공하기 때문에 프로젝트를 살펴보기 좋은 시작점이 된다.

@Configuration
public class AppConfig {

    public @Bean TransferService transferService() {
        return new TransferServiceImpl(accountRepository());
    }

    public @Bean AccountRepository accountRepository() {
        return new InMemoryAccountRepository();
    }

}

Granted, 이 예제는 진짜 JDBC Datasource를 사용하지도 않는 매우 간단한 애플리케이션이다. 하지만 이 글의 목적은 기본 개념을 제공하는 것이기 때문에 괜찮다. @Bean을 선언한 메서드는 실행 시점에 스프링 컨테이너에의해 호출되며 반환되는 객체를 스프링 컨테이너가 다른 빈들처럼 관리한다. 다른 빈과의 의존성은 간단하게 다른 빈 메서드를 호출하는 것으로 표현할 수 있다. TransferServiceImpl은 AccountRepository를 생성자 인자로 필요로 하기 때문에 간단하게 accountRepository() 메서드를 호출했다.

경험많은 스프링 사용자라면 이 예제를 보고 "빈 스코프는 어떻게 되는가?"라고 질문할 것이다. 좋은 질문이다. 아시다시피 모든 스프링 빈은 스코프를 가지고 있다. 기본으로 빈의 스코프는 '싱글톤'이며 이것은 스프링 컨테이너 당 하나의 빈 인스턴스만 사용된다는 것을 의미한다. 위의 코드를 보면 accountRepository()를 여러번 호출할 때마다 여러 인스턴스를 만들것 처럼 보이지만 실제로 그렇지는 않다! @Configuration 클래스가 실행 시점에 처리되면 동적으로 (CGLIB을 사용하여) 그 하위 클래스를 만들고, 그 하위 클래스의 @Bean 메서드들은 스코프 개념을 보장하도록 코드를 조작한다.

보시다시피, @Bean 메서드를 정의하는 것은 매우 간단하다. 이제 컨테이너를 동작시키고 저 객체들을 사용해보자.

TransferServiceTest JUnit 시스템 테스트와 transfer100Dollars() @Test 메서드를 보자. 가장 먼저 눈에띄는 것은 AnnotationConfigApplicationContext 사용일 것이다. 이것은 새로운 ApplicationContext 구현체로 @Configuration 클래스를 직접 사용하여 스프링 컨테이너 생성을 지원하도록 스프링 3에 추가되었다. 이 컨텍스트는 AppConfig.class를 생성자 매개변수로 사용하며, 그런다음 TransferService와 AccountRepository 타입의 빈을 getBaen(Class) 메서드를 이용하여 가져온다. 메서드의 남은 부분은 일반적인 JUnit 검증 과정으로 TransferService와 AccountRepository API를 확인하고 예상한대로 잘 동작하는지 확인한다.

public class TransferServiceTest {

    @Test
    public void transfer100Dollars() {
        // create the spring container using the AppConfig @Configuration class
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);

        // retrieve the beans we'll use during testing
        AccountRepository accountRepository = ctx.getBean(AccountRepository.class);
        TransferService transferService = ctx.getBean(TransferService.class);

        // create accounts to test against
        accountRepository.add(new Account("A123", 1000.00));
        accountRepository.add(new Account("C456", 0.00));

        // check account balances before transfer
        assertThat(accountRepository.findById("A123").getBalance(), equalTo(1000.00));
        assertThat(accountRepository.findById("C456").getBalance(), equalTo(0.00));

        // perform transfer
        transferService.transfer(100.00, "A123", "C456");

        // check account balances after transfer
        assertThat(accountRepository.findById("A123").getBalance(), equalTo(900.00));
        assertThat(accountRepository.findById("C456").getBalance(), equalTo(100.00));
    }

}

바로 이거다! 간단하고, 순수한 자바이고(사실 저는 개인적으로 이부분에 대해서는 약간의 이견을 가지고 있지만;;), 타입-안정적인 설정이다. 이것이 스프링의 핵심 의존성 주입 기능의 편리하고 강력한 추가기능임을 알았으면 좋겠다.

물론, 오늘 여기서는 매우 간략하게 살펴봤다. @Configuration 클래스로 더 많은 것을 할 수 있으며 자세한 기능은 다음 글에서 설명하도록 하겠다. 하지만 날 기다리지말고 스스로 스프링 3 레퍼런스 문서의 @Configuration 부분을 읽어보길 바란다. 이 예제 프로젝트를 시작점으로 빠르게 새로운 기능의 나머지 부분을 확읺보기 바란다.

피드백을 기대한다. @Configuration과 모든 새로운 스프링 3 기능을 즐기기 바란다. 즐거운 연휴 되시길!

* Thanks to Erich Eichinger for (half-jokingly) coining the phrase 'Plain Old Configuration'. You can take a look at the work he and the Spring.NET team are doing with their similar 'CodeConfig' project here.

ps: 이번글에도 링크가 굉장히 많은데;; 귀찮아서 생략했습니다. 언젠가 안 귀찮을 때 수정하겠습니다.
top


스프링 3.0의 MVC 간편화

Spring/3.0 : 2009.12.22 11:42


원문: http://blog.springsource.com/2009/12/21/mvc-simplifications-in-spring-3-0/
봄싹위키: http://springsprout.org/wiki/1556.do

유겐아렌이 언급했듯이, 모든 자바 개발자는 부담없이 스프링 3.0으로 버전을 올릴 수 있다. 이제 스프링 3이 나왔고 나는 여러분이 아직 모르고 있을 법한 MVC 기능을 소개하고자 한다. 이 기능들이 여러분에게 유용하고 웹 애플리케이션 개발을 빠르게 시작하는데 도움이 되기를 바란다.

또한 이 글은 "스프링 3 간편화" 시리즈의 시작으로 앞으로 몇일 또는 몇주 동안 이와 비슷한 글들이 올라올 것으로 예상한다.

설정 간편화

스프링 3은 mvc 네임스페이스를 도입하여 스프링 MVC 설정을 대폭 간편화했다. 지금까지 다른 개선사항들은 스프링 MVC 애플리케이션을 구성하고 실행하는것을 간편화 시켜주지는 않았다. mvc-basic 예제를 통해 살펴보도록 하자.

mvc-basic 예제는 스프링 MVC 기능의 기본 구성을 보여주도록 만들었다. 프로젝트는 spring-samples SVN 저장소에서 얻을 수 있으며 메이븐으로 빌드하고 이클립스로 import할 수 있다. web.xml 부터 시작하여 거기에 있는 설정부터 살펴보자. 주목할 것은 DispatcherServlet이 단일 마스터 스프링 설정 파일로 설정되어 있고 그 안에서 모든 애플리케이션 구성요소를 초기화한다. URL Rewrite를 설정하여 모든 요청을 깔끔하고 REST-스러운 URL로 DispatcherServlet에 보낸다.

마스터 설정 app-config.xml에서 일반적인 구성을 살펴볼 수 있다. 컴포넌트 스캔으로 클래스패스에서 애플리케이션 구성요소를 찾는다. MessageSource를 설정하여 지역화된 리소스 번들을 설정한다. 마지막으로 애플리케이션의 스프링 MVC 설정을 import한다.

mvc-config.xml 안에서 스프링 3의 첫번째 새 기능을 볼 수 있다.

<!-- Configures the @Controller programming model -->
<mvc:annotation-driven />

이 태그는 요청을 @Controller로 디스패치할 때 필요한 HadlerMapping과 HandlerAdapter를 등록한다. 또한 클래스패스에 무엇이 있는지에 따른 감각적인 기본값(sensible defaults)을 제공한다. 그러한 기본값은 다음과 같다.

- 자바빈 PropertyEditor 보다 더 견고한 대체제인 스프링 3 타입 ConversionService 사용하기
- @NumberFormat으로 숫자 필드 포매팅 지원
- @DateTimeFormat을 사용하여 Date, Calendar 포매팅 지원. Joda Time이 클래스패스에 있다면 Joda Time도 포매팅 지원.
- JSR-303 공급자가 클래스패스에 있다면 @Controller 입력값을 @Valid를 사용하여 검증
- JAXB가 클래스패스에 있다면 XML 읽기/쓰기 지원
- Jackson이 클래스패스에 있다면 JSON 읽기/쓰기 지원

꽤 멋지지 않은가? 훗

계속해서 mvc-config.xml 다음 줄에서 또다른 새로운 기능을 살펴보자.

<!-- Forwards requests to the "/" resource to the "welcome" view -->
<mvc:view-controller path="/" view-name="welcome" />

mvc:view-controller는 랜더링할 뷰를 선택하는 ParameterizableViewController를 등록한다. "/"를 요청하면 welcome 뷰를 랜더링하도록 설정했다. 실제 뷰 템플릿은 /WEB-INF/views 디렉토리의 .jsp가 랜더링 된다.

계속해서 mvc-config에서 살펴보지 않은 새로운 기능을 보도록 하자.

<!-- Configures Handler Interceptors -->
<mvc:interceptors>
    <!-- Changes the locale when a 'locale' request parameter is sent; e.g. /?locale=de -->
    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
</mvc:interceptors>

mvc:interceptors 태그는 모든 컨트롤러에 적용할 HandlerInterceptor를 등록하게 해준다. 이전에는 반복적으로 모든 HandlerMapping 빈에 interceptor들을 등록했었다. 또한 이 태그를 사용하여 인터셉터를 적용할 URL 경로를 제한할 수 있다.

자 지금까지 살펴본 것을 종합하여, 예제를 배포하면 다음과 같은 welcome 뷰가 랜더링 될 것이다.


다른 지역 링크를 클릭하여 LocaleChangeInterceptor가 사용자 지역을 교체하도록 해보자.

데이터 바인딩 간편화

다음으로 설명할 새 기능은 @Controller 바인딩과 검증이다. 몇주 전에 글을 올렸다시피 이 부분에 새로운 기능이 많이 있다.

예제에서, @Controller Example 링크를 클릭하면 다음과 같은 폼이 랜더링 된다.


이 폼은 지역 정보 변경에 따라 국체화 필드 포매팅이 적용된다. 예를 들어, en에서 de로 바꾸면 날짜 형식을 12/21/10에서 12.12.10으로 바꾼다. 이런 동작과 폼 검증 규칙은 모델 애노테이션을 기반으로 한다.

public class Account {

    @NotNull
    @Size(min=1, max=25)
    private String name;

    @NotNull
    @NumberFormat(style=Style.CURRENCY)
    private BigDecimal balance = new BigDecimal("1000");

    @NotNull
    @NumberFormat(style=Style.PERCENT)
    private BigDecimal equityAllocation = new BigDecimal(".60");

    @DateTimeFormat(style="S-")
    @Future
    private Date renewalDate = new Date(new Date().getTime() + 31536000000L);

}

폼 서브밋은 다음의 AccountController 메서드가 처리한다.

@RequestMapping(method=RequestMethod.POST)
public String create(@Valid Account account, BindingResult result) {
    if (result.hasErrors()) {
        return "account/createForm";
    }
    this.accounts.put(account.assignId(), account);
    return "redirect:/account/" + account.getId();
}

이 메소드는 바인딩과 검증 작업 이후에 호출되며, Account 검증은 @Valid 애노테이션에 의해 수행된다. 만약 어떠한 검증 에러라도 존재한다면 createForm을 다시 랜더링 한다. 그렇지 않을 경우 Account는 저장되고 사용자는 http://localhost:8080/mvc-basic/account/1로 리다이렉트 된다.

또 다른 멋진 기능을 사용해보려면 /account/99 같이 존재하지 않는 계정을 요청해보기 바란다.

요약

스프링 3은 다양한 새 기능과 여러 기존 분야에 대한 간편화를 제공한다. 이 글을 통해 여러분에게 유용한 새로운 스프링 MVC 개선 사항들을 파악했기를 바란다. 맨 처음에 언급했듯이 더 많은 "스프링 3 간편화" 시리즈를 통해 계속해서 스프링 최신 버전의 새롭고 흥미로운 기능들을 소개할테니 기대하기 바란다.

즐거운 연휴되시길!

top


스프링 프레임워크 3.0이 GA 됐다.

Spring/3.0 : 2009.12.17 11:12


원문: http://www.springsource.org/node/2266
봄싹 위키: http://springsprout.org/wiki/1495.do

긴 여정을 거쳐, 드디어 스프링 3.0 GA (배포)를 이용할 수 있게 됐다는 것을 발표하게 되어 기쁘다! 스프링소스는 지금 축제 중이다. 여러분도 같이 파티에 참여하기 바란다. :)

가장 최근 소식으로, 스프링 3.0 GA는 이제 (지난주에 배표된 GlassFish v3과 같은) 자바 EE 6 파이널 런타임 환경과 호환가능하며 이미 (EcliseLink 2.0 같은) JPA 2.0 파이널을 지원하고 있다. 또한 (JSR-250 v1.1에) 새로 추가된 @ManagedBean 컴포넌트 스캐닝을 지원하며 애노테이션-주도 의존성 주입에 활용할 (JSR-330) @Inject와도 잘 동작한다.

여러분의 편의를 위해 스프링 3.0 전체 주요 기능을 요약했다.

* 스프링 EL(SpEL): 빈 정의에서 사용할 코어 표현식 파서로, 내부 빈 구조와 환경적인 데이터 구조 속성 값에  #{...} 문법을 사용하여 접근할 수 있다.

* 애노테이션-기반 컴포넌트 지원 확장: (스프링 JavaConfig로 알려져 있는)설정 클래스 개념과 애노테이션 팩토리 메서드를 도입했다. 스프링은 또한 환경 값을 @Value 표현식으로도 주입할 수 있게 해주며, 동적인 #{...} 표현식과 정적인 ${...} 대체공간(placeholder)을 이용하여 설정 구성(configruation settings)을 참조할 수 있게 해준다.

* 강력한 스테레오타입 모델: 메타 애노테이션을 사용한 '숏컷' 애노테이션 생성을 지원한다. 예를 들어 기본 스코프와 기본 트랜잭션 특성을 가지고 있는 커스텀 스테레오타입을 만들 수 있다. @Service, @Scope("request"), @Transactional(readOnly=true)를 모두 가지고 있는 @MyService 애노테이션 한개를 만들어 사용할 수 있다.

* 표준화된 의존성 주입 애노테이션: 스프링 3.0은 자바 애노테이션-기반 의존성 주입에 관한 JSR-330 스펙을 완전히 지원한다. @Inject와 관련된 qualifier와 providier 모델을 스프링 내부의 @Autowired에 대한 대체제로 지원한다.

* 제약 애노테이션 기반의 선언적인 모델 검증: JSR-303 빈 검증기(validation provider)에 대한 스프링-스타일 구성을 했다. 애노테이션을 이용한 검증 옵션을 스프링 MVC에서 이용할 수 있다. 스프링의 바인딩 결과 기능을 통해 제약사항 위반에 대한 일관적인 뷰를 제공한다.

* 강화된 바인딩과 애노테이션-기반 포매팅: 표준 PropertyEditor에 대한 대체제로 Converter와 Formatter SPI를 제공한다. 포맷팅은 JSR-303 제약과 비스한 스타일로 애노테이션을 이용할 수 있다. 예를 들어 @DateTimeFormat을 사용할 수 있다. 또한 새로운 mvc 네임스페이스를 사용하여 스프링 MVC에 포맷팅과 검증기를 편하게 설정할 수 있으니 확인해 보기 바란다.

* 방대한 REST 지원: REST스타일 요청 매핑, @PathVariable 매개변수를 사용한 URI 변수 추출, 컨텐츠 교섭(content negotiation)을 통한 뷰 판단과 같은 고유의 REST를 스프링 MVC에서 사용할 수 있다. 클라이언트쪽 REST 지원은 RestTemplate 클래스를 이용할 수 있다.

* 풍부한 고유 포틀릿 2.0 지원: 스프링 MVC는 포틀릿 2.0 환경과 포틀릿 2.0의 새로운 이벤트와 리소스 요청 모델을 지원한다. 일반적인 포틀릿 요청 특징에 따른 구체화된 매핑 기능을 제공한다. @ActionMapping, @RenderMapping, @ResourceMapping, @EventMapping.

* 객체/XML 매핑(OXM): 스프링 웹 서비스에 있었지만, 지금은 스프링 프레임워크 코어로 이동했다. JAXB2, Castor 등을 지원하는 마샬링 언마샬링 추상화를 제공한다. 스프링 MVC와 스프링 JMS에 XML 페이로드(payload) 연동 옵션을 제공한다.

* 차세대 스케줄링 기능: cron을 지원하는 TaskScheduler와 Trigger 매터니즘을 새로 추가했다. 스프링 3.0은 편리한 task 네임스페이스와 @Async, @Scheduled 애노테이션을 제공한다. 고유의 쓰레드 풀 또는 서버가 관리하는 쓰레드 풀에서 실행될 수 있다.

이러한 큰 테마들 이외에, 스프링 2.5에 업그레이드 할 때 특히 좋아할 수백개의 구체적인 개선 사항사항이 있으니 변경사항과 자바독을 확인하기 바란다.

시스템 요구사항에 따르면, 스프링 3.0은 다양한 환경을 다룬다. 두 가지 주요 특징으로, 스프링 3.0은 자바 SE 5 이상서블릿 2.4 이상을 지원한다. 예를 들어 톰캣 5.x와 6.x를 지원한다. 또한 웹스피어 6.1과 웹로직 9.2와 같은 유명한 엔터프라이스 서버와도 호환가능하다. GlassFish v3은 이미 지원하고 있다.

마무리 하자면, 스프링 3는 여러분의 서버를 업그레이할 필요 없이 완전히 새로운 컴포넌트 모델 기능과 JSR-330 주입과 JSR-303 검증같은 표준을 제품 환경에 가져다 준다! 여러분이 해야 할 일은 스프링을 사용중인 애플리케이션의 라이브러리를 스프링 3.0으로 바꾸는 것 뿐이다.

줄겨라 그리고 다음에 이어질 구체적은 스프링 3 기능에 대한 글과 스프링 3.0에서 돌아가는 예제들을 기대하기 바란다!

ps: 반가운 마음에 급하게 번역하느라.. 좀 날림입니다. 이해해 주세요;
top


[스프링 3.0] 스프링 bean과 일반 자바 객체가 호출하는 @Bean 메서드의 차이

Spring/3.0 : 2009.12.11 20:51


public class JavaConfigTest {

    AnnotationConfigApplicationContext ac;

    @Before
    public void setUp(){
        ac = new AnnotationConfigApplicationContext(AppConfig.class);
        assertThat(ac, is(notNullValue()));
    }

    @Test
    public void getBean(){
        SampleBean bean1 = ac.getBean("sampleBean", SampleBean.class);
        SampleBean bean2 = ac.getBean("sampleBean", SampleBean.class);
        assertThat(bean1, is(notNullValue()));
        assertThat(bean2, is(notNullValue()));
        assertThat(bean1, is(bean2));

        AppConfig config1 = new AppConfig();
        SampleBean bean3 = config1.sampleBean();
        assertThat(bean3, is(not(bean2)));

        AppConfig config2 = ac.getBean("appConfig", AppConfig.class);
        SampleBean bean4 = config2.sampleBean();
        assertThat(bean4, is(bean2));
    }

}

스프링 3.0 애노테이션 기반 설정을 익히기 위해서 처음 만들어본 테스트입니다. 이 테스트에서 알 수 있는 건 바로 이 글의 제목에서처럼 @Bean이 붙어있는 메서드를 어떤 객체를 이용해서 호출하느냐에 따라 그 결과가 다를 수 있다는 겁니다.

@Configuration
public class AppConfig {

    @Bean
    public SampleBean sampleBean(){
        return new SampleBean();
    }
}

이건 설정한 빈이고 SampleBean은 뭐 암거나;; @_@;

결론은 new 로 만든 AppConfig와 스프링에서 가져온 AppConfig의 @Bean이 붙은 메서드가 반환해주는 값이다르다는 겁니다. 전자는 일반적인 Java 문맥대로 sampleBean()에서 new SampleBean()으로 새로운 객체를 만들어서 받은 것이고, 후자는 sampleBean() 메서드 호출을 가로채서 기존의 bean을 반환해준 겁니다.

이 경우는 매우 간단한 경우에 속합니다. 하나는 스프링이 관리하는 빈이었고, 하나는 일반 자바 객체였으니까요. 그런데 만약에 둘 다 스프링의 빈이라면? 그 중에 하나는 @Configuration, 다른 하나는 @Component라면?

@Component로 설정한 빈 내부에 위와 똑같은 설정이 들어있다면? 반환하는 객체의 값이 조금 다르나면??

빈 스캔은 어찌하나?

정말 복잡하군요;; 하나씩 차근 차근 해보겠습니다.



top


[스프링 3.0] @Async 사용하기

Spring/3.0 : 2009.12.10 13:59


참조: http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/ch25s05.html#scheduling-annotation-support-async

봄싹 사이트에서 스터디 개설/변경, 모임 개설/변경 시에 알림 서비스로 이메일, 구글 메신저, 트위터로 알림 메시지를 전송합니다. 그런데, 사용자가 많아지다보니 해당 작업을 완료하는데 걸리는 시간이 너무 길어졌습니다. 불편해졌죠. 사실 관리자 권한이 있는 사람들만 만드는거라, 스터디 참여자 입장에서는 그런 불편을 알 수가 없을테지만, 저희는 내부적으로 좀 불편해 하고 있었습니다.

그러던 중 보게 된 것이 바로 @Async.. 딱 원하던 기능입니다.

필요한 빈 설정은

    <!-- ============================================================= -->
    <!--  Spring 3.0 @Task @Async                                      -->
    <!-- ============================================================= -->
    <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>

    <task:executor id="myExecutor" />

    <task:scheduler id="myScheduler" />

적용하려면, 원래 작업을 별도의 클래스로 빼내고 그것을 참조하도록 수정하는 것이 좋겠습니다. 안그럼 동작하질 않더라구요,

@Service
public class BeanService {

    public void normal(){
        System.out.println("===========================");
        System.out.println(Thread.currentThread());
        System.out.println("===========================");
        System.out.println("do it");
        this.more();
    }

    @Async
    public void more() {
        System.out.println("===========================");
        System.out.println(Thread.currentThread());
        System.out.println("===========================");
        System.out.println("more");
    }

}

예를 들어, 위와 같은 경우 normal() 내부에서 more()를 호출할 경우에는 @Async가 적용되지 않습니다.
more()를 직접 호출할 떄는 적용됩니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class AsyncAnnoTest {

    @Autowired BeanService beanService;

    @Test
    public void async(){
        assertThat(beanService, is(notNullValue()));
        beanService.normal();
        beanService.more();
    }

}

대충 만든 테스트로 돌려보면 다음과 같은 결과를 볼 수 있습니다.





top

TAG @Async

스프링의 로깅 의존성

Spring/etc : 2009.12.08 17:56


원문: http://blog.springsource.com/2009/12/04/logging-dependencies-in-spring/

봄싹 위키: http://springsprout.org/wiki/1439.do

이 글은 스프링이 결정한 선택 사항들과 개발자들이 스프링을 사용하여 애플리케이션을 만들 때 로깅으로 이용할 수 있는 옵션을 다룬다. 무언가를 많이 변경했기 때문이 아니라, 여러분이 애플리케이션에서 로깅을 구현하고 설정할 때 정보에 근거할 수 있도록 이제는 출시가 임박한 스프링 3.0을 따라야 할 시점이다. 먼저 스프링에 어떤 필수 의존성이 있는지 살펴본 다음, common logging 라이브러리 예제를 사용하여 애플리케이션에 로깅을 설정하는 방법을 좀 더 자세히 살펴보겠다. 예를 들어 메이븐 중앙 스타일 구성물 작명 규약을 사용하는 의존성 설정을 보여주겟다.

스프링 의존성과 스프링에 의존하는 것들

스프링은 광범위한 엔터프라이즈 및 여러 외부 도구를에 대한 연동과 지원을 제공하지만, 내부적으로 필수 의존성을 최소한으로 유지한다. 간단한 경우에 스프링을 사용할 때는 많은 수의 라이브러리를 다운로드(혹은 자동으로) 받을 필요가 없다. 기본적인 의존성 주입은 오직 하나의 외부 의존성을 필요로 하며, 그건 바로 로깅이다.(로깅 옵션에 대해서는 아래의 보다 자세한 설명을 참조하자.) 만약 의존성 관리를 메이븐으로 하고 있다면 로깅 의존성을 명시적으로 설정할 필요도 없다. 예를 들어, 애플리케이션 컨텍트를 만들고 의존성 주입을 이용하고 있다면 애플리케이션에 다음과 같이 설정하기만 하면 된다. 메이븐 의존성은 다음과 같을 것이다.

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.0.0.RELEASE</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

이게 전부다. 기본적인 의존성 주입을 사용하는 경우처럼, 일반저긍로 스프링 API를 컴파일 할 필요가 없다면 scope가 runtime으로 설정해도 된다는 것에 주목하자.

위 예제에서 메이븐 중앙 작명 규약을 사용했기 때문에, 메이븐 중앙 또는 스프링소스 S3 메이븐 저장소와 잘 동작한다. S3 메이븐 저장소를 이용하려면(마일스톤 또는 개발자 스냅샷 버전을 사용하기 위해), 저장소 위치를 메이븐 설정 파일에 명시해야 한다. 전체 배포는 저장소는 다음과 같다.

<repositories>
   <repository>
      <id>com.springsource.repository.maven.release</id>
      <url>http://s3.amazonaws.com/maven.springsource.com/release/</url>
      <snapshots><enabled>false</enabled></snapshots>
   </repository>
</repositories>

마일스톤 저장소는 다음과 같다.

<repositories>
   <repository>
      <id>com.springsource.repository.maven.milestone</id>
      <url>http://s3.amazonaws.com/maven.springsource.com/milestone/</url>
      <snapshots><enabled>false</enabled></snapshots>
   </repository>
</repositories>

스냅샷은 저장소는 다음과 같다.

<repositories>
   <repository>
      <id>com.springsource.repository.maven.snapshot</id>
      <url>http://s3.amazonaws.com/maven.springsource.com/snapshot/</url>
      <snapshots><enabled>true</enabled></snapshots>
   </repository>
</repositories>

스프링소스 EBR을 사요하려면 의존성을 나타낼 때 다른 이름 규약을 사용해야 한다. 이름은 보통 유추하기 쉽다. 예를 들어 이번 경우에는 다음과 같다.

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>org.springframework.context</artifactId>
      <version>3.0.0.RELEASE</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

물론 저장소도 명시적으로 설정해 줘야 한다.

<repositories>
   <repository>
      <id>com.springsource.repository.bundles.release</id>
      <url>http://repository.springsource.com/maven/bundles/release/</url>
   </repository>
</repositories>

만약 의존성을 손으로 관리한다면, 위 저장소 설정에 보이는 URL은 브라우저로 볼수가 없고, http://www.springsource.com/repository에 사용자 인터페이스가 있으니 검색하여 의존성을 다운 받을 수 있다. 또한 이 사이트에는 간편한 메이븐과 Ivy 설정이 있으니 복사하여 붙여넣기로 쉽게 해당 도구에서 사용할 수 있다.

만약 Ivy를 사용하여 의존성 관리하는 것을 선호한다면 위와 비슷한 이름과 설정 옵션이 관리 시스템에 있거나 예제 코드를 살펴보기 바란다.

로깅

로깅은 매우 중요한 스프링 의존성이다. 왜냐하면 a) 유일한 외부 의존성이자, b) 모두가 자신이 사용하는 도구에서 무언가 출력되는 것을 보고 싶어하며, c) 스프링은 로깅에 의존성을 가지는 다양한 툴과 연동하기 때문이다. 애플리케이션 개발자의 목적 중 하나는 보통 외부 컴포넌트를 포함한 전체 애플리케이션에 대해 중앙의 통합된 로깅 설정을 가지는 것이다. 로깅 프레임워크가 다양해지면서 예전보다 선택하기가 더 어려워졌다.

스프링에 있는 필수 로깅 의존성은 Jakarta Commons Logging API(JCL)이다. JCL을 컴파일하고 JCL Log 객체를 스프링 프레임워크를 확장한 클래스에서 볼 수 있다. 스프링의 모든 버전에서 동일한 로깅 라이브러리를 사용하는 것이 사용자에게 중요하다. 스프링을 확장한 애플리케이션이더라도 뒷 단의 호환성이 보장되기 때문에 이전하기가 용이하다. 우리는 스프링의 모듈 중 하나를 명시적으로 commons-logging(JCL)에 의존하게 하고 다른 모듈들이 컴파일 시점에 해당 모듈에 의존하도록 하는 방법을 사용한다. 예를 들어, 메이븐을 사용하고 있으며, 어디서 commons-loggins에 대한 의존성을 가져와야 하는지 궁금하다면, 스프링에서 가져오도록 하고 명시적으로는 spring-core 모듈에서 가져온다.

commons-logging의 좋은 점은 애플리케이션이 동작하는데 필요한 것이 아무것도 없다는 것이다. 런타임 감지 알고리즘을 가지고 있어서 클래스패스에 잘 알려진 로깅 프레임워크가 있다면 적절한 것을 찾아 사용하도록 되어있다.(또는 여러분이 어떤것을 사용하고 싶은지 알려줄 수도 있다) 만약에 사용할 수 있는 것이 아무것도 없다면 JDK(java.util.logging 또는 줄여서 JUL)로부터 로그를 가져온다. 여러분의 스프링 애플리케이션이 대부분의 경우 잘 동작하며 잘 로깅 된다는 것을 알아야 하며, 그것은 중요하다.

불행히도, commons-logging의 안좋은 점은 역시 런타임 감지 알고리즘이다. 만약 시간을 되돌려 스프링을 새로운 프로젝트로 시작할 수 있다면 우리는 다른 로깅 의존성을 사용했을 것이다. 아마도 첫번째 선택은 많은 사람들이 애플리케이션에서 스프링과 함께 사용하고 있는 Simple Logging Facade for Java(SLF4J)일 것이다.

commons-loggins을 교체하는 것은 쉽다. 실행중에 클래스패스에 없도록 만들자. 메이븐에서 의존성을 제외할 수 있다. 스프링 의존성 선언 방법으로 인해 다음과 같이 한번만 선언하면 된다.

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.0.0.RELEASE</version>
      <scope>runtime</scope>
      <exclusions>
         <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
         </exclusion>
      </exclusions>
   </dependency>
</dependencies>

이제 이 애플리케이션은 JCL API가 클래스패스에 없으니 깨질 것이다. 새로운 것을 제공하여 그것을 고쳐보자. 다음 절에서 우리는 어떻게 JCL 구현체의 대안으로 SLF4J를 사용하는지 예제를 살펴보겠다.

SLF4J 사용하기

SLF4J는 연동할 다른 프레임워크를 찾는 작업을 실행중에 하지 않고 컴파일 시점에 바인딩 하기 때문에 commons-loggins 보다 보다 깔끔한 의존성과 실행시 보다 더 효율적이다. 이것은 즉 실행중에 여러분이 어떤 것을 사용하고 싶은지 보다 명시적으로 선언하고 그것을 적절하게 설정해야 한다는 뜻이다. SLF4J는 여러 로깅 프레임워크에 대한 바인딩을 지원하기 때문에 여러분들이 기존에 사용하고 있던 것을 선택하고 해당 설정과 관리로 바인딩할 수 있다.

SLF4J는 JCL을 포함한 여러 로깅 프레임워크에 대한 바인딩을 제공한다. 또한 그 반대로 마찬가지다. 다른 로깅 프레임워크와 자신을 연결해준다. 따라서 SLF4J를 스프링에서 사용하기 위해서는 commons-logging 의존성을 SLF4J-JCL 브릿지 의존성으로 교체해야 한다. 그렇게 한번 하면 스프링 내부의 로깅 호출이 SLF4J에 대한 로깅 호출로 바뀐다. 따라서 만약 애플리케이션에서 다른 라이브러리들도 해당 API를 사용하고 있다면, 로깅을 설정하고 관리할 곳은 한 부분이다.

일반적인 선택은 스프링에서 SLF4J로 연결하고, SLF4J에서 Log4J로 명시적인 바인딩을 하는 것이다. 4개의 의존성을  명시적으로 제공해야 한다. brigde, SLF4J API, binding to Log4J, Log4J 구현체. 메이븐에서 다음과 같이 설정할 수 있다.

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.0.0.RELEASE</version>
      <scope>runtime</scope>
      <exclusions>
         <exclusion>
           <groupId>commons-logging</groupId>
           <artifactId>commons-logging</artifactId>
         </exclusion>
      </exclusions>
   </dependency>
   <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>jcl-over-slf4j</artifactId>
      <version>1.5.8</version>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.5.8</version>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.5.8</version>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

로깅을 하기위해서는 좀 많은 의존성처럼 보인다. 실제로 그렇긴 하지만 꼭 그래야 하는건 아니며 순수commons-logging 보다 더 잘 동작할 것이다. 특히 OSGi 플랫폼같이 제약적인 컨테이너를 사용한다면 말이다. 이른바 실행시점이 아니라 컴파일 시점에 바인딩을 하기 때문에 성능상 장점을 얻을 수 있다.

SLF4J 사용자들 사이에서 보다더 일반적인 선택은 Logback에 지접 바인딩하여 과정을 짧게 하고 의존성을 더 줄이는 것이다. Logback이 SLF4J를 지겁 구현하기 때문에 추가적인 바인딩 과정을 제거해주기 때문에, 4개가 아니라 오직 두 개 라이브러리만 필요하다. (jcl-over-slf4j와 logback). 만약 그렇게 하면 slf4j-api 의존성 자체로 명시적인 의존성에서 제거할 필요가 있을지 모르겠다. 오직 한 버전 API 만 클래스패스에서 사용하길 원하기 때문이다.

Log4J 사용하기

많은 개발자들은 Log4J를 로깅 프레임워크로 사용하고 설정하고 관리한다. 효율적이며 잘 만들어졌다. 실제로 스프링을 만들고 테스트할 때 실행시에 사용하는 것이기도 하다. 스프링 또한 Log4j 설정과 초기화를 간편히 할 수 있는 것들을 제공한다. 따라서 몇몇 모듈은 Log4J에 대한 부가적인 컴파일 시점 의존성을 가지고 있다.

Log4j를 기본 JCL 의존성으로 사용할 때 필요한 것은 Log4J를 클래스패스에 두는것이 전부이다. 그리고 (log4j.properties 또는 log4j.xml을 클래스패스 루트에) 설정 파일을 제공하면 된다. 따라서 메이븐 사용자는 다음과 같이 의존성 선언을 하면된다.

<dependencies>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.0.0.RELEASE</version>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
      <scope>runtime</scope>
   </dependency>
</dependencies>

그리고 다음은 콘솔에 로깅하는 log4j.properties다.

og4j.rootCategory=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L – %m%n

log4j.category.org.springframework.beans.factory=DEBUG

네이티브 JCL과 런타임 컨테이너

많은 사람들이 스프링 애플리케이션을 JCL 구현체 자체를 제공하는 컨테이너에서 구동한다. IBM 웹스피어 애플리케이션 서버(WAS)가 그중 하나이다. 이것은 보통 문제를 야기하고, 불행히도 정답은 없다. commons-logging을 애플리케이션에서 간단하게 제외하는 것이 대부분 상황에서 충분한건 아니다.

정리하자면: 문제는 JCL 또는 common-logging 자체가 아니라 commons-logging을 다른 프레임워크(보통 Log4J)에 바인딩하는 것과 관련이 있다. commons-logging이 런타임 탐색하는 방법이 몇몇 컨테이너가 사용하고 있는 예전 버전(1.0)과 현재 대부분의 사람들이 사용하고 있는 버전(1.1)과 다르기 때문이다. 스프링은 JCL API에서 별다른 것을 사용하지 않기 때문에 깨질것은 없지만, 조만간 로깅을 하려는 스프링 또는 여러분의 애플리케이션이 Log4J로 바인할 때 동작하지 않을 수 있다.

그런 경우 WAS를 사용할 때는 클래스로더 계층 구조(IBM은 이것을 "parent last"라 부른다.)를 변경하여 컨테이너가 아니라 애플리케이션이 JCL 읜존성을 제어하게 할 수 있다. 하지만 이 방법이 항상 가능한 건 아니지만,  여러분이 사용하는 컨테이너틔 버전과 기능에 따라 다양한 대안들이 공공연히 제시되고 있다.
top


[스프링 3.0] RC2 릴리즈~

Spring/3.0 : 2009.11.16 22:54


요즘 스프링에 관심이 슬슬 가시기 시작하네요. 벌써 3일이나 지나서야 RC2로 버전을 올리다니.. 예전 같았으면 공개되자마자 버전을 올렸을텐데 말이죠. 하긴.. 뭐 3일 정도 늦었지만, 그래도 봄싹은 아마도 한국에서 가장 빨리 스프링 최신 버전을 적용한 프로젝트가 될 것 같습니다. 후훗

중요 변경 사항을 살펴보겠습니다.

* updated to final versions of JSR-330 "javax.inject" and JSR-303 "javax.validation" APIs
* full compliance with the JSR-330 TCK (i.e. full compliance with the final specification)
* support for Hibernate Validator 4.0 GA (as the JSR-303 reference implementation)
* added support for load-time weaving in JBoss 5.x
* added support for recent EHCache 1.6 configuration properties to EHCacheFactoryBean
* added AnnotatedBeanDefinitionReader helper for programmatic registration of annotated classes
* added AnnotationConfig(Web)ApplicationContext for convenient registration/scanning of classes
* added GenericXmlApplicationContext with flexible configuration options for its XML support
* AbstractApplicationContext can also start up in case of system properties access failure
* internal MergedBeanDefinitionPostProcessors apply after all other post-processors
* inner beans detected as ApplicationListeners as well (only supported for inner singletons)
* child bean definition's scope attribute can be inherited from parent bean definition now
* introduced SmartLifecycle interface with auto-startup and shutdown order support
* introduced LifecycleProcessor delegate, customizable through "lifecycleProcessor" bean
* MessageListenerContainers and Quartz SchedulerFactoryBean start up on refresh instead of init
* added initialize-database tag to jdbc namespace for populating external data sources with data
* PathMatchingResourcePatternResolver leniently ignores non-existing root directories
* DefaultConversionService understands "on"/"off", "yes"/"no", "1"/"0" as boolean values
* CustomEditorConfigurer supports PropertyEditor instances again (with deprecation warning)
* revised MethodParameter's annotation accessor methods
* ClassUtils is now parameterized with Class<?> and Class<T> where appropriate
* DataBinder now accepts var-args to set allowed, disallowed, and required fields
* DataBinder auto-grows nested paths on traversal (avoiding NullValueInNestedPathException)
* fixed enum binding regression with WebRequestDataBinder (as used by @MVC data binding now)
* fixed FieldError to expose rejected input value as String value instead of as array
* JSR-303 Validator will only register validation failures if no binding failure happened
* ContentNegotiatingViewResolver works with ignoreAcceptHeader and defaultContentType as well
* added Spring MVC namespace, with convenient mvc:annotation-driven configuration element
* default number and datetime formatters configured when using the Spring MVC namespace
* full support for datetime formatting using the Joda Time library (automatically enabled)
* added convenient @NumberFormat and @DateTimeFormat annotations for declarative formatting
* implicit T.valueOf(S) and constructor T(S) lookup if no explicit S->T converter matches
* AbstractExcelView is compatible with Apache POI 3.0 as well as 3.5 now
* TilesConfigurer only sets up EL support if JSP 2.1 is present (for JSP 2.0 compatibility)
* re-introduced Struts 1.x support ("org.springframework.web.struts") in deprecated form
* deprecated scheduling support for JDK 1.3 Timer ("org.springframework.scheduling.timer")
* deprecated remoting support for JAX-RPC (in favor of JAX-WS)


top




: 1 : 2 : 3 : 4 : ··· : 12 :





티스토리 툴바