Skip to main content

应用模块化

应用模块化#

通常我们在进行应用开发过程中,会按功能或其他维度进行模块化封装,但这些封装一般依赖开发人员自行协商约定或按公司规定进行模块化开发。 模块之间的耦合关系根据业务逻辑和开发人员的思路不同,可能会是紧耦合,也可能是松耦合,不同的团队之间可能也没有一致的标准。 本框架为上述场景提供了模块化的开发方案,方便实施业务模块化和团队任务分工。

模块设计#

  1. 定义模块的包名,每个模块需要有独立的包,多个模块不要在相同的包。
  2. 继承AppModule.class,实现模块核心类,该类需要直接放在模块包下,不能放在子包下。
  3. 根据模块实际需要,实现几个特定方法:initialize()模块初始化、verifyInstallation()模块安装状态校验、install()模块安装、uninstall()模块卸载、upgrade()模块升级、destroy()模块销毁等。

请看下面的实例:

package work.ready.examples.modules.first; // 这里first就是给FirstModule专用的包,所有该模块的类都需要放在这个包或子包下
import work.ready.core.log.Log;
import work.ready.core.log.LogFactory;
import work.ready.core.module.AppModule;
import work.ready.core.server.Ready;
import java.util.Map;
// 模块类,这里是FirstModule,必须要放在模块包下,不能放入子包下面
public class FirstModule extends AppModule { // 继承自AppModule
private static final Log logger = LogFactory.getLog(FirstModule.class);
public static final String CONFIG_NAME = FirstModule.class.getSimpleName();
public FirstModule(){
logger.info("FirstModule is enabled.");
}
// 这里定义了一个方法,演示如何读取模块的外部配置文件
public Map<String, Object> getConfig() {
return Ready.config().getMapConfig(CONFIG_NAME);
}
// 模块初始化
@Override
protected void initialize() {
logger.info("FirstModule is initializing ...");
// config()是框架原生提供的模块配置获取方法,该方法从主应用配置bootstrap/application.yml中获取模块的配置
// 下面演示先从外部模块的独立配置文件加载配置,然后将主配置中的配置项合并在一起
Ready.config().getMapConfig(CONFIG_NAME).putAll(config());
}
// 模块通过程序动态停止的时候才触发,通常不需要
@Override
protected void destroy(){
logger.warn("FirstModule is destroying ...");
}
// 如果你的模块需要安装例如建表或其他模块初始化工作,需要这里检查是否就绪,如果返回false,就会触发执行install(),否则不会执行install()。
@Override
protected boolean verifyInstallation(){
return true; // 这个例子无需安装,所以这里直接返回 true
}
// 如果你的模块需要安装,例如建表或其他模块初始化工作,那么请在这里设计
@Override
protected void install(){
logger.warn("FirstModule is installing ...");
}
// 模块卸载时清理关联数据
@Override
protected void uninstall(){
logger.warn("FirstModule is uninstalling ...");
}
// 模块升级逻辑放在这里
@Override
protected void upgrade() {
logger.warn("FirstModule is upgrading ...");
}
}

下面是第二个模块,和上面稍有不同,演示了安装调用的过程

// 这是第二个模块,包名和上面区分开了
package work.ready.examples.modules.second; // 这里second就是给SecondModule专用的包,所有该模块的类都需要放在这个包或子包下
import work.ready.core.log.Log;
import work.ready.core.log.LogFactory;
import work.ready.core.module.AppModule;
import work.ready.core.server.Ready;
import java.util.Map;
// 模块类,这里是SecondModule,必须要放在模块包下,不能放入子包下面
public class SecondModule extends AppModule { // 继承自AppModule
private static final Log logger = LogFactory.getLog(SecondModule.class);
public static final String CONFIG_NAME = SecondModule.class.getSimpleName();
public SecondModule(){
logger.info("SecondModule is enabled.");
}
// 这里定义了一个方法,演示如何读取模块的外部配置文件
public Map<String, Object> getConfig() {
return Ready.config().getMapConfig(CONFIG_NAME);
}
// 模块初始化
@Override
protected void initialize() {
logger.info("SecondModule is initializing ...");
// config()是框架原生提供的模块配置获取方法,该方法从主应用配置bootstrap/application.yml中获取模块的配置
// 下面演示先从外部模块的独立配置文件加载配置,然后将主配置中的配置项合并在一起
Ready.config().getMapConfig(CONFIG_NAME).putAll(config());
}
@Override
protected void destroy(){
logger.warn("SecondModule is destroying ...");
}
@Override
protected boolean verifyInstallation(){
return false; // 注意这里和上面不同,这里返回 false,演示安装过程
}
@Override
protected void install(){
logger.warn("SecondModule is installing ..."); // 由于上面返回false,这一步会被调用
}
@Override
protected void uninstall(){
logger.warn("SecondModule is uninstalling ...");
}
@Override
protected void upgrade() {
logger.warn("SecondModule is upgrading ...");
}
}

模块配置#

模块的配置有种方式:

  1. 单独定义一个和模块类名一样的yaml配置文件,例如:
FirstModule.yml
# Module configuration
---
first-config-item1: 1111111
first-config-item2: 2222222
SecondModule.yml
# Module configuration
---
second-config-item1: aaaaaaa
second-config-item2: bbbbbbb

如果上述配置文件是不需要改动的,可以放在对应的模块下的resources下的config文件夹下,和模块一起打入Jar包。如果配置项是需要后续经常变更的,建议放到工作空间的配置目录下。

  1. 如果配置项较少,可以直接定义在调用该模块的应用的主配置文件中,例如:
# Application configuration
---
readyWork:
profiles:
active: dev
server:
# This is the default binding address.
ip: 0.0.0.0
# Http port if enableHttp is true.
httpPort: 8080
appModule:
moduleConfig:
// 这里定义了FirstModule的配置项目,注意模块类名的首字母小写作为配置键名
firstModule:
config-item1: 1111111
config-item2: 2222222
// 这里定义了SecondModule的配置项目,注意模块类名的首字母小写作为配置键名
secondModule:
config-item1: 3333333
config-item2: 4444444

模块注册#

  1. 通常模块封装在独立的包中,有单独Jar包,而主应用是在另外的包中,所以需要先在pom中引入模块:
<dependencies>
<dependency>
<groupId>work.ready</groupId>
<artifactId>first-module</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>work.ready</groupId>
<artifactId>second-module</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>

上面引入了2个模块,first-module和second-module,具体根据实际情况引入。

  1. 然后在应用中通过静态方法registerModules注册模块。
public class Main extends Application {
public static void registerModules(List<Class<? extends AppModule>> moduleList){
moduleList.add(FirstModule.class);
moduleList.add(SecondModule.class);
}
public static void main(String[] args) {
Ready.For(Main.class).Work(args);
}
}

启动该应用后,各模块下的资源会被启用,service和controller也会自动注册和映射。

多应用#

如果希望将应用的服务分拆在不同的域名或端口下提供服务,一种方法是拆分应用变成多个独立的应用,这里框架提供了另一种方法,多应用并行。 多应用适合于主应用如果有辅助型的轻应用时,可以将他们挂接在一起,并行提供服务,而且应用间还可以通讯和资源共享。见下面的实例:

public class Static extends Application {
@Override
protected void appConfig(ApplicationConfig config) {
// 每个应用可以对应用层配置项进行操作
}
@Override
protected void initialize() {
applicationConfig().getServer().setHttpPort(8081);
applicationConfig().getStaticResource().getVirtualHost().addHost(
new VirtualHost().setDomain("localhost")
.setPath("/")
.setBasePath("/static/images/")
.setDirectoryListingEnabled(true) // 如果不需要文件列表,可以关闭此项
);
handler().addHandler(VirtualHostHandler.class);
}
}

上面定义了一个轻应用,将工作空间下的/static/images/目录绑定在localhost域名下,通过8081端口暴露服务。然后我们稍微修改一下上面模块化实例的主应用,让它挂载这个轻应用。

public class Main extends Application {
public static void registerModules(List<Class<? extends AppModule>> moduleList){
moduleList.add(FirstModule.class);
moduleList.add(SecondModule.class);
}
@Override
protected void globalConfig(ApplicationConfig config) {
// 主应用可以通过globalConfig对bootstrap配置进行操作
}
@Override
protected void appConfig(ApplicationConfig config) {
// 每个应用还可以对应用层配置项进行操作
}
public static void main(String[] args) {
// 请看这里,把上面定义的Static轻应用和主应用挂载在一起。
Ready.For(Main.class, Static.class).Work(args);
}
}

启动该应用,我们就可以看到主应用绑定在8080端口,轻应用绑定在localhost的8081端口,主应用可以通过IP和域名都能访问,而轻应用因为绑定了localhost域名,只能通过localhost:8081来访问。

更多文档#

以上为本框架提供的常用模块化应用介绍,还有更多模块化方面的文档将在后续不断完善,重点内容将以专题形式提供。