Skip to content

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的一种实现方式,通过容器动态地将依赖对象注入到组件中。

三种注入方式

  1. 构造器注入(推荐)
java
@Service
public class UserService {
    private final UserDao userDao;

    @Autowired
    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }
}
  1. Setter注入
java
@Service
public class UserService {
    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
}
  1. 字段注入(不推荐)
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 AOPAspectJ
实现方式动态代理编译时织入
织入时机运行时编译时/类加载时
功能方法级别字段、方法、构造器等
性能较低较高
易用性简单复杂

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失效的场景?

答案:

  1. 方法不是public
java
@Transactional
private void method() {} // 失效
  1. 同类方法调用
java
@Service
public class UserService {
    public void methodA() {
        methodB(); // 失效,没有走代理
    }

    @Transactional
    public void methodB() {}
}
  1. 异常被捕获
java
@Transactional
public void method() {
    try {
        // 业务代码
    } catch (Exception e) {
        // 异常被捕获,事务不回滚
    }
}
  1. 异常类型不匹配
java
@Transactional // 默认只回滚RuntimeException
public void method() throws Exception {
    throw new Exception(); // 不回滚
}

// 解决方案
@Transactional(rollbackFor = Exception.class)
  1. 数据库不支持事务(如MyISAM)

练习题

  1. @Autowired和@Resource的区别?
  2. Spring如何实现单例Bean的线程安全?
  3. BeanFactory和ApplicationContext的区别?
  4. Spring Boot自动配置原理?

Released under the MIT License.