SpringBoot配置Swagger2接口未顯示分析


0.背景

  • 項目A繼承自項目B,SpringBoot1.5.16.RELEASE
    <parent>
        <artifactId>B</artifactId>
        <groupId>com.****</groupId>
        <version>****-SNAPSHOT</version>
    </parent>

1.配置信息

  • 項目A中配置的Swagger2版本
 <!--		swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
            <exclusions>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-models</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.5.21</version>
        </dependency>

        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
            <exclusions>
                <exclusion>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
  • 項目A的Swagger2Config.class
@EnableSwagger2
@Configuration
@EnableWebMvc
public class Swagger2Config extends WebMvcConfigurerAdapter {

    /**
     * 添加對swagger資源的放行
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2).groupName("myDocket")
                .apiInfo(apiInfo())
                .pathMapping("/")
                .enable(true)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.****.controller"))
                .apis(RequestHandlerSelectors.withClassAnnotation(ApiOperation.class))
                .paths(PathSelectors.any())
                .build();
    }

    //構建 api文檔的詳細信息函數
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                //頁面標題
                .title("Spring Boot Swagger2 構建RESTful API")
                //條款地址
                .description("*** Web Service Api Document")
                .version("1.0.0")
                .license("Apache 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0")
                //描述
                .build();
    }
}

3.定位

配置如上信息的時候后啟動端口進行調試:

正常的顯示是沒問題,但是所有的接口信息都不能展示,加了Swagger的的注解,未生效,報No operations defined in spec!

這種原因大部分是因為package沒有被掃到,即上面的 .apis(RequestHandlerSelectors.basePackage("com.****.controller")),這段一開始,在debug過程中,也確實走了這段代碼,但是未生效

之后試了下另外一個完全新搭建的項目(這個項目是從老項目中整體中剝離出來的,所以才有了繼承父pom的寫法),同樣配置是完全OK的,正常顯示的。

4.解決

上圖中的1,2兩個標志指向了一個一個郵箱地址,但在項目A中沒找到此郵箱地址,去B項目搜了下,發現是這個SwaggerConfig.class影響到了

@Configuration
@Profile("!prod")
@EnableSwagger2
public class SwaggerConfig extends ApolloAware{
    private static final String BASE_CONTROLLER_LEAF_NAME = "web";

    @Value("${spring.application.name}")
    private String appName;

    protected ApplicationContext applicationContext;
    public SwaggerConfig(@Autowired ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Bean
    public Docket api() {
        Object app = apolloApp(applicationContext);
        ApolloApplication apollo = app.getClass().getAnnotation(ApolloApplication.class);
        String defaultBasePackage = app.getClass().getPackage().getName() + "." + BASE_CONTROLLER_LEAF_NAME;
        String scanPackage = apollo.swaggerBasePackage().trim().isEmpty() ? defaultBasePackage : apollo.swaggerBasePackage();
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(getApiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage(scanPackage))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo getApiInfo() {
        Contact contact = new Contact("****.cloud", "http://*****", "****.cloud@****.com");
        return new ApiInfoBuilder()
                .title(appName)
                .description("Web Service Api Document")
                .version("1.0.0")
                .license("Apache 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0")
                .contact(contact)
                .build();
    }

這其實也能解釋在A項目中,一開始沒有單獨命名groupName("myDocket")會提示docket重復沖突的錯誤,在B項目的SwaggerConfig.class中,``groupName是默認的,也能發現這個里面有一個寫法是默認的package`:

    String defaultBasePackage = app.getClass().getPackage().getName() + "." + BASE_CONTROLLER_LEAF_NAME;

重新審視了A項目的注解,主要是啟動類上的注解,發現了采用了復合注解的寫法,下面的擴展內容會說到

@ApolloApplication
@ServletComponentScan
public class XXXApplication {
    public static void main(String[] args) {
        SpringApplication.run(XXXApplication.class, args);
    }
}

@ApolloApplication注解是復合注解,源碼是這樣的:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface ApolloApplication {
    @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
    Class<?>[] exclude() default {};
    
    @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
    String[] excludeName() default {};
    
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {"com.******"};
    
    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
    
    String swaggerBasePackage() default "";
}

此注解集成了@SpringBootConfiguration,@EnableAutoConfiguration@ComponentScan等注解,其中的一個參數swaggerBasePackage,指的是不指定包掃描的路徑,所以調整下啟動類的注解

@ApolloApplication(swaggerBasePackage = "com.****.controller")
@ServletComponentScan
public class XXXApplication {
    public static void main(String[] args) {
        SpringApplication.run(XXXApplication.class, args);
    }
}

接口信息順利加載出來

注解擴展

@EnableAutoConfiguration

@EnableAutoConfiguration的作用是啟動自動配置,意思是Spring Boot會根據你添加的jar包來配置你項目的默認設置,比如你添加了Spring Boot提供的spring-boot-starter-web依賴,其中包含了Tomcat和Spring MVC,這個注釋就會假設你正在開發一個Web應用程序,自動地幫你添加Web項目中所需要的Spring配置。

@ComponentScan

@ComponentScan的作用是掃描當前包及其子包下被@Component注解標記的類並納入到Spring容器中進行管理。是Spring傳統XML配置的context:component-scan的替代。

@Controller,@Service,@Repository是@Component的子注解,所以也會被@ComponentScan掃描並做和@Component相同的處理。

@ComponentScan提供了basePackage參數定義要掃描的包,如果不設置,默認會掃描包的所有類,即默認掃描**/*.class路徑,建議加上該參數以減少加載的時間。

@SpringBootApplication提供的參數

exclude

exlude參數繼承自@EnableAutoConfiguration注解的同名參數,根據class來排除特定的類加入Spring容器,傳入參數的value類型是class類型數組。

@SpringBootApplication(exclude = {Good.class, Bad.class})

excludeName

exludeName參數繼承自@EnableAutoConfiguration注解的同名參數,根據className來排除特定的類加入Spring容器,傳入參數的value類型是class的全類名字符串數組。

@SpringBootApplication(excludeName = {"com.yanggb.xxx.Good", "com.yanggb.yyy.Bad"})

exclude和excludeName可以用來關閉指定的自動配置,比如關閉數據源相關的自動配置。

scanBasePackages

scanBasePackages參數繼承自@ComponentScan注解的basePackages參數,指定要掃描的包,傳入參數的value類型是包名的字符串數組。

@SpringBootApplication(scanBasePackages = {"com.yanggb.xxx", "com.yanggb.yyy"})

scanBasePackageClasses

scanBasePackageClasses參數繼承自@ComponentScan注解的basePackageClasses參數,指定要掃描的包,傳入參數的value類型是包名的字符串數組。

@SpringBootApplication(scanBasePackageClasses = {Good.class, Bad.class}

Reference

1.springboot注解@SpringBootApplication分析


免責聲明!

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



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