SprignMVC + Tomcat Embeddedで作成したAPをJar化し、起動する際のメモ

Spring Bootを使用せず、Spring MVCとTomcat Embeddedを使用し、Webアプリケーションを作成した際に発生した事象について、備忘録として記録する。

### 環境
・Spring MVC 4.3.2.RELEASE
・Tomcat Embedded 8.5.2
・Maven

### 事象
Webapp Runnerを参考に、AP起動時に一時ディレクトリにTomcatのベースディレクトリ作成、及び、Jarファイルの展開を実施後、web.xmlファイルをロードさせ、Springの起動時に以下の例外が発生。

```application.log
ERROR org.springframework.web.servlet.DispatcherServlet - Context initialization failed
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/mvc]
Offending resource: class path resource [WEB-INF/spring/appServlet/servlet-context-thymeleaf.xml]

	at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:70)
	at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)
	at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:80)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.error(BeanDefinitionParserDelegate.java:301)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1408)
	at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1401)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:172)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:142)
	at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:94)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:508)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:392)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
	at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
	at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
	at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125)
	at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94)
	at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
	at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:612)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:513)
	at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:668)
	at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:634)
	at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:682)
	at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:553)
	at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:494)
	at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
	at javax.servlet.GenericServlet.init(GenericServlet.java:158)
	at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1194)
	at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1110)
	at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1000)
	at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4919)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5229)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:152)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1405)
	at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1395)
	at java.util.concurrent.FutureTask.run(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
```

### 原因
Jar化する際に、「maven-assembly-plugin」プラグインを使用し、行っていたが、その際、META-INF内に格納される「spring.schemas」「spring.handlers」がSpringのJarに含まれているものと異なるため、スキーマ解決が出来ず、エラーが発生。

### 対処方法
「maven-shade-plugin」を使用し、以下の定義でJar化する。

```pom.xml定義
<plugin>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.2</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.handlers</resource>
                    </transformer>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                        <resource>META-INF/spring.schemas</resource>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>
```

[参考にしたサイト](http://stackoverflow.com/questions/24592666/spring-error-unable-to-locate-spring-namespacehandler-for-xml-schema-namespace)