add 插件日志

master
jay 2024-05-06 20:14:32 +08:00
parent 80fc742f4a
commit df2872a980
5 changed files with 248 additions and 0 deletions

View File

@ -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();
} }

View File

@ -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>

View File

@ -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();

View File

@ -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();
}
} }
} }

View File

@ -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();
}
}