对于maven多模块的spring web项目,本地开发时,启动的方式一般有如下几种:

  1. 使用容器(tomcat/jetty/resin等),该方式需要ide支持,而社区版的idea并不支持;
  2. maven插件(jetty/tomcat),该方式只需在web模块的pom文件中加入插件依赖,然后运行 mvn jetty:runmvn tomcat7:run 即可,不过对于多模块项目而言,代码时改动需要使用maven进行构建,而maven构建需耗时良久;
  3. jettymain()方法启动,该方式启动时,并不需要使用maven进行构建,代码改动时仅需重新编译改动的源文件,且社区版的idea支持良好,本地开发时,是个不错的选择。

1. maven引入包

<!-- jetty相关依赖包,注意scope为test-->
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-webapp</artifactId>
    <version>9.3.2.v20150730</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-annotations</artifactId>
    <version>9.3.2.v20150730</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>apache-jsp</artifactId>
    <version>9.3.2.v20150730</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>apache-jstl</artifactId>
    <version>9.3.2.v20150730</version>
    <scope>test</scope>
</dependency>

2. 编写启动类

由于引入包时,scopetest,因此该类需位于src/test/java下。

import org.eclipse.jetty.jsp.JettyJspServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

import java.io.File;

public class JettyStart {

    public static void main(String[] args) throws Exception {
        //添加你的模块名,对于maven多模块项目,需要指定
        String moduleName = "module";
        String dir = System.getProperty("user.dir");
        if (!dir.contains(moduleName)) {
            dir = dir + File.separator + moduleName;
            System.setProperty("user.dir", dir);
        }

        int port = 8081;
        String contextPath = "/";

        WebAppContext webContext = new WebAppContext("src/main/webapp", contextPath);
        webContext.setClassLoader(Thread.currentThread().getContextClassLoader());
        //处理jsp解析问题
        webContext.addBean(new JspStarter(webContext));
        webContext.addServlet(JettyJspServlet.class, "*.jsp");
        //处理静态资源热加载问题
        webContext.setDefaultsDescriptor("src/test/resources/webdefault.xml");

        Server server = new Server(port);
        server.setHandler(webContext);
        server.setStopAtShutdown(true);
        server.start();
        server.join();

    }

}

3. 解决jsp处理异常

如果项目中有jsp页面,访问时,可能会出现以下异常:

org.apache.jasper.JasperException: Unable to compile class for JSP
	at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:600)
	at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:363)
	at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:396)
	at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:340)
	at org.eclipse.jetty.jsp.JettyJspServlet.service(JettyJspServlet.java:107)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:816)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:583)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:566)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1114)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1048)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:199)
	at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:74)
	at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:168)
	at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
	at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1246)
	at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1029)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:973)
	......

解决该异常的方法是,在JettyStart.java的同一包下,加入以下类:

import org.apache.tomcat.util.scan.StandardJarScanner;
import org.eclipse.jetty.apache.jsp.JettyJasperInitializer;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;

public class JspStarter extends AbstractLifeCycle implements ServletContextHandler.ServletContainerInitializerCaller {

    JettyJasperInitializer sci;
    ServletContextHandler context;

    public JspStarter(ServletContextHandler context) {
        this.sci = new JettyJasperInitializer();
        this.context = context;
        this.context.setAttribute("org.apache.tomcat.JarScanner", new StandardJarScanner());
    }

    @Override
    protected void doStart() throws Exception {
        ClassLoader old = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(context.getClassLoader());
        try {
            sci.onStartup(null, context.getServletContext());
            super.doStart();
        } finally {
            Thread.currentThread().setContextClassLoader(old);
        }
    }

}

4.解决jstl异常

项目的jsp页面中使用了jstl标签,还是会报错:

org.apache.jasper.JasperException: The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application
	at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:55)
	at org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:277)
	at org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:75)
	at org.apache.jasper.compiler.TagLibraryInfoImpl.generateTldResourcePath(TagLibraryInfoImpl.java:243)
	at org.apache.jasper.compiler.TagLibraryInfoImpl.<init>(TagLibraryInfoImpl.java:124)
	at org.apache.jasper.compiler.Parser.parseTaglibDirective(Parser.java:411)
	at org.apache.jasper.compiler.Parser.parseDirective(Parser.java:469)
	at org.apache.jasper.compiler.Parser.parseElements(Parser.java:1430)
	at org.apache.jasper.compiler.Parser.parse(Parser.java:139)
	at org.apache.jasper.compiler.ParserController.doParse(ParserController.java:227)
	at org.apache.jasper.compiler.ParserController.parse(ParserController.java:100)
	at org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:199)
	at org.apache.jasper.compiler.Compiler.compile(Compiler.java:356)
	at org.apache.jasper.compiler.Compiler.compile(Compiler.java:336)
	at org.apache.jasper.compiler.Compiler.compile(Compiler.java:323)
	at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:585)
	at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:363)
	at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:396)
	at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:340)
	at org.eclipse.jetty.jsp.JettyJspServlet.service(JettyJspServlet.java:107)

这时,需要找到 jstl 实现包,这里使用的是 glassfish 实现,在maven仓库路径为C:\Users\Administrator\.m2\repository\org\glassfish\web\jstl-impl\1.2,在该路径下找到jstl-impl-1.2.jar,用解压工具解压,在 META-INF 目录下找到以下几个文件:

jetty搭建java项目 jetty怎么启动项目_开发工具

复制到项目的WEB-INF/jstl-tld目录下(jstl-tld需要手动创建):

jetty搭建java项目 jetty怎么启动项目_jetty搭建java项目_02

5.处理静态文件不能热加载问题

经过以上步骤后,项目能正常启动,也能正常访问了,但有一点特别坑:静态文件(html/js/css等)不能热加载,每次修改后要重启才能看到效果,大大地降低了开发效率,解决办法如下:

  1. 找到jetty-webapp-{version}.jar
  2. org/eclipse/jetty/webapp/路径下找到webdefault.xml,复制一份放到src/test/resources/
  3. src/test/resources/webdefault.xml中找到
<init-param>
  <param-name>useFileMappedBuffer</param-name>
  <param-value>true</param-value> <!-- 将值修改为false -->
</init-param>

param-value值修改为true即可。

重启项目,接下来修改html/js/css等,只要刷新浏览器就能看到效果了。

最后,附上本人的代码结构:

jetty搭建java项目 jetty怎么启动项目_apache_03