2010-10-27 11 views
2

我已經作出越來越重使用新@Bean配置風格的春季3,爲更多的類型安全的替代XML bean定義文件。但偶爾,由於Java缺乏類型表現力和Spring範圍代理,這種類型安全性可能會阻止你做有效的事情。春@Bean CONFIGS和Java多態性

演示此問題的完整單元測試如下,但簡要說明我有一個類ServiceBean,它實現了接口ServiceAServiceB。這個bean是一個作用域代理(在這種情況下是會話作用域)。我也有豆ClientAClientB,這是與ServiceA類型和ServiceB的對象分別注入。

在Spring XML配置,有一個與這個沒有問題。 Spring爲ServiceBean生成一個JDK代理,該代理實現了兩個接口,並且兩者都被注入到客戶端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) {} 
} 

現在,Spring的bean定義:

@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.ScopedProxyConfig $$ EnhancerByCGLIB被轉換爲test.ServiceBean $$ d293ecc3

所致。服務() 在test.ScopedProxyConfig.clientA(ScopedProxyConfig.java:26)

這不是很清楚,我到底是什麼,這是告訴我,但它似乎是JDK代理之間的衝突(實現和ServiceBean對象。

我已經試過越來越狡猾泛型:

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

但是,這並不編譯。

這不是一個特別奇特的情況,我想,我之前幾次碰到的吧。在過去,解決方法是使用TARGET_CLASS代理而不是接口代理,但這不是我的選擇。

任何人都可以弄清楚如何使這項工作?

+0

BTW:我沒有看到使用java配置風格的好處。如果你使用STS,那麼XML風格使用起來會更加舒適,Spring構建器將比java編譯器能夠分析和報告更多的錯誤。 – 2010-10-27 13:41:00

+0

@seanizer:我明白你在說什麼,這是個人喜好的問題。兩者都是有效的。 – skaffman 2010-10-27 13:54:07

+0

爲真。並且我知道spring多年來一直是XML地獄,但是用瞭解* XML的IDE,它實際上非常有趣並且非常高效。 – 2010-10-27 14:00:10

回答

1

我認爲你必須去一個更基於接口的解決方案:

創建一個接口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代理。

+0

是的,我正在考慮這個,但是這兩個接口並沒有真正相關,所以引入了一個人造傘型感覺不對。不過,這可能是最不討厭的解決方案。 – skaffman 2010-10-27 13:29:30

1

這其中至少編譯,也許它會工作:

@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()); 
} 
+0

有趣的是,它適用於'clientA',但對於'clientB',運行時類拋出異常失敗,就好像編譯器已經從ServiceA&ServiceB表達式中選擇了第一個類型。 – skaffman 2010-10-27 13:36:11