WicketとSpringを悪魔合体させる その2
0. ではがったいさせるぞ(通算二度目)
WicketとSpringを悪魔合体させるアプローチとして、前回の記事では
- WicketをベースにSpringのDIを個別に組み込む
- 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クラスから利用できるようになる。
ページに値を張った結果はこんな感じ。
あとは、ビジネスロジックやデータアクセスに関する部分は、Springのもつ強力な機能を活用すればいいだろう。
最近のコメント