• Spring MVC + jetty 内嵌启动webapp

    2022-01-13
  • 这篇文章主要向大家介绍Spring MVC jetty 内嵌启动webapp,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

    最近项目须要研究webapp以自带容器的方式启动,因而研究了如何使用jetty做为web容器,并嵌入到webapp里面,实现webapp自带容器启动。最终的目标是 :css

    命令行执行java -jar jetty-embeded-webapp.war,就能直接运行webapp。html

    在网上搜索了相关教程,最终整合成功,记录以下 :java

    【舒适提示】linux

    这里采用maven来构建项目,方便依赖管理以及后期项目编译打包。项目最终的演示功能为 : Spring MVC jetty 内嵌启动webapp。该demo项目的源码能够查看个人github :git

    https://github.com/ZzzCrazyPig/jetty-embeded-webapp-demogithub

    下面记录实战的每个步骤 : web

    1. Eclipse构建maven webapp项目

    使用Eclipse构建maven webapp projectspring

    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配置apache

     ?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
     property name="prefix" value="" / 
     property name="suffix" value=".jsp" / !--可为空,方便实现自已的依据扩展名来选择视图解释类的逻辑 -- 
     !-- property name="viewClass"
     value="org.springframework.web.servlet.view.JstlView" / -- 
     /bean 
     /beans 

    web.xml配置windows

     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";
    
    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!”,即表示运行成功!