1. Overview
Aspectran’s auto-reloading (Hot-Reloading) feature is a powerful mechanism that allows dynamic application of configuration changes during application runtime without restarting the JVM. The main purposes of this feature are as follows:
- Improved Development Productivity: Shortens the development cycle by allowing immediate verification of changes after modifying code or configurations during development, without the time-consuming server restart.
- Dynamic Updates in Production: Enables the application of configuration changes (e.g., toggling feature flags, updating data source information) on a running server without downtime.
The core of this mechanism works by detecting file changes and then destroying and rebuilding the ActivityContext, the heart of an Aspectran application, from scratch.
2. Core Components
The auto-reloading feature is implemented through the organic interaction of the following core classes:
ContextReloadingTimer: A timer class that internally uses aScheduledExecutorServiceto schedule and execute the reloading task at a configured interval.ContextReloadingTask: ARunnableimplementation that performs the actual task of monitoring for file changes. It records thelastModifiedtimestamp of each configuration file and periodically checks if it has changed from the previously stored value.ActivityContextBuilder: The component responsible for creating and starting theContextReloadingTimerat the final stage of the context build process if theautoReloadsetting is enabled.ServiceLifeCycle: The interface thatContextReloadingTaskcalls to trigger an application restart when a change is detected.
3. Operational Flow
Auto-reloading is performed through the following clear steps:
- Configuration and Activation: A developer enables the feature by adding the
autoReloadsetting to thecontextsection of theaspectran-config.aponfile.context: { autoReload: { enabled: true scanIntervalSeconds: 5 } } - Initialization and Monitoring Start: At the final stage of the context build process, if the
autoReloadsetting istrue,ActivityContextBuilderstarts theContextReloadingTimer. At this time, all configuration files referenced during the build process and resources specified in<resourceLocations>are registered as monitoring targets. - Change Monitoring:
ContextReloadingTaskchecks thelastModifiedtimestamp of the registered files at the interval specified byscanIntervalSeconds(e.g., every 5 seconds). - Change Detection and Restart Trigger: The reloading logic is designed to be safe against incomplete file updates (e.g., when copying multiple files). A restart is triggered only after a “quiet period” with no detected changes has passed.
- When a file change is first detected, the task simply notes the change and waits for the next scan.
- If the next scan reveals no further changes, it means a quiet period equivalent to
scanIntervalSecondshas passed. The task then calls therestart()method of theServiceLifeCycleinstance. - If changes are detected continuously in every scan, the restart is deferred until a scan completes with no new changes.
- Restart Execution: The service that receives the
restart()call destroys the existingActivityContextand re-executes the context build process from the beginning. In this process, a newActivityContextwith the changed settings applied is created and reflected in the application.
4. Hierarchical Services and Reloading
Aspectran supports a hierarchical structure where multiple services can have parent-child relationships. The auto-reloading mechanism operates as follows to maintain stability and consistency in this structure:
- Distributed Monitoring, Centralized Execution: Each sub-service can have its own
autoReloadsettings, which leads to the creation of an independentContextReloadingTimerthat monitors only the resources belonging to it. However, regardless of which sub-service’s timer detects a change, the actualrestart()command is always passed to theRootService. - Ensuring Safety: This ‘centralized execution’ approach is guaranteed by the logic within
AbstractActivityContextBuilder.// The restart command must be delivered to the root service to safely reload the entire application. contextReloadingTimer = new ContextReloadingTimer(masterService.getRootService().getServiceLifeCycle());By concentrating all restart requests to the root service, the
Assert.state(isRootService(), ...)constraint defined inAbstractServiceLifeCycleis naturally satisfied. This is a safe design that prevents the complex state inconsistency problems that can arise from partial reloads and ensures the consistency of the entire application.
5. SiblingClassLoader and Reloading
Auto-reloading is closely related to Aspectran’s proprietary SiblingClassLoader to reflect class-level changes.
hardReloadMode: If thereloadMode="hard"option is given in theautoReloadsetting, the restart process discards the entire existingSiblingClassLoadergroup and creates a new instance. This allows new classes to be loaded when JAR files specified in<resourceLocations>are changed.- Fundamental Limitation: This mechanism only applies to classes loaded by
SiblingClassLoader. Classes loaded by the application’s main classloader (the parent classloader), such as framework core classes or application bootstrap-related classes, cannot be reloaded without a JVM restart.
6. Constraints and Practical Use
While the auto-reloading feature is very powerful, it is important to understand and use it with the following characteristics and constraints in mind:
- In-Memory State Loss: Reloading is a process that destroys and recreates the entire
ActivityContext, so all in-memory states, such as instance variables of singleton beans, are reset. - Brief Downtime: The service may be unable to process requests for the short period during which the context is restarting.
- IDE Constraint: When running in an IDE,
<resourceLocations>settings may be ignored to avoid class loading conflicts. Therefore, detecting JAR file changes is mainly relevant in actual deployment environments. - Recommended Deployment Strategy: An effective strategy is to separate the build/deployment units of the root application and sub-services, and then update by copying the changed sub-service JAR files to the directory specified by
<resourceLocations>.
7. Advanced Topics: Partial Restarts and Comparison with Other Frameworks
a. The Complexity of Restarting Intermediate Services
While Aspectran supports a hierarchical service structure, the auto-reloading mechanism is intentionally centralized to restart the entire root service. This is by design, as independently restarting only an intermediate sub-service carries the following potential risks:
- State Inconsistency: The consistency of shared resources or states between the restarting sub-service and the continuously running parent/sibling services could be broken.
- Broken Dependencies: If other components in the parent context were referencing a bean from the restarting sub-service, they could temporarily enter a state where dependencies cannot be resolved.
- Difficulty in Handling Ongoing Tasks: Safely completing or rolling back transactions or asynchronous tasks that the service was processing at the time of restart is a very complex problem.
Therefore, instead of the complexity of partial restarts, Aspectran has adopted a method that ensures a predictable and stable state by rebuilding the entire context.
b. Comparison with Other Frameworks
Dynamic reloading is a challenge for all frameworks, and they approach it in various ways.
- OSGi: A technical specification for managing the independent lifecycle of modules (bundles), which allows for true ‘partial restarts’. However, it is not widely used due to its high complexity.
- Spring Boot DevTools: Uses an approach very similar to Aspectran’s, quickly restarting the entire application context when a change is detected. This is a ‘fast full restart’ rather than a partial restart, and it is a pragmatic approach adopted by most modern frameworks.
- Microservices Architecture (MSA): Solves the problem at the architectural level rather than the framework level. Each service is separated into an independent process, allowing it to be restarted individually without affecting other services.
- JRebel: Goes beyond simple class hot-swapping. Through framework-aware plugins, it re-creates only the beans associated with the changed class within the container (e.g., Spring
ApplicationContext) and re-injects dependencies. This is a very powerful commercial tool that requires deep integration with the framework.
c. The Trade-off between Performance and Flexibility
If a framework were designed from the ground up to be perfectly dynamically swappable like JRebel, it would likely suffer a runtime performance penalty. This is because dynamic swapping requires layers of indirection like proxies for all component calls, hinders JIT compiler optimizations, and increases the use of reflection.
Most mainstream frameworks, including Aspectran, are optimized under the premise that ‘once built, it rarely changes’. They are designed to perform heavy optimization tasks at startup and then operate on the fastest path at runtime. This is a rational design choice that prioritizes ‘stable runtime performance’ over ‘dynamic flexibility’.
8. Conclusion
Aspectran’s auto-reloading mechanism is a powerful feature that improves development productivity and enables dynamic updates. It notably adopts a centralized ‘full root service restart’ model for safety and predictability, avoiding the complexities of partial reloads. Developers should leverage the benefits of this mechanism while being aware of its characteristics, such as state loss and brief downtime, to apply it appropriately to their scenarios.