1、添加pom文件依赖
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.22.v20191022</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-deploy</artifactId>
<version>9.4.22.v20191022</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
2、编写代码
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
/**
* Spring root context keeper
*/
@Component
public class ApplicationContextKeeper implements ApplicationContextAware {
private static WebApplicationContext webAppCtx;
@Override
public void setApplicationContext(ApplicationContext appCtx)
throws BeansException {
XmlWebApplicationContext xmlWebAppCtx = new XmlWebApplicationContext();
xmlWebAppCtx.setParent(appCtx);
xmlWebAppCtx.setConfigLocation("");
xmlWebAppCtx.setClassLoader(appCtx.getClassLoader());
xmlWebAppCtx.refresh();
xmlWebAppCtx.registerShutdownHook();
webAppCtx = xmlWebAppCtx;
}
public static WebApplicationContext getWebAppCtx() {
return webAppCtx;
}
}
import org.eclipse.jetty.server.Server;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
/**
* 服务生命周期辅助程序, 用于:
* 1. 优雅关闭API服务, 加入JVM钩子, 保护数据.
* 2. 可用于缓存持久化至数据库, 文件等操作
*/
public final class Bootstrap implements Lifecycle, InitializingBean, ApplicationListener<ContextRefreshedEvent> {
private static final Logger LOG = LoggerFactory.getLogger(Bootstrap.class);
private Server jettyServer;
@Value("${rest.status}")
private int status;
@Value("${server.port}")
private int port;
/**
* The set of registered LifecycleListeners for event notifications.
*/
private LifecycleListener[] listeners = new LifecycleListener[0];
private final Object listenersLock = new Object(); // Lock object for changes to listeners
@Override
public void addLifecycleListener(LifecycleListener listener) {
synchronized (listenersLock) {
LifecycleListener[] results = new LifecycleListener[listeners.length + 1];
System.arraycopy(listeners, 0, results, 0, listeners.length);
results[listeners.length] = listener;
listeners = results;
}
}
@Override
public void removeLifecycleListener(LifecycleListener listener) {
synchronized (listenersLock) {
int n = -1;
for (int i = 0; i < listeners.length; i++) {
if (listeners[i] == listener) {
n = i;
break;
}
}
if (n < 0) {
return;
}
LifecycleListener[] results = new LifecycleListener[listeners.length - 1];
int j = 0;
for (int i = 0; i < listeners.length; i++) {
if (i != n) {
results[j++] = listeners[i];
}
}
listeners = results;
}
}
/**
* @return true: no error, false: has error
*/
private boolean doStop() {
try {
jettyServer.stop(); // 1. Gracefully shutdown
// 2
for (LifecycleListener listener : listeners) {
listener.lifecycleEvent(new LifecycleEvent(this, Lifecycle.AFTER_STOP_EVENT));
}
return true;
} catch (Exception e) {
LOG.error("Shutdown jetty server error: ", e);
}
return false;
}
@Override
public void afterPropertiesSet() {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if (doStop()) {
LOG.info("Shutdown completed");
} else {
LOG.warn("Shutdown completed with error, please check logs !!!");
}
}));
LOG.info("Added JVM shutdown hook ...");
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) { // Notify when ROOT CONTEXT loaded
try {
/*
* Start Jetty container after all bean have been initialize.
*
* 1.In case of load big data from DB slowly but the WEB PORT
* have been open, some request maybe send to here but
* application can't process them.
*
* 2. Jetty will parse web.xml, DispatcherServlet need WebApplicationContext to make
* handlerMapping (URL -> process bean), so if the Spring Container haven't load
* controllers specified on "springmvc-context.xml" and Jetty parse web.xml,
* springmvc maybe not found the beans, finally will throw 404 error to client.
*/
if (status == 1) {
jettyServer.start();
System.out.println("Start http server on port:" + port);
}
} catch (Exception e) {
LOG.error("Start jetty server error: ", e);
System.exit(1);
throw new RuntimeException(e);
}
}
}
public void setJettyServer(Server jettyServer) {
this.jettyServer = jettyServer;
}
}
import org.springframework.web.servlet.DispatcherServlet;
public class CustomDispatcherServlet extends DispatcherServlet {
private static final long serialVersionUID = 1L;
public CustomDispatcherServlet() {
super(ApplicationContextKeeper.getWebAppCtx());
}
}
public interface Lifecycle {
String BEFORE_START_EVENT = "before_start";
String AFTER_START_EVENT = "after_start";
String BEFORE_STOP_EVENT = "before_stop"; // Before jetty gracefully shutdown
String AFTER_STOP_EVENT = "after_stop"; // After jetty gracefully shutdown
void addLifecycleListener(LifecycleListener listener);
void removeLifecycleListener(LifecycleListener listener);
}
import java.util.EventObject;
public class LifecycleEvent extends EventObject {
private static final long serialVersionUID = 1L;
/**
* The event type this instance represents.
*/
private final String type;
public LifecycleEvent(Object source, String type) {
super(source);
this.type = type;
}
public String getType() {
return type;
}
}
/**
* Interface defining a listener for significant events (including "server
* start" and "server stop")
*/
public interface LifecycleListener {
/**
* Acknowledge the occurrence of the specified event.
*
* @param event LifecycleEvent that has occurred
*/
void lifecycleEvent(LifecycleEvent event);
}
3、配置bean
<bean id="bootstrap" class="com.xuanwu.msggate.mto.mos.server.Bootstrap">
<property name="jettyServer" ref="jettyServer" />
</bean>
4、配置mvc注解驱动
<mvc:annotation-driven />
通过mvc:annotation-driven/配置,Spring MVC会启用对注解的支持,使得可以在控制器类和方法上使用注解来定义请求映射、参数绑定等操作。
具体来说,mvc:annotation-driven/会启用以下功能:
支持@Controller注解,用于标识控制器类
支持@RequestMapping注解,用于定义请求映射
支持@RequestParam注解,用于绑定请求参数
支持@ResponseBody注解,用于将方法返回值直接作为HTTP响应体返回
支持@PathVariable注解,用于绑定URI模板变量
注意: 如果没有mvc注解支持。则会出现No mapping for POST
5、添加jetty配置文件
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<bean id="contextHandlerCollection" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"></bean>
<bean id="jettyServer" class="org.eclipse.jetty.server.Server" destroy-method="stop">
<constructor-arg name="pool">
<bean class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<constructor-arg name="minThreads" value="${rest.api.jetty.poolSize.minThreads}" />
<constructor-arg name="maxThreads" value="${rest.api.jetty.poolSize.maxThreads}" />
</bean>
</constructor-arg>
<property name="stopAtShutdown" value="true" />
<property name="stopTimeout" value="3000" />
<property name="connectors">
<array>
<bean class="org.eclipse.jetty.server.ServerConnector">
<constructor-arg name="server" ref="jettyServer" />
<property name="port" value="${server.port}" />
<property name="acceptQueueSize" value="1024" />
</bean>
</array>
</property>
<property name="handler">
<bean class="org.eclipse.jetty.server.handler.HandlerCollection">
<property name="handlers">
<list>
<ref bean="contextHandlerCollection" />
</list>
</property>
</bean>
</property>
<property name="beans">
<list>
<ref bean="deploymentManager" />
</list>
</property>
</bean>
<bean id="deploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
<property name="contexts" ref="contextHandlerCollection" />
<property name="appProviders">
<list>
<ref bean="webAppProvider" />
</list>
</property>
</bean>
<bean id="webAppProvider" class="org.eclipse.jetty.deploy.providers.WebAppProvider">
<property name="monitoredDirName" value="${webapps.dir}webapps" />
<property name="scanInterval" value="5" />
<property name="extractWars" value="true" />
<property name="configurationManager">
<bean class="org.eclipse.jetty.deploy.PropertiesConfigurationManager" />
</property>
</bean>
</beans>
6、在spring中导入
<import resource="classpath:/com/xuanwu/msggate/mto/mos/jetty-context.xml"/>
7、创建webapps目录并创建web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>rest api</display-name>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>com.xuanwu.msggate.rest2mtreceiver.server.CustomDispatcherServlet</servlet-class>
<init-param>
<param-name>detectAllHandlerAdapters</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<!-- Spring字符集过滤器 建议放前面 不然拦截可能有问题-->
<filter>
<filter-name>SpringEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SpringEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>HttpRequestFilter</filter-name>
<filter-class>com.xuanwu.msggate.rest2mtreceiver.rest.filter.HttpRequestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpRequestFilter</filter-name>
<url-pattern>/api/authorization/*</url-pattern>
</filter-mapping>
</web-app>