主题
Spring
Spring 是一个广受欢迎的企业级 Java 应用程序开发框架。它由 Rod Johnson 创立,并于2003年首次发布。Spring 框架的核心价值在于为开发者提供了简化 Java 开发复杂性的工具和基础设施,尤其是对于构建企业级应用程序而言。
核心特性
轻量级:Spring 不强制使用特定的应用服务器或全套的 J2EE 组件,而是通过非侵入式的编程方式,使开发者能够选择性地使用其需要的功能,保持应用的轻量化。
控制反转 (Inversion of Control, IoC) 和 依赖注入 (Dependency Injection, DI):Spring 提供了一个 IoC 容器,用于管理应用程序中的对象及其相互依赖。通过 DI,对象无需自行创建或查找依赖对象,而是由容器负责创建对象并将其所需的依赖注入进来。这种设计模式有助于降低代码间的耦合度,提高组件的复用性和系统的可测试性。
面向切面编程 (Aspect-Oriented Programming, AOP):Spring AOP 允许开发者定义"切面",这些切面包含了跨越多个对象或服务的关注点,如事务管理、日志记录、权限检查等。通过 AOP,可以将这些横切关注点从核心业务逻辑中解耦出来,实现模块化和代码复用,同时减少代码的冗余和交叉污染。
事务管理 (Transaction Management):Spring 提供了一致的、与底层持久层技术无关的事务管理抽象层,支持声明式事务处理。开发者可以通过简单的配置或注解来管理事务边界,简化了事务控制的复杂性。
数据访问/集成 (Data Access/Integration):Spring 支持多种数据访问技术,包括 JDBC、Hibernate、JPA 等。它提供了诸如
JdbcTemplate、JpaTemplate等工具类以及对 ORM 框架的良好整合,简化了数据库操作和 DAO 层的开发。此外,Spring 还支持与消息中间件、NoSQL 数据库等其他数据存储系统的集成。模型-视图-控制器 (Model-View-Controller, MVC) 框架:Spring MVC 是一个用于构建 web 应用程序的模块,它遵循 MVC 设计模式,提供了清晰的角色划分和松耦合的组件。Spring MVC 可以与各种视图技术(如 JSP、Thymeleaf、FreeMarker 等)配合使用,为构建灵活、可扩展的 web 应用提供强大支持。
测试支持:Spring 提供了方便的测试工具和 mock 对象库,如
Spring TestContext Framework,简化了单元测试和集成测试的编写,使得在 Spring 环境中测试代码变得更加容易和高效。配置管理:Spring 通过 XML、Java 配置类或基于注解的配置方式,提供了灵活的组件装配和管理机制。随着 Spring Boot 的出现,这种配置方式进一步简化,通过自动配置和starter模块极大地减少了手动配置的工作量。
简单示例
假设有一个简单的业务需求,即查询用户的个人信息。为了实现这一功能,通常会设计如下三个组件:
User 类(POJO):表示用户实体,包含姓名、年龄等属性及其对应的 getter/setter 方法。
UserService 类:业务逻辑层,提供查询用户信息的方法,如
getUser()。此方法内部需要调用 UserDao 来获取数据。UserDao 类:数据访问层,实现了从数据库或其他数据源查询用户数据的具体逻辑,例如
findUser()方法。
配置 Spring 容器
使用 XML 配置文件(或 Java 配置类、注解配置)来定义 Spring 容器中的 Bean:
xml
<!-- spring-config.xml -->
<beans>
<bean id="userDao" class="com.example.UserDaoImpl"/>
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="userDao"/>
</bean>
</beans>使用 Spring 容器
java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserService userService = context.getBean("userService", UserService.class);
User user = userService.getUser();
System.out.println("User Details: " + user);
}
}控制反转 (IoC)
控制反转(IoC)是一种软件设计原则,其核心思想是将对象的创建、管理及依赖关系的维护从应用程序代码中抽离出来,转交给一个专门的第三方容器(如 Spring 容器)来负责。
依赖注入方式
Setter 注入
java
public class UserService {
private UserDao userDao;
// Setter 方法注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public User getUser() {
return userDao.findUser();
}
}构造函数注入
java
public class UserService {
private final UserDao userDao;
// 构造函数注入
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public User getUser() {
return userDao.findUser();
}
}字段注入(@Autowired 注解)
java
public class UserService {
@Autowired
private UserDao userDao;
public User getUser() {
return userDao.findUser();
}
}Spring 容器
Spring IoC 的实现离不开 Spring 容器。容器负责:
- Bean 定义:通过 XML 配置文件、Java 配置类或注解,定义 Bean 的类型、作用域、生命周期回调方法、依赖关系等信息。
- Bean 实例化:根据 Bean 定义,创建 Bean 实例。
- 依赖解析与注入:解析 Bean 间的依赖关系,通过上述注入方式将依赖对象注入到目标 Bean 中。
- Bean 管理:管理 Bean 的生命周期,包括初始化、销毁等过程,以及单例或多例模式下的缓存管理。
面向切面编程 (AOP)
Spring 核心之一是面向切面编程 (Aspect-Oriented Programming, AOP),这是一种编程范式,用于处理那些分散在应用各处、与主业务逻辑分离但仍需在多个位置应用的横切关注点(cross-cutting concerns)。AOP 通过将这些关注点封装成"切面"(aspects),并定义何时(when)、何处(where)以及如何(how)应用这些切面,来实现对系统行为的统一管理和模块化。
核心概念
切面 (Aspect)
切面是 AOP 中的基本单元,它封装了特定的横切关注点,如日志记录、事务管理、安全检查、性能监控等。切面通常包含:
- 通知 (Advice):实际执行的代码,定义了在程序执行过程中何时和如何插入切面逻辑。常见的通知类型有:
- 前置通知 (Before advice):在目标方法执行前执行。
- 后置通知 (After advice):在目标方法正常执行后执行,无论方法是否抛出异常。
- 返回通知 (After returning advice):在目标方法成功执行并返回结果后执行。
- 异常通知 (After throwing advice):在目标方法抛出异常后执行。
- 环绕通知 (Around advice):包围目标方法执行,可以决定是否执行目标方法,何时执行,以及何时退出。
- 切入点 (Pointcut):一组匹配规则,用于定义哪些连接点(JoinPoint)应该被通知(advice)处理。
- 连接点 (JoinPoint):程序执行过程中的特定位置,如方法调用、异常抛出等。
代理 (Proxy)
Spring AOP 通常通过代理模式来实现切面的织入。有两种代理方式:
- JDK 动态代理:基于接口创建代理对象,代理对象实现了目标对象所实现的所有接口。
- CGLIB 字节码生成代理:针对没有接口的类,通过生成子类的方式创建代理对象。
Spring AOP 实现示例
java
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logMethodEntry(JoinPoint joinPoint) {
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
System.out.println("Entering method: " + className + "." + methodName);
}
// 其他通知类型和切入点定义...
}AOP 应用场景
AOP 适用于处理与业务逻辑分离且需要在多个地方应用的横切关注点,如:
- 事务管理:在方法执行前后添加事务开启、提交或回滚逻辑。
- 日志记录:在方法执行前后记录方法调用情况、输入输出参数、执行结果等信息。
- 权限检查:在方法调用前验证用户是否有足够的权限访问资源。
- 性能监控:统计方法执行时间、资源消耗等指标,用于性能分析和优化。
- 缓存控制:在方法调用前检查缓存是否存在结果,存在则直接返回,否则执行方法并更新缓存。
Spring MVC
Spring MVC是Spring框架中的一个模块,它遵循模型-视图-控制器(Model-View-Controller, MVC)设计模式,用于构建Web应用程序。
Spring MVC简介
- 模型(Model):模型代表数据和业务逻辑。在Spring MVC中,模型通常是Java对象,这些对象封装了应用程序的数据和操作这些数据的逻辑。
- 视图(View):视图负责展示模型中的数据给用户。在Spring MVC中,视图可以是JSP、Thymeleaf、FreeMarker等技术生成的HTML页面。
- 控制器(Controller):控制器负责接收用户的请求,处理请求并选择合适的视图来响应用户。在Spring MVC中,控制器通常是一个Java类,其中的方法(称为处理器方法)通过注解(如
@RequestMapping)与特定的URL映射。
Spring MVC工作流程
- 用户发起HTTP请求到服务器。
- 请求被Spring MVC的前端控制器
DispatcherServlet捕获。 DispatcherServlet根据请求的URL和预先配置的处理器映射,找到对应的处理器(Controller)。DispatcherServlet调用处理器,并将处理请求所需的参数传递给处理器方法。- 处理器执行业务逻辑后,返回一个包含模型数据的逻辑视图名给
DispatcherServlet。 DispatcherServlet通过视图解析器,根据逻辑视图名找到实际的视图。- 视图将模型数据渲染成用户可以理解的页面(HTML等)。
DispatcherServlet将最终的响应发送回用户浏览器。
Controller示例
java
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class LoginController {
@GetMapping("/login")
public String showLoginForm() {
return "login";
}
@PostMapping("/login")
public String handleLogin(@RequestParam("username") String username,
@RequestParam("password") String password,
Model model) {
if ("admin".equals(username) && "123456".equals(password)) {
model.addAttribute("message", "登录成功!");
return "success";
} else {
model.addAttribute("errorMessage", "用户名或密码错误!");
return "login";
}
}
}事务管理
Spring事务管理是Spring框架中用于处理数据库操作时保证数据一致性和完整性的关键功能。它允许你在服务层的代码中声明性地管理事务,而不需要在具体的SQL操作中手动控制事务边界。Spring事务管理支持编程式和声明式两种方式,但声明式事务管理因其简洁和解耦的特性而被更广泛使用。
基本概念
事务:事务是数据库操作的基本单元,它包含了一组数据库操作命令。这些操作要么全部成功,要么全部失败,以确保数据的完整性。
ACID原则:事务应遵循原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)四个基本属性。
Spring事务管理类型
编程式事务管理:通过编写代码(如使用TransactionTemplate或者直接使用PlatformTransactionManager)来管理事务的开始、提交或回滚。这种方式更加灵活,但会使得代码与事务管理逻辑紧密耦合。
声明式事务管理:通过在配置文件或者注解中声明事务的边界,由Spring自动管理事务的开始、提交或回滚。这种方式更加简洁,易于维护。
@Transactional注解使用
放置位置:可以放在类级别或方法级别。类级别表示该类中的所有公共方法都将启用相同的事务配置;方法级别则覆盖类级别的配置,为特定方法提供不同的事务设置。
常用属性:
propagation:事务传播行为,默认为REQUIRED,定义了事务方法之间的交互规则。isolation:事务的隔离级别,如READ_COMMITTED、REPEATABLE_READ等。readOnly:是否为只读事务,默认为false。timeout:事务超时时间,默认无限制。rollbackFor:遇到指定异常时回滚事务。noRollbackFor:遇到指定异常时不回滚事务。
七种事务传播行为
PROPAGATION_REQUIRED(默认):如果当前没有事务,就新建一个事务;如果已经存在一个事务,则加入到这个事务中。适用于大多数业务操作,确保操作的一致性。
PROPAGATION_SUPPORTS:该传播行为表示当前方法不需要事务上下文,但是如果存在当前事务的话,也可以在这个事务中执行。适用于那些可以独立于事务之外运行的查询操作。
PROPAGATION_MANDATORY:如果当前存在事务,则方法在该事务中运行;如果不存在事务,则抛出异常。适用于那些必须在事务上下文中执行的方法。
PROPAGATION_REQUIRES_NEW:总是新建一个事务,如果当前存在事务,则把当前事务挂起。适用于需要独立事务处理的场景。
PROPAGATION_NOT_SUPPORTED:该传播行为明确表示该方法不应该在事务中运行,如果存在事务,则将当前事务挂起。适用于那些不需要事务且可能对性能有严格要求的操作。
PROPAGATION_NEVER:方法不应该在事务中执行,如果当前存在事务,则抛出异常。适用于绝对不允许在事务中执行的操作。
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则其行为类似于PROPAGATION_REQUIRED。嵌套事务可以有多个保存点,回滚时可以回滚到某一个保存点,而不是直接回滚整个事务。
事务失效场景
::: caution 有多种场景可能会导致事务失效
在使用Spring框架的@Transactional注解进行声明式事务管理时,有多种场景可能会导致事务失效:
- 非public方法:如果
@Transactional注解应用在非public修饰的方法上,事务将不会生效。 - 内部方法调用:当一个类的某个方法被
@Transactional注解,并在同一个类的另一个方法内部直接调用该方法时,事务可能不会生效。 - 异常处理不当:如果在事务方法内捕获了异常并直接处理了,没有再次抛出,那么Spring默认的事务回滚策略不会生效。
- 事务传播行为设置错误:如果
@Transactional的propagation属性配置不恰当,例如设置为NOT_SUPPORTED或NEVER,则即使方法中发生异常也不会启动事务或回滚事务。 - 数据库不支持事务:如果你使用的数据库引擎不支持事务(如某些MySQL存储引擎设置为MyISAM),即使应用层面配置了事务也不会有效果。
- 没有启用事务管理器:忘记在Spring配置中启用事务管理器也会导致事务注解失效。
:::
总结
Spring 是一个全方位的支持企业级 Java 应用开发的框架,它通过提供一系列核心功能和工具,帮助开发者构建松散耦合、易于测试、高度可维护的应用系统。随着时间的推移,Spring 生态系统不断发展壮大,衍生出 Spring Boot、Spring Cloud 等项目,为微服务架构、云原生应用开发提供了全面的支持。