使用iPOJO时抛出异常java.lang.UnsupportedOperationException: Cannot add elements inside this collection

 

felix ipojo

felix ipojo

Felix iPOJO的功能很强大,配置也比较复杂,配置和实现代码之间有着千丝万缕的关联,完全脱离代码去配置iPOJO的component和instance势必不会达到自己想要的依赖注入效果,甚至很难查到问题的原因。尽管iPOJO的配置有iPOJO xml schemaiPOJO Annotation,但是仅仅按照schema和annotation来不一定能配置正确,还需要理解iPOJO配置背后的一些机制和原理,否则会出现一些奇怪的异常。见如下的代码和iPOJO annotation注解:

上面的代码乍一看没啥问题,但是其实这个component的timeoutConfigList属性是无法注入成功的,这和iPOJO的一些的依赖注入实现机制有关系,如果用以上的代码来配置instance,在启动这个component和instance所在的bundle时会抛出以下的异常堆栈:

我们可以看到属性timeoutConfigList是List集合类型,它上有@Requires的annotaion,又有对这个属性的@Bind和@Unbind方法。这种用法是不合适的。如果使用不当就会出现上面的异常。因为当在一个属性上注明了@Requires的annotaion时,就表示这个属性需要由ipojo来负责依赖注入,ipojo默认会为集合类型的属性创建一个ServiceCollection类的代理对象,也就是说timeoutConfigList属性在被替换成了ServiceCollection类的对象,调用这个对象里的所有修改方法都会抛出UnsupportedOperationException异常,因为这个属性已经交由ipojo的来维护了,对其进行修改、添加或者删除的话,可能会导致ipojo维护对象的混乱。而@Bind和@Unbind方法是在属性的代理对象创建后,当有适合的属性类型的对象满足注入条件时回调的方法,在以上的代码中有add和remove的操作自然会导致抛出上面的异常了。

解决办法

方法一: 同时使用@Requires注解以及@Bind和@Unbind方法时,保证@Bind和@Unbind方法中没有修改操作(可以执行非修改操作,比如contains、size、get等,详见ServiceCollection类的源码)

方法二: @Requires注解与@Bind、@Unbind方法不同时出现。其实@Bind、@Unbind方法基本包含了@Requires注解的功能,而且还可以对这个属性对象的注入进行aop操作,相比@Requires注解只是需要多写些判空和初始化的代码而已。

方法三:在示例代码的基础上,在@Requires的注解中添加proxy=false,这样示例代码就不会抛出异常了(这个时候这个属性在没有被bind过前,其值为null,所以需要做判空处理),比如下面的写法:

(如果没有使用annotation,而是component的配置也是用的xml方式,解决方案还是如上面的讲的三点相类似。)

 

Post Footer automatically generated by wp-posturl plugin for wordpress.



  1. 我只说我知道的哈,同步的话最好同步代码块,对整个方法同步的话性能会比代码块效率低,有的地方同步方法会好一些,比如单例模式的实例获取。

    • 兄台说的没错,你的意思是在两个方法里面对timeoutConfigList加同步语句块吧,不过上面的场景有点特别,因为timeoutConfigList可能为null,同步语句块是不能加在null对象上,那样会抛空指针异常,如果把同步语句块加在if语句里面,则达不到线程安全的效果,在方法上对当前对象进行同步是个退而求其次的偷懒方案。当然可能更好的方案是在这个类中增加无意义的private final Object obj = new Object()成员,在示例的两个方法中对这个obj成员进行同步,这个方案可能更合适吧。欢迎继续探讨。

  2. 多谢!另外请教一下:

    @Requires(id=”timeoutConfigList”)

    这儿的id是怎么定义的?在声明一个服务的时候,
    @Component
    @Provides
    @Instantite
    这几个注解上都没有发现定义id的地方。

    唯一在@Instantite上有一个name属性,发现服务注册以后,它对应服务属性:instance.name

    那么id从何而来呢?

    多谢了!

    • @Requires与spring的@Autowired类似,表示这个字段会自动的被ipojo注入对象,默认情况注入这个field的类型的对象。id是开发的时候自定义,它要与ipojo的instance xml配置中的property的name的值相同。如果配置了id,那么可以在ipojo的xml instance配置中对注入的对象进行一定的筛选甚至指定。

      比如指定timeoutConfigListInstanceA注入到下面的instance中:

      比如指定一个筛选条件(requestTime>1000)的instance注入到imgOfferResultInstance:

      详见ipojo的官方文档:http://felix.apache.org/documentation/subprojects/apache-felix-ipojo/apache-felix-ipojo-userguide/describing-components/service-requirement-handler.html#field-injection