Spring核心
IoC容器
什么是IoC(控制反转)?
答案: IoC (Inversion of Control) 是一种设计思想,将对象的创建和依赖关系的管理交给容器,而不是由对象自己控制。
传统方式:
java
public class UserService {
private UserDao userDao = new UserDaoImpl(); // 自己创建依赖
}IoC方式:
java
public class UserService {
private UserDao userDao; // 由容器注入
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}优点:
- 降低耦合度
- 提高可测试性
- 便于维护和扩展
什么是DI(依赖注入)?
答案: DI (Dependency Injection) 是IoC的一种实现方式,通过容器动态地将依赖对象注入到组件中。
三种注入方式:
- 构造器注入(推荐)
java
@Service
public class UserService {
private final UserDao userDao;
@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
}- Setter注入
java
@Service
public class UserService {
private UserDao userDao;
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}- 字段注入(不推荐)
java
@Service
public class UserService {
@Autowired
private UserDao userDao;
}为什么推荐构造器注入?
- 保证依赖不可变(final)
- 保证依赖不为null
- 便于单元测试
- 避免循环依赖
Spring Bean的生命周期?
答案:
1. 实例化 (Instantiation)
↓
2. 属性赋值 (Populate)
↓
3. 初始化 (Initialization)
- BeanNameAware.setBeanName()
- BeanFactoryAware.setBeanFactory()
- ApplicationContextAware.setApplicationContext()
- BeanPostProcessor.postProcessBeforeInitialization()
- @PostConstruct / InitializingBean.afterPropertiesSet() / init-method
- BeanPostProcessor.postProcessAfterInitialization()
↓
4. 使用 (In Use)
↓
5. 销毁 (Destruction)
- @PreDestroy / DisposableBean.destroy() / destroy-method代码示例:
java
@Component
public class MyBean implements InitializingBean, DisposableBean {
public MyBean() {
System.out.println("1. 构造器");
}
@PostConstruct
public void postConstruct() {
System.out.println("2. @PostConstruct");
}
@Override
public void afterPropertiesSet() {
System.out.println("3. InitializingBean.afterPropertiesSet()");
}
@PreDestroy
public void preDestroy() {
System.out.println("4. @PreDestroy");
}
@Override
public void destroy() {
System.out.println("5. DisposableBean.destroy()");
}
}Bean的作用域有哪些?
答案:
| 作用域 | 说明 |
|---|---|
| singleton | 单例(默认),整个容器只有一个实例 |
| prototype | 原型,每次获取都创建新实例 |
| request | 每个HTTP请求一个实例(Web) |
| session | 每个HTTP会话一个实例(Web) |
| application | 整个ServletContext一个实例(Web) |
java
@Component
@Scope("prototype")
public class PrototypeBean {
}
// 或使用常量
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)Spring如何解决循环依赖?
答案:
三级缓存机制:
java
// 一级缓存:成品对象
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二级缓存:半成品对象(已实例化,未初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三级缓存:对象工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);解决流程(以A依赖B,B依赖A为例):
1. 创建A,放入三级缓存
2. A注入B,发现B不存在
3. 创建B,放入三级缓存
4. B注入A,从三级缓存获取A(半成品)
5. B初始化完成,放入一级缓存
6. A注入B成功,初始化完成,放入一级缓存注意:
- 只能解决setter注入的循环依赖
- 不能解决构造器注入的循环依赖
- 不能解决prototype的循环依赖
java
// 可以解决
@Service
public class A {
@Autowired
private B b;
}
@Service
public class B {
@Autowired
private A a;
}
// 无法解决
@Service
public class A {
private B b;
@Autowired
public A(B b) {
this.b = b;
}
}AOP
什么是AOP?
答案: AOP (Aspect Oriented Programming) 面向切面编程,通过预编译或运行时动态代理实现程序功能的统一维护。
核心概念:
- 切面(Aspect):横切关注点的模块化
- 连接点(JoinPoint):程序执行的某个点(方法调用、异常抛出等)
- 切点(Pointcut):匹配连接点的表达式
- 通知(Advice):在切点执行的动作
- 织入(Weaving):将切面应用到目标对象的过程
通知类型:
- @Before:前置通知
- @After:后置通知
- @AfterReturning:返回后通知
- @AfterThrowing:异常通知
- @Around:环绕通知
java
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Before("serviceLayer()")
public void before(JoinPoint joinPoint) {
System.out.println("方法执行前: " + joinPoint.getSignature().getName());
}
@Around("serviceLayer()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long end = System.currentTimeMillis();
System.out.println("方法执行耗时: " + (end - start) + "ms");
return result;
}
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
System.out.println("方法抛异常: " + ex.getMessage());
}
}Spring AOP和AspectJ的区别?
答案:
| 特性 | Spring AOP | AspectJ |
|---|---|---|
| 实现方式 | 动态代理 | 编译时织入 |
| 织入时机 | 运行时 | 编译时/类加载时 |
| 功能 | 方法级别 | 字段、方法、构造器等 |
| 性能 | 较低 | 较高 |
| 易用性 | 简单 | 复杂 |
JDK动态代理和CGLIB代理的区别?
答案:
| 特性 | JDK动态代理 | CGLIB代理 |
|---|---|---|
| 实现方式 | 基于接口 | 基于继承 |
| 要求 | 目标类必须实现接口 | 目标类不能是final |
| 性能 | 较低 | 较高 |
| 使用场景 | 有接口 | 无接口 |
java
// JDK动态代理
public interface UserService {
void save();
}
@Service
public class UserServiceImpl implements UserService {
public void save() {}
}
// CGLIB代理
@Service
public class UserService { // 没有接口
public void save() {}
}Spring选择策略:
- 目标类实现了接口 → JDK动态代理
- 目标类没有接口 → CGLIB代理
- 可以强制使用CGLIB:
@EnableAspectJAutoProxy(proxyTargetClass = true)
事务管理
Spring事务的传播行为?
答案:
| 传播行为 | 说明 |
|---|---|
| REQUIRED(默认) | 有事务就加入,没有就新建 |
| REQUIRES_NEW | 总是新建事务,挂起当前事务 |
| SUPPORTS | 有事务就加入,没有就非事务执行 |
| NOT_SUPPORTED | 非事务执行,挂起当前事务 |
| MANDATORY | 必须在事务中执行,否则抛异常 |
| NEVER | 必须非事务执行,否则抛异常 |
| NESTED | 嵌套事务,外层回滚内层也回滚 |
java
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder() {
// 保存订单
orderDao.save(order);
// 调用库存服务
stockService.reduceStock(); // 加入当前事务
}
}
@Service
public class StockService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void reduceStock() {
// 减库存(新事务,独立提交)
}
}Spring事务的隔离级别?
答案:
| 隔离级别 | 说明 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|---|
| READ_UNCOMMITTED | 读未提交 | ✓ | ✓ | ✓ |
| READ_COMMITTED | 读已提交 | ✗ | ✓ | ✓ |
| REPEATABLE_READ | 可重复读 | ✗ | ✗ | ✓ |
| SERIALIZABLE | 串行化 | ✗ | ✗ | ✗ |
java
@Transactional(isolation = Isolation.READ_COMMITTED)
public void method() {
}@Transactional失效的场景?
答案:
- 方法不是public
java
@Transactional
private void method() {} // 失效- 同类方法调用
java
@Service
public class UserService {
public void methodA() {
methodB(); // 失效,没有走代理
}
@Transactional
public void methodB() {}
}- 异常被捕获
java
@Transactional
public void method() {
try {
// 业务代码
} catch (Exception e) {
// 异常被捕获,事务不回滚
}
}- 异常类型不匹配
java
@Transactional // 默认只回滚RuntimeException
public void method() throws Exception {
throw new Exception(); // 不回滚
}
// 解决方案
@Transactional(rollbackFor = Exception.class)- 数据库不支持事务(如MyISAM)
练习题
- @Autowired和@Resource的区别?
- Spring如何实现单例Bean的线程安全?
- BeanFactory和ApplicationContext的区别?
- Spring Boot自动配置原理?