The spring context module (org.springframework:spring-context) is the Maven artifact that provides Spring's ApplicationContext, the IoC container almost every Spring application is built on, and declaring it transitively pulls in spring-core, spring-beans, spring-aop, and spring-expression. That last part is the bit people miss. When you add one line to your pom.xml, you are actually adopting five Spring Framework modules, and your vulnerability exposure is the union of all of them. Spring4Shell lived in spring-beans. The SpEL denial-of-service bugs lived in spring-expression. Neither was "in" spring-context, but everyone who declared spring-context shipped both.
What spring-context Actually Contains
If spring-core and spring-beans are the engine (the BeanFactory, type conversion, resource loading), spring-context is the car. It layers the application-level machinery on top:
ApplicationContextand its implementations (AnnotationConfigApplicationContext,ClassPathXmlApplicationContext)- Annotation-driven configuration:
@Configuration,@ComponentScan,@Bean,@Autowiredprocessing - The event system (
ApplicationEventPublisher,@EventListener) Environmentand property source abstraction,@Valueresolution- Scheduling and async support (
@Scheduled,@Async) - Caching abstraction (
@Cacheable), validation hooks, JNDI and EJB support, i18n viaMessageSource
In practice you rarely declare it directly in a Spring Boot project, because every starter that matters (spring-boot-starter, and therefore spring-boot-starter-web, -data-jpa, and the rest) already brings it in. You declare it explicitly when you are using Spring Framework without Boot, writing a library that needs the container, or pinning a version to escape a vulnerable range.
The spring-context Maven Coordinates and Dependency Tree
The plain spring-context maven declaration looks like this:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.2.5</version>
</dependency>
Run mvn dependency:tree afterwards and you will see the fan-out:
org.springframework:spring-context:jar:6.2.5
+- org.springframework:spring-aop:jar:6.2.5
+- org.springframework:spring-beans:jar:6.2.5
+- org.springframework:spring-core:jar:6.2.5
| \- org.springframework:spring-jcl:jar:6.2.5
\- org.springframework:spring-expression:jar:6.2.5
Five artifacts from one declaration. This is why "we don't use SpEL" is not the same as "we don't ship SpEL". The expression module is on your classpath whether you write a single #{...} expression or not, and a scanner that inventories your actual dependency graph will keep flagging it until you upgrade the whole family.
The versions must move together. Mixing spring-context:5.3.x with spring-beans:6.0.x is a runtime lottery, which is why Spring publishes the spring-framework-bom and why Spring Boot's dependency management pins every module for you.
The CVEs That Rode In on spring-context
Spring4Shell: CVE-2022-22965
The big one. CVE-2022-22965 was a remote code execution vulnerability via data binding, exploitable in Spring MVC and WebFlux applications running on JDK 9 or newer. The flaw was in the data-binding machinery in spring-beans, one of the modules spring-context pulls in transitively. It affected Spring Framework 5.3.0 through 5.3.17, 5.2.0 through 5.2.19, and everything older; the fixes shipped in 5.3.18 and 5.2.20 on March 31, 2022 (Spring Boot 2.6.6 and 2.5.12 picked them up). CVSS 9.8, exploited in the wild within days, and famously easy to have on your classpath without knowing it.
The lesson was not really about the bug. It was that thousands of teams discovered they could not answer "which of our services ships spring-beans below 5.3.18?" in under a day. If your inventory lives in an SCA tool that resolves transitive graphs, that question is a filter, not a fire drill.
SpEL Denial of Service: CVE-2023-20863
A year later, CVE-2023-20863 landed in spring-expression. A crafted SpEL expression could drive the parser into excessive recursion and memory exhaustion, a straightforward denial of service. Affected ranges were 6.0.0 to 6.0.7, 5.3.0 to 5.3.26, and 5.2.0.RELEASE to 5.2.23.RELEASE; fixes arrived in 6.0.8, 5.3.27, and 5.2.24. It was found by OSS-Fuzz, which tells you something about how this class of parser bug keeps getting discovered.
DoS bugs get triaged as "meh" too often. If any code path in your app evaluates expressions that contain user-controlled fragments (query DSLs, rule engines, templating glue), a SpEL DoS is an unauthenticated way to take your service down.
The pattern
Neither CVE was in the spring-context artifact itself, but both were in artifacts nobody added on purpose. Your remediation unit is the Spring Framework version, not the individual jar. Bump the framework, and all five modules move.
Version Support: Where the Cliff Is
Spring Framework 5.3.x and 6.0.x both reached end of open-source support on August 31, 2024. Commercial support from Broadcom/Tanzu continues for 5.3 for a few more years, but if you are on the free tier, a CVE in spring context 5.3.x after that date gets you no patched release. The 6.1.x and 6.2.x lines are where open-source fixes land now, and 6.x requires Java 17 and the jakarta.* namespace, which is why so many 5.3 stragglers exist: the upgrade is a migration, not a version bump.
That makes the EOL date itself a finding. A clean CVE scan against spring-context:5.3.39 today tells you there are no known issues; it says nothing about the next one, which will be fixed only in branches you are not on.
How to Keep spring-context Patched Without Thinking About It
The teams that handled Spring4Shell in hours rather than weeks all did some version of the following:
- Manage the version in exactly one place. Spring Boot users: inherit
spring-boot-starter-parentor importspring-boot-dependencies, and upgrade Boot patch releases promptly. Non-Boot users: importspring-framework-bomindependencyManagementso every module is pinned identically. - Ban direct version overrides in leaf modules. A stray
<version>5.3.9</version>in one service's pom is how you end up with a mixed tree that your scanner reports and your build quietly tolerates. - Scan the resolved graph, not the pom.
mvn dependency:tree -Dincludes=org.springframeworkshows what you actually ship. An SCA tool such as Safeguard resolves the same graph continuously and flags when a transitive module likespring-expressionfalls into a vulnerable range, even though it appears nowhere in your build files. - Rehearse the framework bump. Keep a CI job that builds against the latest Spring patch release. When the next CVE drops, the question "does 6.2.x break us?" is already answered.
If you are comparing tooling for this, the meaningful difference is transitive resolution quality and how fast advisories show up, which is worth testing head-to-head against your own dependency tree rather than trusting datasheets.
Should You Ever Exclude or Trim spring-context?
Occasionally someone tries to exclude spring-expression or spring-aop to shrink the attack surface. It almost never survives contact with reality: @Value resolution, @Cacheable, transaction proxies, and security annotations all lean on those modules, and the failure mode is a ClassNotFoundException at runtime in a code path your tests didn't cover. The supported way to reduce exposure is to stay current, not to amputate modules.
The one trimming exercise that does pay off is checking whether you need the container at all in small utilities. A CLI tool that uses Spring purely for @Autowired convenience is carrying five jars and their CVE history for what a constructor call would do.
FAQ
What is the difference between spring-core and spring-context?
spring-core provides low-level utilities and the fundamental container SPI; spring-context builds the full ApplicationContext on top of it, adding annotation config, events, scheduling, caching, and property resolution. Declaring spring-context gives you spring-core transitively, so applications normally declare only spring-context.
Do I need to declare spring-context in a Spring Boot project?
No. Every Spring Boot starter brings it in through spring-boot-starter. Declare it directly only in non-Boot projects or libraries, and even then prefer importing the spring-framework-bom so all modules stay on one version.
Was Spring4Shell a vulnerability in spring-context?
Not literally. CVE-2022-22965 was in the data-binding code in spring-beans, but spring-context depends on spring-beans, so every application declaring spring-context on an affected version (5.3.0-5.3.17, 5.2.0-5.2.19) shipped the vulnerable class. Exploitability required Spring MVC or WebFlux on JDK 9+.
Which spring-context versions still get security fixes?
Open-source fixes land in Spring Framework 6.1.x and 6.2.x. The 5.3.x and 6.0.x lines ended open-source support on August 31, 2024 and now receive fixes only under commercial support. If you are on 5.3.x without a support contract, plan the Java 17 / Jakarta migration rather than waiting for a forcing CVE.