programing

스프링 부트:내장된 Tomcat에 다른 WAR 파일을 추가하려면 어떻게 해야 합니까?

elseif 2023. 3. 5. 09:34

스프링 부트:내장된 Tomcat에 다른 WAR 파일을 추가하려면 어떻게 해야 합니까?

Spring Boot에 내장된 Tomcat은 개발 및 도입에 매우 편리합니다.

그러나 다른 (서드파티제의) WAR 파일(예: GeoServer)을 추가해야 하는 경우에는 어떻게 해야 합니까?

통상적인 순서는 다음과 같습니다.

  1. 일반 Tomcat 서버를 설치합니다.
  2. Spring Boot 애플리케이션을 WAR 파일로 빌드하여 Tomcat의 webapps 폴더에 추가합니다.
  3. 다른 (서드파티제의) WAR 파일도 webapps 폴더에 추가합니다.

하지만 다음과 같은 구성이 가능하다면 좋을 것 같습니다.

  1. Spring 부팅 응용 프로그램을 내장된 Tomcat을 포함하는 독립 실행형 Jar로 빌드합니다.
  2. 스프링 부트 애플리케이션 Jar를 전개합니다.
  3. 내장된 Tomcat이 인식하는 폴더에 다른 (서드파티제의) WAR 파일을 추가합니다.
  4. 내장된 Tomcat을 사용하여 Spring 부팅 응용 프로그램 콘텐츠와 다른 WAR 콘텐츠를 모두 처리합니다.

어떻게 할 수 있을까요?

갱신하다

스프링 부트 어플리케이션이 fat jar(=sublicable jar)로 되어 있는 경우, 답변의 코드는 충분하지 않습니다.변경된 내용은 다음과 같습니다.

@Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
    return new TomcatEmbeddedServletContainerFactory() {

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            try {
                Context context = tomcat.addWebapp("/foo", "/path/to/foo.war");
                WebappLoader loader =
                    new WebappLoader(Thread.currentThread().getContextClassLoader());
                context.setLoader(loader);
            } catch (ServletException ex) {
                throw new IllegalStateException("Failed to add webapp", ex);
            }
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }

    };
}

fat jar의 jar 파일은 시스템클래스로더로 로드할 수 없으므로 명시적인 부모클래스로더를 지정해야 합니다.그렇지 않으면 WAR을 추가한 스프링 부트애플리케이션의 fat jar에 라이브러리 jar를 로드할 수 없습니다.

다음을 사용하여 내장 Tomcat에 전쟁 파일을 추가할 수 있습니다.Tomcat.addWebappjavadoc이 말하는 것처럼 "Tomcat의 웹 앱 디렉토리에 웹 애플리케이션을 추가하는 것과 동등합니다."Spring Boot에서 이 API를 사용하려면 커스텀을 사용해야 합니다.TomcatEmbeddedServletContainerFactory서브클래스:

@Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
    return new TomcatEmbeddedServletContainerFactory() {

        @Override
        protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
                Tomcat tomcat) {
            // Ensure that the webapps directory exists
            new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs();

            try {
                Context context = tomcat.addWebapp("/foo", "/path/to/foo.war");
                // Allow the webapp to load classes from your fat jar
                context.setParentClassLoader(getClass().getClassLoader());
            } catch (ServletException ex) {
                throw new IllegalStateException("Failed to add webapp", ex);
            }
            return super.getTomcatEmbeddedServletContainer(tomcat);
        }

    };
}

답변은 Spring Boot 1.x 입니다.이 클래스는 Spring Boot 2.x에는 존재하지 않게 되었습니다.버전 2를 사용할 때는 다른 버전을 사용해야 합니다.

    @Bean
    @ConditionalOnProperty(name = "external.war.file")
    public TomcatServletWebServerFactory servletContainerFactory(@Value("${external.war.file}") String path,
                                                                 @Value("${external.war.context:}") String contextPath) {
        return new TomcatServletWebServerFactory() {

            @Override
            protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
                new File(tomcat.getServer().getCatalinaBase(), "webapps").mkdirs();

                Context context = tomcat.addWebapp(contextPath, path);
                context.setParentClassLoader(getClass().getClassLoader());

                return super.getTomcatWebServer(tomcat);
            }

        };
    }

또한 Spring boot enbedded Tomcat에는 기본적으로 JSP에 대한 의존관계가 포함되어 있지 않습니다.외부 전쟁에서 JSP를 사용하는 경우 JSP를 포함해야 합니다.

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>

업데이트: Spring Boot 1과 Spring Boot 2의 셋업 방법에 대한 자세한 블로그 투고를 작성했습니다.

Spring Boot 2의 경우 답변이 모두 제대로 작동하지 않았기 때문에 이 문제를 해결하는 데 시간이 걸렸습니다.드디어 다음과 같은 것이 생각났습니다(SSL이 유효하게 되어 있는 경우).WarRun.java와 Gradle 의존관계를 아래에 나타냅니다.

장점:

컨텍스트 경로 포함 Tomcat / at https://localhost:8070

sample.war at https://localhost:8070/sample

샘플 Web Apphttps://localhost:8070/yo에서의 전쟁

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Properties;
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.ClassPathResource;

@ComponentScan({ "com.towianski.controllers" })
@SpringBootApplication
@Profile("server")
public class WarRun extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(WarRun.class).web(  WebApplicationType.SERVLET );
    }

    public static void main(String[] args) {

        SpringApplication app = new SpringApplication(WarRun.class);
        System.out.println( "Entered WarRun.main");

        String loggingFile = "";
        String dir = "";

        for ( int i = 0; i < args.length; i++ )
            {
//            logger.info( "** args [" + i + "] =" + args[i] + "=" );
            System.out.println( "** args [" + i + "] =" + args[i] + "=" );
            if ( args[i].toLowerCase().startsWith( "-dir" ) )
                {
                dir = args[i].substring( "-dir=".length() );
                }
            else if ( args[i].toLowerCase().startsWith( "--logging.file" ) )
                {
                loggingFile = args[i].substring( "--logging.file=".length() );
                stdOutFilePropertyChange( loggingFile );
                stdErrFilePropertyChange( loggingFile );
                }
            }

        Properties properties = new Properties();
//        properties.setProperty( "spring.resources.static-locations",
//                               "classpath:/home/stan/Downloads" );
        properties.setProperty( "server.port", "8070" );
//        System.setProperty("server.servlet.context-path", "/prop");     <--- Will set embedded Spring Boot Tomcat context path
        properties.setProperty( "spring.security.user.name", "stan" );
        properties.setProperty( "spring.security.user.password", "stan" );
        System.out.println( "Entered WarRun.main after set properties");
        app.setDefaultProperties(properties);
        System.out.println( "Entered WarRun.main after call set props. before app.run");

        app.run(args);
        System.out.println( "Entered WarRun.main after app.run()");
    }

    @Bean
    public ServletWebServerFactory servletContainer() {
        return new TomcatServletWebServerFactory() {
            protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
                System.out.println( "tomcat.getServer().getCatalinaBase() =" + tomcat.getServer().getCatalinaBase() + "=" );
                new File(tomcat.getServer().getCatalinaBase(), "/webapps").mkdirs();
    //            try {
    //                Files.copy( (new File( "/home/stan/Downloads/sample.war" ) ).toPath(), (new File( tomcat.getServer().getCatalinaBase() +"/webapp/sample.war") ).toPath());
    //            } catch (IOException ex) {
    //                Logger.getLogger(WarRun.class.getName()).log(Level.SEVERE, null, ex);
    //            }
                try {
                    System.out.println( "Entered ServletWebServerFactory servletContainer()");
                    Context context2 = tomcat.addWebapp("/sample", new ClassPathResource("file:/home/stan/Downloads/sample.war").getFile().toString());
                    Context context3 = tomcat.addWebapp("/yo", new ClassPathResource("file:/home/stan/Downloads/SampleWebApp.war").getFile().toString());
    //                Context context = tomcat.addWebapp("/what", new ClassPathResource( "file:" + tomcat.getServer().getCatalinaBase() +"/webapps/sample.war" ).getFile().toString() );

                    context2.setParentClassLoader(getClass().getClassLoader());
                    context3.setParentClassLoader(getClass().getClassLoader());

    //  also works but above seems better
    //                WebappLoader loader2 = new WebappLoader(Thread.currentThread().getContextClassLoader());
    //                WebappLoader loader3 = new WebappLoader(Thread.currentThread().getContextClassLoader());
    //                context2.setLoader(loader2);
    //                context3.setLoader(loader3);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
                return super.getTomcatWebServer(tomcat);
            }
        };
    }
}

그라들:

apply plugin: 'war'

war {
    enabled = true
}

. . . .
dependencies {
    compile("org.springframework.boot:spring-boot-starter:2.1.6.RELEASE")
    compile("org.springframework.boot:spring-boot-starter-web:2.1.6.RELEASE") 
    compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '9.0.21'
    compile("org.springframework.boot:spring-boot-starter-security:2.1.6.RELEASE")
    compile 'org.apache.httpcomponents:httpclient:4.5.7'
    compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.5.6'
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.jcraft:jsch:0.1.55'

    testCompile group: 'junit', name: 'junit', version: '4.12'
}

언급URL : https://stackoverflow.com/questions/31374726/spring-boot-how-to-add-another-war-files-to-the-embedded-tomcat