Spring Security和Swagger2集成报错

  出现问题的项目算是一个新项目,但基本的脚手架代码是从另一个项目里迁过来的,原项目并没有报错,只有新项目才报异常。看报错内容,判断发生冲突的主要是spring-boot-starter-security和springfox-swagger2这两个jar包的文件。

  新项目错误如下:

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
15:22:32.350 spotcheck-system [main] ERROR o.s.b.d.LoggingFailureAnalysisReporter - 

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

Description:

Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a single bean, but 2 were found:
    - requestMappingHandlerMapping: defined by method 'requestMappingHandlerMapping' in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]
    - swagger2ControllerMapping: defined by method 'swagger2ControllerMapping' in class path resource [springfox/documentation/swagger2/configuration/Swagger2DocumentationConfiguration.class]

Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain' defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' available: expected single matching bean but found 2: requestMappingHandlerMapping,swagger2ControllerMapping

  我的项目中出错代码是这一行,在原项目并没有报错。

Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();

  getBean方法默认是byType来加载bean对象的,如果取到相同类型的2个就会报错。我分别查看了错误中提示的DelegatingWebMvcConfiguration.class和Swagger2DocumentationConfiguration.class。

  1. DelegatingWebMvcConfiguration.class里并没有requestMappingHandlerMapping方法,DelegatingWebMvcConfiguration继承了WebMvcConfigurationSupport.class类,在WebMvcConfigurationSupport.class类中有requestMappingHandlerMapping方法,返回值是RequestMappingHandlerMapping.class。代码如下:

    /**
     * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
     * requests to annotated controllers.
     */
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
                ......
        }

  2. Swagger2DocumentationConfiguration.class里有swagger2ControllerMapping方法如下:

  @Bean
  public HandlerMapping swagger2ControllerMapping(
      Environment environment,
      DocumentationCache documentationCache,
      ServiceModelToSwagger2Mapper mapper,
      JsonSerializer jsonSerializer) {
      return new PropertySourcedRequestMappingHandlerMapping(environment, new Swagger2Controller(environment, documentationCache, mapper, jsonSerializer));
  }

  这个方法里的返回值类型PropertySourcedRequestMappingHandlerMapping继承了RequestMappingHandlerMapping类,

public class PropertySourcedRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    ......
}

  所以可能Spring加载时,根据byType形式会认错了吧。

 

  解决办法:既然Spring加载时使用byType形式会报错,那我们就用byName形式加载。Spring中使用@Bean声明的方法,如果没有指定名称,默认的name就是方法名。所以我们把出错代码改成:

        RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping) applicationContext.getBean("requestMappingHandlerMapping");
        Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();

  到此问题解决。

 

补充一下:

  假设自己项目中有一个接口,同时有两个实现类,并且实现类都使用了@Service注解,你在使用过程中使用了@Autowired注解进行引用。这时Spring初始化时就会报错:expected single matching bean but found 2 ,因为@Autowired默认是byType形式加载的。

  解决办法就是用@Primary、@Qualifier这两个注解来解决问题

  @Primary注解是用来声明2个实现类那个是主要的。与@Component、@Service等注解放到一起使用。这时Spring通过@Autowired加载时,就会判断一下, 优先使用@Primary注解的类。

  @Qualifier显示声明加载类的名称,与@Autowired放到一起使用,这样Spring加载时判断类型相同,会在通过名称筛选出需要的类,就不会报错了。其实@Autowired + @Qualifier = @Resource,如果真需要byName形式加载,直接使用@Resource就可以了。

 

posted @ 2020-06-11 15:48  闲人鹤  阅读(4830)  评论(0编辑  收藏  举报