[spring-projects/spring-boot]使用 Actuator 时,具有名为 DispatcherServlet 的自定义 DispatcherServlet bean 的应用程序无法启动

2024-04-23 407 views
6

修复https://github.com/spring-projects/spring-boot/issues/12934https://github.com/spring-projects/spring-boot/issues/13106所做的更改意味着像这样的应用程序:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.DispatcherServlet;

@SpringBootApplication
public class CustomDispatcherServletApplication {

    public static void main(String[] args) {
        SpringApplication.run(CustomDispatcherServletApplication.class, args);
    }

    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

}

会因为这样的失败而无法启动:

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

2018-06-19 20:20:20.305  INFO 8473 --- [           main] c.e.d.CustomDispatcherServletApplication : Starting CustomDispatcherServletApplication on aw-rmbp.local with PID 8473 (/Users/awilkinson/dev/workspaces/spring/spring-boot/2.0.x/custom-dispatcher-servlet/target/classes started by awilkinson in /Users/awilkinson/dev/workspaces/spring/spring-boot/2.0.x/custom-dispatcher-servlet)
2018-06-19 20:20:20.308  INFO 8473 --- [           main] c.e.d.CustomDispatcherServletApplication : No active profile set, falling back to default profiles: default
2018-06-19 20:20:20.352  INFO 8473 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@35083305: startup date [Tue Jun 19 20:20:20 BST 2018]; root of context hierarchy
2018-06-19 20:20:21.517  INFO 8473 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2018-06-19 20:20:21.541  INFO 8473 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2018-06-19 20:20:21.542  INFO 8473 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.31
2018-06-19 20:20:21.550  INFO 8473 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/awilkinson/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
2018-06-19 20:20:21.643  INFO 8473 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-06-19 20:20:21.643  INFO 8473 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1294 ms
2018-06-19 20:20:21.872 ERROR 8473 --- [ost-startStop-1] o.s.b.web.embedded.tomcat.TomcatStarter  : Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'servletEndpointRegistrar' defined in class path resource [org/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.endpoint.web.ServletEndpointRegistrar]: Factory method 'servletEndpointRegistrar' threw exception; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' available
2018-06-19 20:20:21.889  INFO 8473 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2018-06-19 20:20:21.894  WARN 8473 --- [           main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
2018-06-19 20:20:21.902  INFO 8473 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-06-19 20:20:22.007 ERROR 8473 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

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

Description:

Method servletEndpointRegistrar in org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration required a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' that could not be found.
    - Bean method 'mainDispatcherServletPathProvider' not loaded because Default DispatcherServlet found dispatcher servlet bean dispatcherServlet

Action:

Consider revisiting the conditions above or defining a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' in your configuration.

我不完全确定如何解决这个问题。我们需要获取将调度到任何 servlet 端点的调度程序 servlet,并获取它映射到的路径。

EndpointRequest回到“""如果没有” DispatcherServletPathProvider,但我也不确定这是否正确。

回答

1

我在工作中面临这个问题。

我目前找到的解决方法是添加缺少的 bean,如下所示:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class DispatcherServletPathProvider implements org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider {

    @Value("${server.servlet.context-path}")
    private String servletContextPath;

    @Override
    public String getServletPath() {
        return servletContextPath;
    }

}

我不知道这个 bean 的默认实现是否可以解决这个问题?

8

@lpetit-yseop 这是一个很好的解决方法,但我认为你想使用server.servlet.path而不是server.servlet.context-path.

如果我们想要处理自定义调度程序 servlet 注册 bean,通用解决方案会更加复杂。他们可能没有使用该server.servlet.path属性来配置 servlet 的 URL 映射,因此我们需要找到注册 bean 并根据其 URL 映射对路径进行逆向工程。

3

谢谢你的确认。

我很难真正理解server.servlet.context-path和之间的关系server.servlet.path。目前,我只在 Spring Boot 文档中找到了一些简单的描述,例如server.servlet.path=/ # Path of the main dispatcher servlet.,在 Spring MVC 文档中没有提及......

我是否错过了一些记录的地方,例如“控制器的完整真实路径导致被/${server.servlet.context-path}/${server.servlet.path}预先添加到在控制器级别定义的路由”等?

6

server.servlet.context-path因此,如果我希望我的解决方法能够更好地适应未来的意外变化,我应该使用和server.servlet.path(如return servletContextPath + "/" + servletPath;)的组合还是仅servletPath像您提到的那样?

4

我正在查看对 spring-projects/spring-boot#13106 所做的修复。不幸的是,当我们尝试从 2.0.2 升级到 SpringBoot 2.0.3 时,该修复破坏了我们的一些集成测试。适用于需要使用 Jersey 的 MVC 上下文的场景。当我们需要提供静态文件时,我们需要带有 Jersey 的 MVC 吗?

我们看到以下错误

Method servletEndpointRegistrar in org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration required a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' that could not be found.
    - Bean method 'mainDispatcherServletPathProvider' not loaded because Default DispatcherServlet found non dispatcher servlet bean dispatcherServlet

Action:

Consider revisiting the conditions above or defining a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' in your configuration.

我觉得 ServletEndpointManagementContextConfiguration.java 中的第 57 行应该有另一个条件:@Conditional(DefaultDispatcherServletCondition.class)

我们现在使用的解决方法是在测试中使用以下配置

@Bean
@Primary
public DispatcherServletPathProvider testDispatcherServletPathProvider() {
    return () -> "";
}
2

@ranarula 我认为这与这里讨论的问题略有不同。您能否用一个最小的样本提出一个单独的问题来重现您所描述的行为?

5

谢谢@mbhave,将创建一个示例和一个问题,但在某种意义上似乎与此相关,在我的情况下,我们在上下文中有一个真正的 DispatcherServlet (类而不是 bean),而不是自定义 DispatcherServlet

尽管仔细观察似乎只是添加上述建议的更改可能无法单独工作,因为@ConditionalOnMissingClass泽西配置上有

4

@wilkinsona - 这个问题在 2.0.4.RELEASE 中仍然存在。定义了自定义 DispatcherServlet。启动过程中,出现如下错误:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:346)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:333)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:344)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:333)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:344)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:333)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107)
    at org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration.servletEndpointRegistrar(ServletEndpointManagementContextConfiguration.java:72)
    at org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration$$EnhancerBySpringCGLIB$$3c361396.CGLIB$servletEndpointRegistrar$0(<generated>)
    at org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration$$EnhancerBySpringCGLIB$$3c361396$$FastClassBySpringCGLIB$$7b0e7375.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:361)
    at org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration$$EnhancerBySpringCGLIB$$3c361396.servletEndpointRegistrar(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
    ... 70 more
5

@ltorzynski 这根本不是同一个错误,请查看堆栈跟踪。如果您注册自己的,DispatcherServlet则需要提供一个类型的 bean DispatcherServletPath,该 bean 提供“主”调度程序 servlet 的路径。如果您有一个调度程序 servlet 并且需要自己注册它,最简单的方法是使用DispatcherServletRegistrationBean.我可以看到这没有正确记录,所以我创建了#14145

7

@snicoll 感谢您的解释 - 我正在从 1.5.X 迁移到 2.0.X,刚刚发现了这个。

现在我明白了,但这很奇怪 - DispatcherServletRegistrationBean 来自 org.springframework.boot.autoconfigure.web.servlet 包,并记录为:“ServletRegistrationBean} for the auto-configured {@link DispatcherServlet}”。如果现在还需要手动注册 DispatcherServlet,则应更新其包和文档。

1

如果现在还需要手动注册 DispatcherServlet,则应更新其包和文档。

其实不然,只是为了方便而已。您可以加入我们的Gitter吗?我们应该停止劫持这个问题,我有兴趣了解为什么您需要自定义DispatcherServlet设置。

6

我有 apache cxf 调度程序,当我添加执行器依赖性时

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

我也有同样的问题。

这是我的 CXF 调度程序 servlet 声明:

    @Bean
    public ServletRegistrationBean dispatcherServlet() {
        return new ServletRegistrationBean(new CXFServlet(), "/Soap/*");
    }

    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpoint = new EndpointImpl(springBus(), serviceImpl);
        endpoint.publish("/");

        // http://cxf.apache.org/docs/interceptors.html
        endpoint.getOutFaultInterceptors().add(soapFaultInterceptor());

        return endpoint;
    }

这是错误:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPathProvider' available
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:347)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:334)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1107)
6

@chrisleves 您能否打开一个新问题并提供一个显示问题的示例应用程序。