Talking about springfox-swagger principles and difficulties encountered during use,

Source: Internet
Author: User

Talking about springfox-swagger principles and difficulties encountered during use,

About swagger

Swagger is really a good thing. It can automatically generate relevant api documentation based on Business Code, especially for restful projects. developers can hardly need to maintain rest APIs, this framework can automatically generate restfut APIs for your business code, and provides corresponding test interfaces to automatically display responses in json format. This greatly facilitates communication and joint debugging costs between background developers and the front-end.

Springfox-swagger Introduction

With the powerful functions of swagger, the spring framework of java open source community quickly keeps up. It makes full use of its own advantages to integrate swagger into its own project, forming a spring-swagger, then it evolved into springfox. Springfox only uses its own aop features to integrate swagger through plug. It still relies on swagger to generate service APIs.

There are few documents about this framework on the Internet, most of which are simple for entry-level use. I have encountered many pitfalls when integrating this framework into my own project. To solve these pitfalls, I have to open its source code to find out. This article describes my understanding of springfox and what you need to pay attention to when using springfox.

General principles of springfox

The general principle of springfox is that, during project startup, spring context is initializing, and the framework automatically loads some swagger-related beans to the current context according to the configuration, and automatically scans the classes in the system that may need to generate the api documentation, and generates the corresponding information to cache. If springMvc is used at the project MVC control layer, all Controller classes are automatically scanned to generate corresponding api documents based on the methods in these Controller classes.

Because my project is SpringMvc, this article takes the integration of spring fox with Srping mvc as an example to discuss the usage and principles of spring Fox.

SpringMvc integration steps

First, add the following three dependencies to the project:

<! -- Sring mvc dependency --> <dependency> <groupId> org. springframework </groupId> <artifactId> spring-webmvc </artifactId> <version> 4.2.8.RELEASE </version> </dependency> <! -- Swagger2 core dependency --> <dependency> <groupId> io. springfox </groupId> <artifactId> springfox-swagger2 </artifactId> <version> 2.6.1 </version> </dependency> <! -- Swagger-ui provides an api display and test interface for the project --> <dependency> <groupId> io. springfox </groupId> <artifactId> springfox-swagger-ui </artifactId> <version> 2.6.1 </version> </dependency>

The preceding three dependencies are the most basic dependencies for spring MVC and spring Fox integration. Other dependencies are omitted here. The first is the basic dependency of springmvc, the second is the swagger dependency, and the third is the interface dependency. This is not necessary. If you do not want to use the springfox api interface, you do not need to use this, but also write a set of interfaces suitable for your project. After adding these dependencies, the system will automatically add some jar packages related to springfox and swagger. I have a rough look at the following:

Springfox-swagger2-2.6.1.jar

Swagger-annotations-1.5.10.jar

Swagger-models-1.5.10.jar

Springfox-spi-2.6.1.jar

Springfox-core-2.6.1.jar

Springfox-schema-2.6.1.jar

Springfox-swagger-common-2.6.1.jar

Springfox-spring-web-2.6.1.jar

Guava-17.0.jar

Spring-plugin-core-1.2.0.RELEASE.jar

Spring-plug-metadata-1.2.0.RELEASE.jar

Spring-swagger-ui-2.6.1.jar

Jackson-databind-2.2.3.jar

Jackson-annotations-2.2.3.jar

The above is the jar that I visually think springfox may need. It may not be an example of all the jar needed by springfox. From the jar above, we can see that in addition to swagger dependency, pringfox also needs dependency packages such as guava, spring-plug, and jackson (note that jackson is a jar package required to generate json, if this dependency is not added to the project, you must add the dependency to integrate swagger ).

Simple use of springfox

If you only use the default configuration of springfox, it is very easy to integrate with springmvc. Just write a class similar to the following code and put it in your project. The Code is as follows:

@Configuration@EnableWebMvc@EnableSwagger2publicclass ApiConfig {}

Note that the above is an empty java class file, and the class name can be specified at will, but the annotation @ Configuration, @ EnableWebMvc, and @ EnableSwagger2 marked in the above class must be added, this completes the basic integration of springmvc and springfox. With three annotations, you can directly view the api list using a URL similar to the following after the project is started: http: // 127.0.0.1: 8080/jadDemo/swagger-ui.html

This is indeed a magical effect. With three simple annotations, the system automatically displays all the APIs of all Controller classes in the project. Now let's start with this configuration class and briefly analyze its principles. This class does not have any code. Obviously, the three annotations play a crucial role. The @ Configuration annotation is inherent in the spring framework. It is an annotation identified by the @ Component meta annotation. Therefore, with this annotation, spring will automatically instantiate this class into a bean and register it in the spring context. The second annotation @ EnableWebMvc is used to enable srpingmvc. In Eclipse, click this annotation to take a brief look. It is through the meta annotation @ Import (DelegatingWebMvcConfiguration. class) adds a DelegatingWebMvcConfiguration bean to the spring context. I think the purpose of this class is to provide some springmvc configurations for swagger. The third annotation: @ EnableSwagger2, which can be thought of by name, is used to integrate swagger 2. It uses the meta annotation: @ Import ({Swagger2DocumentationConfiguration. class}), and introduces a Swagger2DocumentationConfiguration bean, which is the core configuration of Swagger. The code in it is as follows:

@Configuration@Import({ SpringfoxWebMvcConfiguration.class, SwaggerCommonConfiguration.class })@ComponentScan(basePackages = { "springfox.documentation.swagger2.readers.parameter",  "springfox.documentation.swagger2.web",  "springfox.documentation.swagger2.mappers"})publicclassSwagger2DocumentationConfiguration { @Bean public JacksonModuleRegistrar swagger2Module() {  returnnewSwagger2JacksonModule(); }}

This Class header introduces SpringfoxWebMvcConfiguration class and SwaggerCommonConfiguration class through some annotations, and automatically scans springfox. swagger2 related beans to spring context through ComponentScan annotation. Here, I am most interested in the SpringfoxWebMvcConfiguration class. I guess this class is the configuration that spring Fox integrates with the mvc core. Click it and see the following code:

@Configuration@Import({ModelsConfiguration.class })@ComponentScan(basePackages = {  "springfox.documentation.spring.web.scanners","springfox.documentation.spring.web.readers.operation","springfox.documentation.spring.web.readers.parameter","springfox.documentation.spring.web.plugins","springfox.documentation.spring.web.paths"})@EnablePluginRegistries({ DocumentationPlugin.class,  ApiListingBuilderPlugin.class,  OperationBuilderPlugin.class,  ParameterBuilderPlugin.class,  ExpandedParameterBuilderPlugin.class,  ResourceGroupingStrategy.class,  OperationModelsProviderPlugin.class,  DefaultsProviderPlugin.class,  PathDecorator.class})publicclassSpringfoxWebMvcConfiguration {}

The code below in this class is nothing more than adding some new beans through @ Bean annotations. I am not very interested in it, what I'm most interested in is what the header adds through @ EnablePluginRegistries. Springfox integrates swagger Based on the spring-plug mechanism. How does spring-plug implement it? I have no time to study the principles of spring-plug. But in the following article, we will mention writing a plug-in to expand the swagger function. The above plug added by @ EnablePluginRegistries does not have time to view all its code. Currently, the code I have read mainly includes ApiListingBuilderPlugin. class, OperationBuilderPlugin. class, ParameterBuilderPlugin. class, ExpandedParameterBuilderPlugin. class,

The first ApiListingBuilderPlugin has two implementation classes: ApiListingReader and SwaggerApiListingReader. ApiListingReader automatically generates an api list based on the Controller type, while SwaggerApiListingReader generates an Api list based on the class marked with @ api annotation. The OperationBuilderPlugin plug-in is used to generate specific api documentation. There are many implementation classes for this type of plug-ins. They have their own division of labor and perform their respective tasks. I did not go over the specifics carefully, only one of the implementation classes is followed: OperationParameterReader, which is the Plugin used to read api parameters. It depends on the ModelAttributeParameterExpander tool class, the non-simple type of command pairs in the interface method parameters in the Controller can automatically parse its internal properties to get a list of parameters containing all the properties (here there is a possibility of infinite recursion, ). The ExpandedParameterBuilderPlugin plug-in is mainly used to extend some functions of interface parameters, such as determining the Data Type of this parameter and whether it is a required parameter of this interface. In general, springfox-swagger is actually transferred from this series of plug. They are called when the system is started, some are used to scan the interface list, some are used to read interface parameters, and so on. They scan all the api interfaces in the system and cache them for users to view. So how are these plug tables tuned and where are their execution portals?

Extends umentation. spring. web. plugins package, which can be found in the springfox-spring-web-2.6.1.jar. In this package, we found two very core classes: DocumentationPluginsManager and DocumentationPluginsBootstrapper. For the first DocumentationPluginsManager, It is a bean that does not implement any interfaces, but it has many PluginRegistry-type attributes, and the attribute values are injected through the @ Autowired annotation. In terms of its class name, it is easy to think that this is a manager that manages all plug. It is easy to understand, because of the ComponentScan annotation configuration, all plug instances will be instantiated into a bean by spring, and then injected into this DocumentationPluginsManager instance for unified management. Another important class in this package, DocumentationPluginsBootstrapper, is probably the start class of plug. When you click it, you can find that it is a Component identified by @ Component, and the DocumentationPluginsManager instance just described is injected into its constructor, and the most critical, it also implements the SmartLifecycle interface. Anyone who knows about the life cycle of spring bean knows that this component will automatically call its start () method when it is instantiated as a bean and managed into the srping context. When you click start () to view the code, you will find that it has a line of code scanDocumentation (buildContext (each), which is used to scan the api documentation. Further tracking the code of this method, we can find that this method will eventually call all plug together through its DocumentationPluginsManager attribute to scan the entire system and generate api documentation. The scan result is cached in a map attribute of the DocumentationCache class.

The above is the general principle of integrating spring fox with srpingMvc. It injects a series of beans into the srping context through the EnableSwagger2 annotation, and automatically scans the Controller class of the system when the system starts, generates the corresponding api Information and caches it. In addition, it injects some Controller classes identified by the @ Controller annotation as the portal for the ui module to access the api list. For example, the Swagger2Controller class in the springfox-swagger2-2.6.1.jar package. This Controller is the interface address used to access the api list in the ui module. When you access the address http: // 127.0.0.1: 8080/jadDemo/swagger-ui.html to view the api list, you can see through the browser capture, it is similar to http: // 127.0.0.1: 8080/jadDemo/v2/api-docs? An address like group = sysGroup asynchronously obtains api information (in Json format) and displays it on the interface. The Controller entry corresponding to this address background is the Swagger2Controller class above. After receiving the request, this class, directly engage in retrieving api Information in the cache before initialization to generate a json string and return it.

After learning about springfox's principles, let's take a look at the pitfalls I encountered during springfox's use.

Springfox's first pitfall: the bean generated by the configuration class must share the same context with spring mvc.

As described above, in the springmvc project, you only need to write the following simple configuration class in the project without any business code.

@Configuration@EnableWebMvc@EnableSwagger2publicclass ApiConfig {}

Because of the role of the @ Configuration annotation, spring will automatically instantiate it into a bean and inject it into the context. Note that the context of the bean must be the same as that of spring mvc. How can this problem be solved? In actual spring mvc projects, there are usually two contexts: one is the context and the other is spring mvc (it is the sub-context of the context ). The context is web. the org. springframework. web. context. request. requestContextListener listener, the loaded context, usually we will write a configuration file called spring-contet.xml, the bean will eventually initialize to the context, it mainly includes the service in the system, beans such as dao, including data sources and transactions. Another context is spring mvc, which uses web. the org. springframework. web. servlet. the DispatcherServlet is loaded and usually has a configuration file called a spring-mvc.xml. When we write the ApiConfig class, if we decide to use the @ Configuration annotation for loading, make sure that the path of this class is within the base-package range configured by springmvc component-scan. This is because when ApiConfig is loaded by spring, a series of beans will be injected. In these beans, In order to automatically scan all Controller classes, some beans depend on some beans in SpringMvc. If the project separates the context of Srpingmvc from the context, it serves as the subcontext of the context. If you accidentally load the bean of the ApiConfig type to the above, because the configuration classes in the context of spring mvc do not exist in the root context, an error is reported.

In fact, I do not agree to configure Swagger through the @ Configuration annotation, because I think the Swagger api function is dispensable for production projects. Swagger is often used for testing environments for development by the Project front-end team or for interface integration by other systems. After the system is launched, these api lists may be hidden in the production system. However, if the Configuration is written to the java code through the @ Configuration annotation, you will be embarrassed to modify the java code and re-compile it when you want to remove this function when going online. Based on this, I recommend using spring's most traditional xml file configuration method. The specific method is to remove the @ Configuration annotation, And then it writes a file similar to <bean class = "com. jad. web. mvc. swagger. conf. configure bean like ApiConfig "/> to the xml configuration file of spring. In the project separated by the root context and mvc context, directly configured in the spring-mvc.xml, so that it and springmvc context must be in the same context.

Springfox's second biggest pitfall: parameters of the Controller class. Be sure to avoid infinite recursion.

Spring mvc has a powerful parameter binding mechanism that can automatically bind request parameters to a custom command object. Therefore, when writing a Controller, many developers directly use an object as a parameter of the Controller method to get lazy. For example, the following sample code:

@RequestMapping(value = "update")public String update(MenuVomenuVo, Model model){}

This is the code that most programmers like to write in Controller to modify an object. There is a big pitfall when integrating with swagger. If all the attributes in the MenuVo class are of the basic type, it's okay. However, if this class contains some other attributes of the custom type, and this property directly or indirectly exists in its own type, then there will be a problem. For example, if the MenuVo class is a menu class, a parent that contains the MenuVo class represents its parent menu. In this case, the swagger module directly reports an error when the system starts because the api cannot be loaded. The error is reported because the parameters of the update method are parsed during the loading of this method, and the MenuVo parameter is not a simple type, all its class attributes are automatically interpreted recursively. In this way, it is easy to fall into an infinite recursive endless loop.

To solve this problem, I only wrote an OperationParameterReader plug-in implementation class and the ModelAttributeParameterExpander tool class that it relies on. I replaced the two classes in the original srpingfox class by configuring the plug-in, the logic of Parameter Parsing is replaced by a column-by-bar switch, and infinite recursion is avoided. Of course, this is equivalent to modifying the source code level. I have not yet found a more perfect solution to this problem, so I only recommend that you avoid this infinite recursion when using spring-fox Swagger. After all, this does not conform to the springmvc command object specification. The springmvc parameter command object should contain only simple basic type attributes.

Springfox's third biggest pitfall: api group-related, Docket instances cannot be loaded late

Springfox divides all APIs into one group by default, so that when accessing through an address like http: // 127.0.0.1: 8080/jadDemo/swagger-ui.html, all api lists are loaded on the same page. In this way, if the system is a little larger and the api is a little more, the page will be suspended, so it is necessary to group the api. Api groups are defined through @ Bean annotations in the configuration file ApiConf. Common configurations on the Internet are as follows:

@EnableWebMvc@EnableSwagger2publicclass ApiConfig {@Bean public Docket customDocket() {    return newDocket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());  }}

In the above Code, inject a Docket through @ Bean. This configuration is not required. Without this configuration, the framework will generate a default Docket instance. This Docket instance is used to specify the public information of all the APIS it can manage, such as the api version and author, and specify which APIs are listed only (filtered by api addresses or annotations ).

There can be multiple Docket instances, such as the following code:

@EnableWebMvc@EnableSwagger2publicclass ApiConfig {@Bean public Docket customDocket1() {    return newDocket(DocumentationType.SWAGGER_2).groupName("apiGroup1").apiInfo(apiInfo()).select().paths(PathSelectors.ant("/sys/**"));  }@Bean public Docket customDocket2() {    return newDocket(DocumentationType.SWAGGER_2).groupName("apiGroup2").apiInfo(apiInfo()).select().paths(PathSelectors.ant("/shop/**"));  }}

When multiple Docket instances are configured in the project, you can group the APIs. For example, the code above divides the APIS into two groups. In this case, you must specify a different name for each group, for example, in the above Code, "apiGroup1" and "apiGroup2 ", each group can use a paths address expression in the ant style to specify which group of APIs to manage. For example, in the preceding configuration, the first group of Apis whose management addresses start with/sys/and the second group of Apis whose management addresses start with/shop. Of course, there are many other filtering methods, such as data-dependent annotation, method annotation, and address regular expression. After grouping, you can select different api groups from the drop-down menu in the upper-right corner of the api list interface. In this way, the project's api list is distributed to different pages. In this way, it is convenient to manage, and it does not end up with the need to load too many APIs on the page.

However, like using @ Configuration, I do not agree to use @ Bean to configure the Docket instance to the api group. Because of this, the code will also die. Therefore, we recommend that you configure the Docket instance in the xml file to implement these similar functions. Of course, considering the many attributes in the Docket, directly configuring beans is troublesome. You can write a FactoryBean for the Docket and configure FactoryBean in the xml file. However, when you configure the Docket to xml. Another big pitfall is that spring loads beans in a delayed manner by default. After these Docket instance beans are directly configured in xml. You will find that there is no effect. Your group items are not displayed in the drop-down list in the upper left corner of the page.

This problem has plagued me for several hours. Later, it was suggested by experience that the sping bean delayed loading by default. This Docket instance has not been loaded into spring context. It turns out that my guess is correct. I don't know if this is a bug of springfox, or because I shouldn't have moved the configuration of the Docket from the original java code to the xml configuration file.

Springfox's other pitfalls:Springfox also has some other pitfalls. For example, in the @ ApiOperation annotation, if the httpMethod attribute is not specified as a get or post method, it will get, post, delete, put and other methods are listed, and too many api lists are repeated, Which is ugly. In addition, you may encounter logon permissions during testing. This pile of traps is easy to solve. I will not talk about them much because of the limited space. There are also annotations such as @ Api, @ ApiOperation, and @ ApiParam. I will not repeat many documents on the Internet.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.