タツノオトシゴのブログ

主にJavaに関するものです。

Without XML Configuration for Spring MVC

Spring3.1から追加された機能のJavaConfig機能を利用して、XMLを使用しない設定方法を説明します。

用意するもの

Javaクラスのファイル XMLを用いる場合 用途
MyWebapplicationInitializer.java web.xml 文字コード変換用のフィルタやSpring関連の初期化を行う。
WebConfig.java servlet-context.xml Spring MVC関連のSpringBeanの定義ファイル
AppConfig.java ApplicationContext.xml 業務ロジック、DAOなどの共通のSpringBean設定ファイル。

ファイル構成(XMLを使用しない)

  • AppConfig.javaや、パッケージ「sample.core」以下は、プログラムによっては存在しない。
  • パッケージ「sample.web」以下に、設定関連のクラスを配置する。ComponentScanの記述で、sample.web.xxxx以下のパッケージを自動的に走査するようにする。
<プロジェクトフォルダ>
├─src
│  ├─main
│  │  ├─java
│  │  │  └─sample
│  │  │      ├─core
│  │  │      │  └AppConfig.java <=== 業務ロジック、DAO用のSpringのJavaConifigクラス。
│  │  │      │  
│  │  │      │  ├─service <==== 業務ロジックのクラスを格納
│  │  │      │  └─dao     <==== DAOのクラスを格納
│  │  │      │  └─dto     <==== DTO(Entity)クラスを格納。
│  │  │      │  
│  │  │      └─web
│  │  │          ├─MyWebapplicationInitializer.java <=== web.xmlに定義するフィルタなどの定義。
│  │  │          ├─WebConfig.java <=== |Spring MVC関連のSpringのJavaConifigクラス。
│  │  │          ├ common <== 業務ごとのControllerクラスなどを格納する。
│  │  │          ├ admin  <== 業務ごとのControllerクラスなどを格納する。
│  │  │          ・・・
│  │  ├─resources
│  │  │  └─message
│  │  └─webapp
│  │      └─WEB-INF
│  │          ├─lib
│  │          ├─resources
│  │          │  ├─css
│  │          │  ├─image
│  │          │  └─js
│  │          └─view
│  └─test
│      ├─java
│      └─resources
└ pom.xml

MyWebapplicationInitializer.java

  • インタフェース「org.springframework.web.WebApplicationInitializer」を実装したクラスで、web.xmlに記載するフィルタなどを定義する。
  • Servlet3から追加された、アノテーション@Filterを使用してもよいが、CharacerEncodingFilterなど予め存在するクラスはできないので、ここでまとめて行う。
  • 業務ロジックやDAOなどのJavaConfig、MVCのJavaConfigを読み込み初期化を行う。
package sample.web;

import java.util.EnumSet;

import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import jp.sf.amateras.functions.filter.FunctionsFilter;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.DispatcherServlet;

import sample.core.ApplicationConfig;


/**
 * Spring MVCの初期化用クラス。
 * <p>web.xmlに記載するDispacherクラスなどを定義する。
 * <p>サンプル:http://www.rockhoppertech.com/blog/spring-mvc-configuration-without-xml/
 *
 */
public class MyWebapplicationInitializer implements WebApplicationInitializer {
    
    @Override
    public void onStartup(final ServletContext servletContext) throws ServletException {
        
        // EncodingFilterの登録
        final FilterRegistration characterEncodingFilter = servletContext.addFilter("CharacterEncodingFilter", CharacterEncodingFilter.class);
        characterEncodingFilter.setInitParameter("encoding", "UTF-8");
        characterEncodingFilter.setInitParameter("forceEncoding", "true");
        characterEncodingFilter.addMappingForUrlPatterns(null, false, "/*");
        
        // Amateras Java Standard EL FunctionsのFilterの登録
        final FilterRegistration functionsFilter = servletContext.addFilter("functionsFilter", FunctionsFilter.class);
        functionsFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "*");
        
        // Service/DAOなどのAppConfigの初期化 
        final AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(ApplicationConfig.class);
        rootContext.refresh();
        servletContext.addListener(new ContextLoaderListener(rootContext));
        
        // Spring MVC関連のAppConfigの初期化
        final AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
        mvcContext.register(WebConfig.class);
        
        // DispatcherServletの登録
        final ServletRegistration.Dynamic appServlet = 
                servletContext.addServlet("dispatcher", new DispatcherServlet(mvcContext));
        appServlet.setAsyncSupported(true);
        appServlet.setMultipartConfig(
                new MultipartConfigElement(null, 10000, 1000000, 1000000));
        appServlet.setLoadOnStartup(1);
        appServlet.addMapping("/");
        
    }
    
}

WebConfig.java

  • Spring MVCは初期登録するBeanが多くあるため、抽象クラス「org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter」を継承する。
  • クラスにアノテーション「@EnaleWebMvc」を付ける。これは、XMLの場合でいうXML Schemaの仕様定義で、タグを使用する代わりと覚えておけばよい。
  • リソースファイルやインターセプターなど、通常のSpringのJavaConfigでは記載ができないものは、メソッドが用意されており、それらを継承して定義する。
  • Component Scanの定義として正規表現を利用すると、任意のパッケージsample.web.が読みこめるようになるため、パッケージを追加するごとに定義を追加する必要がない。
package sample.web;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;


/**
 * Spring MVCのJavaCofnig
 *
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages="sample.web", 
    excludeFilters={@ComponentScan.Filter(type=FilterType.REGEX, pattern={"(\\..*)+"})})
public class WebConfig extends WebMvcConfigurerAdapter {
    
    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/js/**").addResourceLocations("/WEB-INF/resources/js/");
        registry.addResourceHandler("/css/**").addResourceLocations("/WEB-INF/resources/css/");
        registry.addResourceHandler("/images/**").addResourceLocations("/WEB-INF/resources/images/");
    }
    
    // 標準ValidatorをBeanValidationにする。
    @Override
    public Validator getValidator() {
        final LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        return validator;
    }
    
    @Bean
    public ViewResolver viewResolver(){
        UrlBasedViewResolver resolver = new UrlBasedViewResolver();
        resolver.setViewClass(JstlView.class);
        resolver.setPrefix("/WEB-INF/view/");
        resolver.setSuffix(".jsp");
        return resolver;
    }
    
}

AppConfig.java

  • 業務ロジックやDAOなどプレゼンテーション層のプログラムとは異なるもの定義する。
  • Springの通常のAppConfigを使って定義する。
package sample.core;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;


/**
 * Springの設定ファイル
 *
 */
@Configuration
@ComponentScan(basePackages={"sample.core.service", "sample.core.dao"})
@PropertySource(value={"classpath:app.properties"})
public class AppConfig {
    
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:app",
                "classpath:message/messages", "classpath:message/label");
        return messageSource;
        
    }
    
    @Bean
    public MessageSourceAccessor messageSourceAccessor() {
        return new MessageSourceAccessor(messageSource());
    }
}

注意点など

  • web.xmlにしか定義できない、セッションのタイムアウトなどを指定する場合は、web.xmlを用意する。
  • 定義例を見てもらうとわかるが、コード量はXMLと変わらず量が多いので、既存のXMLを使った方が見やすいという場合がある。
  • Javaクラスによる定義のメリットとして、定義間違いをした際にコンパイルエラーでわかる場合がある。また、IDEリファクタリングによるクラス名を変更したときなど、呼び出し先も自動的に変更されるため、整合性を気にする必要がない。ただし、SpringBean名などクラス名に合わせる場合は、呼び出し先も変更する必要がある。
  • XMLの方が簡単に定義できる場合、AOPなどの定義、DAOのソースを自動生成した場合は、XMLファイルによる定義を使った方がよい場合があるため、そのような場合は、XMLと混在して利用する。ただし、複数人で開発する場合は混乱が生じるため、プロジェクトで規約を決めておいた方がよい。