Seasar2 DI注入的底层实现
Seasar2 DI注入的底层实现
1. 前言
Searsar2 是一个日本的开源DI容器,类似于spring。
项目组有个同学问我,Searsar2的底层是如何实现DI机制的。于是我调查了一下,调查结果不敢独享。
和Spring一样,Searsar2可以有很多种部署的方式,在web工程中一般以servlet的方式, 在独立jar包部署的情况下,可以硬编码部署。
这边以硬编码举例。按照Searsar2 官方手册,下载一系列jar包,配好一堆配置文件之后(具体搭建方法请自行上网调查),用以下的代码就可以启动该容器
- SingletonS2ContainerFactory.setConfigPath("app.dicon");
- SingletonS2ContainerFactory.init();
- S2Container container = SingletonS2ContainerFactory.getContainer();
SingletonS2ContainerFactory.setConfigPath("app.dicon"); SingletonS2ContainerFactory.init(); S2Container container = SingletonS2ContainerFactory.getContainer();
2. 读取配置文件,生成DI容器对象
※本文作者蒋彪 原发于http://blog.csdn.net/nanjingjiangbiao 转载请注明
A. SingletonS2ContainerFactory.init()方法中
- container = S2ContainerFactory.create(configPath);
container = S2ContainerFactory.create(configPath);
在此处生成单例的(static)的容器对象
B. SingletonS2ContainerFactory.create()方法中
- if (!initialized) {
- // 初期化DI容器工厂,读取DI容器本身的配置文件
- configure();
- }
- //用DI容器工厂中关联的特定的provider
- return getProvider().create(path);
if (!initialized) { // 初期化DI容器工厂,读取DI容器本身的配置文件 configure(); } //用DI容器工厂中关联的特定的provider return getProvider().create(path);
C. SingletonS2ContainerFactory.configure()方法中
在class根目录下找[s2container.dicon], 实例化DI容器工厂
首先,生成该工厂配套的 provider(用来加载和注入组件)和 Builder(用来读取配置文件)
- if (provider == null) {
- provider = new DefaultProvider();
- }
- if (defaultBuilder == null) {
- defaultBuilder = new XmlS2ContainerBuilder();
- }
if (provider == null) { provider = new DefaultProvider(); } if (defaultBuilder == null) { defaultBuilder = new XmlS2ContainerBuilder(); }
一般说来初期化provider/defaultBuilder都是null,除非从SingletonS2ContainerInitializer启动DI容器的时候,可以指定
D. 接下来,读取[s2container.dicon],自定义DI容器工厂
- final S2ContainerBuilder builder = new XmlS2ContainerBuilder();
- configurationContainer = builder.build(configFile);
- configurationContainer.init();
- Configurator configurator;
- if (configurationContainer.hasComponentDef(Configurator.class)) {
- configurator = (Configurator) configurationContainer
- .getComponent(Configurator.class);
- } else {
- configurator = new DefaultConfigurator();
- }
- configurator.configure(configurationContainer);
final S2ContainerBuilder builder = new XmlS2ContainerBuilder(); configurationContainer = builder.build(configFile); configurationContainer.init(); Configurator configurator; if (configurationContainer.hasComponentDef(Configurator.class)) { configurator = (Configurator) configurationContainer .getComponent(Configurator.class); } else { configurator = new DefaultConfigurator(); } configurator.configure(configurationContainer);
这边提一下,这边所谓的自定义DI容器工厂,其实是Searsar2框架提供的一个plugin功能,也就是可以自定义DI容器工厂,比如在配置文件中定义自己的provider,自己的classloader,并且可以指定该容器可否热部署。但是如果不想自定义容器,完全可以不写[s2container.dicon],用系统默认的DI工厂
再多说几句, 和Spring类似,Searsar2也推出了自己容器热部署机制,比如在Web环境下,当某个class或者jar包被替换掉之后,DI容器会重classloader一把,具体的实现,Searsar2是通过提供一个HotDeploy专用的provider。(官网上说对多线程支持不好,要慎用)
http://s2container.seasar.org/2.4/ja/S2.4SmartDeploy.html#HotDeploy
E. 用provider 实例化DI容器实例
SingletonS2ContainerFactory.create()方法中
- return getProvider().create(path);
return getProvider().create(path);
出发,默认到DefaultProvider中的create(path)
F. 如果没有自定义classLoader, 默认用当前jvm环境下的classLoader
- if (configurationContainer != null
- && configurationContainer
- .hasComponentDef(ClassLoader.class)) {
- classLoader = (ClassLoader) configurationContainer
- .getComponent(ClassLoader.class);
- } else {
- classLoader = Thread.currentThread().getContextClassLoader();
- }
if (configurationContainer != null && configurationContainer .hasComponentDef(ClassLoader.class)) { classLoader = (ClassLoader) configurationContainer .getComponent(ClassLoader.class); } else { classLoader = Thread.currentThread().getContextClassLoader(); }
G. 开始实例化DI容器
- S2Container container = StringUtil.isEmpty(path) ? new S2ContainerImpl()
- : build(path, classLoader);
S2Container container = StringUtil.isEmpty(path) ? new S2ContainerImpl() : build(path, classLoader);
一般默认都会有app.dicon,因此会走进build(path, classLoader)
H. 真正的实例化,发生在
- final S2Container container = getBuilder(ext).build(realPath,
- classLoader);
final S2Container container = getBuilder(ext).build(realPath, classLoader);
在没有自定义的情况下,实际是在XmlS2ContainerBuilder.build中执行
I. 接下来的代码就稍有奇幻色彩
- protected S2Container parse(final S2Container parent, final String path) {
- final SaxHandlerParser parser = createSaxHandlerParser(parent, path);
- final InputStream is = getInputStream(path);
- try {
- return (S2Container) parser.parse(is, path);
- } finally {
- InputStreamUtil.close(is);
- }
- }
protected S2Container parse(final S2Container parent, final String path) { final SaxHandlerParser parser = createSaxHandlerParser(parent, path); final InputStream is = getInputStream(path); try { return (S2Container) parser.parse(is, path); } finally { InputStreamUtil.close(is); } }
读取xml采用的是Sax开源项目,Sax的原理请大家自己上网搜
J. 生成标准的SaxParser , 生成自定义的SaxHandler
- final SAXParser saxParser = SAXParserFactoryUtil.newSAXParser(factory);
final SAXParser saxParser = SAXParserFactoryUtil.newSAXParser(factory);
- final SaxHandler handler = new SaxHandler(rule);
final SaxHandler handler = new SaxHandler(rule);
这里想提一下,其中的rule
- protected S2ContainerTagHandlerRule rule = new S2ContainerTagHandlerRule();
protected S2ContainerTagHandlerRule rule = new S2ContainerTagHandlerRule();
是把DI容器中配置文件可能出现的所有关键字,都作了tagHandler的映射,
在S2ContainerTagHandlerRule中能看到定义如下
- addTagHandler("/components", new ComponentsTagHandler());
- addTagHandler("component", new ComponentTagHandler());
- addTagHandler("arg", new ArgTagHandler());
- addTagHandler("property", new PropertyTagHandler());
- addTagHandler("meta", new MetaTagHandler());
- addTagHandler("initMethod", new InitMethodTagHandler());
- addTagHandler("destroyMethod", new DestroyMethodTagHandler());
- addTagHandler("aspect", new AspectTagHandler());
- addTagHandler("interType", new InterTypeTagHandler());
- andler("/components/include", new IncludeTagHandler());
addTagHandler("/components", new ComponentsTagHandler()); addTagHandler("component", new ComponentTagHandler()); addTagHandler("arg", new ArgTagHandler()); addTagHandler("property", new PropertyTagHandler()); addTagHandler("meta", new MetaTagHandler()); addTagHandler("initMethod", new InitMethodTagHandler()); addTagHandler("destroyMethod", new DestroyMethodTagHandler()); addTagHandler("aspect", new AspectTagHandler()); addTagHandler("interType", new InterTypeTagHandler()); addTagHandler("/components/include", new IncludeTagHandler());
K. 将SaxHandler和saxParser包装起来,以SaxHandlerParser的面目返回
- returnnew SaxHandlerParser(handler, saxParser);
return new SaxHandlerParser(handler, saxParser);
L. 解析配置文件,生成DI容器实例,就发生在SaxHandlerParser.parse()中
最终的有效代码是
- publicstaticvoid parse(SAXParser parser, InputSource inputSource,
- DefaultHandler handler) {
- try {
- parser.parse(inputSource, handler);
- } catch (SAXException e) {
- thrownew SAXRuntimeException(e);
- } catch (IOException e) {
- thrownew IORuntimeException(e);
- }
- }
public static void parse(SAXParser parser, InputSource inputSource, DefaultHandler handler) { try { parser.parse(inputSource, handler); } catch (SAXException e) { throw new SAXRuntimeException(e); } catch (IOException e) { throw new IORuntimeException(e); } }
用SAX标准的SAXParser.parser进行配置文件(inputSource)解析,然后把解析成功的对象mapping塞入handler中
下面基本上就是SAX开源框架的事情,根据配置文件的元素命名,向下循环解析,找到命中的tag,就调用对应的TagHandler
比如我们以常见的components举例,一般在配置文件中都配成
- <componentname="xxxConfig"class="xxxxxConfigImpl">
- <propertyname="xxx">"xxx"</property>
- </component>
<component name="xxxConfig" class="xxxxxConfigImpl"> <property name="xxx">"xxx"</property> </component>
在ComponentsTagHandler中如下解析该段xml,把情报塞入TagHandlerContext
- publicvoid start(TagHandlerContext context, Attributes attributes) {
- S2Container container = createContainer();
- String path = (String) context.getParameter("path");
- container.setPath(path);
- String namespace = attributes.getValue("namespace");
- if (!StringUtil.isEmpty(namespace)) {
- container.setNamespace(namespace);
- }
- String initializeOnCreate = attributes.getValue("initializeOnCreate");
- if (!StringUtil.isEmpty(initializeOnCreate)) {
- container.setInitializeOnCreate(Boolean.valueOf(initializeOnCreate)
- .booleanValue());
- }
- S2Container parent = (S2Container) context.getParameter("parent");
- if (parent != null) {
- container.setRoot(parent.getRoot());
- }
- context.push(container);
- }
public void start(TagHandlerContext context, Attributes attributes) { S2Container container = createContainer(); String path = (String) context.getParameter("path"); container.setPath(path); String namespace = attributes.getValue("namespace"); if (!StringUtil.isEmpty(namespace)) { container.setNamespace(namespace); } String initializeOnCreate = attributes.getValue("initializeOnCreate"); if (!StringUtil.isEmpty(initializeOnCreate)) { container.setInitializeOnCreate(Boolean.valueOf(initializeOnCreate) .booleanValue()); } S2Container parent = (S2Container) context.getParameter("parent"); if (parent != null) { container.setRoot(parent.getRoot()); } context.push(container); }
3. 用DI容器注入所有对象
A. S2ContainerImpl.init()方法中
先循环读取子元素,递归init
- for (int i = 0; i < getChildSize(); ++i) {
- getChild(i).init();
- }
for (int i = 0; i < getChildSize(); ++i) { getChild(i).init(); }
再循环,初期化组件
- for (int i = 0; i < getComponentDefSize(); ++i) {
- getComponentDef(i).init();
- }
for (int i = 0; i < getComponentDefSize(); ++i) { getComponentDef(i).init(); }
其中每个Component都是你配置文件中定义好的
B. 组件的初期化最后落地到ComponetDefImpl.init中
- publicvoid init() {
- getConcreteClass();
- getComponentDeployer().init();
- }
public void init() { getConcreteClass(); getComponentDeployer().init(); }
C. getConcreteClass 用来做AOP
- concreteClass = AopProxyUtil.getConcreteClass(this);
concreteClass = AopProxyUtil.getConcreteClass(this);
具体的原理是,根据配置文件中定义的pointcut, 加强和混淆class文件。下面分支很多,不讲
D. getComponentDeployer()的生成
默认情况下是,在InstanceSingletonDef.createComponentDeployer中执行
- return ComponentDeployerFactory
- .createSingletonComponentDeployer(componentDef);
return ComponentDeployerFactory .createSingletonComponentDeployer(componentDef);
E. 最后的init发生在SingletonComponentDeployer的assemble()中
- privatevoid assemble() {
- if (instantiating) {
- thrownew CyclicReferenceRuntimeException(getComponentDef()
- .getComponentClass());
- }
- instantiating = true;
- try {
- component = getConstructorAssembler().assemble();
- } finally {
- instantiating = false;
- }
- getPropertyAssembler().assemble(component);
- getInitMethodAssembler().assemble(component);
- }
private void assemble() { if (instantiating) { throw new CyclicReferenceRuntimeException(getComponentDef() .getComponentClass()); } instantiating = true; try { component = getConstructorAssembler().assemble(); } finally { instantiating = false; } getPropertyAssembler().assemble(component); getInitMethodAssembler().assemble(component); }
F. 在默认情况下,对象的instance由以下代码生成
- protected Object assembleDefault() {
- Class clazz = getComponentDef().getConcreteClass();
- Constructor constructor = ClassUtil.getConstructor(clazz, null);
- return ConstructorUtil.newInstance(constructor, null);
- }
protected Object assembleDefault() { Class clazz = getComponentDef().getConcreteClass(); Constructor constructor = ClassUtil.getConstructor(clazz, null); return ConstructorUtil.newInstance(constructor, null); }
以下代码负责把参数注入进去
- getPropertyAssembler().assemble(component);
getPropertyAssembler().assemble(component);
在这里需要注意一点,如果代码中注解,比如
- @Resource(name = "JdbcManager")
@Resource(name = "JdbcManager")
此时的注入就发生在
- AutoPropertyAssembler.assemble()中
- PropertyDef propDef = cd.getPropertyDef(i);
- AccessTypeDef accessTypeDef = propDef.getAccessTypeDef();
- accessTypeDef.bind(cd, propDef, component);
AutoPropertyAssembler.assemble()中 PropertyDef propDef = cd.getPropertyDef(i); AccessTypeDef accessTypeDef = propDef.getAccessTypeDef(); accessTypeDef.bind(cd, propDef, component);
最后底层是调用OGNL达到注入的效果,name一致即注入
#以上