2011-10-14 12 views
5

que tengo una clase de servicio que no es serializable y un grano que debe ser serializable pero debe tener acceso a esta clase de servicio:primavera inyección de dependencias en los granos de serializables

class SomeBean implements Serializable 
{ 
    private StuffFactory factory; 

    @Autowired 
    public SomeBean(StuffFactory factory) 
    { 
     this.factory = factory; 
    } 

    public getOther() 
    { 
     return this.factory.getSomeOtherStuff(); 
    } 
} 

Esto, obviamente, no funciona porque ahora el SomeBean clase ya no es serializable. ¿Cuál es la forma correcta de resolver esto en primavera? Cuando hago que el campo factory sea transitorio, ¿pierdo la instancia de fábrica inyectada en la deserialización o no? Y cuando hago que el StuffFactory también sea serializable, esta clase ya no será un singleton porque cada instancia de SomeBean tendrá su propia fábrica luego de la deserialización.

Respuesta

0

Tal vez tienen una SomeBeanFactory:

class SomeBeanFactory { 
    @Autowired 
    private StuffFactory stuffFactory; 

    public SomeBean deserialize(...) { 
     SomeBean someBean = ...; 
     someBean.setStuffFactory(stuffFactory); 
     return someBean; 
    } 
} 

Obviamente, tendrá que crear una incubadora de factory en SomeBean.

+0

La serialización y la deserialización se realiza mediante el contenedor web (la instancia de SomeBean está en algún lugar de la sesión HTTP) por lo que no puedo delegar la deserialización en una fábrica. – kayahr

0

Java proporciona una forma de controlar la serialización y deserialización mediante la implementación de los dos métodos:

  • private void writeObject(ObjectOutputStream out) throws IOException;
  • private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;

Así se puede cambiar la escritura para almacenar el grano sin la referencia al servicio y luego uno mientras lee el objeto "inyectar" la referencia nuevamente.

@see http://java.sun.com/developer/technicalArticles/Programming/serialization/

Usted DEBE crear la instancia en readObject como frijol. Si solo usa un simple new, no se convertirá en un frijol. Por lo tanto, necesita acceder al contexto de Spring, por ejemplo, a través de un método estático. De mayo, la mejor manera es utilizar una fábrica de frijoles para crear la nueva instancia.


Otra manera se puede tratar de hacer que la instancia de un grano de primavera está utilizando @Configurable pero que requiere verdadera AspectJ.

+0

Pero, ¿cómo puedo obtener la instancia 'stuffFactory' en el método' readObject' para reiniciar la dependencia? Esta dependencia es inyectada automáticamente por Spring cuando se crea el bean. No tengo contexto de aplicación disponible en el método readObject para obtener el bean manualmente desde allí. Esperaba algo de magia de primavera para manejar esto. – kayahr

+1

@kayahr: ver mi respuesta extendida – Ralph

+0

Pero un Spring Bean normalmente no conoce la primavera, por lo que no es una buena idea obtener el contexto de la aplicación a través de un método estático. Cuando hago esto, simplemente puedo descartar toda la idea de la inyección de dependencia y acceder a 'StuffFactory' mediante un método estático' getInstance' o algo así. – kayahr

2

Necesita algún tipo de contexto para este magic para funcionar.

Una manera fea que se me ocurre es una estática ThreadLocal sosteniendo la ApplicationContext - así es como funciona la seguridad de muelles usando SecurityContextHolder.

Pero la mejor si usted sería capaz de exteriorizar la lógica que requiere la StuffFactory a algún tipo de Singleton SomeBeanService, es decir:

public class SomeBeanService { 
    @Autowired 
    private StuffFactory stuffFactory; 

    public void doWithSomeBean(SomeBean bean) { 
     // do the stuff using stuffFactory here 
    } 
} 

Actualización:

El punto en el anterior alternativa a ThreadLocal es deshacerse por completo de la dependencia en StuffFactory de SomeBean. Esto debería ser posible, pero requerirá cambios en la arquitectura.La separación de preocupaciones (una de, no solo reglas básicas de Spring) implica que podría ser una buena idea dejar que SomeBean sea un simple objeto de transferencia de datos y la lógica de negocio se mueva a la capa de servicio.

Si no puede lograr esto, entonces la única forma es usar algún tipo de contexto static (como también dijo Ralph). La implementación de dicho contexto puede implicar el uso de un ThreadLocal. Esto permitiría acceder al ApplicationContext para obtener el StuffFactory, pero es casi tan feo como las variables globales, así que evítelo si es posible.

Update2:

acabo de ver a su comentario, que SomeBean se almacena en la sesión HTTP, y por lo tanto el problema de serialización/deserialización. Ahora, incluso más, le aconsejo cambiar su diseño y eliminar la dependencia. Haga SomeBean un DTO simple, lo más pequeño posible para evitar sesiones sobrecargadas. No debe haber ninguna lógica que requiera acceso a los beans Spring individuales en el SomeBean. Dicha lógica debe colocarse en el controlador o capa de servicio.

+0

Pero luego tengo que inyectar este SomeBeanService en su lugar. ¿O te refieres a acceder sin primavera? Entonces esto es algo similar al patrón del Delegador de Servicio que normalmente es usado por los beans no administrados. Pero mi 'SomeBean' está administrado por Spring, así que esperaba que Spring tuviera algo de magia para manejar dependencias transitorias. Bueno, si no aparece una solución mejor, creo que tengo que aceptar tu respuesta. – kayahr

0

Una reconsideración de su arquitectura es potencialmente práctica en este escenario. Si puede aislar los datos que se deben serializar en un objeto de transferencia de datos (DTO) simple, entonces debería ser posible proporcionar un contenedor a esa DTO que a su vez puede contener dependencias a otros beans.

Por ejemplo:

public interface IDataBean { 
    void setSomething(String someData); 
} 

// This is your session bean - just a plain DTO 
public class MyDataBean implements IDataBean, Serializable { 

    private String someData; 

    public void setSomething(String someData) { 
     this.someData = someData; 
    } 
} 

// This is the wrapper that delegates calls to a wrapped MyDataBean. 
public class MyDataBeanWithDependency() implements IDataBean { 

    private SomeOtherService service; 

    private MyDataBean dataBean; 

    public SimpleDataBeanWithDependency(MyDataBean dataBean, SomeOtherService service) { 
     this.dataBean = dataBean; 
     this.service = service; 
    } 

    public void setSomething(String someData) { 
     // Here we make a call to the service to perform some specific logic that may, for example, hit a DB or something. 
     String transformedString = service.transformString(someData); 
     dataBean.setSomething(transformedString); 
    } 
} 

public class SomeService { 

    // This is a Spring session scoped bean (configured using <aop:scoped-proxy/>) 
    @Autowired 
    private MyDataBean myDataBean; 

    // Just a plain singleton Spring bean 
    @Autowired 
    private SomeOtherService someOtherService; 

    public IDataBean getDataBean() { 
     return new MyDataBeanWithDependency(myDataBean, someOtherService); 
    } 
} 

Lo siento por todo el código! Sin embargo, déjame tratar de explicar lo que quiero decir aquí. Si siempre recupera el bean de sesión a través de un servicio (en este caso, SomeService), tiene la opción de crear un contenedor alrededor del bean de sesión. Este contenedor puede contener cualquier dependencia de bean (autoenlazada en SomeService) que desee utilizar como parte de la ejecución de la lógica junto con el bean de sesión.

Lo bueno de este enfoque es que también se puede programar en una interfaz (ver IDataBean). Esto significa que, si usted, por ejemplo, tenía un controlador que obtiene el bean de datos del servicio, hace que las pruebas/burlas unitarias sean muy limpias.

Posiblemente, un enfoque incluso más limpio desde la perspectiva del código, sería que MyDataBeanWithDependency se registrara en el contenedor de resorte utilizando el alcance de "solicitud". Así que podrías conectar ese bean directamente a SomeService. Esto esencialmente trataría de forma transparente con la creación de instancias, por lo que no es necesario crear una instancia de MyDataBeanWithDependency manualmente desde el servicio.

¡Espero haber hecho lo suficiente para explicarme aquí!

Cuestiones relacionadas