4.Java based Configuration

Spring 3에서는 Spring Java Configuration 프로젝트의 일부 주요 특징들을 추가함으로써 Java 기반의 Configuration 정의가 가능하도록 지원하고 있다. Java 기반의 속성 정의는 Java 코드를 중심으로 이루어지므로 Injection 속성 정의시 Type 오류가 있으면 컴파일부터 수행되지 않으므로 Type Safety를 보장하게 된다. 또한 Bean 인스턴스 관리를 로직으로 직접 구현해주기 때문에 Bean 구현체가 Spring에 의존되지 않고, 순수한 Java 코드로만 구현될 수 있도록 보장해준다.

활용 가능한 Annotation들은 위에서 나열한 바와 같으며, 본 섹션에서는 이러한 Annotation들에 대해 예제와 함께 자세히 살펴보도록 하자.

Java 기반의 Configuration 정의시 가장 기본이 되는 Annotation은 @Configuration과 @Bean이다. @Configuration은 클래스 레벨에 정의가능한 Annotation이다. @Configuration 정의를 포함한 클래스는 Bean 정의 정보를 담고 있어 Spring Container에 의해 처리되는 Configuration 클래스임을 의미한다. @Bean은 메소드 레벨에 정의 가능한 Annotation으로 XML 기반의 속성 정보 중 <bean/>과 동일한 역햘을 수행한다.

@Configuration
public class MovieFinderConfig {
    // ...
    @Bean
    public MovieFinder movieFinder() {
        return new MovieFinderImpl(movieDao);
    }
}

위 코드에서 언급한 MovieFinderConfig 클래스는 Configuration 클래스로써 'movieFinder'라는 이름을 가진 Bean을 정의하고 있음을 알 수 있다. 위 코드 내용을 XML 형태로 변경해 보면 다음과 같다.

<bean id="movieFinder" class="org.anyframe.sample.javaconfig.moviefinder.service.impl">
	<constructor-arg ref="movieDao"/>
</bean>

4.1.Bean Management

앞서 언급한 바와 같이 @Bean은 메소드 레벨에 정의 가능한 Annotation으로 특정 Bean을 정의하기 위해 사용한다. XML 기반의 속성 정보 중 <bean/>과 동일한 역햘을 수행하며, @Configuration 또는 @Component 클래스 내에 정의 가능하다. @Bean 정의가 추가된 메소드는 해당하는 Bean의 인스턴스 생성하여 전달하는 로직을 포함하고 있어야 하며 기본적으로 Spring Container는 메소드명을 Bean 이름으로 등록한다.

@Bean
public MovieFinder movieFinder() {
    return new MovieFinderImpl(movieDao);
}

위 코드에 의하면 @Bean 정의가 추가된 movieFinder() 메소드로 인해 'movieFinder'라는 이름의 Bean이 Spring Container에 등록될 것이다. 또한 'movieFinder' Bean을 요청하면 정의된 메소드 로직에 의해 MovieDao 객체가 셋팅된 MovieFinderImpl 객체가 전달될 것이다.

4.1.1.Naming

@Bean Annotation은 'name'이라는 속성 정보를 가지고 있다. name 속성에 대해 값을 부여하는 경우 이 값이 해당 Bean의 이름이 된다.

@Bean(name="movieFinderImpl")
public MovieFinder movieFinder() {
    return new MovieFinderImpl(movieDao);
}

4.1.2.Lifecycle Management

@Bean을 이용하여 정의된 Bean들에 대해서도 XML이나 Annotation 기반의 Bean들과 동일하게 기본 Lifecycle 관리가 가능하다. 즉, 해당 Bean이 @PreDestroy, @PostConstruct와 같은 JSR-250 Annotation을 포함하고 있거나 Spring의 InitializingBean, DisposableBean 등과 같은 인터페이스르 구현하였을 경우 Spring Container에 의해 해당 Bean의 Lifecycle이 관리된다. 이 외에도 @Bean은 'init-method', 'destroy-method'라는 속성 정보를 가질 수 있어서 속성값을 부여하는 경우 초기화/소멸화시에 정의된 메소드가 실행된다. 이것은 <bean/>의 init-method, destroy-method와 동일한 역할을 수행한다.

@Bean(initMethod = "initialize", destroyMethod = "destroy")
public MovieFinder movieFinder() {
    return new MovieFinderImpl(movieDao);
}

위 코드에 의하면 'movieFinder'라는 Bean의 초기화 시점에는 MovieFinderImpl.initialize(), 소멸화 시점에는 MovieFinderImpl.destroy() 메소드가 각각 실행될 것이다.

Spring Container는 시작 시점에 모든 Singleton Bean을 미리 로딩함으로써, 그 Bean이 필요할 때 즉시 사용될 수 있도록 보장해준다. 그러나 Container 시작 시점에 특정 Singleton Bean을 인스턴스화 시키지 않고 처음으로 해당 Bean에 대해 요청이 들어왔을 때 인스턴스화 시키기 위해서는 @Lazy 설정을 부여해 주어야 한다. 이것은 <bean/>의 lazy-init과 동일한 역할을 수행한다.

@Bean
@Lazy
public MovieFinder movieFinder() {
    return new MovieFinderImpl(movieDao);
}

4.1.3.Scope

@Bean과 함께 @Scope 정의를 추가하는 경우 해당 Bean에 대해 특정 Scope을 부여할 수 있다. @Scope을 부여하지 않는 경우 기본적으로 Singleton Scope이 적용된다.

@Bean
@Scope("prototype")
public MovieFinder movieFinder() {
    return new MovieFinderImpl(movieDao);
}

또한 request, session, globalSession Scope의 Bean에 대한 요청시 전달될 AOP Proxy 객체를 만들기 위해서 'proxyMode'라는 속성값을 추가적으로 부여할 수 있다. 'proxyMode'는 기본적으로 ScopedProxyMode.NO로 지정되며 ScopedProxyMode.TARGET_CLASS 또는 ScopedProxyMode.INTERFACES으로 정의 가능하다. 이것은 <bean/> 하위의 <aop:scoped-proxy/>와 동일한 역할을 수행한다.

@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public MoviePreferences moviePreferences() {
    return new MoviePreferences();
}

@Bean
public MovieFinder movieFinder() {
    return new MovieFinderImpl(moviePreferences);
}

4.1.4.Dependency Injection

Bean 사이에 참조 관계가 성립될 경우 기본적으로 Injection은 참조하려는 Bean에 해당하는 메소드를 호출함으로써 이루어진다.

@Configuration
public class MovieFinderConfig {
    @Bean
    public MovieFinder movieFinder() {
        return new MovieFinderImpl(movieDao());
    }
    
    @Bean
    public MovieDao movieDao() {
        return new MovieDao();
    }    
}

'movieFinder' Bean이 'movieDao' Bean을 참조하고 있다라고 가정해 보자. 이를 Java 기반의 Configuration으로 표현하기 위해서는 위의 코드에서와 같이 movieFinder() 메소드 내에서 MovieFinderImpl 인스턴스 생성시 movieDao()라는 메소드를 호출함으로써 'movieDao' Bean을 Injection 할 수 있다. 또는 MovieFinderImpl 객체의 setter를 호출할 때 movieDao() 호출 결과를 전달함으로써 'movieDao' Bean을 Injection할 수도 있을 것이다.

@Configuration
public class MovieFinderConfig {
    @Bean
    public MovieFinder movieFinder() {
        MovieFinderImpl movieFinder = new MovieFinderImpl();
        movieFinder.setMovieDao(movieDao());
        return movierFinder;
    }
    
    @Bean
    public MovieDao movieDao() {
        return new MovieDao();
    }    
}

참조 대상 Bean이 XML/Annotation 기반으로 정의되었거나 다른 Configuration 클래스에 정의된 경우 Spring에서 Dependency Injection 처리를 위해 지원하는 Annotation(@Inject, @Autowired, @Resource)을 그대로 적용할 수도 있다.

@Configuration
public class MovieDaoConfig {

    @Bean
    public MovieDao movieDao() {
        MovieDao movieDao = new MovieDao();
        return movieDao;
    }
}
@Configuration
@Import(value = { MovieDaoConfig.class })
public class MovieFinderConfig {
    @Autowired
    private MovieDao movieDao;

    @Bean
    public MovieFinder movieFinder() {
        return new MovieFinderImpl(movieDao);
    }
}

해당 Bean 이전에 초기화되어야 하는 하나 이상의 Bean을 명시적으로 강제하기 위해서는 @DependsOn을 활용할 수 있다. 이것은 <bean/>의 depends-on와 동일한 역할을 수행한다.

@Configuration
public class MovieFinderConfig {
    @Bean
    public MovieService movieService(){
        return new MovieServiceImpl();
    }

    @Bean
    @DependsOn(value = { "movieService" })		
    public MovieFinder movieFinder() {
        return new MovieFinderImpl(movieDao());
    }

    // ...
}

위 코드에 의하면 @DependsOn 속성 부여에 의해 'movieFinder' Bean이 초기화되기 전에 'movieService' Bean이 초기화 될 것을 짐작할 수 있다.

동일한 Type을 가지는 Bean이 여러개 정의되어 있어서 Type Injection 대상이 되는 Bean이 여러개 식별되었을 경우 @Primary를 부여한 Bean이 우선적으로 Injection 후보가 된다. 이것은 <bean/>의 primary와 동일한 역할을 수행한다.

@Configuration
public class MovieDaoConfig {
    @Bean
    public MovieDao defaultMovieDao() {
        return new MovieDaoImpl();
    }

    @Bean
    @Primary
    public MovieDao anotherMovieDao() {
        return new AnotherMovieDaoImpl();
    }
}

위와 같이 Configuration을 정의한 경우 @Autowired MovieDao movieDao;와 같은 코드에 의해 Injection되는 Bean은 @Primary 속성을 부여한 'anotherMovieDao' Bean이 될 것이다.

4.1.5.Method Injection

Setter injection과 Constructor injection을 사용할 경우, 기본적으로 Singleton Bean은 참조하는 Bean들을 Singleton 형태로 유지하게 된다. 그런데 Singleton Bean이 Non Singleton Bean(즉, Prototype Bean)과 참조 관계가 있을 경우에는 다음과 같이 처리해야 한다.

  1. Singleton Bean의 구현체 내에는 참조하려는 Non Singleton Bean 타입을 리턴하는 abstract 메소드 정의.

  2. Singleton Bean의 구현체 내의 비즈니스 메소드에서는 abstract 메소드를 이용해 Non Singleton Bean을 Injection하여 로직 수행.

  3. Java 기반 Configuration 정의시 Singleton Bean에 해당하는 메소드 내에서 인스턴스 생성과 함께 앞서 정의한 abstract 메소드 구현 로직 추가. 이 때 abstract 메소드 구현 로직에서는 Non Singleton Bean의 인스턴스 생성하여 리턴.

  4. 위와 같은 순서로 처리된 경우 Singleton Bean의 비즈니스 메소드 내에서 abstract 메소드가 호출될 때마다 해당 Bean의 인스턴스가 가진 abstract 메소드 구현 로직에 의해 새로운 Non Singleton Bean의 인스턴스 전달이 가능해짐. 즉, Singleton Bean에서 Non Singleton Bean에 대한 참조가 가능해짐.

다음은 Singleton Bean('movieFinder')에서 Non Singleton Bean('movieDao')에 대한 참조가 이루어질 수 있도록 하기 위해 정의된 Configuration 클래스의 내용이다.

@Configuration
public class MovieFinderConfig {
    @Bean
    @Scope("prototype")
    public MovieDao movieDao() {
        return new MovieDaoImpl();
    }

    @Bean
    public MovieFinder movieFinder() {
        return new MovieFinderImpl() {
            protected MovieDao getMovieDao() {
                return movieDao();
            }
        };
    }
}

위 Configuration 클래스에서 언급한 MovieFinderImpl 클래스는 다음과 같은 모습을 취할 것이다.

public abstract class MovieFinderImpl implements MovieFinder {
    protected abstract MovieDao getMovieDao();

    public List<Movie> getPagingList(Movie movie, int pageIndex) 
        throws Exception{
        return getMovieDao().getPagingList(movie, pageIndex);
    }
}

4.1.6.Spring Expression Language

Java 기반 Configuration 정의시 @Value와 함꼐 Spring Expression Language를 정의하면 Expression 처리 결과를 Bean의 인스턴스 생성시 반영하는 것도 가능하다.

@Configuration
public class MovieFinderConfig {
    private @Value("${jdbc.url}") String dbUrl;
    private @Value("${jdbc.username}") String userName;
    private @Value("${jdbc.password}") String password;

    @Bean
    public MovieDao movieDao() {
        return new MovieDaoImpl(dbUrl, userName, password);
    }
}

4.1.7.Code Equivalents for Spring's XML namespaces

코드 기반으로 Spring의 XML 네임스페이스(<context:component-scan/>, <tx:annotation-driven/> and <mvc:annotation-driven>)에 대응하는 @Enable Annotation이 Spring 3.1 버전부터 추가되었다. 이러한 Annotation은 Spring 3.0부터 도입된 @Configuration 태그와 같이 사용하도록 설계되어 있다.

4.1.7.1.@ComponentScan

@Configuration 태그와 함께 사용하며 <context:component-scan>과 동일한 기능을 수행할 수 있다.

@Configuration
@ComponentScan("com.acme.app.services")
public class AppConfig {
    // various @Bean definitions ...
}

4.1.7.2.@EnableTransactionManagement

Spring에서 제공하는 Annotation 기반의 트랜잭션 관리 기능을 활성화시킨다. @Configuration 태그와 함께 사용하며, <tx:*>과 동일한 기능을 수행할 수 있다.

@Configuration
@EnableTransactionManagement
public class AppConfig {
    @Bean
    public FooRepository fooRepository() {
        // configure and return a class having @Transactional methods
        return new JdbcFooRepository(dataSource());
    }

    @Bean
    public DataSource dataSource() {
        // configure and return the necessary JDBC DataSource
    }

    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

4.1.7.3.@EnableWebMvc

@Configuration 태그와 함께 사용하여 WebMvcConfigurationSupport클래스에 정의된 Spring MVC의 설정을 다음과 같이 불러올 수 있다.

@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses = { MyConfiguration.class })
public class MyWebConfiguration {

}

WebMvcConfigurer 인터페이스를 구현하거나, WebMvcConfigurerAdapter 기반의 클래스를 확장하고 메소드를 오버라이딩 하여 불러들여진 설정을 다음과 같이 사용자화 할 수 있다.

@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses = { MyConfiguration.class })
public class MyConfiguration extends WebMvcConfigurerAdapter {

       @Override
       public void addFormatters(FormatterRegistry formatterRegistry) {
               formatterRegistry.addConverter(new MyConverter());
       }

       @Override
       public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
               converters.add(new MyHttpMessageConverter());
       }

       // More overridden methods ...
}

4.2.Combining Java and XML Configuration

@Import/@ImportResource를 활용하면 XML 또는 다른 @Configuration 클래스에 정의된 Bean 정보를 참조할 수 있게 된다.

4.2.1.Combine Java Configuration

@Import 정의시 다른 @Configuration 클래스를 속성값으로 부여해주면 현재 @Configuration 클래스에서 다른 @Configuration 클래스 내에 정의된 @Bean 정보를 참조할 수 있게 된다. Import 대상이 되는 @Configuration 클래스가 다수일 경우 { } 내에 ','를 식별자로 하여 클래스를 명시해주면 된다. @Import는 <import/>와 동일한 역할을 수행한다.

@Configuration
@Import(value = { MovieDaoConfig.class })
public class MovieFinderConfig {
    @Autowired
    private MovieDao movieDao;

    @Bean
    public MovieFinder movieFinder() {
        return new MovieFinderImpl(movieDao);
    }
}

@Configuration
public class MovieDaoConfig {
    // ...

    @Bean
    public MovieDao movieDao() {
        MovieDao movieDao = new MovieDao();
        return movieDao;
    }
}

위에서 언급한 @Configuration MovieFinderConfig 클래스는 MovieDaoConfig 클래스를 @Import하고 있어서 이 클래스 내에 정의된 Bean 'movieDao'를 참조할 수 있게 된다. 다른 @Configuration 클래스 내에 정의된 @Bean을 참조하기 위해서는 @Autowired를 사용하고 있음을 알 수 있다. @Inject, @Resource를 사용하는 것 또한 가능하다.

4.2.2.Combine XML Configuration

@ImportResource 정의시 XML 속성 파일의 위치를 속성값으로 부여해주면 현재 @Configuration 클래스에서 XML 내에 정의된 Bean 정보를 참조할 수 있게 된다. Import 대상이 되는 XML 파일이 다수일 경우 @Import와 동일한 형태로 { } 내에 ','를 식별자로 하여 XML 파일명을 명시해주면 된다.

@Configuration
public class MovieDaoConfig {

    @Bean
    public MovieDao movieDao() {
        MovieDao movieDao = new MovieDao();
        return movieDao;
    }
}

위 코드에서는 다른 XML 내에 정의된 Bean을 참조하기 위해 @Autowired를 사용하고 있음을 알 수 있다. @Inject, @Resource를 사용하는 것 또한 가능하다.

4.3.Instantiating spring container

Spring 3에서는 @Configuration 클래스를 인식하여 정의된 Bean들을 관리할 수 있도록 하기 위해 ApplicationContext의 구현체인 AnnotationConfigApplicationContext를 추가적으로 제공하고 있다. AnnotationConfigApplicationContext는 @Configuration 클래스 외에도 Stereotype Annotation, JSR-330 Annotation에 대해 인식 가능하다. 다음에서는 @Configuration 클래스를 기반으로 Spring Container를 시작시키는 방법에 대해서 살펴보도록 하자.

4.3.1.AnnotationConfigApplicationContext

XML/Annotation 기반에서 Spring Container를 시작시키기 위해서는 XmlWebApplicationContext, FileSystemXmlApplicationContext, ClassPathXmlApplicationContext와 같은 구현체를 활용했었다.

String[] locations = new String[] { "classpath:spring/context-*.xml" };
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(locations, false);
context.refresh();

그러나 @Configuration 클래스를 인식할 수 있도록 하기 위해서는 AnnotationConfigApplicationContext 구현체를 이용하여 Spring Container를 시작시켜야 한다. 인식해야 할 @Configuration 클래스가 다수일 경우 해당되는 클래스들을 입력 인자의 값으로 정의해주면 된다.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MovieFinderConfig.class, ...);

또는 AnnotationConfigApplicationContext의 Default Constructor를 호출하여 인스턴스를 생성한 뒤 인식 대상이 되는 @Configuration 클래스들을 register할 수도 있다.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(MovieFinderConfig.class, ...);
context.register(...);
context.refresh();

Spring Container가 Annotation 기반의 Bean을 검색할 수 있게 하기 위해 정의한 <context:component-scan/>과 유사하게 AnnotationConfigApplicationContext을 이용하여 특정 패키지 하위에 대한 scan도 가능하다. 이렇게 하는 경우 해당 패키지 하위에 속한 모든 @Configuration 클래스가 검색되어 Container에 의해 처리된다.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("org.anyframe.sample");
context.refresh();

4.3.2.AnnotationConfigWebApplicationContext

웹어플리케이션에서 @Configuration 클래스를 인식하여 Spring Container를 시작시키기 위해서는 ContextLoaderListener Listener의 속성 정보인 contextClass, contextConfigLocation의 값을 입력해주면 된다. 이 때, contextClass는 AnnotationConfigWebApplicationContext로 정의해주고, 이를 통해 로드될 @Configuration 클래스들을 contextConfigLocation의 속성값으로 부여해주도록 한다.

<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>org.anyframe.samples.moviefinder.basic.config.MovieFinderConfig</param-value>
</context-param>

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

Java 기반 Configuration 정의시 유의사항

다음 코드에서는 'movieFinder1', 'movieFinder2' Bean이 'movieDao' Bean을 참조하고 있다. 'movieDao' Bean을 참조하기 위해 movieDao() 메소드를 호출하고 있기 때문에 'movieFinder1', 'movieFinder2' Bean이 참조하는 MovieDao의 인스턴스가 다를 것이라고 기대할 것이다.

@Configuration
public class MovieFinderConfig {
			
    @Bean 
    public MovieFinder movieFinder1() {
        return new MovieFinderImpl(movieDao());
    }
    
    @Bean 
    public MovieFinder movieFinder2() {
        return new MovieFinderImpl(movieDao());
    }
		
    @Bean
    public MovieDao movieDao() {
        return new MovieDao();
    }
}

그러나 Spring에서는 초기화시에 CGLIB을 이용하여 모든 @Configuration 클래스에 대해 subclass화하고 subclass 내의 메소드에서는 특정 Bean의 인스턴스를 생성하기 전에 Container가 Caching된 Singleton Bean의 인스턴스가 있는지 체크하도록 처리하고 있기 때문에 'movieFinder1', 'movieFinder2'는 동일한 'movieDao' 인스턴스를 참조하게 된다.

설명한 바와 같이 Spring에서 @Configuration 클래스를 처리하기 위해 CGLIB을 사용하므로 해당 프로젝트에는 CGLIB 라이브러리가 반드시 필요하며, CGLIB을 이용하여 @Configuration 클래스에 대해 subclass화하는 작업을 위해 @Configuration 클래스는 Default Constructor를 반드시 가져야 하고 final로 정의되지 않도록 해야 함에 유의하도록 한다.

4.4.Resources

  • 다운로드

    다음에서 sample 코드를 포함하고 있는 Eclipse 프로젝트 파일을 다운받은 후, 압축을 해제한다.

    • Maven 기반 실행

      Command 창에서 압축 해제 폴더로 이동한 후, mvn compile exec:java -Dexec.mainClass=...이라는 명령어를 실행시켜 결과를 확인한다. 각 Eclipse 프로젝트 내에 포함된 Main 클래스의 JavaDoc을 참고하도록 한다.

    • Eclipse 기반 실행

      Eclipse에서 압축 해제 프로젝트를 import한 후, src/main/java 폴더 하위의 Main.java를 선택하고 마우스 오른쪽 버튼 클릭하여 컨텍스트 메뉴에서 Run As > Java Application을 클릭한다. 그리고 실행 결과를 확인한다.

    표 4.1. Download List

    NameDownload
    anyframe-sample-javaconfig.zipDownload


출처 - http://dev.anyframejava.org/docs/anyframe/plugin/essential/core/1.0.4/reference/html/ch04.html






CODE-BASED CONFIGURATION FOR SPRING MVC

Spring 3.1.0.M2 부터 기존에 사용하던 스프링 XML 설정 파일들을 거의 쓰지 않을 수 있게 되었습니다.

http://blog.springsource.com/2011/06/13/spring-3-1-m2-spring-mvc-enhancements-2/
http://blog.springsource.com/2011/06/10/spring-3-1-m2-configuration-enhancements/
https://github.com/SpringSource/greenhouse

persistence.xml 은 말로는 된다고 하나 코드를 열어 봤더니 아직 삭제할 수가 없고, web.xml을 전통적인 Servlet Spec 하에서 삭제하려면 Servlet 3.0 스펙이 필요해서 tomcat 7 을 써야합니다. 하지만, 현재 tomcat 7.0.16 + Spring 3.1.0.M2 조합하면 <form:form> 태그에서 NPE 가 발생합니다.

마일스톤 릴리즈는 Maven Central 에 없으므로 메이븐 저장소 설정을 해주어야 합니다.

<repositories>
    <repository>
        <id>org.springframework.maven.milestone</id>
        <name>Spring Maven Milestone Repository</name>
        <url>http://maven.springframework.org/milestone</url>
    </repository>
</repositories>

<properties>
    <org.springframework.version>3.1.0.M2</org.springframework.version>
    <org.springsecurity.version>3.1.0.M2</org.springsecurity.version>
</properties>

...

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${org.springframework.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>${org.springframework.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${org.springframework.version}</version>
</dependency>

web.xml 파일에서 Spring Context 로딩 방식이 달라집니다.

spring.xml 파일로 설정을 할 때는 web.xml 에 다음과 비슷하게 적으셨을 겁니다.

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

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/spring-config.xml</param-value>
</context-param>

Java Annotation 방식으로 하려면 아래와 같은 방식으로 수정합니다.

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

<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>com.drypot.sleek.config</param-value>
</context-param>

여기서 contextConfigLocation 은 @Configuration 어노테이션이 붙은 클래스나 클래스가 들어 있는 패키지입니다.

XML 파일을 대신하는 @Configuration 클래스는 다음과 같이 기술됩니다.

@Configuration
@ComponentScan({"com.drypot.spring","com.drypot.sleek"})
@EnableTransactionManagement
@EnableWebMvc
public class SpringConfig extends WebMvcConfigurerAdapter {
...
}

@ComponentScan 은 기존의 <context:component-scan base-package=""/> 역할을 합니다.@EnableTransactionManagement 은 <tx:annotation-driven />@EnableWebMvc +WebMvcConfigurerAdapter 은 <mvc:annotation-driven /> 과 기타등등의 역할을 대신 합니다.

기존에 다음과 같이 <mvc:interceptors> 를 사용하셨다면 이것도 바꿀 수 있습니다.

<mvc:interceptors>
    <bean name="openSessionInViewInterceptor" class="org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor">
        <property name="entityManagerFactory" ref="emf" />
    </bean>
    <bean name="lastVisitUpdater" class="com.drypot.sleek.interceptor.LastVisitUpdater"/>
    <bean name="securityInterceptor" class="com.drypot.sleek.interceptor.SecurityInterceptor"/>
</mvc:interceptors>

를 아래와 같이.

@Bean
public OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor() {
    OpenEntityManagerInViewInterceptor openEntityManagerInViewInterceptor = new OpenEntityManagerInViewInterceptor();
    openEntityManagerInViewInterceptor.setEntityManagerFactory(emf());
    return openEntityManagerInViewInterceptor;
}

@Bean
public LastVisitUpdater lastVisitUpdater() {
    return new LastVisitUpdater();
}

@Bean 
public SecurityInterceptor securityInterceptor() {
    return new SecurityInterceptor();
}

@Override
public void configureInterceptors(InterceptorConfigurer configurer) {
    configurer.addInterceptor(openEntityManagerInViewInterceptor());
    configurer.addInterceptor(lastVisitUpdater());
    configurer.addInterceptor(securityInterceptor());
}

2011-08-29 01:15

Notes: 4 notes


출처 - http://drypot.tumblr.com/post/11188306289/code-based-configuration-for-spring-mvc#notes





@ComponentScan

@EnableAspectJAutoProxy

@EnableAsync

@EnableCaching

@EnableLoadTimeWeaving

@EnableSpringConfigured

@EnableTransactionManagement

@EnableWebMvc


참고  - http://cbeams.github.com/spring-3.1-review/#1





I am using jsf and spring together in web application. I have configured datasource and session factory in one configuration class which uses annotations like @Configuration, @ComponentScan etc. I don't have any applicationContext.xml file in my project as I am handling every entry of context xml in Configuration class. The test case works successfully but when I deploy my web application, it gives me error

java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?

Now if I give listener class in web.xml,

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

it gives me error,

/WEB-INF/applicationContext.xml not found

As per the document of ContextLoaderListener, it's true that if I don't givecontextConfigLocation param in web.xml explicitly, it will search for the default spring context file named applicationContext.xml in web.xml. Now, what should I do if I don't want to use spring context file and do all the configuration with annotations? How should I register listener classContextLoaderListener so that without use of xml file and using annotations only, I be able to run my web application with spring and jsf?

share|improve this question

10% accept rate
please take a look at How Accept Rate Works. Having a 0% accept rate will slow down responses to your questions in the future. – tolitius Nov 10 '11 at 18:12
Was this post useful to you?     

In web.xml you need to bootstrap the context with AnnotationConfigWebApplicationContext:

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </init-param>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            org.package.YouConfigurationAnnotatedClass
        </param-value>
    </init-param>
</servlet>

And don't forget to use @EnableWebMvc for your MVC annotations to kick in.

further reading:

EDIT as a "comments follow up" => to be Turing Complete:

Yes of course you need a listener. Although the above completely answers the question "How to register Spring @Configuration annotated class instead of applicationContext.xml file in web.xml", here is anexample from Spring official documentation that layouts the full web.xml:

<web-app>
  <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
       instead of the default XmlWebApplicationContext -->
  <context-param>
      <param-name>contextClass</param-name>
      <param-value>
          org.springframework.web.context.support.AnnotationConfigWebApplicationContext
      </param-value>
  </context-param>

  <!-- Configuration locations must consist of one or more comma- or space-delimited
       fully-qualified @Configuration classes. Fully-qualified packages may also be
       specified for component-scanning -->
  <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>com.acme.AppConfig</param-value>
  </context-param>

  <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
  <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- Declare a Spring MVC DispatcherServlet as usual -->
  <servlet>
      <servlet-name>dispatcher</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
           instead of the default XmlWebApplicationContext -->
      <init-param>
          <param-name>contextClass</param-name>
          <param-value>
              org.springframework.web.context.support.AnnotationConfigWebApplicationContext
          </param-value>
      </init-param>
      <!-- Again, config locations must consist of one or more comma- or space-delimited
           and fully-qualified @Configuration classes -->
      <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>com.acme.web.MvcConfig</param-value>
      </init-param>
  </servlet>

  <!-- map all requests for /app/* to the dispatcher servlet -->
  <servlet-mapping>
      <servlet-name>dispatcher</servlet-name>
      <url-pattern>/app/*</url-pattern>
  </servlet-mapping>
</web-app>
share|improve this answer
Hi, I am not using Spring MVC here, only JSF and Spring 3.1 RC1. Though I wrote this servlet in my web.xml but still got same error "no ContextLoaderListener registered". I then did entry for listener class of spring and then got other error of applicationContext.xml not found. I hope I am making my point clear. – mitalpritmaniNov 10 '11 at 8:08
Hey, I got it worked when I added these 2 params as context params and spring listener class in web.xml. Thanks, your answer helped me to find out my answer. I am editing your answer to add my solution. You please approve it (as I don't have edit privileges) so that I can mark your answer as final. – mitalpritmani Nov 10 '11 at 9:36
I never received you edit request, but I updated my answer with an example of a full web.xml form the Spring official documentation. – tolitius Nov 10 '11 at 14:35
not an issue. The required answer is there now. Thanks. :) – mitalpritmani Nov 11 '11 at 5:52


출처 - http://stackoverflow.com/questions/8075790/how-to-register-spring-configuration-annotated-class-instead-of-applicationcont





SPRING 3.1 M2: SPRING MVC ENHANCEMENTS

Rossen Stoyanchev

This post focuses on what's new for Spring MVC in Spring 3.1 M2. Here are the topics:

  • Code-based equivalent for the MVC namespace.
  • Customizable @MVC processing.
  • Programming model improvements.

A brief reminder that the features discussed here are in action at theGreenhouse project.

CODE-BASED CONFIGURATION FOR SPRING MVC

As Chris pointed out in his blog post last Friday, XML namespaces cut down configuration dramatically but also reduce transparency and sometimes flexibility. This holds true for the MVC namespace, which supports a number of customizations but not everything that's available. That means you are either able to use it or otherwise leave it. We believe code-based configuration has a solution for that and a path from simple to advanced.

Let's begin with this simple, familiar snippet:

<mvc:annotation-driven />

Although not required for using annotated controllers, <mvc:annotation-driven> does a number of useful things — it detects the presence of a JSR-303 (Bean Validation) implementation and configures data binding with it, it adds a JSON message converter if Jackson JSON library is available, and a few other things that can save quite a bit of configuration.

Now let's match that with code-based configuration:

@Configuration
@EnableWebMvc
public class WebConfig {
}

Here @EnableWebMvc imports an @Configuration class that matches the goodness of<mvc:annotation-driven>. As simple as that.

The next step is to use an attribute in <mvc:annotation-driven> perhaps to provide aFormattingConversionService, or to add a sub-element perhaps configuring message converters, or to use other MVC namespace elements like<mvc:interceptors><mvc:resources>, etc.

Let's see how to do all of that in code-based configuration:

@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
 
    @Override
    public void addFormatters(FormatterRegistry registry) {
        // register converters and formatters...
    }
 
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // add message converters...
    }
 
    @Override
    public void configureInterceptors(InterceptorConfigurer configurer) {
        configurer.addInterceptor(new AccountExposingHandlerInterceptor());
    }
 
    @Override
    public void configureResourceHandling(ResourceConfigurer configurer) {
        configurer.addPathMapping("/resources/**").addResourceLocation("/resources/");
    }
 
    // more @Override methods ...
 
}

Notice this time we've also sub-classed WebMvcConfigurerAdapter, a convenient base class with nothing but empty method implementations of the WebMvcConfigurer interface, which defines configuration callbacks for customizing the Spring MVC configuration.@Configuration classes that implement this interface are detected and given a chance to apply customizations. That's pretty much it.

The above provides parity with the MVC namespace in a way that is arguably moretransparent. You can use familiar Java IDE shortcuts to explore both theWebMvcConfigurer interface and the @Bean methods of the imported@Configuration class.

What about more advanced customizations? Well, this is as far as we can go with the MVC namespace. For code-based configuration, in the next (RC1) release, you'll be able to take the above example as is, drop the imported configuration (i.e. remove @EnableWebMvc) and switch to a base class that contains @Bean methods you can override. This means you can use Spring MVC code-based configuration knowing that any level of customization is possible — either through a simple callback mechanism or by extending directly from the class providing the actual configuration.

Here is a look at the web configuration in Greenhouse.

CUSTOMIZABLE @MVC PROCESSING

The @MVC programming model has been very successful enabling flexible controller method signatures. Yet many of you have asked for the underlying @MVC support classes to be more customizable. In response we've rolled a new set of @MVC support classes that give you more power and give us a better foundation to build on.

Before annotations, the Controller was the processing endpoint. With annotations the individual controller method became the endpoint complete with its own request mappings. Following this logic a HandlerMapping should not be limited to selecting a controller but should pick a controller method instead. Hence it will make sense that we've added aHandlerMethod abstraction and an AbstractHandlerMethodMapping for handler mappings that can select a HandlerMethod.

These are the new @MVC support classes built around the HandlerMethod abstraction:

  • RequestMappingHandlerMapping
  • RequestMappingHandlerAdapter
  • ExceptionHandlerExceptionResolver

As a result we now have a single point of selection in the handler mapping, the handler adapter knows exactly which handler method was selected, and so do other components. For example many of you requested to have interception around the invocation of a handler method, a gap that is now closed. Another less obvious consequence is the freedom to map the same URL across different controllers as long as the mapping differs in some other way (e.g. HTTP method).

Beyond request mapping, the execution of a controller method requires resolving method arguments (@RequestParameter@ModelAttribute, etc) and handling return values (@ResponseBody@ModelAttribute, etc.). The new @MVC support classes expose apluggable mechanism where implementations of HandlerMethodArgumentResolverand HandlerMethodReturnValueHandler can be plugged in to resolve every method argument and handle every return value. You have full control over that — you can either design your own argument types and return value types or customize the processing of the built-in ones. More details on that in a subsequent post.

To try the new @MVC support classes simply upgrade to Spring 3.1 M2. Both the MVC namespace and @EnableWebMvc configure them. Or if you have your own configuration just swap these:

  • DefaultAnnotationHandlerMapping -> RequestMappingHandlerMapping
  • AnnotationMethodHandlerAdapter -> RequestMappingHandlerAdapter
  • AnnotationMethodExceptionResolver ->ExceptionHandlerExceptionResolver

A note on compatibility: The existing support classes will continue to be available. However, we recommend that you switch going forward. For instance all the programming model improvements in the next section are only available that way. The new classes should be a drop-in replacement for the most part but there are two noteworthy differences. One, you can't combine any of the existing AbstractUrlHandlerMapping types (e.g.SimpleUrlHandlerMapping) with the new handler adapter, which expects aHandlerMethod and not a handler. Two, you can't rely on the method name when two@RequestMapping methods match equally to a request.

PROGRAMMING MODEL IMPROVEMENTS

This section lists programming model improvements introduced in the new @MVC support classes.

1. Declared @PathVariable arguments are now automatically added to the model. For example this:

@RequestMapping("/develop/apps/edit/{slug}")
public String editForm(@PathVariable String slug, Model model) {
    model.addAttribute("slug", slug);
    // ...
}

is replaced by:

@RequestMapping("/develop/apps/edit/{slug}")
public String editForm(@PathVariable String slug, Model model) {
    // model contains "slug" variable
}

2. Redirect strings support URI templates expanded with variables from the model (including declared @PathVariables). For example this:

@RequestMapping(
    value="/groups/{group}/events/{year}/{month}/{slug}/rooms",
    method=RequestMethod.POST)
public String createRoom(
    @PathVariable String group, @PathVariable Integer year,
    @PathVariable Integer month, @PathVariable String slug) {
    // ...
    return "redirect:/groups/" + group + "/events/" + year + "/" + month + "/" + slug;
}

is replaced by:

@RequestMapping(
    value="/groups/{group}/events/{year}/{month}/{slug}/rooms",
    method=RequestMethod.POST)
public String createRoom(
    @PathVariable String group, @PathVariable Integer year,
    @PathVariable Integer month, @PathVariable String slug) {
    // ...
    return "redirect:/groups/{group}/events/{year}/{month}/{slug}";
}

3. URI template variables are supported in data binding. For example this:

@RequestMapping("/people/{firstName}/{lastName}/SSN")
public String find(Person person,
                   @PathVariable String firstName,
                   @PathVariable String lastName) {
    person.setFirstName(firstName);
    person.setLastName(lastName);
    // ...
}

is replaced by:

@RequestMapping("/people/{firstName}/{lastName}/SSN")
public String search(Person person) {
    // person.getFirstName() and person.getLastName() are populated
    // ...
}

4. Consumable and producible media types can be specified via @RequestMapping. For example this:

@RequestMapping(value="/pets", headers="Content-Type=application/json")
public void addPet(@RequestBody Pet pet, Model model) {
    // ...
}

is replaced by:

@RequestMapping(value="/pets", consumes="application/json")
public void addPet(@RequestBody Pet pet, Model model) {
    // ...
}

Besides being shorter the above returns NOT_ACCEPTABLE (406) if the URL matches but the input media type doesn't.

5. For producible media types this:

@Controller
@RequestMapping(value = "/pets/{petId}", headers="Accept=application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {
    // ...
}

is replaced by:

@Controller
@RequestMapping(value = "/pets/{petId}", produces="application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId, Model model) {
    // ...
}

The above returns a NOT_SUPPORTED_MEDIA_TYPE (415) if the URL matches but the acceptable media type doesn't.

SUMMARY

There is a lot that's new in this milestone release. I encourage everyone to try the changes and provide feedback ahead of the RC1 and GA releases.

I would also like to turn your attention to another on-going effort to provide integration test support to Spring MVC applications. For server-side test support see the spring-test-mvcproject available on Github. For client-side support check the Spring Social project or track the following JIRA ticket SPR-7951.

SIMILAR POSTS


출처 - http://blog.springsource.com/2011/06/13/spring-3-1-m2-spring-mvc-enhancements-2/





SPRING 3.1 M1: MVC NAMESPACE ENHANCEMENTS AND @CONFIGURATION

Rossen Stoyanchev

In this 5th post of the series describing Spring 3.1 M1 features, I will focus on web applications. In the first half I'll discuss enhancements to the MVC XML namespace. Then I'll show how to create the equivalent of the MVC namespace with all Java configuration. At the end I mention some of the Servlet 3.0 related configuration changes you can expect in 3.1 M2.

MVC NAMESPACE IMPROVEMENTS

Spring MVC 3.0 provided a custom MVC namespace. The centerpiece of the namespace — the <mvc:annotation-driven> element, configured everything required to process requests with annotated controller methods. More importantly though it set up a range of defaults that go along with having to do with type conversion, formatting, validation, reading and writing of the body of requests and respones and so on.

Over time a number of you have requested to gain more control over various aspects the above mentioned default configuration and we've addressed a number of those requests in the 3.1 M1 release.

Registration of Formatters

We'll start with the registration of Converters and Formatters, which is done by supplying your own ConversionService instance as follows:

<mvc:annotation-driven conversion-service="..." />

For custom Formatters you would subclassFormattingConversionServiceFactoryBean and register the Formatters in code. Starting with 3.1 M1 you can register Formatter andAnnontationFormatterFactory types declaratively using a setter:

<mvc:annotation-driven conversion-service="conversionService" />
 
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatters">
        <list>
            <bean class="org.example.EmailFormatter"/>
            <bean class="org.example.PhoneAnnotationFormatterFactory"/>
        </list>
    </property>
</bean>

You still have the option to register Converters and Formatters in code. This is done with the FormatterRegistrar interface introduced in this release. Here is an example:

public class FinancialInstrumentFormatterRegistry implements FormatterRegistrar {
 
    public void registerFormatters(FormatterRegistry registry) {
        // Register custom Converters and Formatters here...
    }
 
}

And this is how your FormatterRegistrary can be plugged in:

<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="formatterRegistrars">
        <list>
            <bean class="org.example.FinancialInstrumentFormatterRegistrar"/>
        </list>
    </property>
</bean>

So, when should you use a FormatterRegistrar as opposed to the formatters setter? AFormatterRegistrar is useful when you need to register multiple related converters and formatters for a specific formatting category from one place. Another case is registering aFormatter indexed under a field type other than its own generic type <T> or perhaps registering a Formatter from a Printer/Parser pair. A good example of an actualFormatterRegistrar implementation is the JodaTimeFormatterRegistrar in the Spring Framework so have a look in there.

One last option in the FormattingConversionServiceFactoryBean is the ability to turn off default Formatter registrations completely through theregisterDefaultFormatters flag.

Registration of HttpMessageConverters

Starting with 3.1 M1 you can register HttpMessageConverters through a sub-element ofmvc:annotation-driven. For example:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.google.protobuf.spring.http.ProtobufHttpMessageConverter"/>
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
            <property name="prefixJson" value="true"/>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

The list of message converters provided this way take priority over the message converters the MVC namespace registers by default. For example, above we've added one custom converter, ProtobufHttpMessageConverter and we've also provided an instance of the Spring MVC's MappingJacksonHttpMessageConvert customized according to application needs.

If you don't want any message converters to be registered by default use the register-defaults attribute on the <mvc:message-converters> element.

Registration of custom WebArgumentResolvers

WebArgumentResolver, if you've never seen one before, is used for resolving custom arguments in @RequestMapping methods. The Spring Mobile project has aSitePreferenceWebArgumentResolver. It resolves SitePreference method parameter types that indicate whether the user wants the mobile or the full version of a page. Starting with Spring 3.1 M1 you can register custom argument resolvers through the MVC namespace:

<mvc:annotation-driven>
    <mvc:argument-resolvers>
        <bean class="org.springframework.mobile.device.site.SitePreferenceWebArgumentResolver"/>
    </mvc:argument-resolvers>
</mvc:annotation-driven>

Custom MessageCodesResolver

Last in the list is the ability to provide a custom MessageCodesResolver:

<mvc:annotation-driven message-codes-resolver="messageCodesResolver" />
 
<bean id="messageCodesResolver" class="org.example.CustomMessageCodesResolver" />

There are lots of other things that could be done with the MVC namespace. The above list should help cover the most common use cases for added flexibility but do let us know if you think there are other important ones we've missed.

FROM XML TO @CONFIGURATION

Update
The information in this section is outdated. The approach was changed in milestone 2. Please read this Spring MVC 3.1 M2 post instead.

I this part of the post I'll take an existing sample application: the mvc-showcasethat many of you may be familiar with from prior posts by Keith Donald and replace its XML configuration with Java-based configuration. Doing that allow to compare the code and configuration before and after.

The resulting sample application is available for checkout at spring-3.1-mvc-java-config. You can browse the source code directly on GitHub or follow the README instructions and get the code locally.

Our first step is to modify web.xml to point to our Java-based configuration and to specify theApplicationContext type to use to process that configuration. Below is the relevant web.xml fragment:

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </init-param>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            org.springframework.samples.mvc.config.MvcFeatures
            org.springframework.samples.mvc.config.MvcBeans
        </param-value>
    </init-param>
</servlet>

Next we will create MvcFeatures and MvcBeans in the ~.config package. MvcFeaturescontributes @Feature methods and is our main point of interest:

@FeatureConfiguration
class MvcFeatures {
 
    @Feature
    public MvcAnnotationDriven annotationDriven(ConversionService conversionService) {
        return new MvcAnnotationDriven().conversionService(conversionService)
            .argumentResolvers(new CustomArgumentResolver());
    }
 
    // ...
 
}

The above snippet is the equivalent of this XML namespace configuration:

<mvc:annotation-driven conversion-service="conversionService">
    <mvc:argument-resolvers>
        <bean class="org.springframework.samples.mvc.data.custom.CustomArgumentResolver"/>
    </mvc:argument-resolvers>
</mvc:annotation-driven>

As you can see MvcAnnotationDriven provides the same options as the XML elements using a convenient chained method API. Also notice that we've declared aConversionService method parameter. This parameter is auto-wired by type and injected. Its declaration can be found in MvcBeans:

@Configuration
public class MvcBeans {
 
    @Bean
    public ConversionService conversionService() {
        DefaultFormattingConversionService bean = new DefaultFormattingConversionService();
        bean.addFormatterForFieldAnnotation(new MaskFormatAnnotationFormatterFactory());
        return bean;
    }
 
    // ...
 
}

Note the use of the DefaultFormattingConversionService rather than the familiarFormattingConversionServiceFactoryBean typically used in XML configuration. This former gives us the same default Converter and Formatter registrations as the latter but is better suited for use with Java configuration — it provides a simple constructor instead of the FactoryBean lifecycle initialization methods invoked by Spring when using XML.

The remaining portion of MvcFeatures declares the equivalent of the the<mvc:resources><mvc:view-controller>, and the <context:component-scan> elements:

@FeatureConfiguration
class MvcFeatures {
 
    // ...
 
    @Feature
    public MvcResources resources() {
        return new MvcResources("/resources/**", "/resources/");
    }
 
    @Feature
    public MvcViewControllers viewController() {
        return new MvcViewControllers("/", "home");
    }
 
    @Feature
    public ComponentScanSpec componentScan() {
        return new ComponentScanSpec("org.springframework.samples").excludeFilters(
                new AnnotationTypeFilter(Configuration.class),
                new AnnotationTypeFilter(FeatureConfiguration.class));
    }
}

There are two things worth noting. One is that a single instance of MvcViewControllersis needed to define any number of view controllers using chained method calls. The second is the use of an exclude filter in the componentScan() method in order to preventMvcFeatures and MvcBeans from getting registered twice — once by theAnnotationConfigWebApplicationContext and a second time by the component scan.

For completeness this is the remaining part of MvcBeans:

@Configuration
public class MvcBeans {
 
    // ...
 
    @Bean
    public InternalResourceViewResolver jspViewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("/WEB-INF/views/");
        bean.setSuffix(".jsp");
        return bean;
    }
 
    @Bean
    public MultipartResolver multipartResolver() {
        return new CommonsMultipartResolver();
    }
}

The last step is to remove the Spring XML configuration located under /WEB-INF/spring.

SUMMARY

So there we are. We've bootstrapped a web application with all Java-based Spring configuration. Now that @FeatureConfiguration and @Feature have been introduced you can expect to see more and more FeatureSpecification implementations as an alternative to custom XML namespaces. I rather like the end result of the Java configuration but of course that doesn't mean my existing applications need to switch. It's all about having choice. If you prefer the declarative nature of XML and you use an IDE with code completion on class and method names in Spring XML configuration, then using XML namespaces is fine as well.

As heard recently in the webinar Introducing Spring Framework 3.1, in milestone 2 of Spring 3.1 you can expect to see Servlet 3.0 support including XML-free web application setup (i.e no web.xml) in combination with AnnotationConfigWebApplicationContext and the environment abstraction demonstrated in this and the previous posts of this blog series.

We hope you like these features and find them useful. Please let us know what you think.

SIMILAR POSTS

Share this Post
  • Digg
  •  
  • Sphinn
  •  
  • del.icio.us
  •  
  • Facebook
  •  
  • Mixx
  •  
  • Google Bookmarks
  •  
  • DZone
  •  
  • LinkedIn
  •  
  • Slashdot
  •  
  • Technorati
  •  
  • Twitter
 


출처 - http://blog.springsource.com/2011/02/21/spring-3-1-m1-mvc-namespace-enhancements-and-configuration/



Posted by linuxism
,