Burdu Burdu - 7 months ago 111
Java Question

Use spring mvc xml project inside spring-boot project

I have this test project I created, composed out of 2 projects: one using spring-boot and one using spring-mvc. Each one of them works fine standalone.
What I want to do is to run the spring-boot and be able to access the web pages of the spring-mvc project by loading its context.
The project is quite simple as I just want to test how to do the mix.

The problem is that when I am running the spring-boot application, the page from spring-mvc is not accessible, as it does not add the webbapp folder (containing WEB-INF) in the build.
I am able to autowire the service from spring-mvc inside the spring-boot application.

The tree looks as follows:

enter image description here

The Application.java class for spring-boot is the following:

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;

import java.util.Arrays;

@SpringBootApplication
@ComponentScan({"org.burdu", "hello"})
//@ImportResource({"classpath:WEB-INF/spring-core-config.xml", "classpath:WEB-INF/spring-mvc-config.xml"})
public class Application {

public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);

System.out.println("Let's inspect the beans provided by Spring Boot:");

String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
}


The root build.gradle

group 'net.burdu'
version '1.0-SNAPSHOT'

apply plugin: 'java'
apply plugin: 'idea'

sourceCompatibility = 1.8

repositories {
mavenCentral()
mavenLocal()
}

dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
}


root settings.gradle

rootProject.name = 'testSpringXMLAndBoot'
include 'spring-mvc'
include 'spring-boot'


spring-boot build.gradle

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'war'
apply plugin: 'spring-boot'

sourceCompatibility = 1.8

repositories {
jcenter()
mavenCentral()
}

buildscript {
repositories {
mavenCentral()
}

dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.3.RELEASE")
}
}

dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile project(':spring-mvc')
}


spring-mvc build.gradle

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'war'
apply plugin: 'jetty'

sourceCompatibility = 1.8

repositories {
mavenCentral()
mavenLocal()
}

dependencies {
compile 'ch.qos.logback:logback-classic:1.1.3'
compile 'org.springframework:spring-webmvc:4.1.6.RELEASE'
compile 'javax.servlet:jstl:1.2'
}

jettyRun{
contextPath = ""
httpPort = 8080
}

jettyRunWar{
contextPath = ""
httpPort = 8080
}


spring-core-config.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">

<context:component-scan base-package="org.burdu.web" />

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>

<mvc:resources mapping="/resources/**" location="/resources/" />

<mvc:annotation-driven />

</beans>


spring-mvc-config.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd ">

<context:component-scan base-package="org.burdu.service" />

</beans>


web.xml inside spring-mvc project

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">

<display-name>Gradle + Spring MVC Hello World + XML</display-name>
<description>Spring MVC web application</description>

<!-- For web context -->
<servlet>
<servlet-name>hello-dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>hello-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<!-- For root context -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-core-config.xml</param-value>
</context-param>

</web-app>


The HelloController inside spring-boot, HelloWorldService and WelcomeController are simple beans. I am not pasting their content here since the question already got too long, but if needed I can add them.

Answer

I've imported the combined projects into my IDE and I found it's hard to integrate two projects into one and keep them original. The real difficulty is spring could not known the resources inside jar of sub-project spring-mvc located in lib/spring-mvc.jar. If I extract it into sub-project spring-boot, it still doesn't work.

Afterwards, I found this documentation about JSP limitations in spring-boot, which says:

An executable jar will not work because of a hard coded file pattern in Tomcat

What's more, my suggest is to convert your application into modern spring-boot form as following:

Convert an existing application to Spring Boot

In this case, you just need convert your servlets and filters defined in web.xml into spring beans as documentation said. Additionally, spring-core-config.xml and spring-mvc-config.xml can keep no change cause they can be imported directly even though they are in embeded jar. It should be easy.

After you converting your application into spring boot, you can take all control over the web container too.

At the end, if you are really want to use them together, following documentation could be useful:

Deploying a WAR in an Old (Servlet 2.5) Container

This time, you can specify SpringBootContextLoaderListener as your listener at your web.xml. It could help to configure your application. However, as what spring says, it's only useful for deploying your application into old container.

But, if I were you, I will convert it to spring-boot format. Cause it can simplify maintenance work.

By default, spring boot only allowed directories /,META-INF/resources,resources,public,static as resources loading path. From this prospective, if all your resources are located under these directories it should work. Of course, specifying document root would be required. If you can tolerant extracting spring-mvc, I think this would be the solution.

If spring context defined in embedded jar is what you want, you can just add a star after classpath such as classpath*:WEB-INF/applicationContext.xml. This will add any WEB-INF/applicationContext.xml in any lib jar. In other words, you only need add a star after classpath at your @ImportResource expression. If all things' going well, you are there.

Comments