java - springframework - ¿Alcance de resorte personalizado?
spring framework tutorial (9)
En mi empresa, hemos creado dos ámbitos personalizados, uno que utilizará Thread o Request y otro que utilizará Thread o Session. La idea es que se pueda usar un alcance único para los beans con ámbito sin tener que cambiar la configuración en función del entorno de ejecución (contenedor JUnit o Servlet). Esto también es útil cuando ejecuta elementos en Quartz y ya no tiene disponible un alcance Request o Session.
¿Alguien sabe de otros ámbitos personalizados de primavera que Servlet Context Scope y ThreadScope ?
Si ha creado un ámbito personalizado de código cerrado, también me interesaría saber qué hace y cómo funcionó. (Me imagino que alguien haría un WindowScope en una aplicación de escritorio?)
Estoy abierto a todos los casos de uso, estoy buscando expandir mi horizonte aquí.
Implementamos nuestro propio alcance de Spring personalizado. Gran parte de nuestro código funciona a un nivel relativamente bajo, cerca de la base de datos, y mantenemos un nivel conceptual además de su propio modelo de objetos de fuentes de datos, enlaces, atributos, etc.
De todos modos, muchos frijoles requieren una llamada StorageDictionary (una encapsulación de este gráfico de objetos) para hacer su trabajo. Cuando hacemos cambios no triviales al gráfico del objeto, el diccionario a veces necesita ser volado y recreado. En consecuencia, implementamos un ámbito personalizado para los objetos que tenían un diccionario de ámbito , y parte de la invalidación de un diccionario determinado implica la eliminación de este ámbito personalizado. Esto le permite a Spring manejar una buena forma de almacenamiento en caché automático para estos objetos. Usted recupera el mismo objeto cada vez hasta que se invalida el diccionario, en cuyo punto obtiene un nuevo objeto.
Esto ayuda no solo con la coherencia, sino que también permite a los objetos almacenar en caché las referencias a las entidades dentro del diccionario, a sabiendas de que la memoria caché será válida siempre que puedan ser recuperadas por Spring. Esto, a su vez, nos permite construirlos como objetos inmutables (siempre que se puedan cablear mediante la inyección del constructor), lo que es muy bueno hacer de todos modos siempre que sea posible.
Esta técnica no funcionará en todas partes y depende mucho de las características del software (por ejemplo, si el diccionario se modificara con regularidad, esto sería terriblemente ineficiente, y si se actualizara nunca sería innecesario y un poco menos eficiente que el acceso directo). Sin embargo, definitivamente nos ha ayudado a pasar esta gestión del ciclo de vida a Spring de una manera que es conceptualmente sencilla y, en mi opinión, bastante elegante.
Oracle Coherence ha implementado un alcance de cuadrícula de datos para Spring beans . En resumen:
Un Bean de Grilla de Datos es un proxy para una instancia de Bean java.io.Serializable que se almacena en un Caché Distribuido por Coherencia que no expira (llamado near-datagridbeans).
Nunca los usé yo, pero parecen geniales.
Apache Orchestra proporciona SpringConversationScope .
Un ámbito local de primavera basado en la configuración regional de los usuarios con una aplicación web
Ver la página wiki relacionada
Fondo:
Trabajo en una sola aplicación web que ejecuta 4 sitios web diferentes en el mismo contexto de servlet. Cada sitio tiene su propio nombre de dominio, por ejemplo, www.examplesite1.com, www.examplesite2.com, etc.
Problema:
Los sitios a veces requieren su propia instancia personalizada de un bean desde el contexto de la aplicación (generalmente para la visualización personalizada de mensajes o el formateo de objetos).
Por ejemplo, supongamos que los sitios 1 y 2 usan el bean "standardDateFormatter", el sitio 3 usa el bean "usDateFormatter" y el sitio 4 usa el bean "ukDateFormatter".
Solución:
Estoy planeando usar un alcance de "sitio".
Tenemos una enumeración del sitio como esta:
enum Site {
SITE1, SITE2, SITE3, SITE4;
}
Luego tenemos un filtro que almacena uno de estos valores del Sitio en el hilo de la solicitud usando un ThreadLocal. Esta es la "id de conversación" del alcance del sitio.
Luego, en el contexto de la aplicación habría un bean llamado "dateFormatter", con "scope =" site "''. Entonces, donde sea que deseemos utilizar un formateador de fecha, se usará el correcto para el sitio actual del usuario.
Agregado después:
Código de muestra aquí:
En mi compañía, también hemos implementado el alcance personalizado de primavera. Tenemos un sistema de múltiples inquilinos donde cada cliente puede personalizar la configuración. Alcance basado en instancia nuestro, almacena en caché los beans que son específicos del cliente. Por lo tanto, cada vez que el usuario de un cliente inicia sesión, esta configuración se guarda en caché y se reutiliza cuando otros usuarios de los mismos clientes inician sesión.
En una aplicación Spring Batch, hemos implementado un alcance de artículo .
Fondo
Tenemos muchos componentes de @Service
que calculan algo en función del elemento del lote actual. Muchos de ellos necesitan el mismo flujo de trabajo:
- Determine las partes relevantes del artículo.
- Inicia cosas basados en el artículo.
- Para cada parte del elemento, calcule algo (usando cosas).
findItemParts(Item)
el flujo de trabajo a un método de plantilla de clase base, por lo que las subclases implementan solo findItemParts(Item)
(haciendo 1 y 2) y computeSomething(ItemPart)
(haciendo 3). Por lo tanto, se volvieron con estado (las cosas inicializadas en findItemParts
se necesitan en computeSomething
), y ese estado debe computeSomething
antes del siguiente elemento.
Algunos de esos servicios también incluyen frijoles primavera inyectados que también se derivan del artículo actual y deben eliminarse después.
Diseño
Implementamos un AbstractScopeRegisteringItemProcessor
que registra el elemento y permite subclases para registrar frijoles derivados. Al final de su método de process
, elimina el elemento de su contexto de ámbito y los beans derivados usando DefaultSingletonBeanRegistry.destroySingleton
.
Cómo funcionó
Funciona, pero tiene los siguientes problemas:
- No logramos limpiar los frijoles derivados sin registrarlos (solo en función de su
@Scope
). El procesador concreto debe crearlos y registrarlos. -
AbstractScopeRegisteringItemProcessor
hubiera sido más agradable al usar la composición e implementar dinámicamente todas las interfaces del procesador subyacente. Pero entonces el bean@StepScope
resultante es un proxy para el tipo de retorno declarado (es decir,AbstractScopeRegisteringItemProcessor
oItemProcessor
) sin las interfaces de devolución de llamada requeridas.
EDITAR
Con la ayuda de la solución @Eliot Sykes y el código compartido más el registro BeanDefinition de @ Cheetah, pude deshacerme del registro como beans singleton. En su lugar, ItemScopeContext
(el almacenamiento utilizado tanto por el procesador como por la implementación de Scope
; configurado por Java a través de un método @Bean
estático) implementa BeanDefinitionRegistryPostProcessor
. Registra un FactoryBean
cuyo getObject()
devuelve el elemento actual o arroja una excepción si no hay ninguno. Ahora, un @Component
anotado con @Scope(scopeName = "Item", proxyMode = ScopedProxyMode.TARGET_CLASS)
simplemente puede inyectar el elemento y no necesita registrarse para la limpieza de final de ámbito.
Entonces, al final, funcionó bien.
Una vez utilicé un tipo de ámbito de conversación para almacenar algunos objetos en el ámbito de la sesión, para mantenerlos al volver a ingresar en la misma página, pero limitado a una sola página para evitar dejar objetos inútiles en la sesión. La implementación solo almacenaba la URL de la página y limpiaba el alcance de la conversación en cada cambio de página.