Spring作为Java开发的核心框架,其知识体系涵盖概念、原理、源码及实战等多个层面。本文从基础概念切入,逐步拆解底层工作机制与源码级启动流程,最终通过极简实战落地验证,为学习者构建清晰的知识脉络,助力快速掌握Spring核心内容。
Spring是一款开源的轻量级Java企业级框架,2003年诞生至今,已从单一框架发展为涵盖核心容器、Web开发、数据访问、事务管理的完整生态。它并非替代JDBC、Servlet等基础技术,而是通过封装与整合,解决传统Java开发中代码耦合度高、配置繁琐、可维护性差等问题,帮助开发者聚焦业务逻辑而非底层依赖管理。
Spring的核心优势体现在“非侵入式”与“松耦合”两大特性:无需修改业务代码即可通过框架特性增强功能,同时降低组件间依赖关系,为系统扩展、测试提供便捷。
传统开发模式中,对象的创建与依赖管理由开发者手动控制,例如在UserService中通过new关键字创建UserDao实例。这种方式会导致组件间强耦合,当依赖类发生变更时,依赖其的类也需同步修改。
IOC(控制反转)机制将对象的创建权、依赖装配权移交至Spring容器。开发者仅需定义对象,容器会统一管理对象的生命周期(创建、初始化、销毁)与依赖关系,彻底摆脱手动创建对象的繁琐,实现组件解耦。
DI(依赖注入)是IOC的具体实现形式,指容器在创建对象时,自动将其依赖的其他对象注入其中,无需开发者手动赋值。结合实际开发场景,常用的注入方式有三种,各有适用场景:
构造器注入:通过构造方法传入依赖,可强制要求依赖必须存在,避免空指针异常,是开发中优先推荐的方式。
Setter注入:通过Setter方法注入,适用于可选依赖场景(部分场景下依赖可允许为null)。
字段注入:直接通过@Autowired注解标注字段完成注入,代码简洁但可读性较差,且无法强制依赖,不建议在核心业务中使用。
AOP(面向切面编程)是对面向对象编程(OOP)的补充,主要解决日志记录、事务管理、权限控制等“横切逻辑”的代码冗余问题。若在每个业务方法中重复编写横切逻辑,不仅会增加代码量,还会导致后续维护成本升高。
AOP通过动态代理机制,在不修改业务代码的前提下,将横切逻辑植入目标方法的执行流程(如方法执行前、后、抛出异常时)。核心术语包括:切面(封装横切逻辑的类)、通知(切面中的具体逻辑,如前置通知、后置通知)、连接点(目标方法的执行节点)、切入点(筛选需植入切面的连接点)。
Spring IOC容器本质是一个“对象工厂”,负责管理所有被称为“Bean”的对象,核心接口包括BeanFactory与ApplicationContext。
BeanFactory是最基础的容器实现,采用延迟初始化策略(仅在获取Bean时才创建实例);ApplicationContext继承自BeanFactory,扩展了即时初始化、国际化、事件发布等功能,是实际开发中应用更广泛的容器类型,注解驱动开发中常用的AnnotationConfigApplicationContext即属于此类。
IOC容器的核心工作流程分为三步,是理解后续启动流程的基础:
资源加载与解析:容器读取配置信息(注解、XML、Java配置类),将Bean的类名、依赖关系、生命周期回调等信息解析为BeanDefinition对象,作为Bean实例化的元数据依据。
Bean实例化与依赖注入:容器根据BeanDefinition,通过反射机制创建Bean实例,再按照配置的注入方式完成依赖Bean的自动装配。
Bean生命周期管理:容器对Bean执行初始化操作(如@PostConstruct注解方法、自定义init-method),之后将Bean存入缓存供使用;容器关闭时,执行Bean的销毁逻辑(如@PreDestroy注解方法、自定义destroy-method)。
AOP的底层基于动态代理实现,Spring会根据目标类是否实现接口,自动选择对应的代理方式:
JDK动态代理:当目标类实现接口时,通过JDK自带的java.lang.reflect.Proxy类创建代理对象,代理对象实现目标接口,进而对目标方法进行增强。
CGLIB动态代理:当目标类无接口时,通过CGLIB字节码生成工具,动态生成目标类的子类作为代理对象,通过重写目标方法实现增强。
AOP的实际执行流程为:调用目标方法时,请求先被代理对象拦截,执行切面中的通知逻辑,再执行目标方法,最后返回结果(或执行后置通知),实现横切逻辑与业务逻辑的解耦。
Spring事务管理基于AOP实现,核心是PlatformTransactionManager事务管理器,支持JDBC、MyBatis、Hibernate等多种持久层框架。开发中仅需为方法添加@Transactional注解,即可快速实现事务控制,无需手动编写事务开启、提交、回滚代码。
其底层逻辑为:AOP生成代理对象拦截目标方法,方法执行无异常时自动提交事务,出现异常时自动回滚事务,大幅简化事务管理的代码复杂度。
Spring容器的初始化与Bean加载流程,核心依赖于AbstractApplicationContext的refresh()方法。该方法是Spring容器启动的“灵魂”,封装了容器从初始化到就绪的全流程逻辑。以下结合注解驱动开发场景,拆解完整启动链路。
Spring容器不会自动启动,需开发者手动创建ApplicationContext实例,加载配置并触发初始化。注解驱动开发中,常用AnnotationConfigApplicationContext作为容器实现,核心启动代码如下:
package com.apierr.springtest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringDemo {
public static void main(String[] args) {
// 加载配置类SpringConfig,同时扫描配置类所在包及子包下的注解Bean
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
// 也可以不依赖配置类,直接指定扫描包路径(适合简单项目)
// AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// context.scan("com.apierr.springtest"); // 扫描指定包
// context.refresh(); // 手动触发刷新,扫描包后必须调用
// ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-bean.xml");
// 从容器获取Bean并使用
UserService userService = context.getBean(UserService.class);
userService.printUserInfo();
// 关闭容器,释放资源
context.close();
}
}
需注意:通过构造器传入配置类时,容器会自动调用refresh()方法;若手动调用scan()方法扫描包,则需显式调用refresh(),否则Bean无法被加载。
refresh()方法定义于AbstractApplicationContext抽象类,是Spring容器初始化的通用规范,适用于注解驱动、XML驱动等多种场景。该方法包含12个关键步骤,涵盖容器初始化的核心逻辑,源码及解析如下:
// AbstractApplicationContext核心方法,容器初始化的核心逻辑
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 刷新前准备:初始化上下文环境、记录启动时间、校验必备属性
prepareRefresh();
// 2. 获取刷新后的BeanFactory(核心:加载BeanDefinition)
// 注解场景下解析@Configuration、@Bean;XML场景下解析XML配置的Bean
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. BeanFactory准备:配置类加载器、注册默认后置处理器(如处理@Autowired的处理器)
prepareBeanFactory(beanFactory);
try {
// 4. BeanFactory后置处理(扩展点:自定义修改BeanFactory配置)
// 可通过实现BeanFactoryPostProcessor接口自定义扩展
postProcessBeanFactory(beanFactory);
// 5. 执行BeanFactory后置处理器(关键:解析配置、扫描Bean)
// 核心执行ConfigurationClassPostProcessor,完成@Configuration解析、@ComponentScan扫描
// 把注解类、@Bean方法都解析成BeanDefinition注册到容器
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册Bean后置处理器(AOP、依赖注入的核心支撑)
// 注册处理@Autowired的AutowiredAnnotationBeanPostProcessor、生成AOP代理的处理器等
registerBeanPostProcessors(beanFactory);
// 7. 初始化消息源(国际化支持,按需使用)
initMessageSource();
// 8. 初始化应用事件多播器(管理事件与监听器,支持事件驱动)
initApplicationEventMulticaster();
// 9. 子类扩展:初始化特殊Bean(非Web场景为空实现,Web场景初始化Servlet相关Bean)
onRefresh();
// 10. 注册应用监听器:将监听器与事件多播器关联
registerListeners();
// 11. 核心:Bean实例化与依赖注入(触发所有非延迟加载Bean的创建)
// 按依赖顺序实例化Bean、注入依赖、执行初始化方法、AOP织入
finishBeanFactoryInitialization(beanFactory);
// 12. 刷新完成:发布容器就绪事件、启动生命周期处理器
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("初始化上下文时出现异常,取消刷新:" + ex);
}
// 异常处理:销毁已创建的Bean,保证容器状态一致
destroyBeans();
// 重置刷新状态
cancelRefresh(ex);
throw ex;
} finally {
// 清除缓存的类加载器,避免内存泄漏
resetCommonCaches();
}
}
}
结合源码,以下聚焦3个对Bean加载、注入至关重要的步骤,明确其底层逻辑与作用:
注解驱动场景下,该方法通过AnnotatedBeanDefinitionReader与ClassPathBeanDefinitionScanner完成BeanDefinition加载:先解析传入的配置类,将其注册为BeanDefinition;再触发@ComponentScan注解的扫描逻辑,将带有@Component、@Service、@Controller、@Repository的类解析为BeanDefinition,最终统一注册至BeanFactory。
BeanDefinition作为Bean的元数据载体,记录了Bean的所有关键信息,是后续Bean实例化的核心依据。
该步骤通过执行Spring内置的ConfigurationClassPostProcessor(BeanFactoryPostProcessor接口实现类),深度解析配置类:处理@Configuration注解(对@Bean方法进行CGLIB代理,保证单例特性)、@Import注解(导入指定类作为Bean)、@PropertySource注解(加载配置文件属性),最终补全所有BeanDefinition,为Bean实例化做准备。
该步骤是Bean从定义到可用的核心环节,容器遍历所有BeanDefinition,按依赖顺序完成Bean的全生命周期创建:
实例化:通过反射调用Bean的无参构造器(或指定构造器)创建实例。
依赖注入:通过AutowiredAnnotationBeanPostProcessor处理@Autowired、@Qualifier注解,自动注入依赖Bean,循环依赖问题通过三级缓存机制解决。
初始化:依次执行@PostConstruct注解方法、InitializingBean接口的afterPropertiesSet()方法、自定义init-method。
AOP织入:若Bean被@Aspect切面拦截,生成动态代理对象替换原Bean实例,存入容器供使用。
Spring注解驱动启动的完整链路可概括为:手动创建AnnotationConfigApplicationContext → 加载配置类/扫描包 → 触发refresh()方法 → 解析并注册BeanDefinition → 执行各类后置处理器 → Bean实例化、依赖注入、初始化与AOP织入 → 容器就绪。
refresh()方法作为核心,通过同步锁保证线程安全;若启动过程中出现异常,容器会销毁已创建的Bean,避免状态不一致,体现了严谨的设计逻辑。
通过Maven构建项目,仅引入spring-context依赖即可支撑Bean管理与依赖注入功能,pom.xml配置如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- 可选:Junit测试用 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
实际开发中,Bean的定义方式有三种,分别适配不同场景,具体实现如下:
通过@Component及其衍生注解标记类,容器扫描后自动注册为Bean,语义清晰且配置简洁,是目前主流的定义方式:
package com.apierr.springtest;
import org.springframework.stereotype.Component;
@Component
public class User {
private String name = "张三";
private Integer age = 25;
// 必须提供无参构造器,Spring默认通过无参构造实例化Bean
public User() {}
// Getter/Setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
对于无法添加@Component注解的第三方工具类,可通过JavaConfig方式定义Bean:以@Configuration标记配置类,@Bean注解标记方法,方法返回值将被注册为Bean。
package com.apierr.springtest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean(name = "user")
public User createUser() {
User user = new User();
user.setName("李四");
user.setAge(30);
return user;
}
@Bean
public UserService createUserService(User user){
UserService userService = new UserService();
userService.setUser(user);
return userService;
}
}
XML式定义是早期Spring开发的主流方式,目前已逐步被注解替代,但在部分老项目中仍有应用:
在resources目录下创建spring-bean.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为Bean名称,class为全类名 -->
<bean id="user" class="com.apierr.springtest.User">
<!-- 通过property标签赋值,依赖Setter方法 -->
<property name="name" value="王五"/>
<property name="age" value="35"/>
</bean>
<bean id="userService" class="com.apierr.springtest.UserService">
<!-- property对应setUser方法,ref引用User的Bean id -->
<property name="user" ref="user"/>
</bean>
</beans>
以UserService依赖User为例,实现四种常用注入方式,可根据依赖是否必选、代码可读性要求灵活选择:
强制依赖必须存在,避免空指针异常,且代码可读性强,适配核心业务场景:
package com.apierr.springtest;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private User user;
public UserService(){
}
public UserService(User user) {
this.user = user;
}
// public void setUser(User user){
// this.user = user;
// }
public void printUserInfo() {
System.out.println("用户信息:" + user);
}
}
适用于可选依赖场景,需通过@Autowired注解标记Setter方法,触发容器注入:
package com.apierr.springtest;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private User user;
public UserService(){
}
// public UserService(User user) {
// this.user = user;
// }
public void setUser(User user){
this.user = user;
}
public void printUserInfo() {
System.out.println("用户信息:" + user);
}
}
代码简洁但可读性差,无法强制依赖,仅适用于非核心业务或简单工具类:
package com.apierr.springtest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private User user;
// public UserService(){
//
// }
// public UserService(User user) {
// this.user = user;
// }
// public void setUser(User user){
// this.user = user;
// }
public void printUserInfo() {
System.out.println("用户信息:" + user);
}
}
当容器中存在多个同类型Bean时,通过@Qualifier注解指定Bean名称,实现精准注入:
package com.apierr.springtest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
@Qualifier("user")
private User user;
// public UserService(){
//
// }
// public UserService(User user) {
// this.user = user;
// }
// public void setUser(User user){
// this.user = user;
// }
public void printUserInfo() {
System.out.println("用户信息:" + user);
}
}