3.2. オンライン版クラウド拡張開発プロジェクトの作成¶
目次
本ガイドラインでは、クラウドネイティブなアプリケーション向けの開発プロジェクトを作成する方法について説明する。
Macchinetta Server Framework for Java (1.x) の マルチプロジェクト構成のブランクプロジェクト を元に、カスタマイズを加えることで作成する。
3.2.1. 開発プロジェクトの作成¶
開発プロジェクトの作成方法は、 Macchinetta Server Framework for Java (1.x) Development Guideline 開発プロジェクトの作成 を参照されたい。
3.2.2. 開発プロジェクトのカスタマイズ¶
開発プロジェクトの作成 で作成したプロジェクトを クラウドネイティブなアプリケーション向けにカスタマイズが必要な箇所がいくつか存在する。
カスタマイズが必要な箇所を以下に示す。
3.2.2.1. Spring Bootの利用¶
Spring Cloud との親和性を高めるために、 オンライン版クラウド拡張開発プロジェクトでは Spring Boot を利用する。 Spring Bootを使用すると、プロジェクトのアーカイブ方式として「実行可能jarファイル」と「デプロイ可能warファイル」が選択できるが、 本ガイドラインでは、Macchinetta Server Framework for Java (1.x) のノウハウを活用するため、デプロイ可能warファイル を採用する。 Spring Bootを使用したデプロイ可能warファイルの作成方法の詳細はSpring Bootの公式リファレンス Traditional deployment を参照されたい。
Spring Bootを使用するとBean定義など多くの設定が自動で行われる。
このような自動設定の仕組みのことをSpring Boot Auto-configuration
といい、アプリケーション開発者は最小限の設定を行うだけでアプリケーションを構築することができる。
詳しくはSpring Bootの公式リファレンス Auto-configuration を参照されたい。
3.2.2.2. 依存ライブラリの追加¶
- project/pom.xml (プロジェクトrootのParent POM)
<dependencyManagement>
<!-- (1) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencyManagement>
- project/xxx-web/pom.xml
<dependencies>
<!-- (2) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- (3) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- (4) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependencies>
項番
|
依存ライブラリ
|
説明
|
---|---|---|
(1)
|
spring-cloud-dependencies |
Spring Cloud のBOM。
プロジェクトのParent POMのdependencyManagement に定義することで、Spring Cloud関連の依存ライブラリのバージョンを解決する。
1.1.1.RELEASE で利用するOSSのバージョンについては、 フレームワークスタック を参照されたい。 |
(2)
|
spring-boot-starter |
Spring Boot の機能を実現するために必要なライブラリの依存関係を集約したもので、 Spring Boot特有のAuto-configuration、ロギング、YAMLなどが利用できるようになる。 |
(3)
|
spring-boot-configuration-processor |
spring-boot-configuration-processor の依存ライブラリを追加することで、
Spring Bootの@ConfigurationProperties アノテーションを使用して定義したプロパティのメタデータを生成することができる。
詳細については、Spring Boot公式リファレンス
Generating your own meta-data using the annotation processor
を参照されたい。 |
(4)
|
spring-cloud-config-client |
Spring Cloud Config を利用するための依存ライブラリ。
spring-cloud-config-client に依存したSpring Bootアプリケーションとしてビルドすることで、Spring Cloud Configが利用できる。 |
3.2.2.3. エントリポイントの作成¶
Spring Bootを利用して、デプロイ可能なwarファイルを作成するために必要な設定クラスを作成する。 このクラスはSpring Bootのエントリポイントとして、アプリケーションの起動時に読み込まれ、Springアプリケーションに必要なサーブレットやフィルタ等の情報を設定する。
Bootstrap.java
package com.example.xxx.app;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ImportResource;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.web.servlet.WebMvcMetricsAutoConfiguration;
//(1)
@ImportResource({ "classpath*:META-INF/spring/applicationContext.xml", "classpath*:META-INF/spring/spring-security.xml",
"classpath*:/META-INF/spring/spring-mvc.xml"}) //(2)
//(3)
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,
JmxAutoConfiguration.class, WebMvcAutoConfiguration.class,
SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class,
WebMvcMetricsAutoConfiguration.class })
//(4)
public class Bootstrap extends SpringBootServletInitializer {
//(5)
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
//(6)
setRegisterErrorPageFilter(false);
return application.sources(Bootstrap.class);
}
}
項番
|
説明
|
---|---|
(1)
|
Spring Frameworkのアノテーションコンフィグの仕組みである@ImportResource を使用してXMLのBean定義ファイルを読み込んでいる。
ここでは、classpath*:META-INF/spring/配下のapplicationContext.xml 、spring-security.xml を読み込むように設定している。 |
(2)
|
web.xml においてDispathcerServlet のcontextConfigLocation で指定していたclasspath*:/META-INF/spring/spring-mvc.xml を追加する。
Spring Bootを使用した際の制約で、DispatcherServlet ではなくエントリポイントでロードする必要がある。
詳細は DIコンテナの構築タイミングによりSpring Bootの機能が一部動作しない を参照されたい。 |
(3)
|
@EnableAutoConfiguration の exclude 属性を使用することで、特定のコンフィギュレーションクラスをAuto-configurationの適用対象から除外できる。
本ガイドラインで作成するプロジェクトでは、
DataSourceAutoConfiguration 、JmxAutoConfiguration 、WebMvcAutoConfiguration 、SecurityAutoConfiguration 、ManagementWebSecurityAutoConfiguration 、WebMvcMetricsAutoConfiguration を除外する必要がある。 |
(4) (5)
|
デプロイ可能なwarファイルを作成するためにSpringBootServletInitializer を継承したクラスを作成し、configure メソッドをオーバーライドする。
この実装を行うことで、通常はSpringが提供するContextLoaderListener が行っているサーブレットコンテキストの構築がSpring Bootによって行われる。 |
(6)
|
エラー画面表示の不具合 への対応。Spring BootのErrorPageFilter を無効にしている。 |
Note
項番(3)で説明されているAuto-configurationクラスについて、除外対象のクラスと除外理由は以下の通り。
除外対象クラス 説明DataSourceAutoConfiguration
データソースを設定するAuto-configurationクラス。Spring Bootがデータソースが一つであることを想定しているため、 Macchinetta Server Framework for Java (1.x) のブランクプロジェクトのように複数のデータソースが定義されている場合、 NoUniqueBeanDefinitionException
が発生する。 これを回避するにはDataSourceAutoConfiguration
をAuto-configurationから除外するか、 データソースの1つにprimary=true
を設定する必要がある。 このクラスを除外せずに複数のデータソースを定義する方法は、Spring Boot公式リファレンス Configure Two DataSource を参照されたい。JmxAutoConfiguration
JMXを設定するAuto-configurationクラス。デフォルトでは同一サーバに複数のAPを起動した場合、 JMXのドメインが重複してBeanが登録できず UnableToRegisterMBeanException
が発生するため除外する。WebMvcAutoConfiguration
Spring MVCを設定するAuto-configurationクラス。 除外しない場合、 <mvc:view-resolvers>
で作成したBeanが上書きされてしまうため不具合が発生する。 詳細は WebMvcAutoConfigurationによる不具合 を参照されたい。SecurityAutoConfiguration
Spring Securityを設定するAuto-configurationクラス。 SecurityAutoConfiguration
が有効である場合、WebSecurityConfigurerAdapter
を継承したSpringBootWebSecurityConfiguration.DefaultConfigurerAdapter
をBean生成しようとするが、DefaultConfigurerAdapter
をBean生成する際に必要となるObjectPostProcessor
がないためにNoSuchBeanDefinitionException
が発生する。 事象を回避する実装としてSecurityAutoConfiguration
をAuto-configurationから除外する。 他にもSecurityConfigurationクラスに@EnableWebSecurity
を付与する実装もあるが、本ガイドラインでは XMLConfigの利用 に則る。ManagementWebSecurityAutoConfiguration
Spring Securityを設定するAuto-configurationクラス。 ヘルスチェック にて導入する Spring Boot Actuator
が有効である場合、SecurityAutoConfiguration
に加え、ManagementWebSecurityAutoConfiguration
も有効化され、SecurityAutoConfiguration
と同様の事象が発生する。 事象を回避する実装としてManagementWebSecurityAutoConfiguration
をAuto-configurationから除外する。WebMvcMetricsAutoConfiguration
WebMvcMetricsFilterを設定するAuto-configurationクラス。 ヘルスチェック にて導入する Spring Boot Actuator
が有効である場合、CharacterEncodingFilter
よりも先にWebMvcMetricsFilter
が適用されてしまい正しくエンコードできない不具合が発生する。 事象を回避する実装としてWebMvcMetricsAutoConfiguration
をAuto-configurationから除外する。
- web.xml
エントリポイントの作成にともなって、web.xmlに下記変更を加える。
<!-- (1) -->
<!-- 削除 ここから -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:META-INF/spring/applicationContext.xml
classpath*:META-INF/spring/spring-security.xml
</param-value>
</context-param>
<!-- 削除 ここまで -->
<!-- omitted -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value> <!-- (2) -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
項番
|
説明
|
---|---|
(1)
|
ContextLoaderListener を削除する。
SpringBootServletInitializer でContextLoaderListener を登録しているので、web.xml での定義は不要になる。 |
(2)
|
DIコンテナの構築タイミングによりSpring Bootの機能が一部動作しない への対処。
DispathcerServlet のcontextConfigLocation 属性の設定値を削除する。
contextConfigLocation 属性を削除してしまうと例外が発生するので、
空を設定することにより、 DispathcerServlet にダミーのコンテキストを設定しこれを回避する。
また、自分でダミーファイルをデフォルト指定(WEB-INF/appServlet-servlet.xml )に
作成することで、contextConfigLocation 属性を削除しても例外が発生しなくなる。 |
- application.yml
エントリポイントの作成にともなって、application.ymlに下記変更を加える。
spring:
main:
# (1)
allow-bean-definition-overriding: true
項番 | 説明 |
---|---|
(1)
|
Spring BootのAutoConfigurationで自動生成されるBeanに対しXML等で定義したBeanで上書きを許容するかの設定。本ガイドラインではXMLによるBean定義で上書きするため
allow-bean-definition-overriding: true のプロパティを設定する。 |
3.2.3. オンライン版クラウド拡張開発プロジェクトで考慮すべき点・制約事項¶
オンライン版クラウド拡張開発プロジェクトを作成するにあたり、下記について考慮しなければならない。
3.2.3.1. Spring Boot使用に伴う制約事項¶
3.2.3.1.1. Logbackの拡張の利用¶
Spring BootではLogbackの拡張を行っており追加の設定を行うことができるが、
デフォルトの設定ファイル名(logback.xml
)では読み込みのタイミングが早すぎるため、Spring BootによるLogbackの拡張を利用することができない。
この拡張を利用するには、Spring Bootではデフォルトのファイル名ではなく、-spring
のサフィックスを付けたlogback-spring.xml
を使用する必要がある。
詳細は、Spring Bootの公式リファレンス Custom log configuration を参照されたい。
また、Logbackの設定例は Macchinetta Server Framework for Java (1.x) Development Guideline Logbackの設定 を参照されたい。
Warning
Spring Cloud Configを利用しlogging.path
の設定値をConfigサーバに持たせる場合、Configサーバからプロパティを取得まするまでの間のログが意図しないディレクトリに出力されてしまう。
これはCustom log configurationに記載されているような設定ファイル名が存在するとSpring Bootが自動で読み込んでしまうが、logging.path
が未解決のためログの出力先を制御することができないため発生する。
logbackを利用する場合、logback.xml
とlogback-spring.xml
以外の名前を利用すれば良い。
設定ファイル名をSpring Bootが読み込みに行かない独自のファイル名に設定し、logging.config
のプロパティを設定することで意図しないログ出力を制御することができる。
この方法を取った場合、logbackが読み込まれてからlogging.path
が解決されるまでの間のログはSpring Bootのデフォルトの設定で標準出力に出力される。
設定ファイル名をappName-logback-spring.xml
とし、Configサーバに持たせるapplication-development.yml
にプロパティを設定した場合の例を以下に示す。
- application-development.yml
logging: config: classpath:appName-logback-spring.xml
Configサーバの使用方法については、 環境依存値の外部管理 を参照されたい。
3.2.3.2. Spring Bootで組み込みTomcatを使用しない場合の制約事項¶
組み込みTomcatを使用しない場合、以下の制約が発生する。
- DIコンテナの構築タイミングによりSpring Bootの機能が一部動作しない
- トランザクショントークンチェックを使用するための設定方法が異なる
- Spring Boot Actuatorのエンドポイントのポートが変更できない
- Spring Cloud Configのリフレッシュ機能が使用できない
- Filterが自動で登録され意図しない動作が発生する
3.2.3.2.1. DIコンテナの構築タイミングによりSpring Bootの機能が一部動作しない¶
DIコンテナの構築タイミングによって、Spring Bootの機能が一部動作しないことがある。
例えば、DispatcherServlet
で行われたコンポーネントスキャンでは、Spring Boot ActuatorにCustom HealthIndicatorを@Component
で定義しても動作させることができない。
Spring Bootではエントリポイントで、ContextLoaderListener
を登録し、コンテキストの読み込みを行っている。
また、Macchinetta Server Framework for Java (1.x) のブランクプロジェクトではDispatcherServlet
でもコンテキストの読み込みを行っている。
読み込みは、ContextLoaderListener
、DispatcherServlet
の順で行われるため、
DispatcherServlet
側で行われたコンポーネントスキャンでは、Spring Bootの機能への組み込みに間に合わず、動作しないことがある。
正常に動作させるためには、DispatcherServlet
で読み込みを行っていたXMLファイルをエントリポイントで読み込む必要がある。
Note
Custom HealthIndicatorの例はあくまで一例であり、類似の意図しない動作が発生する可能性があるためDIコンテナの構築には注意されたい。
3.2.3.2.2. トランザクショントークンチェックを使用するための設定方法が異なる¶
Macchinetta Server Framework for Java (1.x) Development Guideline トランザクショントークンチェックを使用するための設定 に記載されている設定方法を使用してもトランザクショントークンチェックが正常に動作しない。 これは、組み込みTomcatを使用しない場合にSpring BootによるrequestDataValueProcessorの 上書きが行われることにより、JSPにトランザクショントークンが埋め込まれないためである。
以下のような実装を行うことでトランザクショントークンチェックを有効にすることが可能である。
- RequestDataValueProcessorPostProcessor
// (1)
public class RequestDataValueProcessorPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// (2)
ConstructorArgumentValues cav = new ConstructorArgumentValues();
List<RequestDataValueProcessor> values = new ArrayList<RequestDataValueProcessor>();
values.add(new TransactionTokenRequestDataValueProcessor());
values.add(new CsrfRequestDataValueProcessor());
cav.addGenericArgumentValue(values);
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(CompositeRequestDataValueProcessor.class, cav, null);
// (3)
registry.removeBeanDefinition("requestDataValueProcessor");
registry.registerBeanDefinition("requestDataValueProcessor", rootBeanDefinition);
}
}
項番 | 内容 |
---|---|
(1)
|
BeanDefinitionRegistryPostProcessor を実装することで、Beanのインスタンス化前にBean定義の変更を行うことができる。 |
(2)
|
Bean定義を行うオブジェクトを生成する。
ここでは、
TransactionTokenRequestDataValueProcessor とCsrfRequestDataValueProcessor を併用する CompositeRequestDataValueProcessor を定義している。Note トランザクショントークンチェックとCSRFトークンチェックを併用したい場合、 |
(3)
|
作成した
CompositeRequestDataValueProcessor オブジェクトでDIコンテナにrequestDataValueProcessorのBean名で登録されたオブジェクトを上書きする。 |
- xxx-web/src/main/resources/META-INF/spring/spring-mvc.xml
<!-- (1) -->
<bean class="com.example.xxx.app.RequestDataValueProcessorPostProcessor"/>
項番 | 内容 |
---|---|
(1)
|
作成したBean定義の上書きを行うクラスのBean定義を行う。 |
3.2.3.2.3. Spring Boot Actuatorのエンドポイントのポートが変更できない¶
組み込みTomcatを使用しない場合、Spring Boot Actuatorのエンドポイントが使用するポートを アプリケーションが使用するポートと別に設定することができない。 そのため、クラウドベンダが提供するロードバランサの機能を使用してエンドポイントのURLへの外部アクセスを遮断する必要がある。
詳細については、 ヘルスチェック を参照されたい。
3.2.3.2.4. Spring Cloud Configのリフレッシュ機能が使用できない¶
Spring Cloud Configを使用して構築したConfig Clientは設定変更を反映させるrefreshエンドポイント を利用することができるが、組み込みTomcatを使用しない場合は当該機能を利用することができない。
詳細については、 環境依存値の外部管理 を参照されたい。
3.2.3.2.5. Filterが自動で登録され意図しない動作が発生する¶
Spring Bootのデフォルトでは、アプリケーションコンテキスト上のすべてのFilter
を自動で登録する。
本ガイドラインでは、Filter
の登録をweb.xmlを使用して行っているため、Filter
が二重登録されるなど意図しない動作が発生する可能性がある。
以下のような実装を行うことでフィルタの自動登録を制御することが可能である。
- DefaultFiltersBeanFactoryPostProcessor
//(1)
public class DefaultFiltersBeanFactoryPostProcessor implements
BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) throws BeansException {
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) bf;
//(2)
String[] beanNames = beanFactory.getBeanNamesForType(Filter.class);
for (String beanName : beanNames) {
BeanDefinition definition = BeanDefinitionBuilder
.genericBeanDefinition(FilterRegistrationBean.class)
.setScope(BeanDefinition.SCOPE_SINGLETON)
.addConstructorArgReference(beanName)
.addConstructorArgValue(new ServletRegistrationBean[] {})
.addPropertyValue("enabled", false).getBeanDefinition();
beanFactory.registerBeanDefinition(beanName
+ "FilterRegistrationBean", definition);
}
}
}
項番 | 内容 |
---|---|
(1)
|
BeanFactoryPostProcessor を実装することで、Beanのインスタンス化前にプロパティの変更を行うことができる。 |
(2)
|
ConfigurableListableBeanFactory の実装クラスであるDefaultListableBeanFactory からデフォルトで登録されるFilter のBean名を取得し、すべてのFilter を無効にしている。 |
- xxx-web/src/main/resources/META-INF/spring/spring-mvc.xml
<!-- (1) -->
<bean class="com.example.xxx.app.DefaultFiltersBeanFactoryPostProcessor"/>
項番 | 内容 |
---|---|
(1)
|
作成したDefaultFiltersBeanFactoryPostProcessor のBean定義を追加する。 |
3.2.3.3. WebMvcAutoConfigurationによる不具合¶
Spring BootのAuto-configurationにより設定されるWebMvcAutoConfiguration
によって
<mvc:view-resolvers>
で作成したBeanが上書きされてしまうことで下記不具合が発生する。
これらの問題に対処したソースコード例は エントリポイントの作成 を参照されたい。
3.2.3.3.1. ViewResolverが上書きされViewの解決ができない¶
Macchinetta Server Framework for Java (1.x) Development Guideline HTMLを応答する
に従いTilesの連携におけるBean定義<mvc:view-resolvers>
を使用していると、Viewの解決ができなくなる不具合が発生する。
これは、Spring Bootを非組み込みTomcatで使用する場合に、<mvc:view-resolvers>
で定義した
ViewResolver
がWebMvcAutoConfiguration
によって上書きされてしまいViewの解決ができなくなるからで、
WebMvcAutoConfiguration
をAuto-configurationから除外することで回避できる。
3.2.3.3.2. エラー画面表示の不具合¶
Spring Bootを非組み込みTomcatで使用する場合、上記のViewResolver
が上書きされてしまうことに加え、
デフォルトで動作するErrorPageFilter
が意図せぬ動作をしてしまうことで、
システム例外発生時などで定義したエラー画面が表示されず、真っ白な画面が表示されてしまう。
これは、エントリポイントのconfigure
メソッドでErrorPageFilter
を無効化することと、
WebMvcAutoConfiguration
をAuto-configurationから除外することで回避できる。