网站如何优化排名软件许昌网站制作公司
2026/4/2 7:26:49 网站建设 项目流程
网站如何优化排名软件,许昌网站制作公司,网页版微信二维码加载失败,泉州seo培训班前言 上一篇文章还是2年前#xff0c;一是工作太忙#xff0c;二是人也变得懒散#xff0c;好多新东西仅止于脑海里面的印象#xff0c;未能深入#xff0c;不成体系#xff0c;最近主要花了些时间实现Java版本的模块化#xff0c;同时也要重点兼顾小伙伴们从.NET Core移…前言上一篇文章还是2年前一是工作太忙二是人也变得懒散好多新东西仅止于脑海里面的印象未能深入不成体系最近主要花了些时间实现Java版本的模块化同时也要重点兼顾小伙伴们从.NET Core移植模块的成本所以需要全盘考虑的东西会更加实际好在有些Java底子加上AI的出现实现的过程相对会容易一些最近对AGI提起兴趣接下来应该会重点学习这方面的应用开发再来和大家分享好了话不多说接下来的系列文章会讲讲Java版本的模块化和大家一起探讨探讨或许有更好的一些建议我能学习到更多。Spring Pf4j实现效果我们选择【https://github.com/pf4j/pf4j】作为Java模块化的基础设施虽然官方作者提供了pf4j-spring的版本基础使用但能力太弱主要作者对spring boot好像不是非常熟悉并没有任何贬低意思在相关issue作者也做出了表明尤其是我们还要考虑.NET Core模块的移植所以不能完全开箱即用所以我对其进行二次封装。二次封装为Spring版本注意这里我说的是封装为Spring而不是SpringBoot因为SpringBoot是Web应用而Spring提供了SpringBoot的基础能力所以我们只需要引入Spring基础包即可万万不可将SpringBoot全家桶引入到模块化基础设施这点考虑非常重要。最终插件只需要继承封装的插件类即可插件开发者可重写beforeApplicationContextRefresh和afterApplicationContextReady熟悉.NET Core开发的伙伴们应该能猜到等同于ConfigureServices和Configure方法在before方法里可自定义手动注册相关bean当然常见的component和bean等注解会自动注册而after则是上下文刷新完成后可做业务上的初始化工作Spring Pf4j上下文每个插件有独立的上下文所以在启动插件时需创建插件上下文完成创建插件上下文分为4个步骤一是初始化上下文二是提供上述抽象开发者可重写的手动注册三是刷新插件上下文四是上述插件利用上下文进行相关业务初始化操作privateApplicationContext createApplicationContext() {longstartTs System.currentTimeMillis();//Step 1: Pre-create application contextlog.info(Initializing base context for plugin {}, pluginId);longpreCreateStart System.currentTimeMillis(); AnnotationConfigApplicationContext annotationContextpreCreateApplicationContext(); log.info(Initialized base context for plugin {} in {} ms, pluginId, System.currentTimeMillis()-preCreateStart);//Step 2: Customize context before refreshlog.info(Customizing context configuration for plugin {}, pluginId);longhandleStart System.currentTimeMillis(); AnnotationConfigApplicationContext contextbeforeApplicationContextRefresh(annotationContext); log.info(Customized context configuration for plugin {} in {} ms, pluginId, System.currentTimeMillis()-handleStart);if(context null) { contextannotationContext; }//Step 3: Refresh the context (load beans, etc.)log.info(Refreshing Spring context for plugin {}, pluginId);longpostCreateStart System.currentTimeMillis(); postCreateApplicationContext(context); log.info(Refreshed Spring context for plugin {} in {} ms, pluginId, System.currentTimeMillis()-postCreateStart);//Step 4: Post-refresh custom logiclog.info(Executing post-refresh logic for plugin {}, pluginId);longcustomStart System.currentTimeMillis(); afterApplicationContextReady(context); log.info(Completed post-refresh logic for plugin {} in {} ms, pluginId, System.currentTimeMillis()-customStart);//Total timelog.info(Plugin {} context fully initialized in {} ms, pluginId, System.currentTimeMillis()-startTs);returncontext; }整个步骤最重要的属于初始化插件的上下文这里贴一下伪代码Spring控制器动态注册控制器的动态注册必然是等插件上下文刷新完成后去通过插件上下文获取控制器bean同时基于控制器的请求处理映射为RequestMappingHandlerMapping所以我们需要实现自定义的请求处理映射这里我们暂时只需考虑控制器及其方法的动态注册publicclassGJPluginRequestMappingHandlerMappingextendsRequestMappingHandlerMapping {privatestaticfinalLogger log LoggerFactory.getLogger(GJPluginRequestMappingHandlerMapping.class); OverridepublicvoiddetectHandlerMethods(NotNull Object controller) {super.detectHandlerMethods(controller); } }我们将上述自定义请求映射处理作为bean注册到主应用然后在插件上下文创建完成后获取注册到主应用的自定义请求处理映射传入插件伪代码如下GJPluginLifecycle registerController() { GJPluginRequestMappingHandlerMapping pluginRequestMappingHandlerMapping plugin.getMainApplicationContext().getBean(pluginRequestMappingHandlerMapping, GJPluginRequestMappingHandlerMapping.class); pluginRequestMappingHandlerMapping.registerControllers(plugin);returnthis; }插件上下文获取控制器bean并将插件控制器bean注册到主应用上下文以及控制器方法注册到自定义的请求处理映射中publicSetObjectgetControllerBeans(GJPlugin springBootPlugin) { ApplicationContext applicationContextspringBootPlugin.getApplicationContext(); SetObject beans newLinkedHashSet(); MapString, Object controllerBeans applicationContext.getBeansWithAnnotation(Controller.class); MapString, Object restControllerBeans applicationContext.getBeansWithAnnotation(RestController.class); beans.addAll(controllerBeans.values()); beans.addAll(restControllerBeans.values());if(log.isTraceEnabled()) { ListString names beans.stream() .map(b-b.getClass().getSimpleName()) .collect(Collectors.toList()); log.debug(Scanned {} controller beans: {}, beans.size(), names); }returnbeans; }我们再来遍历插件中所有控制器列表进行动态注册即可SpringDoc-OpenApi上述为整个模块化或者插件化的设计方案我们首先需要实现的第一个则是Swagger将所有插件接口列表能够在主应用启动完成后在swagger页面里呈现出来但我们插件控制器为动态注册那么这里如何设计呢我们一步步来。首先是在主应用引入openapi的包dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId /dependency上述只是主应用定义的控制器已被呈现但要使得动态注册的插件控制器在主应用启动后也能在swagger中呈现出来我们还需要完成3个步骤一是在插件基础设施中引入openapi插件化基础设施尽可能轻量无需引入springdoc-openapi-starter-webmvc-ui建议引入springdoc-openapi-starter-common包即可如此插件只需对控制器等等打上标签其他应该都用不到。二是插件注册时需要构建插件控制器的GroupedOpenApi即每个插件对应一个GroupedOpenApi并将其注册到主应用上下文三是主应用需要支持动态注册多GroupedOpenApi。我们重点关注步骤2和步骤3在主应用yml配置文件中对spring-doc的相关配置过于简单此处忽略不讲为了实现多模块的动态注册需要使用springdoc-OpenApi的多GroupedOpenApi延迟注册如下为通用方案ConfigurationpublicclassSpringDocOpenApiCfg { Bean MultipleOpenApiWebMvcResource multipleOpenApiResource(ListGroupedOpenApigroupedOpenApis, ObjectFactoryOpenAPIServicedefaultOpenAPIBuilder, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders, SpringDocCustomizers springDocCustomizers) {returnnewMultipleOpenApiWebMvcResource(groupedOpenApis, defaultOpenAPIBuilder, requestBuilder, responseBuilder, operationParser, springDocConfigProperties, springDocProviders, springDocCustomizers); } }我们封装插件的注册GroupedOpenApi逻辑如下publicclassGJPluginOpenApiInfo {/*** 获取插件Swagger分组名称插件ID即为组名*/publicString getGroupName;publicString getGroupName() {returngetGroupName; }publicvoidsetGroupName(String getGroupName) {this.getGroupName getGroupName; }/*** 获取插件Controller所在包*/privateListStringgetControllerPackages;publicvoidsetControllerPackages(ListStringgetControllerPackages) {this.getControllerPackages getControllerPackages; }publicListStringgetControllerPackages() {returngetControllerPackages; } }publicclassGJPluginOpenApiConfig {publicstaticfinalString PLUGIN_SWAGGER_BEAN_PREFIX pluginGroupedOpenApi-;publicstaticvoidregisterPluginOpenApiBeans(GJPlugin springBootPlugin, GJPluginOpenApiInfo pluginSwaggerInfo) { String groupNamepluginSwaggerInfo.getGroupName(); groupNamegroupName.trim().toLowerCase();if(groupName.trim().isEmpty()) {return; } String beanName PLUGIN_SWAGGER_BEAN_PREFIX groupName; String finalGroupNamegroupName; GroupedOpenApi groupedOpenApiGroupedOpenApi.builder() .group(finalGroupName.trim()) .displayName(finalGroupName.trim()) .packagesToScan(pluginSwaggerInfo.getControllerPackages().toArray(newString[0])) .build(); springBootPlugin.registerBeanToMainContext(beanName, groupedOpenApi); } }在上述我们遍历控制器列表动态注册控制器时此时调用上述封装注册插件的GroupedOpenApi代码如下我们搞一个Demo插件控制器看能不能在swagger界面中呈现出来此时我们发现插件GroupedOpenApi有了但插件接口列表没有呈现同时主应用的接口列表悄无声息已无于是乎开始自定义OpenApiResource调试等等系列操作底层最后在构建计算接口列表等等时有一个方法引起重要关注上述严格判断插件控制器方法的bean到底是不是属于对应的控制器于是我们回过头去看我们动态注册控制器的bean和将控制器的方法注册到请求处理映射的逻辑如下爱再重点标识一下以免小伙伴们忘记了未曾注意到这一细节我们发现了问题注册控制器到主应用上下文的bean用的控制器名称而将控制器方法的注册传入的是控制器对象而不是简单的控制器名称所以获取到的方法控制器bean则是控制器的hash值而控制器的bean实际是字符串所以传入方法的控制器也修改为控制器的名称总结如上基于pf4j二次封装的整个设计思路其中还涉及一些细节并未详细展开细节主要是对pf4j底层实现的深入了解然后在封装以及安全等等上做出了进一步的打磨若有需要了解的小伙伴们可在评论留言我们可一起碰撞碰撞思路本文暂到此为止感谢阅读。你所看到的并非事物本身而是经过诠释后所赋予的意义

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询