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就可以了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM