`
javatracker
  • 浏览: 25291 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

做一个自己的AppServer-JDStream(三) IOC容器

阅读更多

     Guice和PicoContainer的比较

      感谢mineral在此系列博客的上一篇当中推荐的IOC容器Guice,没来得及看源码,初步了解了一下,发现Guice确实有自己的独到之处,它和Spring的最大区别应该是构造够轻量,设计够巧妙,配置够简单,但它也有很多自己的不足(个人观点)。上一篇博客站在JDStream使用的角度简单的比较了Spring、JBoss MicroContainer、PicoContainer和JFox优缺点,决定采用PicoContainer作为JDStream的IOC容器。由于以前没有接触过Guice,今天看了些介绍后就Guice和PicoContainer做个比较,不足之处希望能得到各位大侠的指教。

      1、Guice有三种注入方式:Field、contructor和setter;PicoContainer同样也有这三种注入方式;

      2、Guice使用Inject注释根据业务接口来注入Bean,如此减少了XML配置的繁杂,同时又不像其它IOC的Annotation注入那样繁琐,任何Bean的注入只需@Inject即可。但对于一个业务接口有多个实现的情况,单凭接口并不能够定位到相应的Bean,Guice需要以下辅助配置来定位:

            A:通过在业务接口上添加@ImplementedBy注释来指定实现类;

            B:通过实现Module接口硬编码,将实现类绑定到接口,同时用自定义Annotation来作为分支标记或自定义一个字符串作为分支标记;

            C:可以在@Inject的下面用@Name来指定实现类;

同时Guice还可以通过实现Provider接口来动态注入注入;

            而PicoContainer也可以通过@Inject注入,对于一个接口或超类多个子类的情况也是通过自定义Annotation来区分;

      3、其它方面PicoContainer有Bean的监视器、生命周期管理、Container父子级联等实现,Guice可能也有这方面的实现,没有做深入的研究。

      4、Guice有更多的注释,同时又分绑定作用域,这个和PicoContainer的父子级联比较相似,都是实现实例的分割,相当于全局变量和局部变量。而PicoContainer只有Inject、Bind、Cache三个注释,并且都不带参数;

      由上面的比较基本可以看出PicoContainer有点像Guice的精简版,它更多的是通过Container接口将功能暴露给使用者,在外层并没有做太多的封装,它比Guice更轻量,更适合嵌入其它容器框架。而Guice更适合独立作为一个轻量的IOC框架存在。另外从JDStream的需求来看PicoContainer也更适合被集成于其中,具体原因后面阐述。

 

      IOC容器的原理

      比较了上面两个IOC容器后再看IOC容器的原理:

      IOC容器其实就是将一个类拆分了后和需要注入的参数及配置的操作缓存起来,大部分的IOC容器都逃不出以下几步:
      1、根据配置文件找到类名称,用加载器加载;
      2、用反射从类中分离出Field,Constructor,Method,Annotation,Interface,SuperClass等等分类缓存;
      3、缓存需要注入的参数;
      4、根据配置文件配置的操作或其它程序的调用,用Constructor生成Object,注入参数,此实例或缓存,或不缓存,或用弱引用缓存;
      5、生命周期操作,Annotation操作;
      从原理上说IOC容器和JMX实现没有太大的区别,JMX其实也算是一种IOC容器。但从实现上来说IOC容器比JMX灵活的多,它不必考虑J2EE规范的限制,完全自由发挥,可以设计的如Spring般繁杂,也可以设计的如PicoContainer般小巧。它可以使自己管理的Bean更加POJO,不必再亲自去实现框架管理接口,而是由框架生成被管理Bean的子类或业务接口的实现类作为代理去实现管理接口,然后一切业务调用都从代理接口传入,再去完成被管理Bean的调用。Spring和EJB就是这样管理的。这中间可以注册监视器来监控Bean的任何状态变化,注册拦截器来监控外部对Bean的操作。
      IOC容器给人的最大好处就是灵活多变,耦合松散,但这样的结果也是有代价的,首先越灵活配置越复杂,大量的XML和Annotation也会成为灾难,其次大量的动态代理、反射、CGLIB、拦截器、监视器等管理手段的使用,极大的降低了效率,对高并发环境下的应用是不可以接受的。JDStream的一个最重要的设计准则是在高并发通路上尽量避免使用以反射为基础的操作,以此来提高效率。
      由于JDStream偏向于高并发处理能力的需要,对上面的优缺点做了平衡,那就是插件的入口Bean类和插件内部的某些Bean类(需要对其管理但又不在高并发通路上的)以及业务程序里面的一些Bean类(同样需要对其管理但又不在高并发通路上的)使用IOC容器管理,需要对配置文件热修改以及对内部监控提供视图的Bean类用JMX容器管理,其它的Bean类并不提供框架管理功能。

 

     JDStream的IOC容器实现

     现在说说为什么PicoContainer更适合JDStream。首先JDStream的IOC容器最主要的职责是管理插件入口的Bean类,这些Bean的相互依赖不多,而且都肩负着插件配置参数的输入(如IP地址、端口号等),这些参数在部署后要经常修改,不可能采用Annotation注入,也不可能硬编码注入,只能用XML注入,所以Guice的Annotation就用不上了;其次JDStream的另一个职责是管理业务模块的一些Bean,这些Bean上面将标注属于JDStream自己的Annotation,这些Annotation的解析应该放在Bean的注册过程中,因此扩充PicoContainer更好一点;再次Guice管理的Bean基本上都要实现接口,这点和框架有点耦合;最后最重要的一点是PicoContainer的XML解析器已经完成了大部分了,目前先这样写下去,以后发觉不恰当的话在更换。

      上面的比较说明,就是对JDStream的IOC容器的基本要求。首先从XML解析开始,由于配置文档不大,所以采用Dom4j的dom方式解析配置文件。为了做到与框架完全无关,就不能实现框架的任何接口,但PicoContainer管理Bean的生命周期又必须实现生命周期接口,这样就需要像上面说的那么样框架生成一个代理。代理有两种生成方式:动态代理和CGLIB,动态代理要求被管理的Bean必须有接口实现,此处似乎对被管理的Bean要求多了点,所以考虑用CGLIB生成被管理Bean的子类,同时这个子类实现框架的生命周期接口,然后以无参数的构造方法生成实例来作为代理(此处要求被管理的Bean必须提供无参数的构造方法),然后以XML配置文件中配置的Bean的名称为注册名注册到PicoContainer中,实际要注册的Bean按配置文件构造实例,注入参数,以配置的Bean名称加一固定字符串注册到PicoContainer中。当框架调用代理的生命周期接口时,代理委托给配置文件中定义Bean的相应的生命周期方法,当业务程序调用代理的业务方法时,代理委托给Bean的相同名称和参数的方法。

     

      遇到的问题

当业务程序调用代理的业务方法时,代理委托给Bean的相同名称和参数的方法

      这个地方出现了问题,由于上面委托给Bean的方法是拦截所得,只知道实例、方法和参数,在Bean中有多个同名的重载方法时,可能无发判断到底调用哪一个方法。看如下代码:

public class Test{
	
	public static void main(String[] args){
		Test test = new Test();
		
		Test1 test13 = new Test3();
		Test3 test3 = new Test3();
		Test4 test4 = new Test4();
		//(1)
		test.tst(test3,test4);
		//(2)
		test.tst(test13, test4);
	}
	
	public void tst(Test1 test1,Test4 test4){
		System.out.println("test1  test4");
	}
	public void tst(Test3 test3,Test2 test2){
		System.out.println("test3  test2");
	}	
}

public interface Test1{
	
}

public interface Test2{

}

public class Test3 implements Test1{

}

public class Test4 implements Test2{

}

      上面注释(1)下面的调用编译通不过,说明sun也无法判断到底要调用Test哪个tst重载方法,可以不去管它。

       注释(2)下面的调用根据变量声明,能调用到下面的方法:

public void tst(Test1 test1,Test4 test4){
              System.out.println("test1  test4");
}

      但如果要通过反射调用此方法,即:知道Test的一个实例是test,方法名是tst,两个参数是两个Object实例,参数类分别是Test3和Test4,这样如何完成test.tst(test13, test4)这样的调用(比如此方法用动态代理送过来后test13和test4失去原有Test1和Test4的声明,只知道他们是被声明为Object的实例)?

4
0
分享到:
评论
4 楼 javatracker 2009-01-07  
stone2oo6 写道

参数是Test3的实例只能得到Test3.class, Test4实例参数能拿到Test4.class, 寻找方法参数为(Test3, Test4)的方法;若是不存在该方法,拿Test3.class的上一级接口类Test1.class与Test4.class组合寻找方法为(Test1, Test4);若还是找不着,则找Test4.class的上一级接口Test2.class与Test3.class组合...以此内推,直到找到方法或遍历所有接口为止。 缺点:复杂而且反射查找方法的次数太多约为M的N次方(n指参数个数,m指参数层次上实现的接口数) 是否可以约定参数只为上一级接口或自身实现类,这样复杂度只有2的N次方.


感谢回复,你说的不错,是可以约定上一级接口或superclass,这样虽然失去了广泛性,但也不失为一种实现。目前基本也就采用这种方法。另外一种方法就是你说的递归向上查找,但找到的不一定是方法中声明的超类,也是一种不太准确的方法。IOC中基本采用这两种方法,虽然有点问题,但能满足一般需要。

另外现在正写的RMI工厂也涉及到这个问题,但这里会多几种实现,你上面说的约定接口,让使用者自己分发,这是最高效的一种,同时也增加了使用成本。另外还准备提供一种就是JDK6的新特性-动态编译,当这个实例注册是就动态生成它的代理类,根据方法的toString()(一般各个方法都不同)来分发到实例的各个方法。之所以用动态编译,是为了提高效率,抛弃反射。
3 楼 stone2oo6 2009-01-07  
javatracker 写道

Method m = test.getClass().getMethod("tst", new Class[]{Test1.class, Test4.class});    关键问题在这里,一般从动态代理和cglib里面拦截后得到的参数事Object[] args,根据args[0].getClass()得到参数类型,如果此参数是Test3的实例只能得到Test3.class,得不到Test1.class.如果递归找superclass和interface,那样会牵涉到很多父类和父接口,如果此类中又有多个这些父类和父接口得重载方法,就会调用出错。因为在非反射调用里面是根据变量声明来判别得,而在此变量声明被范化成Object了,失去了声明类型。


参数是Test3的实例只能得到Test3.class, Test4实例参数能拿到Test4.class, 寻找方法参数为(Test3, Test4)的方法;若是不存在该方法,拿Test3.class的上一级接口类Test1.class与Test4.class组合寻找方法为(Test1, Test4);若还是找不着,则找Test4.class的上一级接口Test2.class与Test3.class组合...以此内推,直到找到方法或遍历所有接口为止。
缺点:复杂而且反射查找方法的次数太多约为M的N次方(n指参数个数,m指参数层次上实现的接口数)

是否可以约定参数只为上一级接口或自身实现类,这样复杂度只有2的N次方.

2 楼 javatracker 2008-12-31  
Method m = test.getClass().getMethod("tst", new Class[]{Test1.class, Test4.class});   

关键问题在这里,一般从动态代理和cglib里面拦截后得到的参数事Object[] args,根据args[0].getClass()得到参数类型,如果此参数是Test3的实例只能得到Test3.class,得不到Test1.class.如果递归找superclass和interface,那样会牵涉到很多父类和父接口,如果此类中又有多个这些父类和父接口得重载方法,就会调用出错。因为在非反射调用里面是根据变量声明来判别得,而在此变量声明被范化成Object了,失去了声明类型。
1 楼 donnki 2008-12-29  
public static void main(String[] args) throws Exception{   
        Test test = new Test();   
           
        Test1 test13 = new Test3();   
        Test2 test2 = new Test4();
        Test3 test3 = new Test3();   
        Test4 test4 = new Test4();   
        //(1)   
        //test.tst(test3,test4);   
        //(2)   
        test.tst(test13, test4);   
        
        
        Method m = test.getClass().getMethod("tst", new Class[]{Test1.class, Test4.class});        
        m.invoke(test, test13, test4);  //输出test1  test4
        
        m = test.getClass().getMethod("tst", new Class[]{Test3.class, Test2.class});        
        m.invoke(test, test13, test2);  //输出 test3 test2
    }   

相关推荐

    Spring5 框架 ---- IOC容器 ---- 代码

    Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- IOC容器 ---- 代码 Spring5 框架 ---- ...

    Spring.net二----初探IOC容器.rar源代码

    Spring.Demo项目很可能是包含一个简单的Spring.NET应用示例,可能包括了XML配置文件、服务接口和实现类、以及如何使用IoC容器来获取和使用这些对象的代码。通过这个示例,你可以更直观地了解如何在实际项目中运用...

    Java-Spring-SpringIoC容器-SpringIoC的学习

    在Java Spring框架中,Spring IoC(Inversion of Control,控制反转)是核心特性之一,它使得应用程序的组件之间的依赖关系不再由代码直接管理,而是交由Spring IoC容器负责。这种设计模式降低了代码间的耦合,提高...

    spring Ioc容器配置

    spring Ioc容器配置 IOC容器数据源配置 <!-- 配置数据源 --> destroy-method="close"> <value>org.gjt.mm.mysql.Driver <value>jdbc:mysql://localhost:3306/demo <value>root ...

    IOC容器简单实现

    IOC(Inversion of Control)容器是软件设计模式中的一种,它将对象的创建和管理权从代码本身转移到一个外部容器,即IOC容器。这种反转控制使得程序更加灵活,降低了组件之间的耦合性,提高了可测试性和可维护性。本...

    maven-spring-ioc

    IoC(Inversion of Control)是一种设计原则,它将控制权反转,由框架或容器负责管理对象的创建和依赖关系的建立,而不是由对象自己管理。这样可以降低代码的耦合度,提高系统的可测试性和可维护性。 ### 2. 配置...

    手动实现一个ioc容器.zip

    在压缩包子文件"my-spring-ioc-master"中,可能包含了一个完整的示例项目,该项目演示了如何手动构建一个简易的Spring IOC容器。项目中可能包括以下部分: - **BeanDefinition**: 表示Bean定义的类,存储Bean的信息...

    IoC容器的设计(利用反射、注解和工厂模式实现)

    实验要求 1. 利用注解、反射和工厂模式设计一个简单的IoC容器 2. 该IoC容器包含3个...然后我们需要自己实现一个IoC容器类,处理自定义的注解的基本逻辑; 接下来在test类中实例化IoC容器,并从中取得bean,调用其方法

    手写一个SpringIoc容器

    本项目"手写一个SpringIoc容器"旨在模仿Spring的IOC(Inversion of Control,控制反转)功能,帮助开发者深入理解Spring的工作原理,提升对依赖注入(Dependency Injection)模式的认识。 在实现自定义的Spring IOC...

    自己实现的ioc容器

    在IT行业中,Spring框架是Java开发中的一个关键组件,尤其在企业级应用中,它扮演着核心...这个自己实现的IoC容器可以帮助初学者建立起对依赖注入和控制反转概念的直观认识,并为理解和使用Spring框架打下坚实的基础。

    Spring----IOC实现

    IoC是一种设计模式,它的核心理念在于将对象的创建和依赖关系的维护从应用程序代码中解耦出来,交由一个外部容器(在Spring中就是ApplicationContext)来管理。这样做的好处包括提高代码的可测试性、灵活性和可维护...

    Spring IOC容器实现分析.pdf 下载

    IOC,即控制反转,是面向对象编程中的一个设计原则,它的主要思想是将对象的创建和管理权交给外部容器,而不是由对象自身负责。在Spring框架中,IOC容器负责管理对象的生命周期和依赖关系,通过配置文件或注解来定义...

    自己实现IOC容器

    **IOC(Inversion of Control)容器**,全称“控制反转”,是软件设计模式中的一种,主要用于降低系统组件之间的耦合度。在Java领域,Spring框架的IOC容器是最为广泛应用的实现之一。当我们自己动手实现IOC容器时,...

    一个简单的IOC容器实现

    本文将深入探讨一个简单的IOC容器实现,帮助我们理解DI的基本原理。 首先,理解IOC的概念至关重要。在传统的编程模式中,对象通常自行创建依赖的对象,这导致了代码之间的紧密耦合。而在IOC模式下,容器负责管理...

    MVC与IOC容器互动

    IOC,即控制反转,是一种设计原则,它的核心思想是将对象的创建和管理权交给容器,而不是由对象自己管理。这样可以降低对象之间的耦合度,提高系统的灵活性和可维护性。在实际应用中,IoC通常通过依赖注入...

    简单IOC容器demo

    【标题】:“简单IOC容器demo”是一个演示性的项目,它旨在教授如何构建一个基础的控制反转(IOC)容器。这个容器使用注解来实现依赖注入,这是Spring框架的核心特性之一。 【描述】:该示例项目是学习过程的产物,...

    IoC 容器和Dependency Injection 模式

    **控制反转**(IoC)的核心思想是把原本由程序员手动管理的对象创建和依赖注入的工作交给一个容器来完成。这种做法减少了组件间的耦合度,使得每个组件专注于其核心职责,而容器则负责协调这些组件。这种方式不仅...

    Spring框架-IoC容器

    Spring-IoC容器

    B-spring-ioc-container-homework:IoC容器基础知识Spring课程

    本课程围绕“B-spring-ioc-container-homework”展开,旨在深入理解Spring IoC容器的基本概念、工作原理以及如何在实际开发中应用。 IoC,即控制反转,是面向对象编程中的一个设计原则,它将对象的创建和管理权交给...

Global site tag (gtag.js) - Google Analytics