網站建設 講話新聞軟文發(fā)稿平臺
背景
最近在制定團隊內公用的基礎框架,基于單應用多module的架構思路,使用maven管理項目依賴,在項目中定義了一個springboot模塊,該模塊依賴具體的業(yè)務實現模塊,啟動后通過掃描路徑下的類加載服務,業(yè)務開發(fā)同事只需要開發(fā)具體業(yè)務模塊即可。
但是在項目管理時,不期望業(yè)務開發(fā)同事關心和修改基礎框架。最開始的做法是讓業(yè)務開發(fā)同事在項目的module管理模塊下新建業(yè)務module模塊(見下描述),在不同分支開發(fā)不同的業(yè)務,開發(fā)自測的時候,需要在springboot模塊中依賴具體業(yè)務module并啟動。
gm-admin --------------------------------管理后臺(springboot)服務,依賴gm-modules中的具體實現
gm-common--gm-common-core ----------------------基礎包,含最基礎的基類、工具類、異常類等--gm-common-log ----------------------日志實現包,通過注解,記錄web調用參數和結果 --gm-common-ratelimit -----------------限流器實現,若需對用戶進行限制則需要依賴gm-common-security模塊 --gm-common-redis ---------------------redis緩存依賴和分布式鎖工具類--gm-common-security ------------------基礎安全模塊,校驗和設置用戶信息、權限--gm-common-sftp ----------------------sftp工具 --gm-common-storage -------------------對象存儲實現,目前支持本地存儲和騰訊oss
gm-framework ----------------------------框架模塊,主要實現數據源注入等
gm-gateway ------------------------------網關服務,實現報文加解密、限流、路由等
gm-modules--gm-mall -----------------------------商城模塊--gm-partner --------------------------合伙人項目使用,非商城內容--gm-system ---------------------------系統(tǒng)管理模塊,實現系統(tǒng)用戶、角色、權限、菜單、機構等業(yè)務邏輯--gm-third ----------------------------第三方服務模塊,實現對第三方服務的調用,如短信、積分--gm-wechat ---------------------------微信模塊,實現微信用戶授權、生成小程序碼等與微信交互邏輯
這樣的開發(fā)模式導致業(yè)務開發(fā)的同事實際上需要在“框架項目”中進行業(yè)務開發(fā),雖然采用了module進行劃分,但是在開發(fā)中遇到問題總會嘗試或者難免會對框架代碼進行修改、優(yōu)化,最終導致沖突等問題。而我想達到的效果是“框架代碼”對業(yè)務開發(fā)同事盡量透明,類似于之前使用dapeng soa框架開發(fā)應用時一樣,不需要關心容器是怎么跑起來的,只需要關心本業(yè)務自身的業(yè)務和依賴。
大體思路是:開發(fā)一個自定義maven插件,將業(yè)務代碼在單獨的項目中進行開發(fā),需要啟動項目時,在項目目錄下執(zhí)行mvn命令,執(zhí)行maven插件,這個插件會將當前項目的(類)資源和依賴的依賴包添加到類加載器,并啟動springboot項目,實現在springboot項目啟動當前項目的目的。
實現
具體代碼步驟如下供參考:
Maven插件項目pom配置:
<packaging>maven-plugin</packaging>
<name>gm-maven-plugin</name><dependencies>...<!-- SpringBoot容器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.5.10</version></dependency><dependency><groupId>org.apache.maven</groupId><artifactId>maven-core</artifactId><version>3.5.2</version><scope>provided</scope></dependency><dependency><groupId>org.apache.maven</groupId><artifactId>maven-plugin-api</artifactId><version>3.5.2</version></dependency><dependency><groupId>org.apache.maven.plugin-tools</groupId><artifactId>maven-plugin-annotations</artifactId><version>3.5.2</version><scope>provided</scope></dependency><dependency><groupId>org.apache.maven</groupId><artifactId>maven-project</artifactId><version>2.2.1</version></dependency><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-plugin-plugin</artifactId><version>3.5</version></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.6.1</version><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins>
</build>
</dependencies>
關鍵代碼:
@Mojo(name = "run", threadSafe = true, requiresDependencyResolution = ResolutionScope.TEST)
public class GmMavenPlugin extends AbstractMojo {
/*** 獲取項目編譯環(huán)境類路徑*/
@Parameter(defaultValue = "${project}", readonly = true)
protected MavenProject project;@Overridepublic void execute() throws MojoExecutionException {try {// 獲取應用程序的 classpathList<String> classpathElements = project.getRuntimeClasspathElements();URL[] urls = new URL[classpathElements.size()];for (int i = 0; i < classpathElements.size(); i++) {urls[i] = new File(classpathElements.get(i)).toURI().toURL();System.out.println("URL: " + urls[i]);}// // 創(chuàng)建一個新的 ClassLoaderClassLoader classLoader = URLClassLoader.newInstance(urls, Thread.currentThread().getContextClassLoader());Class<?> applicationClass = classLoader.loadClass("com......GmAdminApplication");SpringApplication application = new SpringApplication(applicationClass);application.setResourceLoader(new DefaultResourceLoader(classLoader));application.setMainApplicationClass(applicationClass);application.run();// 掛起當前線程Thread.currentThread().join();} catch (Exception e) {throw new MojoExecutionException("Failed to start Spring Boot application", e);}}
...
編寫完成后將該插件install到本地倉庫(或推送到遠端私庫)。
新建一個業(yè)務項目,完成業(yè)務代碼的開發(fā)和編譯,如下:
@RestController
@RequestMapping("/tracking")
public class TrackingController {private static final Logger logger = LoggerFactory.getLogger(TrackingController.class);@PostConstructpublic void postConstruct() {logger.info("--------------------------------------------");logger.info("Tracking模塊控制器被加載...");logger.info("--------------------------------------------");}
...
然后在項目目錄下執(zhí)行maven命令
mvn compile com.dt26:gm-maven-plugin:2.0.0-SNAPSHOT:run
項目即在springboot容器中啟動,并可以看到日志如下:
...
[INFO] --------------------------------------------
[INFO] Tracking 模塊控制器被加載...
[INFO] --------------------------------------------
[INFO] Exposing 1 endpoint(s) beneath base path '/actuator'
[INFO] Mapped URL path [/v2/api-docs] onto method [springfox.documentation.swagger2.web.Swagger2Controller#getDocumentation(String, HttpServletRequest)]
[INFO] Will not secure any request
[INFO] Starting ProtocolHandler ["http-nio-8080"]
[INFO] Tomcat started on port(s): 8080 (http) with context path '/admin'
...
整體的需求就算滿足了,剩下就是優(yōu)化代碼使得更優(yōu)雅。當前只能實現開發(fā)時啟動項目,在實際打包發(fā)布到測試環(huán)境和生產時,仍然需要gm-admin項目中引入業(yè)務代碼模塊并打包成可執(zhí)行包。這一步后續(xù)可以考慮使用maven命令等方式自動完成。
參考:https://www.cnblogs.com/coder-chi/p/11305498.html