Migrate Struts 2 to Spring
Core principle
Migrate one route and every associated JSP, JavaScript caller, and external client as a functional slice. Preserve observable behavior during Struts removal; modernize Spring, Java, and the container afterward.
Comment rule
Write concise comments that explain migration-specific intent a maintainer cannot
reliably infer from the code: legacy compatibility behavior, protocol or
payload quirks, business rules retained for parity, and non-obvious security,
session, validation, or error-handling decisions. For every new or materially
changed controller, endpoint, or public service method, add short Javadoc that
states its purpose and describes every parameter with @param; include
@return when a returned value has a meaningful contract. Do not comment
obvious syntax, restate method names, or add large block comments.
Workflow
- Classify the request as analysis, planning, diagnosis, implementation, or review. Stay read-only unless implementation is requested.
- Inspect build metadata/JARs,
web.xml, Struts/Spring configuration, filters, security, actions, controllers, JSPs, JavaScript, tests, and consumers. - Establish effective framework versions. If duplicate JARs exist, report uncertainty rather than guessing. Use Context7 to resolve and fetch version-current Struts and Spring documentation before applying framework-specific APIs.
- Inventory routes and select one low-risk slice. Capture method, path, parameters, payload, validation, security, session, errors, redirects, uploads, view, and consumers.
- Write characterization and contract tests before migration changes.
- Replace the action with a thin Spring MVC controller delegating to existing services. Use explicit request, response, and view models.
- Rework the slice's JSP and clients to remove Struts tags, ValueStack access, action names, implicit binding, and response assumptions without changing visible behavior.
- When the canonical route changes, retain the legacy route as a Spring mapping or server-side adapter. Do not redirect when doing so can change method, body, authentication, or status semantics.
- Copy
assets/route-mapping.yamlinto the target project and update it in the same change. Keep the legacy mapping while any registered client is pending. - Add the required concise comments and Javadoc while the migrated behavior is fresh; verify that non-obvious parameters are described.
- Run narrow controller/client tests, then relevant regression, security, upload, and end-to-end checks. Remove migrated Struts configuration only after both routes pass and traffic confirms the old route can retire.
- Remove Struts filters, plugins, configuration, tags, and JARs only after every route and client passes the removal gate.
Task modes
| Request | Action |
|---|---|
| Analyze or review | Report route coupling, compatibility risks, and missing clients. List required contract, security, and regression evidence as three separate items; do not edit. |
| Plan | Produce ordered slices, contracts, catalog entries, tests, rollback, and completion gates. |
| Diagnose | Reproduce behavior and identify the Struts/Spring boundary; do not fix unless requested. |
| Implement | Change the smallest complete route-and-client slice and verify it. |
Resources
- Read
references/migration-guidelines.mdbefore planning, implementing, or reviewing a migration. - Copy and adapt
assets/route-mapping.yamlwhen the target project lacks a route catalog.
Non-negotiable safeguards
- Preserve URLs or provide compatibility mappings, payloads, status codes, validation messages, redirects, cookies, locale, security, and session behavior.
- Keep migrated routes Spring-native end to end. Spring controllers, services,
filters, and compatibility handlers must not inject, instantiate, invoke, or
delegate to Struts action classes or action beans. This prohibits patterns
such as
ObjectFactory<*_A>, action-class imports in Spring web code, and action bean ID references such assmgs_a. - Extract behavior needed by a migrated route into Spring services using the existing DAOs, serializers, and domain helpers. Canonical and compatibility routes must use the same Spring-native implementation.
- A shared Struts action class may remain only for unmigrated operations. Once Spring owns a route and no remaining Struts mapping depends on its former action method, remove that method even if the class remains for other operations.
- Apply the pure-Spring ownership rule to new and materially changed migration slices. Treat existing Spring-to-action adapters as technical debt to remove when their route slice is next touched, not as a reason for an unrelated bulk refactor.
- Do not expose persistence entities as HTTP contracts.
- Do not weaken authentication, authorization, CSRF, CORS, validation, assertions, or tests to complete a migration.
- Do not combine route migration with unrelated business redesign.
- Do not retire a legacy route while its catalog contains a pending client.
- Preserve unrelated changes in dirty worktrees.
Handoff
State the migrated route, compatibility route, clients updated, catalog path, tests run, remaining consumers, rollback method, and retirement gate.