Solución:
Me encontré con problemas similares, pero encontré que alguien más (Francesco Zanutto) fue lo suficientemente amable como para escribir una publicación de blog sobre sus esfuerzos. Su La solución funcionó para mí. No me atribuyo el tiempo que dedicaron a realizar este código.
http://zazos79.blogspot.com/2015/02/spring-boot-12-run-as-windows-service.html
Está usando el modo de inicio y detención de jvm, en comparación con el modo exe que veo en su ejemplo. Con esto, puede extender JarLauncher de Spring Boot para manejar los comandos de “inicio” y “detención” de los servicios de Windows, lo que creo que está buscando para lograr un cierre elegante.
Al igual que con sus ejemplos, agregará múltiples métodos principales, dependiendo de su implementación, deberá indicar cuál debe ser invocado ahora por el lanzador. Estoy usando Gradle y simplemente tuve que agregar lo siguiente a mi build.gradle:
springBoot{
mainClass="mydomain.app.MyApplication"
}
Mi script de instalación de Procrun:
D:appprunsrv.exe //IS//MyServiceName ^
--DisplayName="MyServiceDisplayName" ^
--Description="A Java app" ^
--Startup=auto ^
--Install=%CD%prunsrv.exe ^
--Jvm=%JAVA_HOME%jrebinserverjvm.dll ^
--Classpath=%CD%SpringBootApp-1.1.0-SNAPSHOT.jar; ^
--StartMode=jvm ^
--StartClass=mydomain.app.Bootstrap ^
--StartMethod=start ^
--StartParams=start ^
--StopMode=jvm ^
--StopClass=mydomain.app.Bootstrap ^
--StopMethod=stop ^
--StopParams=stop ^
--StdOutput=auto ^
--StdError=auto ^
--LogPath=%CD% ^
--LogLevel=Debug
Clase de extensión JarLauncher:
package mydomain.app;
import org.springframework.boot.loader.JarLauncher;
import org.springframework.boot.loader.jar.JarFile;
public class Bootstrap extends JarLauncher {
private static ClassLoader classLoader = null;
private static Bootstrap bootstrap = null;
protected void launch(String[] args, String mainClass, ClassLoader classLoader, boolean wait)
throws Exception {
Runnable runner = createMainMethodRunner(mainClass, args, classLoader);
Thread runnerThread = new Thread(runner);
runnerThread.setContextClassLoader(classLoader);
runnerThread.setName(Thread.currentThread().getName());
runnerThread.start();
if (wait == true) {
runnerThread.join();
}
}
public static void start (String []args) {
bootstrap = new Bootstrap ();
try {
JarFile.registerUrlProtocolHandler();
classLoader = bootstrap.createClassLoader(bootstrap.getClassPathArchives());
bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
}
catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
public static void stop (String []args) {
try {
if (bootstrap != null) {
bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
bootstrap = null;
classLoader = null;
}
}
catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
public static void main(String[] args) {
String mode = args != null && args.length > 0 ? args[0] : null;
if ("start".equals(mode)) {
Bootstrap.start(args);
}
else if ("stop".equals(mode)) {
Bootstrap.stop(args);
}
}
}
Mi clase de aplicación principal de Spring:
package mydomain.app;
import java.lang.management.ManagementFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan
@EnableAutoConfiguration
public class MyApplication {
private static final Logger logger = LoggerFactory.getLogger(MyApplication.class);
private static ApplicationContext applicationContext = null;
public static void main(String[] args) {
String mode = args != null && args.length > 0 ? args[0] : null;
if (logger.isDebugEnabled()) {
logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + " Application mode:" + mode + " context:" + applicationContext);
}
if (applicationContext != null && mode != null && "stop".equals(mode)) {
System.exit(SpringApplication.exit(applicationContext, new ExitCodeGenerator() {
@Override
public int getExitCode() {
return 0;
}
}));
}
else {
SpringApplication app = new SpringApplication(MyApplication.class);
applicationContext = app.run(args);
if (logger.isDebugEnabled()) {
logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + " Application started context:" + applicationContext);
}
}
}
}
Esto ahora es posible desde Spring Boot 1.3 usando winsw.
La documentación lo dirige a una implementación de referencia que muestra cómo configurar un servicio.
A partir de springboot v1.2.2, no hay una forma limpia de cerrar una aplicación Spring Boot, empaquetada como un uber jar, usando procrun. Asegúrese de seguir estos temas, ya que esto es algo sobre lo que otros también están preguntando:
- https://github.com/spring-projects/spring-boot/issues/519
- https://github.com/spring-projects/spring-boot/issues/644
No está claro si / cómo los mantenedores de springboot lo manejarán. Mientras tanto, considere descomprimir el súper jar e ignorar el JarLauncher de Spring Boot.
Mi respuesta original a esta pregunta (visible en el historial) propuso una forma que debería funcionar (y pensé que sí), pero no debido a cómo se maneja el cargador de clases en JarLauncher.