4. 1 ) DO 的 properties 由框架来自动装配
2 ) AO 自动注入到 Action 中
3 )新的 Action 不再走 CommandDispather
5. 再来说说 webx 中 service 的初始化
( 重构过程需要注意 )
之前大部分的应用部署, web 和 biz 层的 service 容器都是分离的,中间
通过 CommandDispather 方式来交互;但实际上我们没有用到分布式
的方案(可能以后也不会需要), CommandDispather 没有发挥很大
的作用,反而麻烦并且弱化了类型系统,显得很“鸡肋”。
Apollo 去除了它,在容器层面也比较“单纯”,都在同一个 Service 容器
中。
但考虑对以前代码的兼容,又必须支持 commanddispather 模式,如果在
现有系统上,使用 Apollo 的话,它的布局会很乱,看上去如下:
6. 现有系统上 (Exodus2 重构前 ) 使用 Apollo 注释:
每个 car 的
pipeline 配置中
Web 层全局 Spring 容器 用新的 valve 替
ServiceA MailService Apollo 框架将从这里获取 AO/BO 代,判断请求类
型如果是旧的,
仍使用以前的
commanddispat
her 方式找 biz 层
,新的方式则不
cd cd cd 需要
commanddispat
ctx ctx ctx her ,从 web 层
的全局 spring 中
去寻找。这意味
着 biz 层的所有
对象需要在 web
容器中再配置一
份。
缩写 cd 表示
Commanddispat
AO BO DAO her
缩写 ctx 表示
WebApplicationC
ontextUtilsBean(
MailService TemplateSvc OtherService 见 web-tool.xml)
8. 合并后, Apollo 框架所期望
的
全局 Springx
OtherService ctx
AO BO DAO
MailService cd
Car1 Car2
Spring Spring
ServiceA ServiceA
ServiceB ServiceB
注:红色和黄色表示兼容以前的,绿色表示新的请求方式
9. 注释:
原先每个 car 里的 Spring 容器,不再配置任何对象, cd 和 ctx
移到全局 spring 中,它们之间是级联的,所以 car 中访问 cd
时会得到全局 spring 中的 cd 。
旧的请求仍走以前的 cd 方式,新的则可以直接访问全局
spring 中的 AO ;(同样是因为 spring 容器级联的原因)
对于 cd 和 ctx 我们改变它的访问为全局的 spring 容器(也就
是自身所在的容器)即可,甚至不必修改代码,只要不再配
置 biz 层 service 容器,它就会访问它所在的 spring 容器。
注: cd 和 ctx 到 AO 之间的虚线不是表示它们有引用关系,表
示旧的请求方式仍通过 cd 或 ctx 才能得到 AO 的
10. 解惑
Web 层的 Service 分布在 main-instance( 全局 ) 和
sub-instance(car 级别 )
把 Biz 层合并后,其中 biz 层的 service 要放到 main-
instance 中
同时考虑有些 sub-instance 的 service 是否也可以放
到 main-instance 中,
要做这个调整,必须弄清楚 service 的创建过程
以前在《 Exodus2 大局观》中分享过,但说的不特别
细,这次再详细的了解一下这里,先看一下 web 层
Service 容器的布局 :
11. Web 层 Service 容器
全局: name=null
Service 包装 Service 包装
Car1 Car2 Car3
name=home name=offer name=member
Service 包装 Service 包装 Service 包装
Service 包装 Service 包装 Service 包装
注释:
每个 car(sub-instance) 是一个 ServiceInstanceContext ,它的 name 就是 car 的
name
全局 (main-instance) 也是一个 ServiceInstanceContext ,它的 name=null
12. 在 DefaultServiceManager.initMapping 时,如果
全局配置了某些 Service ,局部没有配置(或者反
之)都会把做一个补全的工作,将全局有,局部没
有的 Service 也在局部创建一份该 Service 的包装
(注意,不是直接创建一个 Service )
这个包装类的名字是 ServiceInstance ,个人感觉命
名很不好,容易理解成其他意思,改为
ServiceWrapper 更好(后续我会用 Wrapper 来指
代)。
13. 创建好这个包装的时候,还没有对其中真正的
Service 进行初始化
后续再对 Service 进行初始化的时候,调用
wrapper 中的 getInstance( 这个方法的命名
也不好,不如直观的叫 getRealService 意思
更清晰些 )
来获取真正的 Service 对象,得到它的 Class ,
返回 class.newInstance ,才真正进行了初始
化。
14. Main-instance?
Sub-instance
全局
sub 指定了 class?
全局指定了 class?
yes
yes
该 class 在全局 该 class 在全局
也指定了 ? 也指定了 ?
返回 Class. return
newInstance null
全局有 全局无
全局有 全局无
该类实现了
MultiInstance 接口 创建一份
Service 实例 该类实现了 return
MultiInstance 接口 null
使用全局 创建一份
Service Service 实例
使用全局 创建一份
Service Service 实例
15. ► 如果这个 wrapper 是在全局( main-instance )中,
► getInstance 直接根据全局配置的 Class ,通过反射创建实例对象返回。
► 如果这个 wrapper 是在局部( sub-instance )中,则:
► 1) 局部配置了 Class :
► 判断这个类是否在全局也配置过?
► 1.1) 全局有,并且该类实现了 MultiInstance 接口,则将全局的那个
Service 对象再做一层包装返回给局部。
► (可以理解成全局和局部共享 service )
► 1.1) 全局无,或者全局有但该 Service 类没有实现 MultiInstance 接口:
局部创建一份 Service 类实例(通过 Class.newInstance )
► 2 )局部没有配置 Class :
► 判断这个类是否在全局也配置过?
► 2.1 )全局无,返回 null
► 2.2) 全局有,并且该类实现了 MultiInstance 接口,则将全局的那个
Service 对象再做一层包装返回给局部。 ( 同 1.1)
► 2.3) 全局有,但该类没有实现 MultiInstance 接口: 局部创建一份
Service 类实例(同 1.2 )
上面是大致的逻辑,全部的逻辑见 DefautlServiceManager 的内部类
ServiceInstance.getInstance 方法。
16. 假设我们为全局配置了 A,B 两个 Service ;
为 car1 配置了 C Service ;为 car2 配置了 D Service 。
A 实现了 MultiInstance 接口 , 它大致是这个样子
InstanceA InstanceB InstanceC InstanceD
A B null null
Car1 Car2
A A
B B
C null
null D
17. 现在解释清楚了 service 配置与创建问题,但每个 service 在配
置文件中的 property 是如何注入到 service 中的呢?
这部分的实现是子 Service.init 方法中实现,对每个 service 要
根据当前 ServiceInstanceContext 中的 Configuration 来获取
它所需要的 properties.
框架初始化时,把所有的配置文件 : webx-
default.xml(webroot 下面 ), webx-turbine-
default.xml(TurbineScheme 的 ), webx-
default.xml(DefaultScheme 的 ) ,以及每个 car 下面的
webx.xml 都合并起来。(当然里面做了去重工作)
21. Exodus2 的现状 , 有 3 个 service 容器
Web 层 service 容器
ViewCache
Service
Car1 Car2 Car3 Viewcache 层
Service 容器
ResourceLoaderSvc
ViewCacheSupport
Service
Biz 层 service 容器
AO/BO/DAO ...
ViewCache
Mail Template Others Service
22. ViewCache 主要是访问我们的类目信息,存放
在 vc.xml.china 文件中,加载到内存中大约
30 多 M
Web 和 biz 层都要访问到,所以把它独立提取
出来了。
现在要把这 3 层 service 容器合并为一个容器
,使得后续更容易迁移到 apollo 框架
23. 合并后
全局 Spring
OtherService ctx
AO BO DAO
ViewCache cd
Car1 Car2
Spring Spring
ServiceA ServiceA
ServiceB ServiceB