代码之家  ›  专栏  ›  技术社区  ›  skaffman

Spring @ bean配置和Java多态性

  •  2
  • skaffman  · 技术社区  · 14 年前

    我越来越频繁地使用新的 @Bean Spring3中的配置样式,作为XML bean定义文件的更安全的类型替代。不过,这种类型的安全性有时会阻止您做应该是有效的事情,这是由于Java缺乏类型表示性和Spring范围代理。

    一个完整的单元测试,它演示了下面的问题,但简单地说,我有一个类 ServiceBean ,实现接口 ServiceA ServiceB . 这个bean是一个作用域代理(在本例中是会话作用域)。我还有豆子 ClientA ClientB ,注入类型为的对象 服务A 服务B 分别。

    在SpringXML配置中,这没有问题。Spring为 服务bean 它实现了两个接口,并且都注入到客户机bean中。它都是反射的,运行时类型也很好。

    试试这个 @豆 -尽管如此,风格还是有问题的。这是演示测试。

    首先,服务:

    public interface ServiceA {}
    
    public interface ServiceB {}
    
    public class ServiceBean implements ServiceA, ServiceB {}
    

    现在,客户:

    public class ClientA {
        public ClientA(ServiceA service) {}
    }    
    
    public class ClientB {
        public ClientB(ServiceB service) {}
    }
    

    现在,SpringBean定义:

    @Configuration
    public class ScopedProxyConfig {
    
        @Bean @Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)
        public ServiceBean services() {
            return new ServiceBean();
        }
    
        @Bean
        public ClientA clientA() {
            return new ClientA(services());
        }
    
        @Bean
        public ClientB clientB() {
            return new ClientB(services());
        }
    }
    

    最后,单元测试和支持上下文:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration
    public class ScopedProxyTest {
    
        private @Resource ClientA clientA;
        private @Resource ClientB clientB;      
    
        public @Test void test() {
            assertThat(clientA, is(notNullValue()));
            assertThat(clientB, is(notNullValue()));
        }
    }
    

    <beans>            
        <context:annotation-config/>
        <bean class="test.ScopedProxyConfig"/>      
    </beans>
    

    (为了清晰起见,省略了XML名称空间)。

    所有这些都很好地编译。但是,运行测试,您会得到一个类型转换运行时异常:

    原因:java.lang.ClassCastException:$proxy11无法强制转换为test.serviceBean 在test.scopedProxyConfig$$EnhancerbyCGlib$$d293ecc3.services()上 在StestDePro CONFIG.cliTon(SeopDeXOXCONFIG.java:26)

    我不清楚这究竟告诉了我什么,但它似乎是JDK代理(实现 服务A 服务B )以及 服务bean 对象。

    我试过用仿制药来变聪明:

    @Bean @Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)
    public <T extends ServiceA & ServiceB> T services() {
        return (T)new ServiceBean();
    }
    

    但它甚至不能编译。

    我想这不是一个特别奇特的情况,我以前也遇到过。在过去,解决方法是 TARGET_CLASS 代理而不是接口代理,但这不是我这里的一个选项。

    有人能想出怎么做这个吗?

    2 回复  |  直到 14 年前
        1
  •  1
  •   Sean Patrick Floyd    14 年前

    我认为你必须寻求一个更基于界面的解决方案:

    创建接口 ServiceC :

    public interface ServiceC extends ServiceA, ServiceB {}
    

    ServiceBean 实现那个接口

    public class ServiceBean implements ServiceC{}
    

    在你的 ScopedProxyConfig :

    @Bean @Scope(value=WebApplicationContext.SCOPE_SESSION,
                 proxyMode=ScopedProxyMode.INTERFACES)
    public ServiceC services() {
        return new ServiceBean();
    }
    

    一致地使用接口应该使Spring能够与JDK代理一起工作。

        2
  •  1
  •   axtavt    14 年前

    这一个至少可以编译,也许可以工作:

    @Bean @Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES) 
    public <T extends ServiceA & ServiceB> T services() { 
        return (T)new ServiceBean(); 
    }
    
    @Bean 
    public ClientA clientA() { 
        return new ClientA(this.<ServiceBean>services()); 
    } 
    
    @Bean 
    public ClientB clientB() { 
        return new ClientB(this.<ServiceBean>services()); 
    }