引言

Spring是Java开发中最流行的框架,几乎是面试必考内容。本文系统梳理Spring核心知识点和高频面试题,帮助你全面掌握Spring框架。

一、Spring核心概念

1.1 什么是Spring?

Spring是一个轻量级的Java开发框架,主要特性:

  1. IOC(控制反转):对象的创建和依赖关系由Spring容器管理
  2. AOP(面向切面编程):通过代理实现横切关注点
  3. 声明式事务:通过注解管理事务
  4. MVC框架:Spring MVC提供Web开发支持
  5. 集成支持:轻松集成MyBatis、Hibernate等框架

1.2 Spring的优势

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 1. 解耦:降低组件间的耦合度
// 传统方式
public class UserService {
private UserDao userDao = new UserDaoImpl(); // 紧耦合
}

// Spring方式
@Service
public class UserService {
@Autowired
private UserDao userDao; // 松耦合,依赖注入
}

// 2. 易于测试:可以注入Mock对象
@Test
public void testUserService() {
UserDao mockDao = mock(UserDao.class);
UserService service = new UserService(mockDao);
// 测试代码
}

// 3. 声明式编程:通过注解简化开发
@Transactional
public void transferMoney(Long from, Long to, BigDecimal amount) {
// Spring自动管理事务
}

二、IOC容器

2.1 什么是IOC?

IOC(Inversion of Control)控制反转:将对象的创建权交给Spring容器。

1
2
3
4
5
6
7
8
9
10
// 传统方式:我们主动创建对象
UserService service = new UserService();
service.setUserDao(new UserDaoImpl());

// IOC方式:Spring容器创建和注入对象
@Service
public class UserService {
@Autowired
private UserDao userDao; // Spring自动注入
}

DI(Dependency Injection)依赖注入是IOC的实现方式。

2.2 依赖注入的三种方式

1. 构造器注入(推荐)

1
2
3
4
5
6
7
8
9
@Service
public class UserService {
private final UserDao userDao;

// 单个构造器可省略@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}

优点:

  • 依赖不可变(final)
  • 避免NullPointerException
  • 便于测试

2. Setter注入

1
2
3
4
5
6
7
8
9
@Service
public class UserService {
private UserDao userDao;

@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}

3. 字段注入(不推荐)

1
2
3
4
5
@Service
public class UserService {
@Autowired
private UserDao userDao; // 不推荐:难以测试,违反封装
}

2.3 Bean的作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 1. singleton(默认):单例
@Scope("singleton")
@Component
public class SingletonBean {
// 整个容器只有一个实例
}

// 2. prototype:每次获取都创建新实例
@Scope("prototype")
@Component
public class PrototypeBean {
// 每次getBean都创建新对象
}

// 3. request:每个HTTP请求一个实例
@Scope("request")
@Component
public class RequestBean {
// Web环境:每个请求创建一个
}

// 4. session:每个HTTP会话一个实例
@Scope("session")
@Component
public class SessionBean {
// Web环境:每个session创建一个
}

// 5. application:整个ServletContext一个实例
@Scope("application")
@Component
public class ApplicationBean {}

2.4 Bean的生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
@Component
public class MyBean implements InitializingBean, DisposableBean {

private String name;

// 1. 构造器
public MyBean() {
System.out.println("1. 构造器");
}

// 2. 设置属性
public void setName(String name) {
System.out.println("2. 设置属性:" + name);
this.name = name;
}

// 3. BeanNameAware
@Override
public void setBeanName(String name) {
System.out.println("3. setBeanName:" + name);
}

// 4. BeanFactoryAware
@Override
public void setBeanFactory(BeanFactory beanFactory) {
System.out.println("4. setBeanFactory");
}

// 5. ApplicationContextAware
@Override
public void setApplicationContext(ApplicationContext context) {
System.out.println("5. setApplicationContext");
}

// 6. BeanPostProcessor前置处理
// 由BeanPostProcessor接口实现

// 7. @PostConstruct
@PostConstruct
public void init() {
System.out.println("7. @PostConstruct");
}

// 8. InitializingBean的afterPropertiesSet
@Override
public void afterPropertiesSet() {
System.out.println("8. afterPropertiesSet");
}

// 9. init-method
public void customInit() {
System.out.println("9. customInit");
}

// 10. BeanPostProcessor后置处理
// 由BeanPostProcessor接口实现

// Bean就绪,可以使用

// 11. @PreDestroy
@PreDestroy
public void preDestroy() {
System.out.println("11. @PreDestroy");
}

// 12. DisposableBean的destroy
@Override
public void destroy() {
System.out.println("12. destroy");
}

// 13. destroy-method
public void customDestroy() {
System.out.println("13. customDestroy");
}
}

简化版流程:

1
2
3
4
实例化 → 属性注入 → Aware接口 → BeanPostProcessor前置 
→ @PostConstruct → InitializingBean → init-method
→ BeanPostProcessor后置 → Bean使用
→ @PreDestroy → DisposableBean → destroy-method

2.5 循环依赖问题

1
2
3
4
5
6
7
8
9
10
11
12
// A依赖B,B依赖A
@Component
public class A {
@Autowired
private B b;
}

@Component
public class B {
@Autowired
private A a;
}

Spring如何解决循环依赖?

通过三级缓存:

1
2
3
4
5
6
7
8
9
10
11
12
// 1. singletonObjects:一级缓存,存放完整的Bean
// 2. earlySingletonObjects:二级缓存,存放早期Bean引用
// 3. singletonFactories:三级缓存,存放Bean工厂

// 创建A的流程:
// 1. 实例化A,放入三级缓存
// 2. 填充A的属性,发现需要B
// 3. 创建B,实例化B,放入三级缓存
// 4. 填充B的属性,发现需要A
// 5. 从三级缓存获取A的早期引用
// 6. B创建完成,放入一级缓存
// 7. A注入B,创建完成,放入一级缓存

注意:构造器注入无法解决循环依赖!

三、AOP面向切面编程

3.1 什么是AOP?

AOP(Aspect Oriented Programming):将横切关注点从业务逻辑中分离出来。

横切关注点:日志、事务、权限等通用功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 没有AOP:业务代码和日志代码混杂
public void transfer(Long from, Long to, BigDecimal amount) {
System.out.println("开始转账"); // 日志代码
// 业务代码
accountDao.deduct(from, amount);
accountDao.add(to, amount);
System.out.println("转账完成"); // 日志代码
}

// 使用AOP:分离关注点
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("开始转账");
Object result = pjp.proceed();
System.out.println("转账完成");
return result;
}
}

// 业务代码只关注业务逻辑
public void transfer(Long from, Long to, BigDecimal amount) {
accountDao.deduct(from, amount);
accountDao.add(to, amount);
}

3.2 AOP核心概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1. Aspect(切面):横切关注点的模块化
@Aspect
@Component
public class LogAspect {}

// 2. Join Point(连接点):程序执行的某个特定位置
// 例如:方法调用、异常抛出等

// 3. Pointcut(切点):匹配连接点的表达式
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethod() {}

// 4. Advice(通知):在切点执行的动作
@Before("serviceMethod()") // 前置通知
@After("serviceMethod()") // 后置通知
@AfterReturning("serviceMethod()") // 返回后通知
@AfterThrowing("serviceMethod()") // 异常通知
@Around("serviceMethod()") // 环绕通知

// 5. Target(目标对象):被代理的对象

// 6. Proxy(代理对象):AOP创建的对象

// 7. Weaving(织入):将切面应用到目标对象的过程

3.3 五种通知类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Aspect
@Component
public class MyAspect {

// 1. 前置通知:方法执行前
@Before("execution(* com.example.service.*.*(..))")
public void before(JoinPoint jp) {
System.out.println("前置通知:" + jp.getSignature().getName());
}

// 2. 后置通知:方法执行后(无论是否异常)
@After("execution(* com.example.service.*.*(..))")
public void after(JoinPoint jp) {
System.out.println("后置通知");
}

// 3. 返回通知:方法正常返回后
@AfterReturning(value = "execution(* com.example.service.*.*(..))",
returning = "result")
public void afterReturning(JoinPoint jp, Object result) {
System.out.println("返回通知:" + result);
}

// 4. 异常通知:方法抛出异常后
@AfterThrowing(value = "execution(* com.example.service.*.*(..))",
throwing = "ex")
public void afterThrowing(JoinPoint jp, Exception ex) {
System.out.println("异常通知:" + ex.getMessage());
}

// 5. 环绕通知:方法执行前后
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
Object result = pjp.proceed(); // 执行方法
System.out.println("环绕后");
return result;
}
}

执行顺序:

1
2
3
4
5
正常执行:
Around前 → Before → 方法执行 → AfterReturning → After → Around后

异常执行:
Around前 → Before → 方法执行(异常)→ AfterThrowing → After

3.4 切点表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 1. execution:最常用
// execution(modifiers? return-type declaring-type?method-name(params) throws?)
@Pointcut("execution(public * com.example.service.*.*(..))")
// public:修饰符(可选)
// *:返回类型
// com.example.service:包名
// *.*:任意类的任意方法
// (..):任意参数

// 2. within:匹配类型
@Pointcut("within(com.example.service.*)") // service包下所有类

// 3. this:代理对象类型
@Pointcut("this(com.example.service.UserService)")

// 4. target:目标对象类型
@Pointcut("target(com.example.service.UserService)")

// 5. args:方法参数类型
@Pointcut("args(Long, String)") // 参数为(Long, String)的方法

// 6. @annotation:方法上有指定注解
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")

// 7. @within:类上有指定注解
@Pointcut("@within(org.springframework.stereotype.Service)")

// 8. @target:目标对象类上有指定注解
@Pointcut("@target(org.springframework.stereotype.Service)")

// 9. @args:参数对象类上有指定注解
@Pointcut("@args(com.example.annotation.Valid)")

// 组合使用
@Pointcut("execution(* com.example.service.*.*(..)) && @annotation(log)")

3.5 AOP实战:性能监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Aspect
@Component
@Slf4j
public class PerformanceAspect {

@Around("@annotation(com.example.annotation.Monitor)")
public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();

String className = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
Object[] args = pjp.getArgs();

log.info("开始执行:{}.{},参数:{}", className, methodName, args);

Object result = null;
try {
result = pjp.proceed();
long end = System.currentTimeMillis();
log.info("执行完成:{}.{},耗时:{}ms", className, methodName, (end - start));
return result;
} catch (Throwable e) {
long end = System.currentTimeMillis();
log.error("执行异常:{}.{},耗时:{}ms,异常:{}",
className, methodName, (end - start), e.getMessage());
throw e;
}
}
}

// 使用
@Service
public class UserService {
@Monitor
public User getUser(Long id) {
// 业务代码
return user;
}
}

四、事务管理

4.1 Spring事务的实现方式

1. 编程式事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Autowired
private TransactionTemplate transactionTemplate;

public void transfer(Long from, Long to, BigDecimal amount) {
transactionTemplate.execute(status -> {
try {
accountDao.deduct(from, amount);
accountDao.add(to, amount);
return true;
} catch (Exception e) {
status.setRollbackOnly();
return false;
}
});
}

2. 声明式事务(推荐)

1
2
3
4
5
@Transactional
public void transfer(Long from, Long to, BigDecimal amount) {
accountDao.deduct(from, amount);
accountDao.add(to, amount);
}

4.2 @Transactional属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Transactional(
// 1. 传播行为
propagation = Propagation.REQUIRED,

// 2. 隔离级别
isolation = Isolation.DEFAULT,

// 3. 超时时间(秒)
timeout = 30,

// 4. 只读事务
readOnly = false,

// 5. 回滚异常
rollbackFor = Exception.class,

// 6. 不回滚异常
noRollbackFor = RuntimeException.class
)
public void method() {}

4.3 事务传播行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 1. REQUIRED(默认):如果有事务就加入,没有就新建
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
methodB(); // B加入A的事务
}

// 2. REQUIRES_NEW:总是新建事务,挂起当前事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// B独立事务,与A互不影响
}

// 3. SUPPORTS:有事务就加入,没有就非事务执行
@Transactional(propagation = Propagation.SUPPORTS)

// 4. NOT_SUPPORTED:总是非事务执行,挂起当前事务
@Transactional(propagation = Propagation.NOT_SUPPORTED)

// 5. MANDATORY:必须在事务中执行,否则抛异常
@Transactional(propagation = Propagation.MANDATORY)

// 6. NEVER:必须非事务执行,否则抛异常
@Transactional(propagation = Propagation.NEVER)

// 7. NESTED:嵌套事务(保存点机制)
@Transactional(propagation = Propagation.NESTED)

4.4 事务失效场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 1. 方法不是public
@Transactional
private void method() {} // 事务失效

// 2. 同类方法调用
@Service
public class UserService {
public void methodA() {
this.methodB(); // 事务失效,未走代理
}

@Transactional
public void methodB() {}
}

// 解决方案:注入自己
@Autowired
private UserService self;

public void methodA() {
self.methodB(); // 走代理,事务生效
}

// 3. 异常被捕获
@Transactional
public void method() {
try {
// 业务代码
} catch (Exception e) {
// 异常被吞掉,事务不回滚
}
}

// 4. 抛出的不是RuntimeException
@Transactional // 默认只回滚RuntimeException
public void method() throws Exception {
throw new Exception(); // 不回滚
}

// 解决:指定回滚异常
@Transactional(rollbackFor = Exception.class)

// 5. 数据库不支持事务(MyISAM)

五、Spring MVC

5.1 MVC架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
客户端请求
↓
DispatcherServlet(前端控制器)
↓
HandlerMapping(处理器映射)
↓
Controller(控制器)
↓
Service(业务层)
↓
Dao(数据访问层)
↓
返回ModelAndView
↓
ViewResolver(视图解析器)
↓
返回视图给客户端

5.2 Spring MVC处理流程

1
2
3
4
5
6
7
8
9
10
// 1. 客户端发送请求到DispatcherServlet
// 2. DispatcherServlet查询HandlerMapping
// 3. HandlerMapping返回HandlerExecutionChain
// 4. DispatcherServlet调用HandlerAdapter
// 5. HandlerAdapter调用Controller
// 6. Controller返回ModelAndView
// 7. DispatcherServlet传递给ViewResolver
// 8. ViewResolver解析并返回View
// 9. DispatcherServlet渲染视图
// 10. 返回响应给客户端

5.3 常用注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 1. @Controller:标记控制器
@Controller
public class UserController {}

// 2. @RestController = @Controller + @ResponseBody
@RestController
public class UserRestController {}

// 3. @RequestMapping:映射请求路径
@RequestMapping("/users")
@GetMapping("/users/{id}")
@PostMapping("/users")
@PutMapping("/users/{id}")
@DeleteMapping("/users/{id}")

// 4. @PathVariable:路径变量
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {}

// 5. @RequestParam:请求参数
@GetMapping("/users")
public List<User> list(@RequestParam(required = false) String name) {}

// 6. @RequestBody:请求体
@PostMapping("/users")
public User create(@RequestBody User user) {}

// 7. @ResponseBody:返回JSON
@ResponseBody
public User getUser() {}

// 8. @ModelAttribute:绑定模型
@ModelAttribute
public void addAttributes(Model model) {}

六、Spring Boot

6.1 Spring Boot的优势

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1. 自动配置
@SpringBootApplication // 包含自动配置
public class Application {}

// 2. 起步依赖
// pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

// 3. 嵌入式服务器
// 无需部署WAR,直接java -jar运行

// 4. 生产级功能
// Actuator提供健康检查、监控等

// 5. 无XML配置
@Configuration
public class MyConfig {}

6.2 自动配置原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@SpringBootApplication
=
@SpringBootConfiguration // @Configuration的别名
+ @EnableAutoConfiguration // 启用自动配置
+ @ComponentScan // 组件扫描

// @EnableAutoConfiguration原理:
// 1. 加载META-INF/spring.factories
// 2. 读取所有xxxAutoConfiguration类
// 3. 根据@Conditional条件决定是否生效

@Configuration
@ConditionalOnClass(DataSource.class) // 有DataSource类时生效
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 没有用户自定义Bean时生效
public DataSource dataSource() {
return new HikariDataSource();
}
}

6.3 常用Starter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- Web开发 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 数据访问 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>

七、面试高频问题

7.1 Spring和Spring Boot的区别?

特性 Spring Spring Boot
配置 大量XML/注解配置 自动配置
依赖管理 手动管理版本 Starter统一管理
服务器 需要外部容器 内嵌容器
开发效率 较低 高

7.2 BeanFactory和ApplicationContext的区别?

  • BeanFactory:Spring的基础容器,延迟加载
  • ApplicationContext:功能更强大,立即加载,支持事件、国际化等

7.3 Spring如何解决循环依赖?

通过三级缓存和early reference机制,但构造器注入无法解决。

总结

Spring核心要点:

  1. IOC:依赖注入、Bean生命周期、循环依赖
  2. AOP:动态代理、切面、通知类型
  3. 事务:传播行为、隔离级别、失效场景
  4. MVC:请求处理流程、常用注解
  5. Boot:自动配置、Starter、内嵌容器

参考资料