Spring MVC + jetty 内嵌启动webapp
最近项目需要研究webapp以自带容器的方式启动,于是研究了如何使用jetty作为web容器,并嵌入到webapp里面,实现webapp自带容器启动。最终的目标是 :命令行执行java -jar jetty-embeded-webapp.war,就能直接运行webapp。在网上搜索了相关教程,最终整合成功,记录如下 :【温馨提示】这里采用maven来构建项目,方便依赖管理以及后期项...
最近项目需要研究webapp以自带容器的方式启动,于是研究了如何使用jetty作为web容器,并嵌入到webapp里面,实现webapp自带容器启动。最终的目标是 :
命令行执行java -jar jetty-embeded-webapp.war
,就能直接运行webapp。
在网上搜索了相关教程,最终整合成功,记录如下 :
【温馨提示】
这里采用maven来构建项目,方便依赖管理以及后期项目编译打包。项目最终的演示功能为 : Spring MVC + jetty 内嵌启动webapp。该demo项目的源码可以查看我的github :
https://github.com/ZzzCrazyPig/jetty-embeded-webapp-demo
下面记录实战的每一个步骤 :
1. Eclipse构建maven webapp项目
使用Eclipse构建maven webapp project
2. 加入pom依赖
2.1 加入jetty依赖
<!-- jetty -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
<!-- <scope>provided</scope> -->
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jsp</artifactId>
<version>${jetty.version}</version>
</dependency>
2.2 加入spring依赖
<!-- spring mvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
3. 配置spring mvc
applicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 启动Springmvc注解驱动 -->
<mvc:annotation-driven />
<!-- 自动将控制器加载到bean -->
<context:component-scan base-package="com.crazypig.demo.embededwebapp"></context:component-scan>
<!-- 配置处理静态资源的请求 -->
<!-- <mvc:resources mapping="assets/js/**" location="assets/js/" />
<mvc:resources mapping="assets/css/**" location="assets/css/" /> -->
<!-- 配置视图解析器 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="" />
<property name="suffix" value=".jsp" /><!--可为空,方便实现自已的依据扩展名来选择视图解释类的逻辑 -->
<!-- <property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" /> -->
</bean>
</beans>
web.xml配置
<web-app>
<display-name>jetty embeded webapp demo</display-name>
<welcome-file-list>
<welcome-file>index</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/index</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
4. 编写Spring MVC Controller
package com.crazypig.demo.embededwebapp;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class IndexController {
@RequestMapping({"/", "/index"})
public ModelAndView index() {
return new ModelAndView("index");
}
}
5. 编写启动类
package com.crazypig.demo.embededwebapp;
import java.io.File;
import java.net.URL;
import java.security.ProtectionDomain;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.webapp.WebAppContext;
/**
*
* web应用启动器
*
* @author CrazyPig
* @since 2016-08-03
*
*/
public class Launcher {
public static final int DEFAULT_PORT = 8080;
public static final String DEFAULT_CONTEXT_PATH = "/jetty-embeded-webapp";
private static final String DEFAULT_APP_CONTEXT_PATH = "src/main/webapp";
public static void main(String[] args) {
runJettyServer(DEFAULT_PORT, DEFAULT_CONTEXT_PATH);
}
public static void runJettyServer(int port, String contextPath) {
Server server = createJettyServer(port, contextPath);
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
server.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static Server createJettyServer(int port, String contextPath) {
Server server = new Server(port);
server.setStopAtShutdown(true);
ProtectionDomain protectionDomain = Launcher.class.getProtectionDomain();
URL location = protectionDomain.getCodeSource().getLocation();
String warFile = location.toExternalForm();
WebAppContext context = new WebAppContext(warFile, contextPath);
context.setServer(server);
// 设置work dir,war包将解压到该目录,jsp编译后的文件也将放入其中。
String currentDir = new File(location.getPath()).getParent();
File workDir = new File(currentDir, "work");
context.setTempDirectory(workDir);
server.setHandler(context);
return server;
}
public static Server createDevServer(int port, String contextPath) {
Server server = new Server();
server.setStopAtShutdown(true);
ServerConnector connector = new ServerConnector(server);
// 设置服务端口
connector.setPort(port);
connector.setReuseAddress(false);
server.setConnectors(new Connector[] {connector});
// 设置web资源根路径以及访问web的根路径
WebAppContext webAppCtx = new WebAppContext(DEFAULT_APP_CONTEXT_PATH, contextPath);
webAppCtx.setDescriptor(DEFAULT_APP_CONTEXT_PATH + "/WEB-INF/web.xml");
webAppCtx.setResourceBase(DEFAULT_APP_CONTEXT_PATH);
webAppCtx.setClassLoader(Thread.currentThread().getContextClassLoader());
server.setHandler(webAppCtx);
return server;
}
}
6. 配置maven编译打包
pom.xml
里面增加编译打包
<build>
<finalName>jetty-embeded-webapp-demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<target>1.7</target>
<source>1.7</source>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>main-class-placement</id>
<phase>prepare-package</phase>
<configuration>
<tasks>
<copy todir="${project.build.directory}/${project.artifactId}/">
<fileset dir="${project.build.directory}/classes/">
<include name="**/*/Launcher.class" />
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.crazypig.demo.embededwebapp.Launcher</mainClass>
</transformer>
</transformers>
<artifactSet>
<includes>
<include>org.eclipse.jetty:*</include>
<include>*:javax.servlet*</include>
<include>org.glassfish:javax.el*</include>
</includes>
</artifactSet>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal><!-- 只运行一次 -->
</goals>
<configuration>
<descriptors> <!--描述文件路径 -->
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
【说明】
其中
maven-shade-plugin
插件用于将特定的第三方依赖追加到将要编译打包的jar里面(or war),使得该jar成为可运行的jar(or war)。
<mainClass>
指向main函数所在的类,在该项目中为Launcher
类。<artifactSet>
的<includes>
里面指定需要整合到jar里面的第三方依赖库。另外,由于项目为web项目,打成war包的时候
Launcher
类所在的目录为/WEB-INF/class
下,我们需要将它移动到war包根目录,因此我们使用maven-antrun-plugin
插件来完成这个需求
7. 打包
使用maven-assembly-plugin
插件来协助进行打包,在pom.xml
文件里面加入maven-assembly-plugin
的配置,如上一节所示。对应的assembly.xml文件如下所示 :
2018-04-16 : 发现之前因为assembly.xml中因为文件路径分隔符写法有问题导致在mac平台上面打包没有bin目录,这里已经将文件分隔符换成 / ,以适配不同的系统
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>standalone</id>
<formats>
<format>zip</format>
</formats>
<dependencySets>
</dependencySets>
<files>
<file>
<source>target/${project.artifactId}.war</source>
<outputDirectory>/</outputDirectory>
</file>
</files>
<fileSets>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>/conf</outputDirectory>
<!-- <includes> <include>some/path</include> </includes> <excludes> <exclude>some/path1</exclude>
</excludes> -->
</fileSet>
<fileSet>
<directory>src/main/assembly/bin</directory>
<outputDirectory>/bin</outputDirectory>
<includes>
<include>start.sh</include>
</includes>
<fileMode>0755</fileMode>
<lineEnding>unix</lineEnding>
</fileSet>
<fileSet>
<directory>src/main/assembly/bin</directory>
<outputDirectory>/bin</outputDirectory>
<includes>
<include>start.bat</include>
</includes>
</fileSet>
</fileSets>
</assembly>
【说明】
maven-assembly-plugin
是一个强大的maven插件,使用它可以让我们自定义打包后的目录结构,其功能类似ant我们加入start.bat和start.sh,用于windows平台和linux平台直接运行该脚本来启动web应用
运行
走到这一步,我们只要运行mvn clean package
打包,在项目源码路径target子目录下找到jetty-embeded-webapp-demo-standalone.zip
压缩包,解压到d:\jetty-embeded-webapp-demo,进入该目录下的bin目录,运行start.bat :
D:\jetty-embeded-webapp-demo\bin>"D:\Java\jdk1.8.0_66/bin/java" -jar ..\jetty-embeded-webapp-demo.war
2016-08-06 14:00:36.276:INFO::main: Logging initialized @175ms
2016-08-06 14:00:36.345:INFO:oejs.Server:main: jetty-9.2.z-SNAPSHOT
log4j:WARN No appenders could be found for logger (org.springframework.web.servlet.DispatcherServlet).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
2016-08-06 14:00:45.124:INFO:/jetty-embeded-webapp:main: Initializing Spring FrameworkServlet 'springmvc'
2016-08-06 14:00:46.334:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@490caf5f{/jetty-embeded-webapp,file:/D:/jetty-embeded-webapp-demo/work/webapp/,AVAILABLE}{file:/D:/jetty-embeded-webapp-demo/jetty-embeded-webapp-demo.war}
2016-08-06 14:00:46.369:INFO:oejs.ServerConnector:main: Started ServerConnector@5e403b4a{HTTP/1.1}{0.0.0.0:8080}
2016-08-06 14:00:46.373:INFO:oejs.Server:main: Started @10272ms
2016-08-06 14:02:34.372:INFO:oejs.ServerConnector:Thread-0: Stopped ServerConnector@5e403b4a{HTTP/1.1}{0.0.0.0:8080}
2016-08-06 14:02:34.372:INFO:/jetty-embeded-webapp:Thread-0: Destroying Spring FrameworkServlet 'springmvc'
等待jetty服务器起来并加载了我们的应用后,打开浏览器,输入访问地址 :
http://localhost:8080/jetty-embeded-webapp/
出现”Hello World!”,即表示运行成功!
更多推荐
所有评论(0)