[Servlet 3.0 @WebServlet]스프링 DispatcherServlet을 서블릿 3.0 @WebServlet으로 올려볼까?

서블릿 3.0에 web.xml없이 애노테이션만 추가하면 서블릿으로 자동 등록되는 기능이 생겼다.

http://www.servletworld.com/servlet-tutorials/servlet3/webservlet-annotation-example.html

자세한 내용은 여기 있고..

우선, pom.xml에 의존성을 추가해야한다.

이렇게 두개가 필요하고, 이런류의 의존성은 보통 provided 스코프로 사용하게 좋은 습관이다. 왜그런지는 생략;

이렇게 간단하게 자바 코드만 추가하고, web.xml을 전혀 만들지 않아도, 브라우저에서 /test를 요청하면 Hello를 볼 수 있다.

난 사실 처음에 스프링 3.1에서 서블릿 3.0을 지원한다길래 이런 모습을 상상했었다.

서블릿 3.0부터는 web.xml을 사용할 필요가 없고, DisaptcherServlet도 HttpServlet을 (매우 깊게) 상속해서 만든 클래스니까 이런식으로 등록할 수 있지 않을까? 싶었다.

해보니까 잘 된다.

그래도 저렇게 쓰진 않겠지;;; ㅋ

[Spring 3.1] 자바 설정 예제

먼저, XML 설정부터 볼까나..

<bean id="userDao" class="chapter06.exercise.aop12.UserDaoJdbc">
    <property name="dataSource" ref="dataSource" />
</bean>

<tx:annotation-driven />

<bean id="userService" class="chapter06.exercise.aop12.UserServiceImpl">
    <property name="userDao" ref="userDao" />
    <property name="mailSender" ref="mailSender" />
</bean>

<bean id="testUserService"
    class="chapter06.exercise.aop12.UserServiceTest$TestUserServiceImpl"
    parent="userService" />

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

<jdbc:embedded-database type="HSQL" id="dataSource">
    <jdbc:script location="member_table_1.sql" />
</jdbc:embedded-database>

<bean id="mailSender" class="chapter06.exercise.aop12.DummyMailSender" />

이 설정은 내가 강의할 때 사용하는 예제 코드에 들어있는 코드로, 토스3에 있는 코드를 기반으로 작성한 샘플 코드다. jdbc:embedded를 사용하는 등, 실습하기 손쉽게 조금 고친 부분이 있다.

이 설정을 스프링 3.1 자바 설정으로 다음과 같이 변경할 수 있다.

@Configuration
@EnableTransactionManagement
public class TestDaoConfg {
   
    @Bean
    public UserDao userDao(DataSource dataSource) {
        UserDaoJdbc userDaoJdbc = new UserDaoJdbc();
        userDaoJdbc.setDataSource(dataSource);
        return userDaoJdbc;
    }
   
    @Bean
    public UserService userService(UserDao userDao, MailSender mailSender){
        UserServiceImpl userServiceImpl = new UserServiceImpl();
        userServiceImpl.setUserDao(userDao);
        userServiceImpl.setMailSender(mailSender);
        return userServiceImpl;
    }
   
    @Bean
    public UserService testUserService(UserDao userDao, MailSender mailSender){
        TestUserServiceImpl userServiceImpl = new TestUserServiceImpl();
        userServiceImpl.setUserDao(userDao);
        userServiceImpl.setMailSender(mailSender);
        return userServiceImpl;
    }
   
    @Bean
    public MailSender mailSender(){
        return new DummyMailSender();
    }
   
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
   
    @Bean(destroyMethod="shutdown")
    public DataSource dataSource(){
        EmbeddedDatabaseFactory factory = new EmbeddedDatabaseFactory();
        factory.setDatabaseName("sample");
        factory.setDatabaseType(EmbeddedDatabaseType.HSQL);
        ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
        populator.addScript(new ClassPathResource("member_table_1.sql"));
        factory.setDatabasePopulator(populator);
        return factory.getDatabase();
    }
   

}

뭐 대부분은 문제가 아닌데… jdbc 네임스페이스를 대체하는 코드는 도무지 외울 수가 없다.. XML이 더 편하게 느껴진다. 잘못해서 destroyMethod라도 설정하지 않으면 어쩔것인가…

@EnableXxx과 TestContext 연동으로 XML에서 자바 설정으로 넘어가지 위해 열심히 코딩해준 건 고맙지만.. 아직 2% 부족하게 느껴진다.

Whiteship’s 스프링 트러블슈팅 서비스

그동안 제 블로그 방명록이나 이메일로 스프링 관련 이슈를 문의해 오신 분들이 제법 있습니다. 블로그를 티스토리에서 워드프레스로 옮긴 뒤로는 질문이 많이 줄었지만 그래도 다시 간간히 질문이 올라오더군요. 그 중에서도 스프링 관련 질문, 또 그 중에서도 문제 상황만 골라서 해결해 드리고자 합니다. 제 블로그 방명록 옆에 새로 페이지를 만들어 뒀습니다.

whiteshipspring

많이 이용해 주세요.

무료랍니다.

Spring Custom XML Namespace 만들기 2. 레퍼런스

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/extensible-xml.html

레퍼런스를 보자. 초반에 먼저 개략적인 개발 단계가 나온다.

  1. 커스텀 엘리먼트를 기술할 XML 스키마 작성
  2. NamespaceHandler 구현체 코딩(매우 간단하고 쉬움)
  3. BeanDefinitionParser 구현체 코딩(실제 작업할 부분)
  4. 위에서 만든것들 스프링에 등록하기(매우 간단하고 쉬움)

근데.. 왠지 이거 순서가 잘못된것 같다. 아니.. 파서도 안만들었는데 뭘 등록하라고 NamespaceHandler를 구현하라는건지.. @_@;; 옛날에는 이런것도 스프링 이슈트래커에 올려주면 금방금방 피드백도 주고 잘 고쳐줬었는데.. 요즘은 어떨런지 모르겠다. 귀찮네;;

1 -> 3 -> 2-> 4 순서가 맞는것 같다.

1. XML 스키마 작성

[xml]
<myns:dateformat id="dateFormat"
pattern="yyyy-MM-dd HH:mm"
lenient="true"/>
[/xml]

이런 형태의 커스텀 엘리먼트를 제공할 계획이라면 다음과 같은 스키마를 작성해야 한다.

[xml]
<!– myns.xsd (inside package org/springframework/samples/xml) –>

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.mycompany.com/schema/myns"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.mycompany.com/schema/myns"
elementFormDefault="qualified"
attributeFormDefault="unqualified">

<xsd:import namespace="http://www.springframework.org/schema/beans"/>

<xsd:element name="dateformat">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="lenient" type="xsd:boolean"/>
<xsd:attribute name="pattern" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>

</xsd:schema>
[/xml]

2. BeanDefinitionParser 구현

[java]
package org.springframework.samples.xml;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

import java.text.SimpleDateFormat;

public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

protected Class getBeanClass(Element element) {
return SimpleDateFormat.class;
}

protected void doParse(Element element, BeanDefinitionBuilder bean) {
// this will never be null since the schema explicitly requires that a value be supplied
String pattern = element.getAttribute("pattern");
bean.addConstructorArg(pattern);

// this however is an optional property
String lenient = element.getAttribute("lenient");
if (StringUtils.hasText(lenient)) {
bean.addPropertyValue("lenient", Boolean.valueOf(lenient));
}
}
}
[/java]

구상한 XML에서 사용할 값을 읽어와서 bean에다 설정해준다.

3. NamespaceHandler 구현

[java]
package org.springframework.samples.xml;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class MyNamespaceHandler extends NamespaceHandlerSupport {

public void init() {
registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
}
}
[/java]

커스텀 엘리먼트와 그것을 처리할 파서를 매핑해준다. 커스턴 엘리먼트는 1번 과정에서 스키마 만들면서 만든거나 다름없고 파서는 2번 과정에서 만들었으니 그것을 사용해서 등록하면 된다.

4. 등록하기

코딩은 끝났고 이제 META-INF 디렉토리에 프로퍼티 파일을 만들어서 핸들러와 스키마를 설정해줘야 한다. 이걸 JAR로 묶어서 클래스패스에 두면 스프링이 자동으로 읽어서 스프링 XML 설정 파일로 사용할 수 있게 해준다. (해봐야지.)

4-1. “META-INF/spring.handlers”

[xml]
http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler
[/xml]

이때 “:” 문자를 백슬러쉬로 이스케이프 시켜줘야 한다.

4-2. “META-INF/spring.schema”

[xml]
http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd
[/xml]

5. 커스텀 XML 설정 사용하기

[xml]
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:myns="http://www.mycompany.com/schema/myns"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.mycompany.com/schema/myns http://www.mycompany.com/schema/myns/myns.xsd">

<!– as a top-level bean –>
<myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>

<bean id="jobDetailTemplate" abstract="true">
<property name="dateFormat">
<!– as an inner bean –>
<myns:dateformat pattern="HH:mm MM-dd-yyyy"/>
</property>
</bean>

</beans>
[/xml]

아직 레퍼런스를 다 보진 못했는데 일단은 대략 어떤식으로 만드는지 파악이 됐다. 중요한건 어제 토비님 글에서 본것처럼 만들려고 구상한 커스텀 설정에 따라 파서 구현 방법이 달라진다는 것이다. 아마 레퍼런스 남은 부분에선 그것을 설명하려는 것 같다. 요건 낼 아침에 일찍 인나면 봐야지..

Spring Custom XML Namespace 만들기 1. 사전 조사

http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/extensible-xml.html

스프링 2.0부터 커스텀 XML 빈 설정 파서를 작성해 스프링 IoC 컨테이너에 연동할 수 있었다.

이 기능은 사실 최초의 Epril 세미나이자 KSUG의 시발점이 되었던 세미나에서 토비님이 발표하셨던 내용이다.

Spring 2.0의 XML 확장기능(1) Spring 2.0의 XML 확장기능(2) Spring 2.0의 XML 확장기능(3)

벌써 3년도 훌쩍넘어 4년이 거의 다 되가는 글인데 XML 확장에 대해서는 이 글 만한 한글 자료가 없다.(두번째 글에 이미지가 링크가 꺠진게 약간 아쉽다.)

일단 레퍼런스를 보기전에 토비님 글을 쭉 읽어봤다. 대략 이런 순인것 같다.

  1. 커스텀 빈 구상
  2. BeanDefinition 파서 구현
  3. XML 스키마 작성
  4. 네임스페이스 핸들러 작성
  5. 네임스페이스 핸들러와 스키마 등록

우선 커스텀 네임스페이스를 이용해 등록할 빈이 top-level 빈 한개 인지, top-level 빈 한개와 거기에 속해있는 inner 빈까지 등록할 것인지, top-level 빈을 여러개 등록할 것인지 등에따라 달라지며 또 두번쨰 경우였던 top-level 한개와 그 안에 inner 빈이 등록되는 경우에는 inner 빈들도 BeanFactory에 등록할지 여부에 따라 구현 방법이 또 조금 다른것 같다.

그러니 커스텀 네임스페이스로 간추릴 빈 설정을 파악하는게 중요하고 그 뒤엔 거기에 맞는 클래스를 사용해 BeanDefinitionParser를 구현하면 되겠다. 친절하게 각 경우에 따른 예제까지도 올려주셨다.

그 다음은 XML 스키마를 작성하는 일인데 이게 쬐금 귀찮다. XML 스키마를 직접 작성해본건 5년전 쯤 대학교에서 과제로 한번 해본게 다다. 그런데 역시나 또 친절하시게도 XML 스키마 작성 방법에 관한 링크도 걸어두셨다.

다음은 BeanDefinitionParser와 그 parser가 처리할 태그를 NamespaceHandler라는 것으로 매핑해주는 것이다.

마지막으로는 이 네임스페이스 핸들러를 spring.handlers라는 파일에 작성해주고 네임스페이스는 spring.schemas 라는 파일에 작성해 두 파일을 META-INF에 넣어두는 것인데.. 이 부분이 쬐금.. 불편하게 느껴진다. 좀 더 편한 방법은 없을런지..

내일은 레퍼런스도 봐야겠다. 어흑 언능 씻고 나가야지.