Use Mixer2 with Spring MVC

SpringMVCは、 DIコンテナとして有名なSpringのサブプロジェクトとして提供されているWebアプリケーションフレームワークです。

Mixer2には、Spring MVC におけるViewResolverおよび抽象Viewクラスも同梱されています。 ViewのテンプレートとしてJSPを使用する通常のControllerクラスの書き方とほとんど同じ形で 普通のhtmlテンプレートをViewとして使用することができます。

Configuration

ビューエンジンとして、Mixer2と通常のJSPの両方を使えるようにする場合、 SpringMVCのDispatcherServlet用のDI定義は次のようになります。

    <!-- other configuration... -->
    <context:component-scan base-package="com.example.yourproject" />
    <mvc:annotation-driven />
    <mvc:resources mapping="/m2static/**" location="classpath:/m2mockup/m2static/" />
    <bean id="mixer2Engine" class="org.mixer2.Mixer2Engine" />
    <bean class="org.mixer2.spring.webmvc.Mixer2XhtmlViewResolver">
        <property name="order" value="1" />
        <property name="prefix" value="classpath:m2mockup/m2template/" />
        <property name="suffix" value=".html" />
        <property name="basePackage" value="com.example.yourproject.web.view" />
        <property name="mixer2Engine" ref="mixer2Engine" />
    </bean>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="order" value="2" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- other configuration... -->

ただし、最近のSpringFrameworkはXML定義よりもJavaConfiguration方式を推奨しています。 JavaConfigを使う場合は下のような形になるでしょう。

@Configuration
@EnableAutoConfiguration
@EnableWebMvc
@ComponentScan("com.example.yourproject")
public MyConfig extends WebMvcConfigurerAdapter {

    @Bean
    public Mixer2Engine mixer2Engine() {
        return new Mixer2Engine();
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/m2static/**")
                .addResourceLocations("classpath:/m2mockup/m2static/");
    }

    @Bean
    public Mixer2XhtmlViewResolver mixer2XhtmlViewResolver() {
        Mixer2XhtmlViewResolver resolver = new Mixer2XhtmlViewResolver();
        resolver.setPrefix("classpath:m2mockup/m2template/");
        resolver.setSuffix(".html");
        resolver.setBasePackage("com.example.yourproject.web.view");
        resolver.setMixer2Engine(mixer2Engine);
        resolver.setOrder(1);
        return resolver;
    }

    @Bean
    public InternalResourceViewResolver internalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/jsp/");
        resolver.setSuffix(".jsp");
        resolver.setOrder(2);
        return resolver;
    }
}

上記の設定は、下のようなソースコードツリー構造を想定しています。 maven標準ディレクトリ構造に習っていますが、 classpathやドキュメントルートからの階層が合っていれば、他の構造でも問題はありません。

src/
└── main/
    ├── java/
    │   └── com/
    │       └── example/
    │           └── yourproject/
    │               ├── Config.java // JavaConfig方式の場合
    │               ├── service/
    │               └── web/
    │                   ├── controller/
    │                   │   ├── FooController.java
    │                   └── view/
    │                       ├── FooView.java
    │
    ├── resources/
    │   ├── mvc-dispatcher-servlet.xml // XML設定方式の場合
    │   └── m2mockup/
    │       ├── m2static/
    │       │   ├── css/
    │       │   │   └── main.css
    │       │   └── img/
    │       │       └── sample.png
    │       └── m2template/
    │           └── foo.html
    └── webapp/
        └── WEB-INF/
            └── jsp/
                └── bar.jsp

Controller code

FooControllerクラスで、"/foo"というURIへのアクセスをMixer2で処理し、 "/bar"というURIへのアクセスをJSPで処理する場合、 Controllerのコードは下記のようになります。

@Controller
public class IndexController {
    @RequestMapping(value = "/foo", method = RequestMethod.GET)
    public String foo(Model model) {
        model.addAttribute("someMessage", "Life is beautiful");
        // attach to FooView class with foo.html template
        // by Mixer2ViewResolver .
        return "foo";
    }

    @RequestMapping(value = "/bar", method = RequestMethod.GET)
    public String bar() {
        // attach to bar.jsp by InternalResourceViewResolver .
        return "bar";
    }
}

View code

"/foo"へのアクセスに対するViewクラスとしてFooViewを作る必要があります。 下記のようなコードになります。

Viewクラスに対して@Componentなどを指定する必要はありません。 前述のDI定義でbasePackageとしてcom.example.yourproject.web.viewを指定したため、 Mixer2ViewResolverはその配下のViewクラスのコンポーネント化とDIを自動的に行います。

...
import org.mixer2.spring.webmvc.AbstractMixer2XhtmlView;
...

public class FooView extends AbstractMixer2XhtmlView {

    // You can use any bean in DI container.
    @Autowired
    private HelloWorldBean helloWorldBean;

    @Override
    protected Html renderHtml(Html html, Map<String, Object> model, HttpServletRequest request,
            HttpServletResponse response) {

        // Mixer2ViewResolver has load "foo.html" template
        // into "html" variable automatically.
        // So, You can modify it.
        Div div = html.getById("hellomsg", Div.class);
        div.replaceInner(helloWorldBean.getHelloWorldMessageString());

        // Of course, you can use model that is delivered from controller.
        String someMessage = (String) model.get("someMessage");
        div.replaceInner(someMessage);

        // replace static file path.
        // For example: <img src="../m2static/img/sample.png" />
        // will be changed to <img src="[contextPath]/m2static/img/sample.png" />
        Pattern pattern = Pattern.compile("^\\.+/.*m2static/(.*)$");
        String ctx = request.getContextPath();
        PathAdjuster.replacePath(html, pattern, ctx + "/m2static/$1");

        return html;
    }

    @Override
    protected String modifyHtmlStringHook(String htmlStr) {
        StringBuilder sb = new StringBuilder();
        sb.append("<!--[if lt IE 9]><script src=\"/static/html5shiv.js\"></script><![endif]-->");
        sb.append("</head>");
        return htmlStr.replaceFirst("</head>", sb.toString());
    }

}

TIPS

  • html テンプレートファイルは必須です。Controllerクラスのメソッドが返すViewNameに該当するhtmlテンプレートファイルが見つからない場合、次のビューリゾルバーに処理を移譲します。
  • Viewクラスの作成は任意です。もしもControllerクラスのメソッドが返すViewNameに該当するViewクラスが見つからない場合、Mixer2ViewResolverはデフォルトのビュークラスを生成し、テンプレートファイルの内容をそのまま返します。
  • mixer2のhtmlタグ型インスタンスでは操作できないことを行いたい場合(たとえばhtmlコメントを埋め込む)には、 modifyHtmlStringHook()をオーバーライドしてください。http resuponseされる直前のhtmlをStringとして操作可能です。 See javadoc for details.

Fruit shop sample application

ECサイトを想定した簡単なサンプルアプリケーションもあります。 Fruit shop sample application for SpringMVC and SpringBoot をご覧ください。