diff --git a/iot-common-doc/src/main/java/cc/iotkit/swagger/config/SwaggerConfig.java b/iot-common-doc/src/main/java/cc/iotkit/swagger/config/SwaggerConfig.java
index f4d9a69..211067a 100755
--- a/iot-common-doc/src/main/java/cc/iotkit/swagger/config/SwaggerConfig.java
+++ b/iot-common-doc/src/main/java/cc/iotkit/swagger/config/SwaggerConfig.java
@@ -71,6 +71,7 @@ public class SwaggerConfig {
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(applicationName)
+ .termsOfServiceUrl("http://ip:port/**")
.description("Swagger API Doc")
.build();
}
diff --git a/iot-spring-brick/spring-brick/pom.xml b/iot-spring-brick/spring-brick/pom.xml
index eeb8f6d..a74be48 100755
--- a/iot-spring-brick/spring-brick/pom.xml
+++ b/iot-spring-brick/spring-brick/pom.xml
@@ -20,6 +20,8 @@
0.9.0
4.11
5.3.27
+ 2.10.5
+
@@ -84,6 +86,12 @@
${jackson.version}
+
+ com.github.xiaoymin
+ knife4j-spring-boot-starter
+ 2.0.9
+
+
\ No newline at end of file
diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java
index fd83179..f165a7c 100755
--- a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java
+++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/ExtendPointWebConfiguration.java
@@ -23,16 +23,20 @@
package com.gitee.starblues.integration;
+import com.gitee.starblues.integration.listener.SwaggerListener;
import com.gitee.starblues.spring.ResolvePluginThreadClassLoader;
import com.gitee.starblues.spring.web.PluginStaticResourceConfig;
import com.gitee.starblues.spring.web.PluginStaticResourceWebMvcConfigurer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
+import org.springframework.context.support.GenericApplicationContext;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.ResourceResolver;
+import springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper;
/**
* 系统web环境配置点
@@ -42,6 +46,7 @@ import org.springframework.web.servlet.resource.ResourceResolver;
@ConditionalOnWebApplication
@Import({
ExtendPointWebConfiguration.PluginStaticResourceConfiguration.class,
+ ExtendPointWebConfiguration.SwaggerListenerConfiguration.class,
})
public class ExtendPointWebConfiguration {
@@ -62,6 +67,24 @@ public class ExtendPointWebConfiguration {
}
}
+ @ConditionalOnClass({ DocumentationPluginsBootstrapper.class })
+ @ConditionalOnProperty(name = "plugin.pluginSwaggerScan", havingValue = "true", matchIfMissing = true)
+ public static class SwaggerListenerConfiguration {
+
+ private final GenericApplicationContext applicationContext;
+
+ public SwaggerListenerConfiguration(GenericApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public SwaggerListener swaggerListener(){
+ return new SwaggerListener(applicationContext);
+ }
+
+ }
+
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new ResolvePluginThreadClassLoader();
diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java
index 81c0d65..f2bf08c 100644
--- a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java
+++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/DefaultInitializerListener.java
@@ -23,6 +23,7 @@
package com.gitee.starblues.integration.listener;
+import com.gitee.starblues.utils.SpringBeanUtils;
import org.springframework.context.ApplicationContext;
/**
@@ -33,8 +34,10 @@ import org.springframework.context.ApplicationContext;
*/
public class DefaultInitializerListener implements PluginInitializerListener{
+ private final SwaggerListener swaggerListener;
public DefaultInitializerListener(ApplicationContext applicationContext) {
+ this.swaggerListener = SpringBeanUtils.getExistBean(applicationContext, SwaggerListener.class);
}
@@ -54,6 +57,9 @@ public class DefaultInitializerListener implements PluginInitializerListener{
}
private void refresh(){
+ if(swaggerListener != null){
+ swaggerListener.refresh();
+ }
}
}
diff --git a/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/SwaggerListener.java b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/SwaggerListener.java
new file mode 100644
index 0000000..898304f
--- /dev/null
+++ b/iot-spring-brick/spring-brick/src/main/java/com/gitee/starblues/integration/listener/SwaggerListener.java
@@ -0,0 +1,210 @@
+/**
+ * Copyright [2019-Present] [starBlues]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.gitee.starblues.integration.listener;
+
+import com.gitee.starblues.core.PluginInfo;
+import com.gitee.starblues.core.descriptor.PluginDescriptor;
+import com.gitee.starblues.loader.utils.ObjectUtils;
+import com.gitee.starblues.utils.MsgUtils;
+import com.gitee.starblues.utils.SpringBeanUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.plugin.core.PluginRegistry;
+import org.springframework.plugin.core.PluginRegistrySupport;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.Parameter;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.DocumentationPlugin;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Swagger 监听事件
+ * @author starBlues
+ * @since 3.0.0
+ * @version 3.0.3
+ */
+public class SwaggerListener implements PluginListener{
+
+ private final Logger log = LoggerFactory.getLogger(this.getClass());
+ private final ApplicationContext mainApplicationContext;
+ private static List parameterList = new ArrayList<>();
+
+ /**
+ * 设置全局头部/参数
+ * ParameterBuilder tokenPar = new ParameterBuilder();
+ * tokenPar.name("参数名称")
+ * .description("参数描述")
+ * .modelRef(new ModelRef("参数数据类型"))
+ * .parameterType("header或者query等")
+ * .required(false);
+ * Parameter param = tokenPar.build();
+ * @param parameters parameters
+ */
+ public static void setParameters(List parameters){
+ parameterList = parameters;
+ }
+
+ public SwaggerListener(ApplicationContext mainApplicationContext) {
+ this.mainApplicationContext = mainApplicationContext;
+ }
+
+ @Override
+ public void startSuccess(PluginInfo pluginInfo) {
+ PluginDescriptor descriptor = pluginInfo.getPluginDescriptor();
+ try {
+ Docket docket = this.createDocket(descriptor);
+ String groupName = docket.getGroupName();
+ PluginRegistry pluginRegistry = this.getPluginRegistry();
+ List plugins = pluginRegistry.getPlugins();
+ List newPlugins = new ArrayList<>();
+ for(DocumentationPlugin plugin : plugins){
+ if(plugin.getGroupName().equals(groupName)){
+ continue;
+ }
+ newPlugins.add(plugin);
+ }
+ newPlugins.add(docket);
+
+ Field field = PluginRegistrySupport.class.getDeclaredField("plugins");
+ field.setAccessible(true);
+ field.set(pluginRegistry, newPlugins);
+ // 如果第一次启动且为跟随系统启动的插件,减少刷新
+ if(!pluginInfo.isFollowSystem() || pluginInfo.getStopTime() != null){
+ this.refresh();
+ }
+ log.debug("插件[{}]注册到 Swagger 成功", MsgUtils.getPluginUnique(descriptor));
+ } catch (Exception e) {
+ log.error("插件[{}]注册到 Swagger 失败,错误为:{}", MsgUtils.getPluginUnique(descriptor) ,e.getMessage());
+ }
+ }
+
+ @Override
+ public void stopSuccess(PluginInfo pluginInfo) {
+ PluginDescriptor descriptor = pluginInfo.getPluginDescriptor();
+ String groupName = getGroupName(descriptor);
+ try{
+ PluginRegistry pluginRegistry = this.getPluginRegistry();
+ List plugins = pluginRegistry.getPlugins();
+ List newPlugins = new ArrayList<>();
+ for(DocumentationPlugin plugin : plugins){
+ if(groupName.equalsIgnoreCase(plugin.getGroupName())){
+ continue;
+ }
+ newPlugins.add(plugin);
+ }
+
+ Field field = PluginRegistrySupport.class.getDeclaredField("plugins");
+ field.setAccessible(true);
+ field.set(pluginRegistry, newPlugins);
+
+ this.refresh();
+ log.debug("插件[{}]从 Swagger 移除成功", MsgUtils.getPluginUnique(descriptor));
+ } catch (Exception e) {
+ log.error("插件[{}]从 Swagger 移除失败,错误为:{}", MsgUtils.getPluginUnique(descriptor), e.getMessage());
+ }
+ }
+
+ void refresh(){
+ try {
+ DocumentationPluginsBootstrapper documentationPluginsBootstrapper = this.getDocumentationPluginsBootstrapper();
+ if(documentationPluginsBootstrapper != null){
+ documentationPluginsBootstrapper.stop();
+ documentationPluginsBootstrapper.start();
+ } else {
+ log.warn("Not found DocumentationPluginsBootstrapper, so cannot refresh swagger");
+ }
+ } catch (Exception e){
+ // ignore
+ log.warn("refresh swagger failure. {}", e.getMessage());
+ }
+ }
+
+ /**
+ * 获取文档 Bootstrapper
+ * @return DocumentationPluginsBootstrapper
+ */
+ private DocumentationPluginsBootstrapper getDocumentationPluginsBootstrapper(){
+ return SpringBeanUtils.getExistBean(mainApplicationContext, DocumentationPluginsBootstrapper.class);
+ }
+
+ /**
+ * 获取文档PluginRegistry
+ * @return PluginRegistry
+ */
+ private PluginRegistry getPluginRegistry(){
+ PluginRegistry registry =
+ SpringBeanUtils.getExistBean(mainApplicationContext, "documentationPluginRegistry");
+ if(registry != null){
+ return registry;
+ }
+ throw new IllegalStateException("项目依赖的 Swagger 版本不支持刷新插件接口, 请切换版本");
+ }
+
+ /**
+ * 创建swagger分组对象
+ *
+ * @param descriptor 插件信息
+ * @return Docket
+ */
+ private Docket createDocket(PluginDescriptor descriptor) {
+ String description = descriptor.getDescription();
+ if (ObjectUtils.isEmpty(description)) {
+ description = descriptor.getPluginId();
+ }
+
+ String provider = descriptor.getProvider();
+ String pluginBootstrapClass = descriptor.getPluginBootstrapClass();
+ String pluginClass = pluginBootstrapClass.substring(0, pluginBootstrapClass.lastIndexOf("."));
+ Contact contact = new Contact(provider, "", "");
+ ApiInfo apiInfo = new ApiInfoBuilder()
+ .title(getGroupName(descriptor))
+ .description(description)
+ .contact(contact)
+ .version(descriptor.getPluginVersion())
+ .build();
+
+ Docket docket = (new Docket(DocumentationType.SWAGGER_2))
+ .apiInfo(apiInfo).select()
+ .apis(RequestHandlerSelectors.basePackage(pluginClass))
+ .paths(PathSelectors.any()).build()
+ .groupName(getGroupName(descriptor));
+
+ if(parameterList != null && !parameterList.isEmpty()){
+ return docket.globalOperationParameters(parameterList);
+ }
+ return docket;
+ }
+
+ /**
+ * 获取组名称
+ * @param descriptor 插件信息
+ * @return 分组信息
+ */
+ private String getGroupName(PluginDescriptor descriptor){
+ return descriptor.getPluginId() +"@" + descriptor.getPluginVersion();
+ }
+}