Additional Capabilities Of The Applicationcontext

Capacidades adicionales de ApplicationContext

Como se explica en la introducción del capítulo, el paquete org.springframework.beans.factory proporciona funcionalidades básicas para gestionar y manipular beans, incluso de forma programática. El paquete org.springframework.context añade la interfaz ApplicationContext, que extiende BeanFactory y otras interfaces para ofrecer funcionalidades adicionales orientadas a aplicaciones.

Muchas personas usan ApplicationContext de forma totalmente declarativa, sin crearlo mediante programación, y dependen de clases de soporte como ContextLoader, que crea automáticamente un ApplicationContext durante el arranque normal de una aplicación web Jakarta EE.

Además de mejorar BeanFactory, el paquete context ofrece las siguientes funcionalidades:

  • Acceso a mensajes con soporte de internacionalización mediante la interfaz MessageSource.
  • Acceso a recursos, como URL y archivos, mediante la interfaz ResourceLoader.
  • Publicación de eventos para beans que implementan ApplicationListener, mediante ApplicationEventPublisher.
  • Carga de varios contextos jerárquicos, permitiendo que cada uno se centre en una capa concreta, como la capa web de una aplicación, mediante HierarchicalBeanFactory.

Internacionalización mediante MessageSource

La interfaz ApplicationContext extiende MessageSource y, por tanto, proporciona funcionalidades de internacionalización (i18n). Spring también ofrece HierarchicalMessageSource, que permite resolver mensajes jerárquicamente. Juntas, estas interfaces constituyen la base del mecanismo de resolución de mensajes de Spring.

Los métodos principales incluyen:

  • String getMessage(String code, Object[] args, String default, Locale loc): recupera un mensaje de MessageSource. Si no se encuentra un mensaje para la configuración regional indicada, se utiliza el mensaje predeterminado. Los argumentos se convierten en valores de sustitución mediante MessageFormat.
  • String getMessage(String code, Object[] args, Locale loc): equivalente al anterior, pero no permite indicar un mensaje predeterminado. Si no se encuentra el mensaje, se lanza NoSuchMessageException.
  • String getMessage(MessageSourceResolvable resolvable, Locale locale): recibe un objeto MessageSourceResolvable que agrupa las propiedades utilizadas por los métodos anteriores.

Cuando se carga un ApplicationContext, busca automáticamente un bean MessageSource llamado messageSource. Si lo encuentra, delega en él las llamadas de resolución de mensajes. Si no existe, intenta encontrar un contexto padre con un bean del mismo nombre. Si tampoco lo encuentra, se crea un DelegatingMessageSource vacío para poder aceptar esas llamadas.

Spring proporciona tres implementaciones de MessageSource: ResourceBundleMessageSource, ReloadableResourceBundleMessageSource y StaticMessageSource. Todas implementan HierarchicalMessageSource para admitir mensajes anidados. StaticMessageSource se utiliza poco, pero permite añadir mensajes mediante programación.

El siguiente ejemplo usa ResourceBundleMessageSource:

<beans>
	<bean id="messageSource"
			class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basenames">
			<list>
				<value>format</value>
				<value>exceptions</value>
				<value>windows</value>
			</list>
		</property>
	</bean>
</beans>

El ejemplo presupone que existen tres paquetes de recursos llamados format, exceptions y windows en el classpath. La resolución de mensajes se realiza siguiendo el comportamiento estándar del JDK mediante objetos ResourceBundle.

Dos archivos de recursos podrían contener lo siguiente:

# en format.properties
message=¡Los caimanes molan!
# en exceptions.properties
argument.required=El argumento {0} es obligatorio.

El siguiente programa utiliza la funcionalidad de MessageSource. Todas las implementaciones de ApplicationContext también implementan MessageSource, por lo que pueden asignarse a esa interfaz:

public static void main(String[] args) {
	MessageSource resources =
			new ClassPathXmlApplicationContext("beans.xml");

	String message = resources.getMessage(
			"message", null, "Predeterminado", Locale.ENGLISH);

	System.out.println(message);
}

La salida es:

¡Los caimanes molan!

En resumen, MessageSource se define en un archivo beans.xml situado en la raíz del classpath. El bean messageSource hace referencia a varios paquetes de recursos mediante su propiedad basenames. Los archivos format.properties, exceptions.properties y windows.properties también se encuentran en la raíz del classpath.

El siguiente ejemplo muestra argumentos pasados a la búsqueda de mensajes. Los argumentos se convierten en String y se insertan en los marcadores de posición del mensaje:

<beans>

	<!-- Este MessageSource se utiliza en una aplicación web. -->
	<bean id="messageSource"
			class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basename" value="exceptions"/>
	</bean>

	<!-- Inyecta el MessageSource anterior en este POJO. -->
	<bean id="example" class="com.something.Example">
		<property name="messages" ref="messageSource"/>
	</bean>

</beans>
public class Example {

	private MessageSource messages;

	public void setMessages(MessageSource messages) {
		this.messages = messages;
	}

	public void execute() {
		String message = this.messages.getMessage(
				"argument.required",
				new Object[] {"userDao"},
				"Obligatorio",
				Locale.ENGLISH);

		System.out.println(message);
	}
}

La salida al invocar execute() es:

El argumento userDao es obligatorio.

Respecto a la internacionalización, las implementaciones de MessageSource de Spring siguen las mismas reglas de resolución de configuración regional y de respaldo que ResourceBundle del JDK.

Por ejemplo, para resolver mensajes de la configuración regional británica (en-GB), deben crearse archivos llamados format_en_GB.properties, exceptions_en_GB.properties y windows_en_GB.properties.

# en exceptions_en_GB.properties
argument.required=Ebagum lad, el argumento ''{0}'' es obligatorio, digo obligatorio.

La configuración regional suele gestionarse por el entorno de la aplicación. El siguiente ejemplo la especifica manualmente:

public static void main(final String[] args) {
	MessageSource resources =
			new ClassPathXmlApplicationContext("beans.xml");

	String message = resources.getMessage(
			"argument.required",
			new Object[] {"userDao"},
			"Obligatorio",
			Locale.UK);

	System.out.println(message);
}

También puedes implementar MessageSourceAware para recibir una referencia a cualquier MessageSource definido. Todo bean definido en un ApplicationContext que implemente esa interfaz recibe el MessageSource del contexto durante su creación y configuración.

Como MessageSource se basa en ResourceBundle de Java, no fusiona paquetes con el mismo nombre base: utiliza únicamente el primer paquete encontrado e ignora los posteriores que tengan ese mismo nombre base.

Como alternativa a ResourceBundleMessageSource, Spring proporciona ReloadableResourceBundleMessageSource. Esta variante admite el mismo formato de archivos, pero es más flexible que la implementación basada en el JDK: permite leer archivos desde cualquier ubicación de recursos de Spring, no solo desde el classpath, y admite la recarga en caliente de archivos de propiedades, manteniéndolos en caché de forma eficiente entre recargas.

Eventos estándar y personalizados

La gestión de eventos en ApplicationContext se proporciona mediante ApplicationEvent y ApplicationListener. Si un bean que implementa ApplicationListener se despliega en el contexto, recibe una notificación cada vez que se publica un ApplicationEvent en ese ApplicationContext. En esencia, se trata del patrón Observer estándar.

Desde Spring 4.2, la infraestructura de eventos incorpora un modelo basado en anotaciones y permite publicar objetos arbitrarios, aunque no extiendan ApplicationEvent. Cuando publicas uno de esos objetos, Spring lo envuelve en un evento.

Eventos integrados

Evento Explicación
ContextRefreshedEvent Se publica cuando el ApplicationContext se inicializa o actualiza, por ejemplo, mediante refresh() en ConfigurableApplicationContext. Inicializado significa que se han cargado todos los beans, se han detectado y activado los posprocesadores, se han preinstanciado los singletons y el contexto está listo para usarse. Mientras no se cierre, puede actualizarse varias veces si la implementación admite actualizaciones en caliente. XmlWebApplicationContext las admite; GenericApplicationContext, no.
ContextStartedEvent Se publica cuando el contexto se inicia con start(). Todos los beans Lifecycle reciben una señal explícita de inicio. Suele emplearse para reiniciar beans después de una parada explícita o para iniciar componentes que no se configuraron para arrancar automáticamente.
ContextStoppedEvent Se publica cuando el contexto se detiene con stop(). Todos los beans Lifecycle reciben una señal explícita de parada. Un contexto detenido puede reiniciarse con start().
ContextClosedEvent Se publica cuando el contexto se cierra con close() o mediante un shutdown hook de la JVM. Se destruyen todos los beans singleton. Una vez cerrado, el contexto llega al final de su vida útil y no puede actualizarse ni reiniciarse.
RequestHandledEvent Evento específico de web que informa de que se ha atendido una petición HTTP. Se publica al terminar la petición y solo se aplica a aplicaciones web que usan DispatcherServlet.
ServletRequestHandledEvent Subclase de RequestHandledEvent que añade información específica de Servlet.

También puedes crear y publicar eventos personalizados. El siguiente ejemplo define un evento que extiende ApplicationEvent:

public class BlockedListEvent extends ApplicationEvent {

	private final String address;

	private final String content;

	public BlockedListEvent(Object source, String address, String content) {
		super(source);
		this.address = address;
		this.content = content;
	}

	// Métodos de acceso y otros métodos...
}

Para publicar un ApplicationEvent personalizado, llama a publishEvent() en un ApplicationEventPublisher. Habitualmente se implementa ApplicationEventPublisherAware y se registra la clase como bean de Spring:

public class EmailService implements ApplicationEventPublisherAware {

	private List<String> blockedList;

	private ApplicationEventPublisher publisher;

	public void setBlockedList(List<String> blockedList) {
		this.blockedList = blockedList;
	}

	public void setApplicationEventPublisher(
			ApplicationEventPublisher publisher) {
		this.publisher = publisher;
	}

	public void sendEmail(String address, String content) {
		if (blockedList.contains(address)) {
			publisher.publishEvent(
					new BlockedListEvent(this, address, content));
			return;
		}

		// Envía el correo electrónico...
	}
}

Durante la configuración, el contenedor detecta que EmailService implementa ApplicationEventPublisherAware y llama automáticamente a setApplicationEventPublisher(). En realidad, el parámetro recibido es el propio contenedor de Spring, con el que interactúas a través de ApplicationEventPublisher.

Para recibir el evento personalizado, crea una clase que implemente ApplicationListener y regístrala como bean:

public class BlockedListNotifier
		implements ApplicationListener<BlockedListEvent> {

	private String notificationAddress;

	public void setNotificationAddress(String notificationAddress) {
		this.notificationAddress = notificationAddress;
	}

	public void onApplicationEvent(BlockedListEvent event) {
		// Notifica a las partes adecuadas mediante notificationAddress...
	}
}

ApplicationListener se parametriza con el tipo de evento personalizado, BlockedListEvent en este caso. De ese modo, onApplicationEvent() conserva la seguridad de tipos y no necesita conversiones descendentes.

Puedes registrar tantos escuchadores como necesites. De forma predeterminada, reciben los eventos de manera síncrona. Esto implica que publishEvent() se bloquea hasta que todos los escuchadores terminan de procesar el evento.

Una ventaja de este comportamiento síncrono y de un solo hilo es que el escuchador ejecuta dentro del contexto transaccional del publicador, cuando existe uno.

Si necesitas otra estrategia de publicación, como procesamiento asíncrono por defecto, puedes configurar una definición de bean applicationEventMulticaster personalizada. En esos casos, los ThreadLocal y el contexto de registro no se propagan al procesamiento de eventos.

<bean id="emailService" class="example.EmailService">
	<property name="blockedList">
		<list>
			<value>[email protected]</value>
			<value>[email protected]</value>
			<value>[email protected]</value>
		</list>
	</property>
</bean>

<bean id="blockedListNotifier" class="example.BlockedListNotifier">
	<property name="notificationAddress" value="[email protected]"/>
</bean>

<!-- Opcional: definición personalizada de ApplicationEventMulticaster. -->
<bean id="applicationEventMulticaster"
		class="org.springframework.context.event.SimpleApplicationEventMulticaster">
	<property name="taskExecutor" ref="..."/>
	<property name="errorHandler" ref="..."/>
</bean>

Al llamar a sendEmail() en el bean emailService, si hay correos que deben bloquearse se publica un BlockedListEvent. El bean blockedListNotifier, registrado como ApplicationListener, recibe el evento y puede notificar a las partes adecuadas.

El mecanismo de eventos de Spring está diseñado para una comunicación sencilla entre beans del mismo ApplicationContext. Para necesidades de integración empresarial más complejas, Spring Integration ofrece soporte para arquitecturas ligeras, orientadas a patrones e impulsadas por eventos.

Escuchadores de eventos basados en anotaciones

Puedes registrar un escuchador en cualquier método de un bean gestionado mediante @EventListener. BlockedListNotifier puede reescribirse así:

public class BlockedListNotifier {

	private String notificationAddress;

	public void setNotificationAddress(String notificationAddress) {
		this.notificationAddress = notificationAddress;
	}

	@EventListener
	public void processBlockedListEvent(BlockedListEvent event) {
		// Notifica a las partes adecuadas mediante notificationAddress...
	}
}

No declares estos beans como diferidos (lazy), porque ApplicationContext respetará esa configuración y no registrará el método para recibir eventos.

La firma del método declara el tipo de evento que escucha, pero puede usar un nombre de método libre y no necesita implementar una interfaz de escuchador. El tipo de evento también puede restringirse con genéricos, siempre que el tipo real de evento resuelva el parámetro genérico dentro de su jerarquía de implementación.

Para escuchar varios eventos, o para usar un método sin parámetros, especifica los tipos directamente en la anotación:

@EventListener({
		ContextStartedEvent.class,
		ContextRefreshedEvent.class
})
public void handleContextStart() {
	// ...
}

Puedes añadir filtrado dinámico con el atributo condition, que contiene una expresión SpEL y debe evaluarse como verdadera para invocar el método:

@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
	// Notifica a las partes adecuadas mediante notificationAddress...
}

Cada expresión SpEL se evalúa dentro de un contexto específico:

Elemento Ubicación Descripción Ejemplo
Evento Objeto raíz El ApplicationEvent real. #root.event o event
Array de argumentos Objeto raíz Los argumentos usados para invocar el método, como array de objetos. #root.args, args o args[0]
Nombre del argumento Contexto de evaluación Nombre de un argumento de método concreto. Si no están disponibles, por ejemplo porque el código se compiló sin -parameters, los argumentos están disponibles mediante #a<índice>, comenzando en 0. #blEvent, #a0, #p0

#root.event permite acceder al evento subyacente incluso si la firma del método se refiere a un objeto arbitrario publicado.

Si necesitas publicar un evento como resultado de procesar otro, puedes devolver el evento que se debe publicar:

@EventListener
public ListUpdateEvent handleBlockedListEvent(
		BlockedListEvent event) {
	// Notifica a las partes adecuadas y después publica un ListUpdateEvent...
}

Esta funcionalidad no está disponible para escuchadores asíncronos. handleBlockedListEvent() publica un nuevo ListUpdateEvent por cada BlockedListEvent procesado. Para publicar varios eventos, puedes devolver una Collection o un array de eventos.

Escuchadores asíncronos

Para procesar eventos de un escuchador concreto de forma asíncrona, puedes reutilizar el soporte normal de @Async:

@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
	// BlockedListEvent se procesa en un hilo independiente.
}

Al utilizar eventos asíncronos, considera estas limitaciones:

  • Si un escuchador asíncrono lanza una Exception, no se propaga al llamador.
  • Los métodos de escuchadores asíncronos no pueden publicar un evento posterior devolviendo un valor. Para ello, inyecta ApplicationEventPublisher y publica el evento manualmente.
  • De forma predeterminada, los ThreadLocal y el contexto de registro no se propagan al procesamiento del evento.

Ordenar escuchadores

Si necesitas que un escuchador se invoque antes que otro, añade @Order al método:

@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
	// Notifica a las partes adecuadas mediante notificationAddress...
}

Eventos genéricos

Puedes utilizar genéricos para definir con más precisión la estructura de los eventos. Por ejemplo, puedes crear un escuchador que reciba solo EntityCreatedEvent para Person:

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
	// ...
}

Debido al borrado de tipos, esto solo funciona si el evento publicado resuelve los parámetros genéricos por los que filtra el escuchador; por ejemplo, con una clase como PersonCreatedEvent extends EntityCreatedEvent<Person>.

Cuando todos los eventos siguen la misma estructura, puede resultar tedioso resolver los tipos genéricos de ese modo. En ese caso, implementa ResolvableTypeProvider:

public class EntityCreatedEvent<T>
		extends ApplicationEvent
		implements ResolvableTypeProvider {

	public EntityCreatedEvent(T entity) {
		super(entity);
	}

	@Override
	public ResolvableType getResolvableType() {
		return ResolvableType.forClassWithGenerics(
				getClass(),
				ResolvableType.forInstance(getSource()));
	}
}

Esto funciona no solo con ApplicationEvent, sino también con cualquier objeto arbitrario que publiques como evento.

Al igual que en las implementaciones clásicas de ApplicationListener, la multidifusión se realiza mediante un ApplicationEventMulticaster para todo el contexto. De forma predeterminada es SimpleApplicationEventMulticaster, con publicación síncrona en el hilo del llamador.

Puedes sustituirlo o personalizarlo mediante una definición de bean applicationEventMulticaster, por ejemplo, para procesar todos los eventos de forma asíncrona o controlar excepciones de escuchadores:

@Bean
ApplicationEventMulticaster applicationEventMulticaster() {
	SimpleApplicationEventMulticaster multicaster =
			new SimpleApplicationEventMulticaster();

	multicaster.setTaskExecutor(...);
	multicaster.setErrorHandler(...);
	return multicaster;
}

Acceso práctico a recursos de bajo nivel

Para aprovechar y comprender los contextos de aplicación, conviene conocer la abstracción Resource de Spring.

Un contexto de aplicación es un ResourceLoader, por lo que puede cargar objetos Resource. Un Resource es, en esencia, una versión más completa de java.net.URL. Cuando corresponde, las implementaciones de Resource envuelven una instancia de java.net.URL.

Resource puede obtener recursos de bajo nivel desde casi cualquier ubicación de forma transparente: classpath, sistema de archivos, cualquier ubicación describible mediante una URL estándar y otras variantes.

Si la cadena de ubicación del recurso es una ruta simple sin prefijos especiales, el origen concreto depende del tipo de contexto de aplicación.

Puedes configurar un bean para implementar ResourceLoaderAware; durante la inicialización, recibe automáticamente el contexto de aplicación como ResourceLoader.

También puedes exponer propiedades de tipo Resource para acceder a recursos estáticos. Se inyectan como cualquier otra propiedad. Puedes indicar esas propiedades como rutas String simples y confiar en la conversión automática de texto a Resource cuando se despliega el bean.

Las rutas proporcionadas al constructor de ApplicationContext son realmente cadenas de recursos. En formato simple se tratan según la implementación concreta. Por ejemplo, ClassPathXmlApplicationContext interpreta una ruta simple como ubicación del classpath.

También puedes usar prefijos especiales para forzar la carga de definiciones desde el classpath o desde una URL, independientemente del tipo de contexto.

Seguimiento del inicio de la aplicación

ApplicationContext gestiona el ciclo de vida de las aplicaciones Spring y proporciona un modelo de programación rico para los componentes. Como resultado, las aplicaciones complejas pueden tener grafos de componentes y fases de inicio igualmente complejos.

Registrar los pasos de inicio mediante métricas específicas ayuda a entender dónde se invierte tiempo durante el arranque y a comprender mejor el ciclo de vida del contexto.

AbstractApplicationContext y sus subclases incorporan ApplicationStartup, que recopila datos StartupStep sobre distintas fases de inicio:

  • Ciclo de vida del contexto: exploración de paquetes base y gestión de clases de configuración.
  • Ciclo de vida de beans: instanciación, inicialización inteligente y posprocesamiento.
  • Procesamiento de eventos de aplicación.

El siguiente ejemplo muestra instrumentación dentro de AnnotationConfigApplicationContext:

// Crea un paso de inicio y comienza a registrar.
try (StartupStep scanPackages =
		getApplicationStartup().start("spring.context.base-packages.scan")) {

	// Añade información de etiquetado al paso actual.
	scanPackages.tag(
			"packages",
			() -> Arrays.toString(basePackages));

	// Ejecuta la fase que se está instrumentando.
	this.scanner.scan(basePackages);
}

El contexto de aplicación ya incorpora varios pasos de instrumentación. Una vez registrados, pueden recopilarse, mostrarse y analizarse con herramientas específicas.

La implementación predeterminada de ApplicationStartup no hace nada, para minimizar la sobrecarga. Por tanto, no se recopilan métricas de inicio de forma predeterminada.

Spring Framework incluye FlightRecorderApplicationStartup, que permite registrar pasos de inicio con Java Flight Recorder. Para usarlo, debes configurar una instancia en el ApplicationContext tan pronto como se haya creado.

Los desarrolladores también pueden usar esta infraestructura al proporcionar una subclase propia de AbstractApplicationContext o cuando necesiten recopilar datos más precisos.

ApplicationStartup está pensado solo para el inicio de la aplicación y el contenedor principal. No sustituye a perfiladores Java ni a bibliotecas de métricas como Micrometer.

Para recopilar StartupStep personalizados, los componentes pueden obtener ApplicationStartup directamente desde el contexto, implementar ApplicationStartupAware o solicitar ApplicationStartup en cualquier punto de inyección.

No utilices el espacio de nombres "spring.*" al crear pasos de inicio personalizados, ya que está reservado para uso interno de Spring y puede cambiar.

Creación práctica de ApplicationContext para aplicaciones web

Puedes crear instancias de ApplicationContext de forma declarativa mediante ContextLoader, o programáticamente mediante una de las implementaciones de ApplicationContext.

Puedes registrar un ApplicationContext usando ContextLoaderListener:

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml
	</param-value>
</context-param>

<listener>
	<listener-class>
		org.springframework.web.context.ContextLoaderListener
	</listener-class>
</listener>

El listener examina el parámetro contextConfigLocation. Si no existe, utiliza /WEB-INF/applicationContext.xml como valor predeterminado.

Si existe, separa su valor usando delimitadores predefinidos —coma, punto y coma y espacios en blanco— y utiliza los valores resultantes como ubicaciones en las que buscar contextos de aplicación. También admite patrones de rutas estilo Ant.

Por ejemplo:

  • /WEB-INF/*Context.xml: todos los archivos cuyo nombre termina en Context.xml dentro de WEB-INF.
  • /WEB-INF/**/*Context.xml: todos los archivos con ese patrón dentro de cualquier subdirectorio de WEB-INF.

Desplegar un ApplicationContext de Spring como archivo RAR de Jakarta EE

Es posible desplegar un ApplicationContext de Spring como archivo RAR, encapsulando el contexto, sus clases de beans y los JAR de bibliotecas necesarios en una unidad de despliegue RAR de Jakarta EE.

Es equivalente a iniciar un ApplicationContext independiente, pero alojado en un entorno Jakarta EE con acceso a las capacidades del servidor.

El despliegue RAR es una alternativa más natural que desplegar un WAR sin puntos de entrada HTTP, utilizado únicamente para iniciar un ApplicationContext en Jakarta EE.

Es adecuado para contextos que no necesitan puntos de entrada HTTP y que solo contienen puntos finales de mensajería y tareas programadas.

Los beans de este contexto pueden usar recursos del servidor de aplicaciones, como el gestor de transacciones JTA, DataSource JDBC y ConnectionFactory JMS vinculados a JNDI, y también pueden registrarse en el servidor JMX de la plataforma, mediante el soporte estándar de Spring para transacciones, JNDI y JMX.

Los componentes también pueden interactuar con el WorkManager de JCA del servidor mediante la abstracción TaskExecutor de Spring.

Para un despliegue sencillo de un ApplicationContext como archivo RAR de Jakarta EE:

  1. Empaqueta todas las clases de la aplicación en un archivo RAR, que es un JAR estándar con otra extensión.
  2. Añade los JAR de bibliotecas requeridos a la raíz del archivo RAR.
  3. Añade un descriptor de despliegue META-INF/ra.xml y los archivos de definición de beans XML de Spring correspondientes, normalmente META-INF/applicationContext.xml.
  4. Copia el archivo RAR resultante en el directorio de despliegue del servidor de aplicaciones.

Estas unidades RAR suelen ser autónomas. No exponen componentes al exterior, ni siquiera a otros módulos de la misma aplicación.

La interacción con un ApplicationContext basado en RAR suele producirse mediante destinos JMS compartidos con otros módulos. También puede programar tareas o reaccionar a nuevos archivos del sistema de archivos.

Si necesita permitir acceso síncrono desde el exterior, puede exportar, por ejemplo, puntos finales RMI que otros módulos de la misma máquina pueden utilizar.