java - Frijoles de ámbito de sesión de primavera(controladores) y referencias a servicios, en términos de serialización
spring jsf (6)
- un caso estándar: usted tiene un controlador (
@Controller
) con@Scope("session")
. - las clases que se colocan en la sesión generalmente deben implementar
Serializable
para que puedan almacenarse físicamente en caso de que se reinicie el servidor, por ejemplo - Si el controlador implementa
Serializable
, esto significa que todos los servicios (otros granos de primavera) a los que hace referencia también serán serializados. A menudo son representantes, con referencias a gestores de transacciones, fábricas de gerentes de entidades, etc. - No es improbable que algún servicio, o incluso controlador, mantenga una referencia al
ApplicationContext
, mediante la implementación deApplicationContextAware
, por lo que esto puede significar que todo el contexto está serializado. Y dado que tiene muchas conexiones, es decir, cosas que no son serializables por idea, se restaurará en estado corrupto.
Hasta ahora, he ignorado principalmente estos problemas. Recientemente pensé en declarar transient
todas mis dependencias de primavera y readResolve()
a readResolve()
en readResolve()
por las clases de utilidad estáticas WebApplicationContextUtils
y las que mantienen la solicitud / ServletContext en un ThreadLocal
. Esto es tedioso, pero garantiza que, cuando el objeto se deserialice, sus dependencias estarán "actualizadas" con el contexto de la aplicación actual .
¿Hay alguna práctica aceptada para esto, o alguna guía para serializar partes del contexto de primavera?
Tenga en cuenta que en JSF, los beans administrados (~ controladores) tienen estado (a diferencia de los marcos web basados en acciones). Entonces quizás mi pregunta sea más para JSF, que para spring-mvc.
Después de probar todas las diferentes alternativas sugerí que todo lo que tenía que hacer era agregar aop: scoped-proxy a mi definición de bean y comenzó a funcionar.
<bean id="securityService"
class="xxx.customer.engagement.service.impl.SecurityContextServiceImpl">
<aop:scoped-proxy/>
<property name="identityService" ref="identityService" />
</bean>
securityService se inyecta en mi managedbean que es view scoped. Esto parece que funciona bien. Según la documentación de primavera, se supone que arrojará una BeanCreationException ya que securityService es un singleton. Sin embargo, esto no parece suceder y funciona bien. No estoy seguro de si esto es un error o cuáles serían los efectos secundarios.
En esta presentación (alrededor de 1:14) el orador dice que este problema se resuelve en la primavera 3.0 al proporcionar un proxy de beans no serializables, que obtiene una instancia del contexto de la aplicación actual (en deserialización)
Esperaría que los controladores de alcance sean ''singleton'', es decir, una vez por aplicación, en lugar de en la sesión.
El alcance de la sesión generalmente se usa más para almacenar información por usuario o funciones por usuario.
Normalmente solo guardo el objeto ''usuario'' en la sesión, y tal vez algunos frijoles se usan para la autenticación o tal. Eso es.
Eche un vistazo a los documentos de primavera para configurar algunos datos del usuario en el alcance de la sesión, usando un proxy aop:
Espero que ayude
La serialización de Dynamic-Proxies funciona bien, incluso entre diferentes JVM, por ej. como se usa para la Sesión-Replicación.
@Configuration public class SpringConfig {
@Bean
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
MyService myService() {
return new MyService();
}
.....
Solo tiene que establecer el ID de ApplicationContext antes de actualizar el contexto (consulte: org.springframework.beans.factory.support.DefaultListableBeanFactory.setSerializationId (String))
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// all other initialisation part ...
// before! refresh
ctx.setId("portal-lasg-appCtx-id");
// now refresh ..
ctx.refresh();
ctx.start();
Funciona bien en Spring-Version: 4.1.2.RELEASE
Parece que la recompensa no atrajo una sola respuesta, así que documentaré mi comprensión limitada:
@Configuration
public class SpringConfig {
@Bean
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
MyService myService() {
return new MyService();
}
@Bean
@Scope("request")
public IndexBean indexBean() {
return new IndexBean();
}
@Bean
@Scope("request")
public DetailBean detailBean() {
return new DetailBean();
}
}
public class IndexBean implements Serializable {
@Inject MyService myService;
public void doSomething() {
myService.sayHello();
}
}
public class MyService {
public void sayHello() {
System.out.println("Hello World!");
}
}
Spring no inyectará el MyService desnudo en IndexBean, sino un proxy serializable. (Probé eso, y funcionó).
Sin embargo, la documentación de primavera writes :
No necesita usar el
<aop:scoped-proxy/>
junto con los beans que tienen un ámbito comosingletons
oprototypes
. Si intenta crear un proxy con ámbito para un bean Singleton, seBeanCreationException
laBeanCreationException
.
Al menos cuando se utiliza la configuración basada en Java, el bean y su proxy se pueden instanciar muy bien, es decir, no se lanza ninguna excepción. Sin embargo, parece que usar proxies con alcance para lograr la serializabilidad no es el uso previsto de dichos proxys. Por lo tanto, me temo que Spring podría solucionar ese "error" y evitar la creación de proxies con ámbito a través de la configuración basada en Java, también.
Además, existe una limitación: el nombre de clase del proxy es diferente después del reinicio de la aplicación web (porque el nombre de clase del proxy se basa en el código hash del consejo utilizado para construirlo, que a su vez depende del hashCode de Objeto de clase de un interceptor. Class.hashCode no anula Object.hashCode, que no es estable durante los reinicios. Por lo tanto, las sesiones serializadas no pueden ser utilizadas por otras máquinas virtuales ni por reinicios.
Recientemente combiné JSF con Spring. Uso RichFaces y la característica @KeepAlive, que serializa el bean JSF que respalda la página. Hay dos maneras en que he logrado que esto funcione.
1) Use @Component ("sesión") en el bean de respaldo de JSF
2) Obtenga el bean de ELContext cuando lo necesite, algo como esto:
@SuppressWarnings("unchecked")
public static <T> T getBean(String beanName) {
return (T) FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(FacesContext.getCurrentInstance().getELContext(), null, beanName);
}