应用程序在第二次启动时没有环境属性启动两次

Application starting twice without environment properties on the second startup

我们的 Spring 引导应用程序启动了两次,但在第二次启动时没有设置环境。所以在第二次启动时,应用程序无法启动。我们需要从外部 tomcat 配置目录设置应用程序。为了使日志记录正常工作,我们进行了以下设置。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.14.RELEASE</version>
        <relativePath />
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <start-class>foo.bar.MyApplication</start-class>
        <tomcat.version>8.0.32</tomcat.version>
    </properties>

    <dependencies>
        <!-- Spring Boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!-- Environment -->
        <dependency>
            <groupId>com.ibm.db2.jcc</groupId>
            <artifactId>db2jcc4</artifactId>
            <version>10.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

主要-class

@SpringBootApplication
@EnableAspectJAutoProxy
public class MyApplication extends SpringBootServletInitializer {

    private static final Logger LOG = LoggerFactory.getLogger(MyApplication.class);

    public MyApplication() {
        LOG.info(">>>>>>>>>>> Welcome to MyApplication");
    }

    @Override
    protected SpringApplicationBuilder configure(final SpringApplicationBuilder builder) {
        return configureApplication(builder);
    }

    public static void main(final String[] args) {
        configureApplication(new SpringApplicationBuilder()).run(args);
    }

    private static SpringApplicationBuilder configureApplication(final SpringApplicationBuilder builder) {
        return builder.sources(MyApplication.class);
    }

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public MyBean getMyBean() {
        LOG.debug("Created request scoped MyBean.");
        return new MyBean();
    }
}

server.xml(精简)

<?xml version="1.0" encoding="UTF-8"?>
<Server port="${tomcat.shutdown.port}" shutdown="SHUTDOWN">

    <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener" />

    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />

    <Service name="Catalina">

        <Connector connectionTimeout="20000" port="8081" protocol="HTTP/1.1"
            redirectPort="${environment.https.redirect.port}" server="Apache" />
        <Connector port="9080" protocol="AJP/1.3"
            redirectPort="${environment.https.redirect.port}" />

        <Engine defaultHost="localhost" jvmRoute="${servername}" name="Catalina">

            <Host appBase="${tomcat.webapp.dir}" autoDeploy="true" deployOnStartup="true" name="localhost" unpackWARs="true"
                xmlNamespaceAware="false" xmlValidation="false">

                <Context docBase="my-app" path="/myapp" reloadable="true">
                    <Parameter name="spring.config.location" value="file:C:/tomcat/conf/" />
                    <Parameter name="spring.config.name" value="myapp" /> <!-- results in myapp.properties -->
                    <Parameter name="org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH" value="true" />
                </Context>
            </Host>
        </Engine>
    </Service>
</Server>

日志

Jul 16, 2018 10:17:14 AM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: Loaded APR based Apache Tomcat Native library 1.2.4 using APR version 1.5.1.
Jul 16, 2018 10:17:14 AM org.apache.catalina.core.AprLifecycleListener lifecycleEvent
INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
Jul 16, 2018 10:17:15 AM org.apache.catalina.core.AprLifecycleListener initializeSSL
INFO: OpenSSL successfully initialized (OpenSSL 1.0.2e 3 Dec 2015)
Jul 16, 2018 10:17:15 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-apr-8081"]
Jul 16, 2018 10:17:15 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-apr-9080"]
Jul 16, 2018 10:17:15 AM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 1404 ms
Jul 16, 2018 10:17:15 AM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Jul 16, 2018 10:17:15 AM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/8.0.32
Jul 16, 2018 10:17:25 AM org.apache.jasper.servlet.TldScanner scanJars
INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
Jul 16, 2018 10:17:26 AM org.apache.catalina.core.ApplicationContext log
INFO: 2 Spring WebApplicationInitializers detected on classpath
  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v1.5.14.RELEASE)

2018-07-16 10:17:27.316  INFO 4880 --- [ost-startStop-1] f.b.MyApplication                        : Starting MyApplication v1.0.2-SNAPSHOT on tomcat with PID 4880 (C:\tomcat\webapps\myapp\WEB-INF\classes started by admin in C:\Eclipse\eclipse-jee-oxygen-1a-win32-x86_64\eclipse)
2018-07-16 10:17:27,362 localhost-startStop-1 ERROR Unable to create directory C:\Eclipse\eclipse-jee-oxygen-1a-win32-x86_64\eclipse${sys:LOG_PATH}18-07
2018-07-16 10:17:27.362 DEBUG 4880 --- [ost-startStop-1] f.b.MyApplication                        : Running with Spring Boot v1.5.14.RELEASE, Spring v4.3.18.RELEASE
2018-07-16 10:17:27.378  INFO 4880 --- [ost-startStop-1] f.b.MyApplication                        : No active profile set, falling back to default profiles: default
2018-07-16 10:17:27.472  INFO 4880 --- [ost-startStop-1] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@f7cbaab2: startup date [Mon Jul 16 10:17:27 CEST 2018]; root of context hierarchy
2018-07-16 10:17:29.562  INFO 4880 --- [ost-startStop-1] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$ddbcd45] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-07-16 10:17:29.859  INFO 4880 --- [ost-startStop-1] o.a.c.c.C.[.[.[/myapp]                   : Initializing Spring embedded WebApplicationContext
2018-07-16 10:17:29.859  INFO 4880 --- [ost-startStop-1] o.s.w.c.ContextLoader                    : Root WebApplicationContext: initialization completed in 2387 ms
2018-07-16 10:17:30.483  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2018-07-16 10:17:30.483  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'errorPageFilter' to: [/*]
2018-07-16 10:17:30.483  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-07-16 10:17:30.483  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-07-16 10:17:30.483  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-07-16 10:17:30.483  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'requestContextFilter' to: [/*]
com.ibm.net.SocketKeepAliveParameters
2018-07-16 10:17:39.501  INFO 4880 --- [ost-startStop-1] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
2018-07-16 10:17:42.903  INFO 4880 --- [ost-startStop-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2018-07-16 10:17:42.919  INFO 4880 --- [ost-startStop-1] f.b.MyApplication                        : >>>>>>>>>>> Welcome to MyApplication
2018-07-16 10:17:44.714  INFO 4880 --- [ost-startStop-1] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@f7cbaab2: startup date [Mon Jul 16 10:17:27 CEST 2018]; root of context hierarchy
2018-07-16 10:17:44.917  INFO 4880 --- [ost-startStop-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/root],methods=[GET]}" [...]
2018-07-16 10:17:45.120  INFO 4880 --- [ost-startStop-1] o.s.w.s.h.SimpleUrlHandlerMapping        : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-07-16 10:17:45.120  INFO 4880 --- [ost-startStop-1] o.s.w.s.h.SimpleUrlHandlerMapping        : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-07-16 10:17:45.214  INFO 4880 --- [ost-startStop-1] o.s.w.s.h.SimpleUrlHandlerMapping        : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-07-16 10:17:45.651  INFO 4880 --- [ost-startStop-1] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-07-16 10:17:46.151  INFO 4880 --- [ost-startStop-1] f.b.MyApplication                        : Started MyApplication in 19.788 seconds (JVM running for 32.145)
2018-07-16 10:17:46.166  INFO 4880 --- [ost-startStop-1] o.a.c.s.HostConfig                       : Deploying web application directory C:\tomcat\webapps\myapp
2018-07-16 10:17:53.420  INFO 4880 --- [ost-startStop-1] o.a.j.s.TldScanner                       : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
2018-07-16 10:17:53.718  INFO 4880 --- [ost-startStop-1] o.a.c.c.C.[.[.[/myapp]                   : 2 Spring WebApplicationInitializers detected on classpath

  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::       (v1.5.14.RELEASE)

2018-07-16 10:17:54.435  INFO 4880 --- [ost-startStop-1] f.b.MyApplication                        : Starting MyApplication v1.0.2-SNAPSHOT on tomcat with PID 4880 (C:\tomcat\webapps\myapp\WEB-INF\classes started by admin in C:\Eclipse\eclipse-jee-oxygen-1a-win32-x86_64\eclipse)
2018-07-16 10:17:54.451  INFO 4880 --- [ost-startStop-1] f.b.MyApplication                        : No active profile set, falling back to default profiles: default
2018-07-16 10:17:54.530  INFO 4880 --- [ost-startStop-1] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@8cc9db4a: startup date [Mon Jul 16 10:17:54 CEST 2018]; root of context hierarchy
2018-07-16 10:17:56.308  INFO 4880 --- [ost-startStop-1] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$95fdde] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-07-16 10:17:56.575  INFO 4880 --- [ost-startStop-1] o.a.c.c.C.[.[.[/myapp]                   : Initializing Spring embedded WebApplicationContext
2018-07-16 10:17:56.575  INFO 4880 --- [ost-startStop-1] o.s.w.c.ContextLoader                    : Root WebApplicationContext: initialization completed in 2045 ms
2018-07-16 10:17:57.136  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2018-07-16 10:17:57.136  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'errorPageFilter' to: [/*]
2018-07-16 10:17:57.136  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-07-16 10:17:57.136  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-07-16 10:17:57.136  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-07-16 10:17:57.136  INFO 4880 --- [ost-startStop-1] o.s.b.w.s.FilterRegistrationBean         : Mapping filter: 'requestContextFilter' to: [/*]
2018-07-16 10:17:57.214  WARN 4880 --- [ost-startStop-1] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Tomcat.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.apache.tomcat.jdbc.pool.DataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
2018-07-16 10:17:57.230  INFO 4880 --- [ost-startStop-1] utoConfigurationReportLoggingInitializer : 

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2018-07-16 10:17:57.230 ERROR 4880 --- [ost-startStop-1] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Cannot determine embedded database driver class for database type NONE

Action:

If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).

错误原因

server.xml(spring.config.locationspring.config.name)中作为上下文属性的配置设置在第二次启动时未被考虑在内。所以 Spring 引导应用程序没有 myapp.properties 可用,因此 spring-boot-starter-data-jpa 找不到任何数据源设置的配置并且初始化失败。

分析

我研究了有关在 classpath 上检测到的两个 WebApplicationInitializers 的日志条目。这不是此行为的原因。有两个 WebApplicationInitializer:

Jersey-initializer 来自依赖项:spring-boot-autoconfigure-1.5.14.RELEASE.jar 以及关于它的代码:

// We need to switch *off* the Jersey WebApplicationInitializer because it
// will try and register a ContextLoaderListener which we don't need
servletContext.setInitParameter("contextConfigLocation", "<NONE>");

Jersey WebApplicationInitializer 基本上什么都不做,这不是此行为的原因。

所以我研究了这种行为的堆栈跟踪并得到以下结果:

org.springframework.web.SpringServletContainerInitializer.onStartup(Set<Class<?>>, ServletContext)

上设置断点

首次启动

SpringServletContainerInitializer.onStartup(Set<Class<?>>, ServletContext) line: 169    
StandardContext.startInternal() line: 5244  
StandardContext(LifecycleBase).start() line: 147    
ContainerBase$StartChild.call() line: 1408  
ContainerBase$StartChild.call() line: 1398  
FutureTask<V>.run() line: 277   
ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1153  
ThreadPoolExecutor$Worker.run() line: 628   
Thread.run() line: 785  

第二次启动

SpringServletContainerInitializer.onStartup(Set<Class<?>>, ServletContext) line: 169    
StandardContext.startInternal() line: 5244  
StandardContext(LifecycleBase).start() line: 147    
StandardHost(ContainerBase).addChildInternal(Container) line: 725   
StandardHost(ContainerBase).addChild(Container) line: 701   
StandardHost.addChild(Container) line: 717  
HostConfig.deployDirectory(ContextName, File) line: 1091    
HostConfig$DeployDirectory.run() line: 1830 
Executors$RunnableAdapter<T>.call() line: 522   
FutureTask<V>.run() line: 277   
ThreadPoolExecutor.runWorker(ThreadPoolExecutor$Worker) line: 1153  
ThreadPoolExecutor$Worker.run() line: 628   
Thread.run() line: 785  

根据第二个堆栈跟踪,tomcat - 出于某种原因 - 想要再次部署目录 /tomcat/webapps/myapp 而这次 server.xml 的上下文属性未被使用。

提示

我们使用 spring.config.location 而不是 @PropertySources 因为我们需要完全控制日志记录。 属性 资源加载可能为时已晚,根本没有选择。

非常感谢您的帮助。

我自己解决了这个问题。原因是 tomcat 服务器本身。配置错误。由于这不是标准 tomcat 安装(客户专业化),因此在此处解释解决方案意义不大。

您必须有两个子 class SpringBootServletInitializer。 class 之一可能缺少@Configuration。如果你想要 运行 两个实例,那么请确保你在 SpringBootServletInitializer 的两个子 classes 上都有 @Configuration。如果你想 运行 单实例然后删除第二个继承。