Seasar2 DI注入的底层实现

Seasar2 DI注入的底层实现 

1. 前言

Searsar2 是一个日本的开源DI容器,类似于spring

项目组有个同学问我,Searsar2的底层是如何实现DI机制的。于是我调查了一下,调查结果不敢独享。

Spring一样,Searsar2可以有很多种部署的方式,在web工程中一般以servlet的方式, 在独立jar包部署的情况下,可以硬编码部署。

这边以硬编码举例。按照Searsar2 官方手册,下载一系列jar包,配好一堆配置文件之后(具体搭建方法请自行上网调查),用以下的代码就可以启动该容器

  1. SingletonS2ContainerFactory.setConfigPath("app.dicon"); 
  2. SingletonS2ContainerFactory.init(); 
  3. S2Container container = SingletonS2ContainerFactory.getContainer(); 
 SingletonS2ContainerFactory.setConfigPath("app.dicon");
 SingletonS2ContainerFactory.init();
 S2Container container = SingletonS2ContainerFactory.getContainer();

 

2. 读取配置文件,生成DI容器对象

本文作者蒋彪 原发于http://blog.csdn.net/nanjingjiangbiao 转载请注明

A. SingletonS2ContainerFactory.init()方法中

  1. container = S2ContainerFactory.create(configPath); 
container = S2ContainerFactory.create(configPath);

  在此处生成单例的(static)的容器对象

 

B. SingletonS2ContainerFactory.create()方法中

  1. if (!initialized) { 
  2.     // 初期化DI容器工厂,读取DI容器本身的配置文件  
  3.             configure(); 
  4.         } 
  5.     //用DI容器工厂中关联的特定的provider  
  6.         return getProvider().create(path); 
if (!initialized) {
	// 初期化DI容器工厂,读取DI容器本身的配置文件
            configure();
        }
	//用DI容器工厂中关联的特定的provider
        return getProvider().create(path);

 

C. SingletonS2ContainerFactory.configure()方法中

class根目录下找[s2container.dicon], 实例化DI容器工厂

首先,生成该工厂配套的 provider(用来加载和注入组件)Builder(用来读取配置文件)

  1. if (provider == null) { 
  2.            provider = new DefaultProvider(); 
  3.        } 
  4.        if (defaultBuilder == null) { 
  5.                defaultBuilder = new XmlS2ContainerBuilder(); 
  6.         } 
 if (provider == null) {
            provider = new DefaultProvider();
        }
        if (defaultBuilder == null) {
                defaultBuilder = new XmlS2ContainerBuilder();
	        }

 

一般说来初期化provider/defaultBuilder都是null,除非从SingletonS2ContainerInitializer启动DI容器的时候,可以指定

 

D. 接下来,读取[s2container.dicon],自定义DI容器工厂

  1. final S2ContainerBuilder builder = new XmlS2ContainerBuilder(); 
  2.             configurationContainer = builder.build(configFile); 
  3.             configurationContainer.init(); 
  4.             Configurator configurator; 
  5.             if (configurationContainer.hasComponentDef(Configurator.class)) { 
  6.                 configurator = (Configurator) configurationContainer 
  7.                         .getComponent(Configurator.class); 
  8.             } else
  9.                 configurator = new DefaultConfigurator(); 
  10.             } 
  11.                 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()方法中

  1. return getProvider().create(path); 
return getProvider().create(path);

  出发,默认到DefaultProvider中的create(path)

 

F. 如果没有自定义classLoader, 默认用当前jvm环境下的classLoader

  1. if (configurationContainer != null 
  2.                     && configurationContainer 
  3.                             .hasComponentDef(ClassLoader.class)) { 
  4.                 classLoader = (ClassLoader) configurationContainer 
  5.                         .getComponent(ClassLoader.class); 
  6.             } else
  7.                 classLoader = Thread.currentThread().getContextClassLoader(); 
  8.                 } 
if (configurationContainer != null
                    && configurationContainer
                            .hasComponentDef(ClassLoader.class)) {
                classLoader = (ClassLoader) configurationContainer
                        .getComponent(ClassLoader.class);
            } else {
                classLoader = Thread.currentThread().getContextClassLoader();
	            }

 

G. 开始实例化DI容器

  1. S2Container container = StringUtil.isEmpty(path) ? new S2ContainerImpl() 
  2.                         : build(path, classLoader); 
S2Container container = StringUtil.isEmpty(path) ? new S2ContainerImpl()
	                    : build(path, classLoader);

 

一般默认都会有app.dicon,因此会走进build(path, classLoader)

 

H. 真正的实例化,发生在

  1. final S2Container container = getBuilder(ext).build(realPath, 
  2.                         classLoader); 
final S2Container container = getBuilder(ext).build(realPath,
                        classLoader);

 

在没有自定义的情况下,实际是在XmlS2ContainerBuilder.build中执行

 

I. 接下来的代码就稍有奇幻色彩

  1. protected S2Container parse(final S2Container parent, final String path) { 
  2.         final SaxHandlerParser parser = createSaxHandlerParser(parent, path); 
  3.         final InputStream is = getInputStream(path); 
  4.         try
  5.             return (S2Container) parser.parse(is, path); 
  6.         } finally
  7.             InputStreamUtil.close(is); 
  8.         } 
  9.     } 
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

  1. final SAXParser saxParser = SAXParserFactoryUtil.newSAXParser(factory); 
final SAXParser saxParser = SAXParserFactoryUtil.newSAXParser(factory);
  1. final SaxHandler handler = new SaxHandler(rule); 
final SaxHandler handler = new SaxHandler(rule);

 

这里想提一下,其中的rule

  1. protected S2ContainerTagHandlerRule rule = new S2ContainerTagHandlerRule(); 
	protected S2ContainerTagHandlerRule rule = new S2ContainerTagHandlerRule();

是把DI容器中配置文件可能出现的所有关键字,都作了tagHandler的映射,

S2ContainerTagHandlerRule中能看到定义如下

  1. addTagHandler("/components", new ComponentsTagHandler()); 
  2. addTagHandler("component", new ComponentTagHandler()); 
  3. addTagHandler("arg", new ArgTagHandler()); 
  4. addTagHandler("property", new PropertyTagHandler()); 
  5. addTagHandler("meta", new MetaTagHandler()); 
  6. addTagHandler("initMethod", new InitMethodTagHandler()); 
  7. addTagHandler("destroyMethod", new DestroyMethodTagHandler()); 
  8. addTagHandler("aspect", new AspectTagHandler()); 
  9. addTagHandler("interType", new InterTypeTagHandler()); 
  10. 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. SaxHandlersaxParser包装起来,以SaxHandlerParser的面目返回

  1. returnnew SaxHandlerParser(handler, saxParser); 
	return new SaxHandlerParser(handler, saxParser);

L. 解析配置文件,生成DI容器实例,就发生在SaxHandlerParser.parse()

最终的有效代码是

 

  1. publicstaticvoid parse(SAXParser parser, InputSource inputSource, 
  2.             DefaultHandler handler) { 
  3.         try
  4.             parser.parse(inputSource, handler); 
  5.         } catch (SAXException e) { 
  6.             thrownew SAXRuntimeException(e); 
  7.         } catch (IOException e) { 
  8.             thrownew IORuntimeException(e); 
  9.         } 
  10.         } 
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举例,一般在配置文件中都配成

  1.     <componentname="xxxConfig"class="xxxxxConfigImpl"> 
  2.     <propertyname="xxx">"xxx"</property> 
  3. </component> 
	<component name="xxxConfig" class="xxxxxConfigImpl">
	<property name="xxx">"xxx"</property>
</component>

 

ComponentsTagHandler中如下解析该段xml,把情报塞入TagHandlerContext

  1. publicvoid start(TagHandlerContext context, Attributes attributes) { 
  2.         S2Container container = createContainer(); 
  3.         String path = (String) context.getParameter("path"); 
  4.         container.setPath(path); 
  5.         String namespace = attributes.getValue("namespace"); 
  6.         if (!StringUtil.isEmpty(namespace)) { 
  7.             container.setNamespace(namespace); 
  8.         } 
  9.         String initializeOnCreate = attributes.getValue("initializeOnCreate"); 
  10.         if (!StringUtil.isEmpty(initializeOnCreate)) { 
  11.             container.setInitializeOnCreate(Boolean.valueOf(initializeOnCreate) 
  12.                     .booleanValue()); 
  13.         } 
  14.  
  15.         S2Container parent = (S2Container) context.getParameter("parent"); 
  16.         if (parent != null) { 
  17.             container.setRoot(parent.getRoot()); 
  18.         } 
  19.         context.push(container); 
  20.     } 
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

  1. for (int i = 0; i < getChildSize(); ++i) { 
  2.                 getChild(i).init(); 
  3.             } 
for (int i = 0; i < getChildSize(); ++i) {
                getChild(i).init();
            }

 

再循环,初期化组件

  1. for (int i = 0; i < getComponentDefSize(); ++i) { 
  2.                 getComponentDef(i).init(); 
  3.             } 
for (int i = 0; i < getComponentDefSize(); ++i) {
                getComponentDef(i).init();
            }

 

其中每个Component都是你配置文件中定义好的

B. 组件的初期化最后落地到ComponetDefImpl.init

  1. publicvoid init() { 
  2.         getConcreteClass(); 
  3.         getComponentDeployer().init(); 
  4.     } 
public void init() {
        getConcreteClass();
        getComponentDeployer().init();
	}

 

C. getConcreteClass 用来做AOP

  1. concreteClass = AopProxyUtil.getConcreteClass(this); 
	concreteClass = AopProxyUtil.getConcreteClass(this);

具体的原理是,根据配置文件中定义的pointcut, 加强和混淆class文件。下面分支很多,不讲

 

D. getComponentDeployer()的生成

默认情况下是,在InstanceSingletonDef.createComponentDeployer执行

  1. return ComponentDeployerFactory 
  2.                 .createSingletonComponentDeployer(componentDef); 
return ComponentDeployerFactory
                .createSingletonComponentDeployer(componentDef);

 

E. 最后的init发生在SingletonComponentDeployerassemble()

  1. privatevoid assemble() { 
  2.         if (instantiating) { 
  3.             thrownew CyclicReferenceRuntimeException(getComponentDef() 
  4.                     .getComponentClass()); 
  5.         } 
  6.         instantiating = true
  7.         try
  8.             component = getConstructorAssembler().assemble(); 
  9.         } finally
  10.             instantiating = false
  11.         } 
  12.         getPropertyAssembler().assemble(component); 
  13.         getInitMethodAssembler().assemble(component); 
  14.     } 
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由以下代码生成

  1. protected Object assembleDefault() { 
  2.         Class clazz = getComponentDef().getConcreteClass(); 
  3.         Constructor constructor = ClassUtil.getConstructor(clazz, null); 
  4.         return ConstructorUtil.newInstance(constructor, null); 
  5.     } 
protected Object assembleDefault() {
        Class clazz = getComponentDef().getConcreteClass();
        Constructor constructor = ClassUtil.getConstructor(clazz, null);
        return ConstructorUtil.newInstance(constructor, null);
	}

以下代码负责把参数注入进去

  1. getPropertyAssembler().assemble(component); 
getPropertyAssembler().assemble(component);

在这里需要注意一点,如果代码中注解,比如

  1. @Resource(name = "JdbcManager"
@Resource(name = "JdbcManager")

此时的注入就发生在

  1. AutoPropertyAssembler.assemble()中 
  2.      PropertyDef propDef = cd.getPropertyDef(i); 
  3.             AccessTypeDef accessTypeDef = propDef.getAccessTypeDef(); 
  4.             accessTypeDef.bind(cd, propDef, component); 
AutoPropertyAssembler.assemble()中
     PropertyDef propDef = cd.getPropertyDef(i);
            AccessTypeDef accessTypeDef = propDef.getAccessTypeDef();
            accessTypeDef.bind(cd, propDef, component);

 

最后底层是调用OGNL达到注入的效果,name一致即注入

#以上

posted on 2012-12-01 21:03  sslshop  阅读(1030)  评论(0编辑  收藏  举报

导航