Spring 是一个开源的 Java 平台框架,最初由 Rod Johnson 在 2003 年提出,旨在简化企业级 Java 应用的开发。它通过提供全面的基础设施支持,帮助开发者专注于业务逻辑,而无需处理底层的复杂性。
1. 控制反转(IoC - Inversion of Control)
IoC的核心概念
(1) 传统开发模式(主动控制)
在传统代码中,对象的创建和依赖管理由开发者手动完成:
public class UserService {
private UserRepository userRepo = new UserRepository(); // 主动创建依赖对象
}
问题:
- 代码耦合度高(
UserService
直接依赖UserRepository
的具体实现)。 - 难以替换依赖(例如切换为
MockUserRepository
进行测试)。
(2) IoC 模式(被动接受依赖)
通过容器管理对象,开发者只需声明依赖,由容器负责注入:
public class UserService {
private UserRepository userRepo; // 不主动创建,依赖由容器注入
// 通过构造器接受依赖(推荐方式)
public UserService(UserRepository userRepo) {
this.userRepo = userRepo;
}
}
优势:
- 解耦:对象不关心依赖的具体实现。
- 可测试性:轻松注入 Mock 对象。
- 灵活性:配置变更无需修改代码(如切换数据库实现)。
2. 依赖注入(DI - Dependency Injection)
IoC 的具体实现方式,通过容器自动装配对象之间的依赖关系。Spring 通过 IoC 容器(如 ApplicationContext
)管理对象的生命周期,具体实现依赖以下技术:
什么是依赖?
- 在面向对象编程中,如果一个类(如
UserService
)需要使用另一个类(如UserRepository
),则称UserService
依赖UserRepository
。 - 传统方式:直接在代码中
new
依赖对象,导致紧耦合。public class UserService { private UserRepository userRepo = new UserRepository(); // 硬编码依赖 }
依赖注入的作用
- 解耦:将依赖关系的创建和绑定从代码中移除,交由容器管理。
- 灵活性:可以轻松替换依赖的实现(如测试时注入 Mock 对象)。
- 可维护性:依赖关系清晰,便于扩展和修改。
DI 的底层实现原理
Spring 容器(如 ApplicationContext
)在启动时,会通过以下步骤完成依赖注入:
- 扫描组件:通过
@ComponentScan
或 XML 配置找到所有需要管理的 Bean(如@Service
、@Repository
)。 - 创建 Bean 实例:调用构造器或无参构造方法实例化 Bean。
- 解析依赖:检查 Bean 的依赖(如
@Autowired
标注的字段或方法)。 - 注入依赖:从容器中查找匹配的 Bean 并注入。
- 初始化:调用
@PostConstruct
方法(如果有)。
示例流程
@Service
public class OrderService {
private final PaymentService paymentService;
@Autowired
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService; // 依赖注入
}
}
@Repository
public class PaymentService { ... }
容器的工作流程:
- 发现
OrderService
和PaymentService
都是 Spring Bean。 - 先创建
PaymentService
(无依赖)。 - 创建
OrderService
时,发现需要PaymentService
,于是从容器中获取并注入。
怎么依赖注入
(1) 依赖注入(DI - Dependency Injection)
IoC 的核心实现手段,常见注入方式:
注入方式 | 示例代码 | 适用场景 |
---|---|---|
构造器注入 |
| 推荐,强制依赖,不可变 |
Setter 注入 |
| 可选依赖,灵活性高 |
字段注入 |
| 简单但不推荐(难测试、隐藏依赖) |
(2) 配置方式
Spring 支持多种配置依赖关系的方式:
- XML 配置(传统方式):
<bean id="userService" class="com.example.UserService"> <constructor-arg ref="userRepository"/> <!-- 构造器注入 --> </bean>
- 注解配置(现代主流):
@Service public class UserService { @Autowired private UserRepository userRepo; }
- Java Config(显式配置):
@Configuration public class AppConfig { @Bean public UserRepository userRepo() { return new UserRepository(); } }
Spring 依赖注入(DI)相关注解详解
Spring 提供了一系列注解来简化依赖注入(DI)的配置,以下是核心注解的分类和用法说明:
重要关注标红的注解
1. 核心 DI 注解
(1) @Autowired
作用:自动注入依赖对象(默认按类型匹配)。
适用场景:构造器、Setter 方法、字段。
示例:
@Service
public class UserService {
@Autowired // 字段注入(不推荐)
private UserRepository userRepo;
@Autowired // Setter 注入
public void setUserRepo(UserRepository userRepo) {
this.userRepo = userRepo;
}
// 构造器注入(推荐,Spring 4.3+ 可省略 @Autowired)
@Autowired
public UserService(UserRepository userRepo) {
this.userRepo = userRepo;
}
}
注意:
- 如果存在多个同类型 Bean,需配合
@Qualifier
指定名称。 - 字段注入虽然简单常用,但难测试、违反封装性。
(2) @Qualifier
作用:当同一类型有多个 Bean 时,按名称指定注入目标。
示例:
@Repository("mysqlUserRepo")
public class MySQLUserRepository implements UserRepository { ... }
@Repository("mongoUserRepo")
public class MongoUserRepository implements UserRepository { ... }
@Service
public class UserService {
@Autowired
@Qualifier("mysqlUserRepo") // 明确指定注入的 Bean
private UserRepository userRepo;
}
(3) @Primary
作用:标记某个 Bean 为同类型的默认注入选项。
示例:
@Configuration
public class AppConfig {
@Bean
@Primary // 优先注入此 Bean
public UserRepository mysqlUserRepo() {
return new MySQLUserRepository();
}
@Bean
public UserRepository mongoUserRepo() {
return new MongoUserRepository();
}
}
2. 组件扫描与注册注解
(1) @Component
作用:通用注解,标记一个类为 Spring 管理的组件(Bean)。
衍生注解(功能相同,语义化分类):
@Service
:业务逻辑层@Repository
:数据访问层(在 MyBatis 项目中,优先用@Mapper
)@Controller
:Web 控制层@Configuration
:配置类
示例:
@Service
public class UserService { ... }
@Repository
public class UserRepository { ... }
(2) @Bean
作用:在配置类中显式定义 Bean(适用于第三方库的类或需要自定义初始化的 Bean)。
示例:
@Configuration
public class AppConfig {
@Bean // 方法返回的对象由 Spring 管理
public DataSource dataSource() {
return new HikariDataSource();
}
}
(3) @ComponentScan
作用:指定 Spring 扫描哪些包下的组件(@Component
、@Service
等),放在启动类。
示例:
@SpringBootApplication
@ComponentScan(basePackages = "com.example")
public class MyApp { ... }
3. 条件化注入注解
(1) @Conditional
作用:根据条件决定是否注册 Bean(需实现 Condition
接口)。
示例:
@Bean
@Conditional(OnProductionEnvCondition.class)
public DataSource prodDataSource() {
return new ProductionDataSource();
}
(2) @Profile
作用:根据环境配置文件(如 dev
、prod
)激活 Bean。
示例:
@Configuration
@Profile("dev") // 仅在 dev 环境下生效
public class DevConfig {
@Bean
public DataSource devDataSource() {
return new EmbeddedDatabaseBuilder().build();
}
}
4. 其他实用注解
(1) @Lazy
作用:延迟初始化 Bean(首次使用时创建)。
示例:
@Service
@Lazy
public class HeavyService { ... } // 启动时不初始化
(2) @Scope
作用:定义 Bean 的作用域(如单例、原型、请求、会话等)。
示例:
@Service
@Scope("prototype") // 每次请求创建新实例
public class PrototypeService { ... }
(3) @Value
作用:注入外部配置(如 application.properties
)的值。
示例:
@Service
public class MyService {
@Value("${app.timeout:5000}") // 默认值 5000
private int timeout;
}
3. 面向切面编程(AOP - Aspect-Oriented Programming)
面向切面编程(AOP,Aspect-Oriented Programming)是 Spring 框架的核心特性之一,用于将横切关注点(Cross-Cutting Concerns)模块化,避免代码重复,提高可维护性。
1. AOP 的核心概念
(1) 什么是横切关注点?
横切关注点是指那些分散在多个模块中,但与核心业务逻辑无关的功能,例如:
- 日志记录(Logging)
- 事务管理(
@Transactional
) - 权限校验(Security)
- 性能监控(Performance Monitoring)
- 异常处理(Exception Handling)
传统方式的问题:
这些代码会重复出现在多个地方,导致:
- 代码冗余:相同的逻辑在多个方法中重复。
- 维护困难:修改日志或事务逻辑时,需要修改所有相关方法。
(2) AOP 的解决方案
AOP 通过动态代理技术,在不修改原有代码的情况下,横向插入额外逻辑(增强)。
核心思想:
- 分离核心业务逻辑和横切逻辑。
- 在运行时动态织入(Weaving)增强代码。
2. AOP 的核心术语
术语 | 说明 | 示例 |
---|---|---|
切面(Aspect) | 封装横切逻辑的模块(如日志、事务)。 | @Aspect 标注的类。 |
连接点(Join Point) | 程序执行过程中的某个点(如方法调用、异常抛出)。 | UserService.addUser() 方法执行时。 |
通知(Advice) | 切面在连接点执行的具体逻辑(如前置、后置、环绕等)。 | @Before 、@After 、@Around 注解的方法。 |
切入点(Pointcut) | 定义哪些连接点会被切面拦截(通过表达式匹配)。 | @Pointcut("execution(* com.example.service.*.*(..))") |
目标对象(Target) | 被代理的原始对象(如 UserService )。 | 未增强的 UserServiceImpl 实例。 |
织入(Weaving) | 将切面应用到目标对象,生成代理对象的过程。 | Spring AOP 使用动态代理(JDK 或 CGLIB)实现。 |
3. Spring AOP 的通知类型
Spring 支持 5 种通知(Advice),通过注解实现:
通知类型 | 执行时机 | 注解 | 示例 |
---|---|---|---|
前置通知 | 目标方法执行前 | @Before | @Before("pointcut()") |
后置通知 | 目标方法执行后(无论是否异常) | @After | @After("execution(* com.example.service.*.*(..))") |
返回通知 | 目标方法正常返回后 | @AfterReturning | @AfterReturning(pointcut="pointcut()", returning="result") |
异常通知 | 目标方法抛出异常后 | @AfterThrowing | @AfterThrowing(pointcut="pointcut()", throwing="ex") |
环绕通知 | 控制目标方法执行(最强大) | @Around | @Around("pointcut()") |
4. 代码示例
(1) 定义切面(Aspect)
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect // 声明这是一个切面
@Component // 交给 Spring 管理
public class LoggingAspect {
// 定义切入点(拦截 service 包下的所有方法)
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 前置通知:在方法执行前打印日志
@Before("serviceMethods()")
public void logBefore(JoinPoint jp) {
System.out.println("调用方法: " + jp.getSignature().getName());
}
// 环绕通知:计算方法执行耗时
@Around("serviceMethods()")
public Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed(); // 执行目标方法
long duration = System.currentTimeMillis() - start;
System.out.println("方法执行耗时: " + duration + "ms");
return result;
}
}
(2) 目标服务类
@Service
public class UserService {
public void addUser(String username) {
System.out.println("添加用户: " + username);
}
}
(3) 测试调用
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(MyApp.class, args);
UserService userService = ctx.getBean(UserService.class);
userService.addUser("Alice");
}
}
输出结果:
调用方法: addUser
添加用户: Alice
方法执行耗时: 2ms
5. AOP 的实现原理
Spring AOP 通过动态代理实现,分为两种方式:
- JDK 动态代理(默认)
- 要求目标类必须实现接口(基于
InvocationHandler
)。
- 要求目标类必须实现接口(基于
- CGLIB 代理
- 通过字节码增强,代理无接口的类(需添加
spring-core
依赖)。
- 通过字节码增强,代理无接口的类(需添加
代理对象生成流程:
- Spring 容器启动时,扫描
@Aspect
注解的类。 - 根据切入点表达式匹配目标 Bean。
- 为目标 Bean 创建代理对象(JDK 或 CGLIB)。
- 调用代理对象时,按通知类型执行增强逻辑。
4. 声明式事务管理
基于AOP(面向切面编程) 技术通过注解简化事务控制,替代手动 JDBC 事务代码,无侵入,业务代码纯净,适用常规 CRUD 操作。
- 关键注解:
@Transactional
- 特性:
- 支持传播行为(如
Propagation.REQUIRED
)。 - 隔离级别、超时设置、回滚规则。
- 支持传播行为(如
@Service
public class OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private PaymentDao paymentDao;
// 声明事务:方法内所有数据库操作作为一个原子单元
@Transactional
public void placeOrder(Order order) {
orderDao.save(order); // 插入订单
paymentDao.process(order); // 处理支付
// 若此处抛出异常,事务会自动回滚
}
}
关键属性
属性 | 作用 | 默认值 |
---|---|---|
propagation | 事务传播行为(如新建事务、加入现有事务等) | REQUIRED( 如果当前没有事务,新建一个事务;如果已存在事务,则加入该事务) |
isolation | 事务隔离级别(解决脏读、幻读等问题) | DEFAULT (数据库默认) |
timeout | 事务超时时间(秒),超时后自动回滚 | -1(无超时) |
readOnly | 是否只读事务(优化查询性能) | false |
rollbackFor | 指定哪些异常触发回滚(默认仅回滚 RuntimeException ) | {} |
noRollbackFor | 指定哪些异常不触发回滚 | {} |
示例:
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = {SQLException.class, BusinessException.class}
)
public void updateData() { ... }
5. 模块化与集成能力
- 模块化设计:按需引入模块(如
spring-webmvc
、spring-data-jpa
)。 - 无缝集成:
- 数据库:JPA/Hibernate、MyBatis、JDBC。
- 消息队列:JMS、Kafka、RabbitMQ。
- 安全:Spring Security(OAuth2、JWT)。
- 测试:
@SpringBootTest
支持集成测试。