诞生与定位
- 背景:早期 Java EE(特别是 EJB)强调"重量级容器",开发往往要写大量接口、配置与样板代码。对象之间的依赖通常在代码里
new出来,导致耦合高、替换难、测试更难(单测时很难把真实依赖换成 Mock)。当业务逐渐复杂,"改一处牵一片"会成为团队效率的瓶颈。 - 目标:Spring 的核心目标不是"再造一个大而全的容器",而是提供一套轻量、可组合的基础设施:用 IOC 容器 管理对象的创建与装配;用 AOP 把事务、日志、监控、安全等横切逻辑抽离;让代码更关注业务表达,而不是被框架细节牵着走。
- 核心理念:Spring 的两大基石是 IOC/DI 和 AOP。IOC/DI 让依赖从"代码里写死"变成"由容器统一装配";AOP 让非业务逻辑以"可插拔"的方式织入。再加上模块化设计(按需引入依赖),就能在不同规模的项目中灵活落地。
核心模块速览
- Core / Beans / Context:这是 Spring 的"地基"。
Beans负责 Bean 的定义与装配(比如解析 XML/注解生成 BeanDefinition);Context在此基础上提供更高级的容器能力(资源加载、国际化、事件机制等)。日常我们用到的ApplicationContext就在这里。 - AOP:Spring AOP 通过代理机制在运行时"包一层"业务对象,从而在方法调用前后执行额外逻辑。最典型的例子是事务:你写的是业务方法,事务开始/提交/回滚由框架统一处理,减少重复代码并避免遗漏。
- Data:当应用需要访问数据库,Spring 提供了 JDBC 的模板化封装(减少 try/catch/close 的模板代码),也能很好地集成 JPA/Hibernate/MyBatis 等。更关键的是它提供了统一的事务抽象,让你不用绑定某一种数据访问实现。
- Web:Spring MVC 是经典的 Web MVC 框架(Controller-View-Model);WebFlux 面向响应式编程模型。实际项目中很多人会配合 Spring Boot 来简化依赖与配置,从"能跑起来"到"工程化可维护"会更顺滑。
- 其他:Security 用于认证鉴权与安全防护;Batch 用于批处理;Integration 用于系统集成;Cloud 用于微服务治理(配置中心、服务发现、熔断限流等)。你可以把 Spring 生态理解成"工具箱",而不是必须全用的"全家桶"。
Spring 的优势
- 轻量与模块化:Spring 不要求你必须运行在特定"应用服务器"里,很多场景一个普通的 Java 程序就能启动容器。模块化意味着你可以只引入
spring-context来用 IOC,也可以再加上 web/data/security 等模块逐步增强。 - 解耦与可测试:当依赖由容器注入后,类之间的关系变得清晰:一个类需要什么依赖,构造器参数就是"需求清单"。测试时也更容易:把
UserRepository换成内存实现或 Mock 即可,不需要改业务代码。 - 横切能力:日志、事务、鉴权、限流、性能统计等往往贯穿很多业务方法。如果每个方法都手写一遍,既重复又容易漏。AOP 把这些能力统一收口,便于团队建立一致规范。
- 成熟生态:Spring 能与 MyBatis/JPA、Redis、Kafka、RabbitMQ、ElasticSearch 等无缝集成,减少"胶水代码"。这意味着系统演进时可以更快引入新能力。
- 学习与社区:遇到问题能快速找到资料与解决方案。对于团队协作来说,选主流框架也降低了人员流动与交接成本。
Spring 在企业中的角色
- 已成为事实标准:在绝大多数 Java 企业项目里,"Spring(尤其是 Spring Boot)"几乎是默认选择。原因很现实:稳定、生态齐、招聘容易、社区成熟,能快速落地并长期维护。
- 与云原生:Spring Boot 强调"约定优于配置",把项目脚手架、依赖管理、自动配置做到极简;Spring Cloud 进一步把微服务治理能力组件化。配合健康检查、指标、链路追踪等可观测性能力,更适合部署到容器平台与云环境。
适合谁
- 想摆脱手写工厂和硬编码依赖的 Java 开发者:如果你经常看到
new A(new B(new C()))这种"套娃式"创建对象,Spring 的 IOC 会显著改善代码结构。 - 希望在日志、事务、安全、监控上少写重复代码的团队:把通用能力框架化/平台化,业务代码更干净,团队协作成本更低。
- 构建微服务、批处理、集成系统的企业项目:这些场景都要求长期可维护与可演进,Spring 的模块化与生态能提供持续支撑。
创建第一个 Hello World
环境准备
- JDK 8+,Maven 或 Gradle,IDE(IntelliJ/Eclipse)。
这一节的目标是把"能运行 Spring 容器"所需的最小条件准备好。Spring 本质上是普通 Java 库,并不强制你使用某种应用服务器;只要能正确引入依赖、让配置文件在类路径中可被加载,你就能跑起来一个最小的 IOC 容器。
建议新手先用 Maven/Gradle 管理依赖,避免手动拷 jar 造成版本冲突。IDEA 用户如果出现依赖无法识别,通常是 Maven 未刷新或仓库下载失败。
- 依赖(Maven):
xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.30</version>
</dependency>
示例一:XML 配置版
- 业务类
java
package demo;
public class HelloService {
public String sayHello() { return "Hello, Spring!"; }
}
这个 HelloService 没有任何 Spring 注解,说明一件事:Spring 管理的是"普通 Java 对象(POJO)"。你不需要为了使用容器而修改业务类结构,这也是 Spring 轻量化的重要体现。
applicationContext.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloService" class="demo.HelloService"/>
</beans>
在 XML 里,id 是 Bean 的名字(也可认为是容器中的"键"),class 指向要创建的类。容器启动后,会根据这些定义创建对象并放到容器中;后续你不再 new HelloService(),而是向容器"要"一个 HelloService。
- 启动
java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloService hello = ctx.getBean("helloService", HelloService.class);
System.out.println(hello.sayHello());
}
}
ClassPathXmlApplicationContext("applicationContext.xml") 表示从类路径 加载配置文件。常见坑是把 XML 放错位置导致找不到文件:一般把它放在 resources(Maven 标准)或确保它最终会被打进 classpath。
getBean 有两种常见用法:按名称拿("helloService"),或按类型拿(HelloService.class)。新手阶段建议显式写类型,能更早发现类型不匹配的问题。
示例二:纯注解(Java Config)版
java
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan("demo")
public class AppConfig {}
package demo;
import org.springframework.stereotype.Component;
@Component
public class HelloService {
public String sayHello() { return "Hello, Spring (Java Config)!"; }
}
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class App {
public static void main(String[] args) {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class)) {
HelloService hello = ctx.getBean(HelloService.class);
System.out.println(hello.sayHello());
}
}
}
这一版更贴近现代 Spring 写法:
@Configuration表示这是配置类,相当于"用 Java 写配置"。@ComponentScan("demo")告诉容器去扫描demo包,把标了@Component(以及@Service/@Repository/@Controller)的类注册为 Bean。AnnotationConfigApplicationContext表示以注解/配置类的方式启动容器。
对比 XML:注解版更少配置文件、更类型安全;XML 更显式、集中管理,适合老项目或需要强外置化配置的场景。两者并不冲突,很多项目会混用(例如:核心用注解,某些第三方 Bean 用 XML)。
常见问题
- 依赖未拉取:检查网络/仓库配置。IDEA 中可以尝试 "Reload Maven Project";也要确认公司代理或镜像仓库是否可用。
- Bean 找不到:注解版优先检查
@ComponentScan的包路径是否包含目标类;XML 版检查applicationContext.xml是否在 classpath、id是否拼写一致。 - 包名/路径不一致:
class="demo.HelloService"必须与package demo;一致;如果移动了类但忘记改 XML,会出现ClassNotFoundException。 - 版本冲突:如果你手动拷 jar 或引入多个 Spring 版本,可能出现奇怪的
NoSuchMethodError;建议统一依赖版本、让构建工具管理传递依赖。
小结
Spring 通过 IOC/DI 与 AOP 把"对象管理"和"横切能力"两件事做成基础设施,配合丰富生态与云原生支持,已经成为 Java 企业开发的主流标准。后续系列将从 Hello World 入手,逐步深入 IOC 原理、Bean 生命周期与依赖注入的最佳实践。 通过 XML 或纯注解配置,Spring 容器都能创建并管理 Bean。本篇最重要的收获是:对象不再由你手写 new 来创建,而是由容器统一创建与装配。下一篇我们会深入 IOC:BeanDefinition 是怎么来的?Bean 在容器里经历了哪些生命周期阶段?为什么很多扩展点都围绕"后置处理器"?