模块依赖管理

这篇是承接《轻量级 Java 开发框架 设计》系列Blog文的后续文章

如果你对 Osgi 有所了解,应该会记得在 OSGi 框架中模块是一个独立的插件项目。作为插件项目而言可以通过引用其他插件来完成第三方库依赖。其实这一思想其实和我们引入其他 Jar 包到自己 Classpath 目录中道理是一样的。

不过不同的是 Osgi 它会单独为每一个插件提供一个独立的 ClassLoader 。然后依赖它的插件自动从它的 ClassLoader 中装载所需的程序资源。这样一来Osgi 容器就会知晓每一个插件工程的依赖项。当Osgi 容器启动时就可以进行模块依赖检查并提示开发者缺少哪些部件。同时也可以为容器根据要求有选择的加载启动。

其实在轻量化软件开发中我们并不需要这么复杂的处理逻辑。通常情况下软件产品一旦被分为多个模块之后,我们就会为不同的模块构建一个它们独立的 Java 工程。但是这样会遇到一些列问题:
1.首先模块并不知道自己的入口程序是什么?
2.其次当A模块依赖B模块时,没有一个明显的启动顺序。
3.模块之间需要一个环境进行彼此协调。

因此,大部分项目会为自己构建一个基本开发环境用来解决这些问题。同时也会通过开发环境提供软件开发所必要的一系列工具类。为了解决上面这些问题在设计模块时引入模块生命周期或者称之为运行阶段:

1.init :初始化阶段,该阶段的目的是为了在正式启动之前给予模块一些准备工作,其中包括了明确依赖。
2.start:启动阶段,这一阶段可以用来注册Servlet/Filter/发布Service服务等。
3.stop:停止阶段,这一阶段可以用来反向启动阶段所做的事情比方解除注册Servlet/Filter等。

下面给出一个例子:

@AnnoModule()
public class Mode1 implements Module {
    public void init(ApiBinder apiBinder) {
        DependencySettings dep = apiBinder.dependency();
        /*弱依赖,目标模块即使没有成功启动也不影响当前模块*/
        dep.weak(Mode2.class);
        /*强依赖,当前模块的启动必须依靠目标模块*/
        dep.forced(Mode3.class);
        // }
    public void start(AppContext appContext) {
        //
        appContext.registerServices(....);//该方法目前并不存在,演示用意
        appContext.registerServices(....);
    }
    public void stop(AppContext appContext) {
        //
        appContext.unRegisterServices(....);//该方法目前并不存在,演示用意
        appContext.unRegisterServices(....);
    }
}
@AnnoModule()
public class Mode2 implements Module {
    public void init(ApiBinder apiBinder) {
        apiBinder.dependency().weak(Mode3.class);
        System.out.println("Mode2 init!");
    }
    public void start(AppContext appContext) {}
    public void stop(AppContext appContext) {}
}
@AnnoModule()
public class Mode3 implements Module {
    public void init(ApiBinder apiBinder) {
        throw new RuntimeException();
        //这里抛出异常的用意是模拟 模块3 初始化失败 用来演示 模块1 因为强制依赖而受到牵连不会启动。
    }
    public void start(AppContext appContext) {}
    public void stop(AppContext appContext) {}
}

这个例子中依赖顺序是:Mode1>Mode2>Mode3。所以启动顺序应该是 3,2,1。下面是启动信息节选:


这样的设计我觉得具有下面几个优点:
1.明确了模块启动各个部分要作的事情,可以使代码逻辑更加清晰易懂。
2.保护加载失败的模块不会造成更大的牵连。
3.数据据服务、用户登陆认证;这样的模块之间可以明确依赖关系,并且通过日志的形式直观的展现出来。
4.将来可以为实现单独停止某一个模块做技术铺垫。
5.为热部署做技术铺垫。

不知道大家对这样的技术方案有何看法,欢迎一起讨论。

补充一下内容
在这个框架的设计目的是为了快速构建开发环境。因此从任何角度来说和Osgi都有很大的区别。之所用Osgi做比较是由于,Module 的管理方式和Osgi插件依赖体系很像。但是它们两者其本质是不同的两个东西。

比方说现在构建一个Web开发环境,需要如下功能:Action服务、ORM服务、数据源服务、业务Bean。根据不同的产品或项目还需要:任务模块、远程同步、消息服务,等等。

这个架构中可以很显然的看到:Action调用调用具体Action类的时候,业务Bean一定是预先可到达的。所以业务Bean服务的启动应当在Action之前。而ORM服务恰恰需要数据源服务的支持,这也是一个依赖的关系。

在设计 Hasor 的时候我考虑的是提供一个开发框架,可以用最简单的方式将上面列出的各种模块服务串联成一整个架构体系。而非设计了一个与Osgi同样目标的轮子。

----------------------------------------------------------------
目前的开发代码存放于包括Demo程序)
Github: https://github.com/zycgit/hasor
git@OSC:http://git.oschina.net/zycgit/hasor

非常感谢您百忙之中抽出时间来看这一系博文。可以通过Maven 中央仓库网站http://search.maven.org/搜索 Hasor 下载 hasor 的相关代码

相关文章

迭代器模式(Iterator)迭代器模式(Iterator)[Cursor]意图...
高性能IO模型浅析服务器端编程经常需要构造高性能的IO模型,...
策略模式(Strategy)策略模式(Strategy)[Policy]意图:定...
访问者模式(Visitor)访问者模式(Visitor)意图:表示一个...
命令模式(Command)命令模式(Command)[Action/Transactio...
生成器模式(Builder)生成器模式(Builder)意图:将一个对...