2010-10-27 3 views
2

Я использую новый стиль конфигурации @Bean в Spring 3, как более безопасную для типов альтернатив файлам XML-компонентов. Иногда, однако, этот тип безопасности может помешать вам делать то, что должно быть правдоподобным, из-за сочетания отсутствия выраженности языка Java и прокси-серверов Spring.Spring @Bean configs и java polymorphism

Полный блок-тест, который демонстрирует проблему, приведен ниже, но вкратце поставил у меня класс ServiceBean, который реализует интерфейсы ServiceA и ServiceB. Этот компонент является областью действия прокси (с учетом сеанса). У меня также есть фасоль ClientA и ClientB, которым вводят объекты типа ServiceA и ServiceB соответственно.

Весной XML config, нет проблем с этим. Spring генерирует JDK-прокси для ServiceBean, который реализует оба интерфейса, и оба они вводятся в клиентские компоненты. Это все рефлексивно, и типы хорошо работают во время выполнения.

Попробуйте это в @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 фасоли:

@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 .Услуги() в test.ScopedProxyConfig.clientA (ScopedProxyConfig.java:26)

это мне не ясно, что именно это говорит мне, но это, кажется, столкновение между прокси JDK (который реализует ServiceA и ServiceB) и объект ServiceBean.

Я пытался получать умный с обобщениями:

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

Но это даже не скомпилируется.

Это не особо экзотическая ситуация, я думаю, и я сталкивался с ней несколько раз.Раньше обходным путем было использование проксирования вместо проксирования интерфейса, но это не вариант для меня здесь.

Может ли кто-нибудь выяснить, как это сделать?

+0

BTW: Я не вижу преимуществ использования стиля конфигурации Java. Если вы используете STS, стиль XML гораздо удобнее в использовании, а конструктор Spring будет анализировать и сообщать больше ошибок, чем компилятор java. –

+0

@seanizer: Я вижу, что вы говорите, это вопрос личных предпочтений. Оба действительны. – skaffman

+0

true. и я знаю, что весна была XML-адом уже много лет, но с IDE, которая * понимает * XML, это на самом деле весело и очень продуктивно. –

ответ

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

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' с классом исключения runtime класса, как будто компилятор выбрал первый тип из выражения 'ServiceA & ServiceB'. – skaffman

Смежные вопросы