« WicketとSpringを悪魔合体させる その1 | トップページ | Spring Batchで改行がめちゃくちゃなCSVと戦う »

2020年5月 4日 (月)

WicketとSpringを悪魔合体させる その2

0. ではがったいさせるぞ(通算二度目)

WicketとSpringを悪魔合体させるアプローチとして、前回の記事では

  1. WicketをベースにSpringのDIを個別に組み込む
  2. Spring BootをベースにフロントをWicketにする

のうち前者にチャレンジしてみたが、今回は後者をやってみる。

1. 準備

Spring Bootをベースにするので、まずは普通にSpring InitializrでSpring Bootなプロジェクトを作る。
この時のポイントは、テンプレートエンジンは入れないこと。Apache WicketはViewよりの機能を提供するので、画面組立はWicketにさせるためだ。

以下、pom.xmlのdependenciesを抜粋。

  <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<!-- Apache Wicket -->
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-spring</artifactId>
<version>${wicket.version}</version>
</dependency>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-core</artifactId>
<version>${wicket.version}</version>
</dependency>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-ioc</artifactId>
<version>${wicket.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

2. 普通のSpring Bootアプリケーションからいじるところ

まず、Spring BootにすることでKotlinのコードはsrc/main/kotlinに、ページに紐づくViewのHTML等それ以外のファイルはsrc/main/resourcesに、それぞれ配置されるようになるが、WicketのQuick Startで作ったプロジェクトではJavaのコードもページに紐づくHTMLも同じ場所に置かれるようになっている。

ここは趣味の問題と言えるけど、Wicketのしきたりに従うなら、pom.xmlのbuildセクションをいじってHTMLをsrc/main/kotlin以下に置けるようにするといい。

  <build>
<resources>
<resource>
<filtering>false</filtering>
<directory>src/main/resources</directory>
</resource>
<resource>
<filtering>false</filtering>
<directory>src/main/kotlin</directory>
<includes>
<include>**</include>
</includes>
<excludes>
<exclude>**/*.kt</exclude>
</excludes>
</resource>
</resources>
<testResources>
<testResource>
<filtering>false</filtering>
<directory>src/test/resources</directory>
</testResource>
<testResource>
<filtering>false</filtering>
<directory>src/test/kotlin</directory>
<includes>
<include>**</include>
</includes>
<excludes>
<exclude>**/*.kt</exclude>
</excludes>
</testResource>
</testResources>
<!-- 以下略 -->
</build>

またWicketには、クラシックなwarで使われるweb.xmlで設定している設定が必要になる。
Create a Wicket Quickstartで作ることができるmvn archetype:generateの結果には、以下のようなweb.xmlが含まれているはず。

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1">

  <display-name>test-wicket-app1</display-name>

  <!--
    There are three means to configure Wickets configuration mode and they
    are tested in the order given.

    1) A system property: -Dwicket.configuration
    2) servlet specific <init-param>
    3) context specific <context-param>

    The value might be either "development" (reloading when templates change) or
    "deployment". If no configuration is found, "development" is the default. -->

  <filter>
    <filter-name>wicket.test-wicket-app1</filter-name>
    <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
    <init-param>
      <param-name>applicationClassName</param-name>
      <param-value>net.formula97.webapps.WicketbootlinApplication</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>wicket.test-wicket-app1</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

Spring BootにすることでJava Configで書くことができるようになるので、これをもとにServletContextInitializerの実装クラスをConfigurationにして書いてやる。

  @Configuration
  class ServletInitializer: ServletContextInitializer {
    override fun onStartup(servletContext: ServletContext) {
      // web.xml の設定をもとに書く
      // WicketFilter は Apache Wicket の ServletFilter
      val registration: FilterRegistration = servletContext.addFilter("wicket.wicketbootlin", WicketFilter::class.java)
      registration.setInitParameter(WicketFilter.APP_FACT_PARAM, SpringWebApplicationFactory::class.java.name)

      // メインクラスのFQCNをここで指定する
      registration.setInitParameter("applicationClassName", WicketbootlinApplication::class.java.name)
      // いわゆるサーブレットフィルタ部分
      registration.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, "/*")
      registration.addMappingForUrlPatterns(null, false, "/*")

      // 起動モード指定
      // spring.profiles.active の値に応じて development と deployment を切り替えるとかもアリだろう
      registration.setInitParameter("configuration", "development")
    }
  }

最後にエントリーポイントになるWebApplicationだが、Spring Bootのエントリーポイントは、作った直後ではこうなっているはず。

  @SpringBootApplication
class WicketbootlinApplication
companion object { @JvmStatic fun main(args: Array) { runApplication(*args) } }

なので、エントリーポイントのクラスをWebApplicationの継承クラスにして、getHomePage()とinit()を実装する。

  @SpringBootApplication
  class WicketbootlinApplication: WebApplication() {
    @Autowired
    private lateinit var applicationContext: ApplicationContext

    companion object {
      @JvmStatic
      fun main(args: Array) {
        runApplication(*args)
      }
    }

    override fun getHomePage(): Class {
      return HomePage::class.java
    }

    override fun init() {
      super.init()

      // レスポンスとマークアップ時のエンコーディングをUTF-8にする
      requestCycleSettings.responseRequestEncoding = CharEncoding.UTF_8
      markupSettings.defaultMarkupEncoding = CharEncoding.UTF_8

      // ComponentScanの結果を反映
      // これで Wicket から Spring による DIコンポーネントを使うことができるようになる
       componentInstantiationListeners.add(SpringComponentInjector(this, applicationContext))

      // todo ページルーティングを書く
      mountPage("/", HomePage::class.java)
    }
  }

これでフロントがWicketのSpring Bootアプリケーションになったので、

  @Service
  interface EnterpriseMessage {
    fun getMessage(): String
    fun getVersionCode(): String
  }

こんなServiceを

  class HomePage(params: PageParameters): WebPage(params) {
    // Page クラスは Spring の配下にあるわけではないので、
    // Autowired ではなく org.apache.wicket.spring.injection.annot.SpringBean を使う
    @SpringBean
    private lateinit var enterpriseMessage: EnterpriseMessage
init { // ページに値を張る add(Label("message", Model.of(enterpriseMessage.getMessage()))) add(Label("versionCode", Model.of(enterpriseMessage.getVersionCode()))) } }

とやることでPageクラスから利用できるようになる。
ページに値を張った結果はこんな感じ。
Injected

あとは、ビジネスロジックやデータアクセスに関する部分は、Springのもつ強力な機能を活用すればいいだろう。

« WicketとSpringを悪魔合体させる その1 | トップページ | Spring Batchで改行がめちゃくちゃなCSVと戦う »

コメント

コメントを書く

(ウェブ上には掲載しません)

« WicketとSpringを悪魔合体させる その1 | トップページ | Spring Batchで改行がめちゃくちゃなCSVと戦う »

2023年12月
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

最近のトラックバック

無料ブログはココログ