add 插件日志
parent
80fc742f4a
commit
df2872a980
|
@ -71,6 +71,7 @@ public class SwaggerConfig {
|
||||||
private ApiInfo apiInfo() {
|
private ApiInfo apiInfo() {
|
||||||
return new ApiInfoBuilder()
|
return new ApiInfoBuilder()
|
||||||
.title(applicationName)
|
.title(applicationName)
|
||||||
|
.termsOfServiceUrl("http://ip:port/**")
|
||||||
.description("Swagger API Doc")
|
.description("Swagger API Doc")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
<java-semver.version>0.9.0</java-semver.version>
|
<java-semver.version>0.9.0</java-semver.version>
|
||||||
<junit.version>4.11</junit.version>
|
<junit.version>4.11</junit.version>
|
||||||
<spring-web.version>5.3.27</spring-web.version>
|
<spring-web.version>5.3.27</spring-web.version>
|
||||||
|
<swagger-spring-web.version>2.10.5</swagger-spring-web.version>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -84,6 +86,12 @@
|
||||||
<version>${jackson.version}</version>
|
<version>${jackson.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.xiaoymin</groupId>
|
||||||
|
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||||
|
<version>2.0.9</version>
|
||||||
|
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
|
@ -23,16 +23,20 @@
|
||||||
|
|
||||||
package com.gitee.starblues.integration;
|
package com.gitee.starblues.integration;
|
||||||
|
|
||||||
|
import com.gitee.starblues.integration.listener.SwaggerListener;
|
||||||
import com.gitee.starblues.spring.ResolvePluginThreadClassLoader;
|
import com.gitee.starblues.spring.ResolvePluginThreadClassLoader;
|
||||||
import com.gitee.starblues.spring.web.PluginStaticResourceConfig;
|
import com.gitee.starblues.spring.web.PluginStaticResourceConfig;
|
||||||
import com.gitee.starblues.spring.web.PluginStaticResourceWebMvcConfigurer;
|
import com.gitee.starblues.spring.web.PluginStaticResourceWebMvcConfigurer;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Import;
|
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.config.annotation.WebMvcConfigurer;
|
||||||
import org.springframework.web.servlet.resource.ResourceResolver;
|
import org.springframework.web.servlet.resource.ResourceResolver;
|
||||||
|
import springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统web环境配置点
|
* 系统web环境配置点
|
||||||
|
@ -42,6 +46,7 @@ import org.springframework.web.servlet.resource.ResourceResolver;
|
||||||
@ConditionalOnWebApplication
|
@ConditionalOnWebApplication
|
||||||
@Import({
|
@Import({
|
||||||
ExtendPointWebConfiguration.PluginStaticResourceConfiguration.class,
|
ExtendPointWebConfiguration.PluginStaticResourceConfiguration.class,
|
||||||
|
ExtendPointWebConfiguration.SwaggerListenerConfiguration.class,
|
||||||
})
|
})
|
||||||
public class ExtendPointWebConfiguration {
|
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
|
@Bean
|
||||||
public WebMvcConfigurer webMvcConfigurer(){
|
public WebMvcConfigurer webMvcConfigurer(){
|
||||||
return new ResolvePluginThreadClassLoader();
|
return new ResolvePluginThreadClassLoader();
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
package com.gitee.starblues.integration.listener;
|
package com.gitee.starblues.integration.listener;
|
||||||
|
|
||||||
|
import com.gitee.starblues.utils.SpringBeanUtils;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,8 +34,10 @@ import org.springframework.context.ApplicationContext;
|
||||||
*/
|
*/
|
||||||
public class DefaultInitializerListener implements PluginInitializerListener{
|
public class DefaultInitializerListener implements PluginInitializerListener{
|
||||||
|
|
||||||
|
private final SwaggerListener swaggerListener;
|
||||||
|
|
||||||
public DefaultInitializerListener(ApplicationContext applicationContext) {
|
public DefaultInitializerListener(ApplicationContext applicationContext) {
|
||||||
|
this.swaggerListener = SpringBeanUtils.getExistBean(applicationContext, SwaggerListener.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,6 +57,9 @@ public class DefaultInitializerListener implements PluginInitializerListener{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refresh(){
|
private void refresh(){
|
||||||
|
if(swaggerListener != null){
|
||||||
|
swaggerListener.refresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Parameter> 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<Parameter> 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<DocumentationPlugin, DocumentationType> pluginRegistry = this.getPluginRegistry();
|
||||||
|
List<DocumentationPlugin> plugins = pluginRegistry.getPlugins();
|
||||||
|
List<DocumentationPlugin> 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<DocumentationPlugin, DocumentationType> pluginRegistry = this.getPluginRegistry();
|
||||||
|
List<DocumentationPlugin> plugins = pluginRegistry.getPlugins();
|
||||||
|
List<DocumentationPlugin> 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<DocumentationPlugin, DocumentationType> getPluginRegistry(){
|
||||||
|
PluginRegistry<DocumentationPlugin, DocumentationType> 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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue