Spring介绍
Spring是企业级应用的核心开发框架,核心理念是:轻量级、分层、解耦合“,目标是简化Java开发。
框架概述
Spring是一个 分层的轻量级开源框架,专注于解决对象管理和模块解耦问题。方便与其他框架整合。
设计理念
- IOC容器:将对象的创建和管理交友框架,而非手动new对象,实现控制反转。
- AOP支持:将日志、事务、权限等 横向管理的功能,与业务逻辑分离,降低耦合。
- 接口编程:基于接口而非实现编程,提高代码灵活性和可扩展性。
- 无侵入式:不强制依赖Spring特定类,原有代码可直接接入Spring。
核心模块
Spring框架分为6大核心模块,各个模块可以独立使用。通过依赖关系相互协作。
模块名称 | 核心作用 |
---|---|
Spring Core 核心容器 | 提供IOC容器核心实现、负责对象创建、依赖注入、Bean管理 |
SpringContext上下文 | 扩展Core功能,提供提供环境配置、资源加载、国际化、事件监听等功能 |
SpringAOP面向切面 | 提供AOP核心实现,支持横切逻辑(如日志、事务)的声明式编程 |
Spring DAO/.ORM | 简化数据访问层开发,提供JdbcTemplate封装JDBC,整合Mybatis、Hibernate等框架。 |
Spring web | 提供Web开发支持,如SpringMVC架构。servlet继承。 |
SpringTest | 整合JUnit、Test NG支持SPring组件的单元测试和集成测试。 |
Spring 核心概念
1、IOC(Inversion of Control,控制反转)
定义:ioc将对象的创建、依赖注入、生命周期管理交给Spring容器,仅需要 声明需要的对象,不关心怎么创建。— 控制反转
- 核心实现:DI依赖注入。
DI是IOC的具体落地方式:Spring容器在创建对象时,自动将其依赖的对象自动注入。 - IOC容器的两种实现
- ClassPathXmlApplicationContext:从类路径(resource目录)加载XML配置文件初始化容器。
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService service = context.getBean(UserService.class);
- AnnotationConfigApplicationContext:从注解配置类中(@Configuration)初始化容器。无XML模式
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
UserService service = context.getBean(UserService.class);
2、AOP(Aspect-Oriented Programming 面向切面编程)
定义:AOP 是一种 “横向编程” 思想:将日志、事务、权限等横跨多个业务模块的 “横切关注点” 抽离为独立的 “切面(Aspect)”,在不修改原有业务代码的前提下,通过 “动态代理” 将切面逻辑植入到业务流程中。
AOP相关术语:
术语 | 定义 |
---|---|
切面(Aspect) | 横切关注点的封装(如“日志切面","事务切面”),由切入点和通知组成 |
通知(Advisor) | 切面的具体逻辑(如日志的打印前后) |
切入点(Pointcut) | 定义“拿下方法需要被切面需要被拦截 |
连接点(JoinPoint) | 所有可能被切面拦截的方法(如所有类的所有方法),切入点时连接点的子集 |
目标对象 | 被切面拦截的原始业务对象 |
代理对象 | Spring为目标对象生成的代理对象,负责执行目标方法+切面逻辑 |
切面由:切入点和通知组成。 连接点:所有可以被切入的点。切入点:切入切面的点。通知:切面的具体逻辑。目标对象,代理对象。
通知的类型
通知类型 | 执行时机 |
---|---|
@Before | 目标方法执行前执行 |
@AfterReturning | 目标方法正常返回后执行(异常时不执行) |
@Afterthrowing | 目标方法抛出异常后执行 |
@After | 目标方法执行后执行,无论正常/异常 |
@Around | 包裹目标方法,可在方法执行前后自定义逻辑(灵活可控制目标方法是否执行) |
AOP实现原理
Spring AOP基于动态代理实现:默认规则
- 若目标对象实现了接口,使用JDK代理(基于接口实现代理类)。
- 若目标对象未实现接口:使用CGLIB代理(基于子类生成代理对象,需要引入CGLIB依赖)
案例:日志切面
// 1. 开启AOP支持(在配置类上添加)
@Configuration
@EnableAspectJAutoProxy // 开启AspectJ风格的AOP
@ComponentScan("com.example")
public class SpringConfig {}// 2. 定义日志切面
@Aspect // 标记为切面
@Component // 纳入IoC容器
public class LogAspect {// 切入点:匹配com.example.service包下所有类的所有方法@Pointcut("execution(* com.example.service.*.*(..))")public void servicePointcut() {}// 前置通知:目标方法执行前打印日志@Before("servicePointcut()")public void beforeLog(JoinPoint joinPoint) {String methodName = joinPoint.getSignature().getName(); // 获取方法名System.out.println("前置日志:执行方法 " + methodName);}// 环绕通知:包裹目标方法@Around("servicePointcut()")public Object aroundLog(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("环绕前:开始计时");long start = System.currentTimeMillis();Object result = joinPoint.proceed(); // 执行目标方法long end = System.currentTimeMillis();System.out.println("环绕后:方法耗时 " + (end - start) + "ms");return result;}
}
Spring Bean 详解
Spring IoC 容器管理的对象称为 Bean,Bean 是 Spring 应用的核心组件。以下总结 Bean 的关键特性:
-
Bean 的生命周期
Spring Bean 的生命周期从 “创建” 到 “销毁” 分为 7 个核心阶段,开发者可通过接口或配置干预生命周期:- 实例化(Instantiation):Spring 调用无参构造创建 Bean 实例。
- 属性注入(Populate):Spring 自动注入 Bean 的依赖(@Autowired 标注的属性)。
- 初始化前(Aware 接口回调):若 Bean 实现 BeanNameAware、ApplicationContextAware 等接口,Spring 会回调接口方法(如设置 Bean 名称、上下文)。
- 初始化(Initialization):
- 若 Bean 实现 InitializingBean 接口,调用 afterPropertiesSet() 方法。
- 执行自定义 init-method(XML 配置)或 @PostConstruct 注解方法。
- 就绪(Ready):Bean 实例化完成,可被容器获取并使用。
- 销毁前(Destruction):
- 若 Bean 实现 DisposableBean 接口,调用 destroy() 方法。
- 执行自定义 destroy-method(XML 配置)或 @PreDestroy 注解方法。
- 销毁(Destroyed):Bean 从容器中移除,资源释放。
-
Bean 的作用域
Bean 的作用域定义了 Bean 实例的 “创建次数” 和 “存活范围”,默认是 singleton(单例)。Spring 提供 6 种作用域:
作用域名称 | 说明 | 适用场景 |
---|---|---|
singleton | 容器中仅存在1 个 Bean 实例,所有请求共享该实例(默认)。 | 无状态组件(如 Service、Dao) |
prototype | 每次获取 Bean 时新建 1 个实例(容器仅创建,不销毁)。 | 有状态组件(如 Request、Session) |
request | 每个 HTTP 请求创建 1 个实例,请求结束后销毁(仅 Web 环境)。 | Web request 级别的数据存储 |
session | 每个 HTTP Session 创建 1 个实例,Session 过期后销毁(仅 Web 环境)。 | Web session 级别的数据存储 |
application | 整个 Web 应用共享 1 个实例,应用停止后销毁(仅 Web 环境)。 | 应用级全局配置 |
websocket | 每个 WebSocket 连接创建 1 个实例(仅 WebSocket 环境)。 | WebSocket 通信 |
Bean定义方式
Spring 支持 3 种 Bean 定义方式,推荐优先使用注解或 Java Config:
-
XML 配置(传统方式,适用于老项目):
-
注解驱动(主流方式,简化配置):
- 用 @Component 及其衍生注解标记类为 Bean:
- @Component:通用组件(无明确分层)。
- @Service:业务层组件(Service)。
- @Controller:Web 层组件(Controller)。
- @Repository:数据访问层组件(Dao)。
- 用 @Component 及其衍生注解标记类为 Bean:
-
Java Config(纯 Java 配置,无 XML):
通过 @Configuration 标记配置类,@Bean 注解定义 Bean:@Configuration public class SpringConfig {// 手动定义Bean,返回值为Bean实例,方法名为Bean ID@Beanpublic UserDao userDao() {return new UserDao();}@Beanpublic UserService userService() {UserService service = new UserService();service.setUserDao(userDao()); // 手动注入依赖return service;} }
spring事务管理
事务是保证数据一致性的核心机制,Spring 提供声明式事务(无侵入)和编程式事务(手动编码),推荐使用声明式事务。
- 事务的 ACID 特性
- 原子性(Atomicity):事务要么全部执行,要么全部回滚(无中间状态)。
- 一致性(Consistency):事务执行前后数据完整性不变(如转账前后总金额不变)。
- 隔离性(Isolation):多个事务并发执行时,相互不干扰。
- 持久性(Durability):事务提交后,数据永久保存到数据库。
- 事务核心接口
- 事务传播行为
事务传播行为定义 “多个事务方法嵌套调用时,事务如何传递”,Spring 提供 7 种传播行为,常用 3 种:
传播行为常量 | 说明 |
---|---|
REQUIRED(默认) | 若当前有事务,加入事务;若无事务,新建事务(如 ServiceA 调用 ServiceB,共用一个事务)。 |
REQUIRES_NEW | 无论当前是否有事务,都新建一个独立事务(如日志记录,不影响主事务)。 |
SUPPORTS | 若当前有事务,加入事务;若无事务,以非事务方式执行(不常用)。 |
-
声明式事务
通过 @Transactional 注解实现,步骤如下:- 配置事务管理器(以Mybatis为例)
@Configuration @EnableTransactionManagement // 开启声明式事务支持 public class TxConfig {// 数据源(省略DataSource配置)@Beanpublic DataSource dataSource() { ... }// 事务管理器@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);} }
- 在业务方法上使用@Transaction:
@Service public class OrderService {@Autowiredprivate OrderDao orderDao;// 声明式事务:默认传播行为REQUIRED,隔离级别DEFAULT@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)public void createOrder(Order order) {orderDao.insertOrder(order); // 插入订单// 若抛出异常,事务自动回滚if (order.getAmount() < 0) {throw new RuntimeException("金额非法");}} }
-
@Transactional 不生效的常见场景
- 方法不是 public 修饰(Spring 事务仅对 public 方法生效)。
- 事务方法自调用(如 serviceA() 调用本类的 serviceB(),AOP 无法拦截)。
- 异常被手动捕获(未抛出到事务管理器,无法触发回滚)。
- 未配置 TransactionManager 或未开启 @EnableTransactionManagement。
- 注解指定的 rollbackFor 不包含实际抛出的异常(默认仅回滚 RuntimeException)。
SpringWeb
常见问题
- Bean 循环依赖如何解决?
场景:A 依赖 B,B 依赖 A(如 A -> B -> A)。
Spring 解决方案:使用三级缓存(singletonObjects、earlySingletonObjects、singletonFactories),提前暴露未初始化完成的 Bean 实例,避免循环等待。
注意:prototype 作用域的 Bean 无法解决循环依赖(Spring 不缓存 prototype Bean)。 - AOP 与事务的执行顺序?
事务是通过 AOP 实现的,多个切面的执行顺序可通过 @Order 注解指定(值越小,优先级越高)。
若未指定顺序,默认事务切面优先级低于自定义切面(如日志切面先执行,事务切面后执行)。 - 如何实现 Spring 容器的启动监听?
实现 ApplicationListener 接口,重写 onApplicationEvent 方法,容器初始化完成后会自动触发。