#spring #spring-boot #spring-mvc #embedded-jetty
#spring #spring-boot #spring-mvc #встроенный-причал
Вопрос:
Загрузка Spring 2.3.7, встроенная упаковка Jetty, JSP и WAR. Я хочу показать свою статическую HTML-страницу во время инициализации контекста Spring. Она должна быть видна при запуске приложения и до обновления контекста Spring. Я попытался использовать это руководство в качестве примера https://www.nurkiewicz.com/2015/09/displaying-progress-of-spring.html но это не работает. Мне нужно запустить встроенный jetty непосредственно при инициализации jetty. Но spring boot запускает встроенный jetty только при обновлении контекста. Как мне это сделать?
Ответ №1:
Я создал класс прогрева Jetty warpper:
public final class WarmupServer {
private final String contextPath;
private final String displayName;
private final DefaultApplicationArguments arguments;
private final String[] welcomeFiles;
private final Resource baseResource;
private Server server;
public WarmupServer(String contextPath,
String displayName,
Resource baseResource,
String[] welcomeFiles,
String... runArgs) {
this.contextPath = StringUtils.defaultIfBlank(contextPath, "/");
this.displayName = StringUtils.defaultIfBlank(displayName, "Warmup");
this.baseResource = ObjectUtils.defaultIfNull(baseResource, Resource.newClassPathResource("/static"));
this.welcomeFiles = ArrayUtils.isEmpty(welcomeFiles) ? new String[]{"html/warmup.html"} : welcomeFiles;
this.arguments = new DefaultApplicationArguments(ArrayUtils.nullToEmpty(runArgs));
}
public Server start() throws Exception {
if (server != null amp;amp; server.isStarted()) {
throw new IllegalStateException("Server already started");
}
server = new Server();
server.setHandler(buildServletHandler());
final String configPath = parseArg(OPT_CONFIG);
if (StringUtils.isBlank(configPath)) {
throw new RuntimeException(OPT_CONFIG " argument is not set");
}
final Config config = ConfigUtils.parseFile(new File(configPath), DEFAULT_CONFIG_FILE_NAME);
configureHttpConnector(config);
configureHttpsConnector(config);
server.start();
return server;
}
public void registerWarmupServerStopLifecycle(ConfigurableApplicationContext context) {
context.getBeanFactory()
.registerSingleton(WarmupStopLifecycle.class.getSimpleName(), new WarmupStopLifecycle(server));
}
private ServletContextHandler buildServletHandler() {
final ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS);
handler.addServlet(DefaultServlet.class, "/");
handler.setDisplayName(displayName);
handler.setContextPath(contextPath);
handler.setWelcomeFiles(welcomeFiles);
handler.setBaseResource(baseResource);
return handler;
}
private void configureHttpConnector(Config config) {
final int httpPort = NumberUtils.toInt(parseArg(OPT_HTTP_PORT), config.getInt(OPT_HTTP_PORT));
final ServerConnector connector = new ServerConnector(server);
connector.setPort(httpPort);
server.addConnector(connector);
}
private void configureHttpsConnector(Config config) {
final int httpsPort = NumberUtils.toInt(
parseArg(OPT_HTTPS_PORT), config.getInt(OPT_HTTPS_PORT));
final String keyStorePath = StringUtils.defaultIfBlank(
parseArg(OPT_KEYSTORE_FILE), config.getString(OPT_KEYSTORE_FILE));
final boolean sslEnabled = StringUtils.isNotBlank(keyStorePath)
amp;amp; Files.isReadable(Paths.get(keyStorePath));
if (sslEnabled) {
final HttpConfiguration configuration = new HttpConfiguration();
configuration.setSecurePort(httpsPort);
configuration.setSecureScheme(HTTPS_SCHEME);
final ServerConnector httpsConnector = new HttpsConnector()
.createConnector(server, configuration, config.getConfig(JETTY_HTTPS_CONFIG), httpsPort);
server.addConnector(httpsConnector);
}
}
private String parseArg(String optionName) {
final List<String> values = arguments.getOptionValues(optionName);
return CollectionUtils.isEmpty(values) ? StringUtils.EMPTY : values.get(0);
}
public static WarmupServer start(String contextPath,
String displayName,
Resource baseResource,
String[] welcomeFiles,
String... runArgs) throws Exception {
final WarmupServer server = new WarmupServer(contextPath, displayName, baseResource, welcomeFiles, runArgs);
server.start();
return server;
}
}
Эта оболочка анализирует аргументы командной строки и создает обработчик Jetty и соединители HTTP и (или) HTTPS, используя предоставленные аргументы командной строки.
И класс реализации жизненного цикла simple Spring:
@RequiredArgsConstructor
class WarmupStopLifecycle implements SmartLifecycle {
private static final Logger logger = LogManager.getFormatterLogger();
private final Server warmupServer;
private volatile boolean isRunning;
@Override
public void start() {
try {
warmupServer.stop();
isRunning = true;
} catch (Exception e) {
logger.error("Failed to stop warmup server", e);
throw new RuntimeException("Failed to stop warmup server", e);
}
}
@Override
public void stop() {
}
@Override
public boolean isRunning() {
return isRunning;
}
/**
* Returns phase of this lifecycle.
* A phase MUST be before the Spring web server starts.
* See {@code org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle} phase.
*/
@Override
public int getPhase() {
return Integer.MAX_VALUE - 2;
}
}
Итак, использование этого:
@SpringBootApplication
public class SpringApplication {
public static void main(String[] args) throws Exception {
final WarmupServer warmupServer = WarmupServer.start(
"/my_context_path", "My Warmup server handler", args);
new SpringApplicationBuilder()
.sources(SpringApplication.class)
.initializers(warmupServer::registerWarmupServerStopLifecycle)
.run(args);
}
}
WarmupServer
запускается сразу после запуска приложения и будет остановлен перед запуском веб-сервера Spring.