Jetty9對於Servlet 3.1的支持很不友善, 配置起來使人不舒服! 在Tomcat 7/8/9都支持Embedded. 後面使用Tomcat8(要求JDK7, 支持Servlet3.1/JSP2.3)練習.html
Maven依賴:java
<properties> <spring.version>4.2.7.RELEASE</spring.version> <tomcat.version>8.5.5</tomcat.version> </properties> <dependencies> <!-- ================================ --> <!-- spring test frameworks --> <!-- ================================ --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- ================================ --> <!-- junit frameworks --> <!-- ================================ --> <!-- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!-- https://mvnrepository.com/artifact/com.github.stefanbirkner/system-rules --> <dependency> <groupId>com.github.stefanbirkner</groupId> <artifactId>system-rules</artifactId> <version>1.16.1</version> </dependency> <!-- ================================ --> <!-- tomcat frameworks --> <!-- ================================ --> <!-- https://devcenter.heroku.com/articles/create-a-java-web-application-using-embedded-tomcat#add-a-launcher-class --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jasper</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jasper-el</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat-jsp-api</artifactId> <version>${tomcat.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <artifactId>maven-source-plugin</artifactId> <version>2.4</version> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Java實現:git
public final class EmbedTomcat extends RiseJUnitTester { public static final String WEBAPP_DIRECTORY = "src/main/webapp/"; public static final String ROOT_CONTEXT = ""; public static final int HTTP_PORT = 80; public static final int HTTPS_PORT = 443; public static final int OFF = -1; public static void start() { start(ROOT_CONTEXT, HTTP_PORT, OFF, null, null, null); } public static void start(int httpPort) { start(ROOT_CONTEXT, httpPort, OFF, null, null, null); } public static void start(String keyAlias, String password, String keystorePath) { start(ROOT_CONTEXT, OFF, HTTPS_PORT, keyAlias, password, keystorePath); } public static void start(int httpsPort, String keyAlias, String password, String keystorePath) { start(ROOT_CONTEXT, OFF, httpsPort, keyAlias, password, keystorePath); } public static void start(int httpPort, int httpsPort, String keyAlias, String password, String keystorePath) { start(ROOT_CONTEXT, httpPort, httpsPort, keyAlias, password, keystorePath); } public static void start(String contextPath, int httpPort, int httpsPort, String keyAlias, String password, String keystorePath) { // FIX: A context path must either be an empty string or start with a '/' and do not end with a '/'. if (contextPath == null || contextPath.equals("/")) { contextPath = ROOT_CONTEXT; } try { // initial processSystemEnvironment(); Tomcat tomcat = new Tomcat(); if (httpsPort > 0) { tomcat.setPort(httpsPort); Connector httpsConnector = tomcat.getConnector(); httpsConnector.setSecure(true); httpsConnector.setScheme("https"); httpsConnector.setAttribute("keyAlias", keyAlias); httpsConnector.setAttribute("keystorePass", password); httpsConnector.setAttribute("keystoreFile", keystorePath); httpsConnector.setAttribute("clientAuth", "false"); httpsConnector.setAttribute("sslProtocol", "TLS"); httpsConnector.setAttribute("SSLEnabled", true); if (httpPort > 0) { Connector httpConnector = new Connector(); httpConnector.setPort(httpPort); httpConnector.setRedirectPort(httpsPort); tomcat.getService().addConnector(httpConnector); } } else if (httpPort > 0) { tomcat.setPort(httpPort); } StandardContext ctx = (StandardContext) tomcat.addWebapp(contextPath, new File(WEBAPP_DIRECTORY).getAbsolutePath()); // Declare an alternative location for your "WEB-INF/classes" dir Servlet 3.0 annotation will work WebResourceRoot resources = new StandardRoot(ctx); resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", new File("target/classes").getAbsolutePath(), "/")); ctx.setResources(resources); ctx.setJarScanner(new WebappStandardJarScanner()); ctx.setDefaultWebXml(new File("src/main/webapp/WEB-INF/web.xml").getAbsolutePath()); // FixBug: no global web.xml found for (LifecycleListener ll : ctx.findLifecycleListeners()) { if (ll instanceof ContextConfig) { ((ContextConfig) ll).setDefaultWebXml(ctx.getDefaultWebXml()); } } tomcat.start(); tomcat.getServer().await(); } catch (Exception e) { throw new RuntimeException("tomcat launch failed", e); } } }
代碼功能:github
另外Tomcat自帶的StandardJarScanner的processManifest()在jar的META-INF/manif.mf含有"Class-Path"項時處理不大正確. 複製其源碼保存爲WebappStandardJarScanner.java(package相同),並註釋下述語句:web
private static final Set<ClassLoader> CLASSLOADER_HIERARCHY; static { Set<ClassLoader> cls = new HashSet<>(); // FixBug: fail to scan // ClassLoader cl = WebappStandardJarScanner.class.getClassLoader(); // while (cl != null) { // cls.add(cl); // cl = cl.getParent(); // } CLASSLOADER_HIERARCHY = Collections.unmodifiableSet(cls); }
Maven配置spring
<dependencies> <!-- https://mvnrepository.com/artifact/org.eclipse.jetty.aggregate/jetty-all --> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.1</version> </dependency> <!-- 準備工做: 嵌入式Tomcat --> <dependency> <groupId>com.yy.risedev</groupId> <artifactId>risedev-test</artifactId> <version>2.0.0</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <!-- compiler plugin --> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.7</source> <target>1.7</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- source plugin --> <plugin> <artifactId>maven-source-plugin</artifactId> <version>2.4</version> <executions> <execution> <id>attach-sources</id> <goals> <goal>jar</goal> </goals> </execution> </executions> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>2.2</version> <configuration> <!-- 無web.xml時maven檢測錯誤 --> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build>
注意failOnMissingWebXml須要配置爲false, 不然maven會顯示錶示錯誤的紅叉叉...apache
<?xml version="1.0" encoding="UTF-8"?> <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_3_0.xsd" version="3.0" metadata-complete="true"> <servlet> <servlet-name>test</servlet-name> <servlet-class>servlet.TestServlet2</servlet-class> </servlet> <servlet-mapping> <servlet-name>test</servlet-name> <url-pattern>/test</url-pattern> </servlet-mapping> </web-app>
servlet.MyServletContainerInitializer
package servlet; import java.util.Set; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.HandlesTypes; @HandlesTypes(WebAppIntializer.class) public class MyServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> arg0, ServletContext arg1) throws ServletException { for (Class<?> c : arg0) { if (WebAppIntializer.class.isAssignableFrom(c)) { try { ((TestWebAppIntializer) (c.newInstance())).onStartup(arg1); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } } } }
package servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; public interface WebAppIntializer { void onStartup(ServletContext ctx) throws ServletException; }
package servlet; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration; public class TestWebAppIntializer implements WebAppIntializer { public void onStartup(ServletContext ctx) throws ServletException { ServletRegistration.Dynamic dyna = ctx.addServlet("testServlet", "servlet.TestServlet"); dyna.addMapping("/test"); } }
經過java服務API,會掃描@HandlesTypes的類型,而後傳遞給MyServletContainerInitializer.onStartup()執行. 假意相識的感受? 這是Spring IOC的DI特性. 看來Spring的影響不小哦!編程
執行結果: 在訪問http://localhost/test時,執行的是TestServlet的邏輯, 而非TestServlet2. 即web.xml配置覆蓋了動態註冊. 若改爲/test2, 則能訪問到動態註冊的TestServlet2了. 同時, 也可看到metadata-complete="true"不影響動態註冊.api
<?xml version="1.0" encoding="UTF-8"?> <web-fragment 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-fragment_3_0.xsd" version="3.0" metadata-complete="true"> <servlet> <servlet-name>test</servlet-name> <servlet-class>servlet.TestServlet2</servlet-class> </servlet> <servlet-mapping> <servlet-name>test</servlet-name> <url-pattern>/test2</url-pattern> </servlet-mapping> </web-fragment>
<?xml version="1.0" encoding="UTF-8"?> <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_3_0.xsd" version="3.0" metadata-complete="false"> </web-app>
注意: metadata-complete="false", 不然pluggability特性會被關閉.tomcat
隨時有想法均可以快速嘗試