口语八股——Spring 面试实战指南(二):事务管理篇、Spring MVC 篇、Spring Boot 篇、Bean生命周期篇

📌 三、事务管理篇

3.1 Spring事务的传播行为有哪些?什么时候用哪种?

✅ 正确回答思路:

Spring事务的传播行为是面试高频考点,我详细说明:

一、什么是事务传播行为?

场景:

java 复制代码
@Service
public class OrderService {
    @Autowired
    private UserService userService;
    
    @Transactional
    public void createOrder() {
        // 插入订单
        orderDao.insert(order);
        
        // 调用另一个有事务的方法
        userService.updateUserPoints(); // UserService的方法也有@Transactional
        
        // 这两个事务是什么关系?
        // - 在同一个事务里?
        // - 各自独立的事务?
        // - 还是其他关系?
    }
}

事务传播行为就是定义:当一个事务方法被另一个事务方法调用时,这个方法应该如何处理事务。

二、7种传播行为

1. REQUIRED(默认)

java 复制代码
@Transactional(propagation = Propagation.REQUIRED)
public void method() {
    // ...
}

含义:

  • 如果当前有事务,就加入这个事务
  • 如果当前没有事务,就新建一个事务

示例:

java 复制代码
@Service
public class OrderService {
    @Autowired
    private UserService userService;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder() {
        orderDao.insert(order);  // 操作1
        userService.updatePoints();  // 操作2
    }
}

@Service
public class UserService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void updatePoints() {
        userDao.update(user);  // 操作3
    }
}

结果 : 操作1、2、3在同一个事务里,任何一个失败都会全部回滚。

2. REQUIRES_NEW

java 复制代码
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void method() {
    // ...
}

含义:

  • 无论当前有没有事务,都新建一个事务
  • 如果当前有事务,把当前事务挂起

示例:

java 复制代码
@Service
public class OrderService {
    @Autowired
    private LogService logService;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder() {
        orderDao.insert(order);  // 事务A
        logService.saveLog();  // 事务B(新事务)
        
        int i = 1 / 0;  // 抛异常,事务A回滚
    }
}

@Service
public class LogService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog() {
        logDao.insert(log);  // 在新事务里,不受外层事务影响
    }
}

结果:

  • 订单插入失败,回滚
  • 但日志插入成功!因为是新事务,不受外层事务影响

使用场景: 记录操作日志,无论业务成功还是失败,日志都要保存。

3. SUPPORTS

java 复制代码
@Transactional(propagation = Propagation.SUPPORTS)
public void method() {
    // ...
}

含义:

  • 如果当前有事务,就加入这个事务
  • 如果当前没有事务,就以非事务方式执行

使用场景: 查询方法,有事务就用事务,没事务也无所谓。

4. NOT_SUPPORTED

java 复制代码
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method() {
    // ...
}

含义:

  • 以非事务方式执行
  • 如果当前有事务,把当前事务挂起

使用场景: 不需要事务的操作,比如发送邮件、调用第三方API。

5. MANDATORY

java 复制代码
@Transactional(propagation = Propagation.MANDATORY)
public void method() {
    // ...
}

含义:

  • 必须在一个事务中执行
  • 如果当前没有事务,抛异常

使用场景: 必须在事务中执行的子方法。

6. NEVER

java 复制代码
@Transactional(propagation = Propagation.NEVER)
public void method() {
    // ...
}

含义:

  • 必须以非事务方式执行
  • 如果当前有事务,抛异常

使用场景: 极少使用。

7. NESTED(嵌套事务)

java 复制代码
@Transactional(propagation = Propagation.NESTED)
public void method() {
    // ...
}

含义:

  • 如果当前有事务,在嵌套事务中执行
  • 如果当前没有事务,等同于REQUIRED

和REQUIRES_NEW的区别:

  • REQUIRES_NEW: 完全独立的新事务,互不影响
  • NESTED: 嵌套事务,子事务回滚不影响父事务,但父事务回滚会影响子事务

示例:

java 复制代码
@Service
public class OrderService {
    @Autowired
    private CouponService couponService;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder() {
        orderDao.insert(order);  // 父事务
        
        try {
            couponService.useCoupon();  // 子事务(NESTED)
        } catch (Exception e) {
            // 子事务失败,不影响父事务
        }
        
        // 订单还是会成功
    }
}

@Service
public class CouponService {
    @Transactional(propagation = Propagation.NESTED)
    public void useCoupon() {
        couponDao.update(coupon);
        throw new RuntimeException("优惠券不可用"); // 子事务回滚
    }
}

结果: 优惠券更新回滚,但订单插入成功。

底层原理: 通过**JDBC的SavePoint(保存点)**实现。

需要注意:NESTED 事务依赖底层数据库和 JDBC 驱动对 SavePoint 的支持,并非所有数据库都支持。

三、传播行为对比表

传播行为 当前有事务 当前无事务 使用场景
REQUIRED(默认) 加入当前事务 新建事务 大部分场景
REQUIRES_NEW 挂起当前事务,新建事务 新建事务 操作日志
SUPPORTS 加入当前事务 非事务执行 查询方法
NOT_SUPPORTED 挂起当前事务,非事务执行 非事务执行 发邮件、调API
MANDATORY 加入当前事务 抛异常 必须在事务中的子方法
NEVER 抛异常 非事务执行 极少使用
NESTED 嵌套事务 新建事务 可选的子操作

四、实际项目经验

场景1: 订单创建流程

java 复制代码
@Service
public class OrderService {
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private LogService logService;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(OrderDTO dto) {
        // 1. 创建订单
        Order order = new Order();
        orderDao.insert(order);
        
        // 2. 扣减库存(同一个事务)
        inventoryService.deductStock(dto.getProductId(), dto.getQuantity());
        
        // 3. 记录日志(独立事务,无论成功失败都要记录)
        logService.saveOperationLog("创建订单", order.getId());
    }
}

@Service
public class InventoryService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void deductStock(Long productId, Integer quantity) {
        // 扣减库存
        inventoryDao.deduct(productId, quantity);
    }
}

@Service
public class LogService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveOperationLog(String operation, Long orderId) {
        // 独立事务,不受外层事务影响
        logDao.insert(new Log(operation, orderId));
    }
}

场景2: 批量导入用户

java 复制代码
@Service
public class UserImportService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void importUsers(List<User> users) {
        for (User user : users) {
            try {
                // 每个用户用嵌套事务,失败不影响其他用户
                importSingleUser(user);
            } catch (Exception e) {
                log.error("导入用户失败: {}", user.getName(), e);
                // 继续导入下一个用户
            }
        }
    }
    
    @Transactional(propagation = Propagation.NESTED)
    public void importSingleUser(User user) {
        userDao.insert(user);
        // 可能抛异常
    }
}

💡 记忆口诀:

  • REQUIRED: 有事务加入,无事务新建
  • REQUIRES_NEW: 挂起旧事务,总是新建
  • NESTED: 嵌套子事务,父影响子,子不影响父
  • SUPPORTS: 有事务支持,无事务也行
  • NOT_SUPPORTED: 挂起事务,非事务执行

3.2 Spring事务什么时候会失效?

✅ 正确回答思路:

这是个常见的坑,很多人踩过!我总结了7种情况:

1. 方法不是public

java 复制代码
@Service
public class UserService {
    
    // ❌ private方法,事务失效
    @Transactional
    private void addUser(User user) {
        userDao.insert(user);
    }
    
    // ❌ protected方法,事务失效
    @Transactional
    protected void updateUser(User user) {
        userDao.update(user);
    }
}

原因 : 基于代理机制的 Spring AOP 默认只对 public 方法生效,

这是由于动态代理的限制(不包含 AspectJ 编译期织入)。

解决: 改成public。

2. 方法被final修饰

java 复制代码
@Service
public class UserService {
    
    // ❌ final方法,事务失效(CGLIB代理)
    @Transactional
    public final void addUser(User user) {
        userDao.insert(user);
    }
}

原因 : CGLIB是通过生成子类来代理的,final方法无法被子类重写。如果类被 final 修饰,CGLIB 无法生成子类代理,事务同样会失效。

解决: 去掉final。

3. 类内部调用

java 复制代码
@Service
public class UserService {
    
    public void method1() {
        this.method2();  // ❌ this是目标对象,不是代理对象,事务失效
    }
    
    @Transactional
    public void method2() {
        userDao.insert(user);
    }
}

原因 : this.method2()调用的是目标对象的方法,不是代理对象的方法,不会走AOP代理。

解决:

java 复制代码
@Service
public class UserService {
    @Autowired
    private ApplicationContext context;
    
    public void method1() {
        // ✅ 从Spring容器获取代理对象
        UserService proxy = context.getBean(UserService.class);
        proxy.method2();
    }
    
    @Transactional
    public void method2() {
        userDao.insert(user);
    }
}

4. 异常被捕获了

java 复制代码
@Service
public class UserService {
    
    @Transactional
    public void addUser(User user) {
        try {
            userDao.insert(user);
            int i = 1 / 0;  // 抛异常
        } catch (Exception e) {
            // ❌ 异常被捕获了,Spring不知道,不会回滚
            log.error("插入失败", e);
        }
    }
}

原因 : Spring事务默认只对未捕获的RuntimeExceptionError回滚。

解决:

java 复制代码
@Transactional
public void addUser(User user) {
    try {
        userDao.insert(user);
        int i = 1 / 0;
    } catch (Exception e) {
        log.error("插入失败", e);
        // ✅ 手动设置回滚
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        // 或者抛出异常
        throw new RuntimeException(e);
    }
}

5. 抛出的异常类型不对

java 复制代码
@Service
public class UserService {
    
    // ❌ 抛出的是checked异常,不会回滚
    @Transactional
    public void addUser(User user) throws Exception {
        userDao.insert(user);
        throw new Exception("插入失败");  // checked异常,不回滚
    }
}

原因 : Spring事务默认只回滚RuntimeExceptionError ,不回滚checked异常

解决:

java 复制代码
// 方案1: 抛出RuntimeException
@Transactional
public void addUser(User user) {
    userDao.insert(user);
    throw new RuntimeException("插入失败");
}

// 方案2: 指定回滚的异常类型
@Transactional(rollbackFor = Exception.class)
public void addUser(User user) throws Exception {
    userDao.insert(user);
    throw new Exception("插入失败");  // 现在会回滚了
}

6. 数据库引擎不支持事务

sql 复制代码
-- ❌ MyISAM引擎不支持事务
CREATE TABLE user (
    id bigint PRIMARY KEY
) ENGINE=MyISAM;

解决: 改成InnoDB引擎。

sql 复制代码
-- ✅ InnoDB支持事务
CREATE TABLE user (
    id bigint PRIMARY KEY
) ENGINE=InnoDB;

7. 没有被Spring管理

java 复制代码
// ❌ 没有@Service注解,不是Spring Bean
public class UserService {
    
    @Transactional
    public void addUser(User user) {
        userDao.insert(user);
    }
}

解决: 加上@Service等注解,让Spring管理。

java 复制代码
// ✅ 被Spring管理
@Service
public class UserService {
    @Transactional
    public void addUser(User user) {
        userDao.insert(user);
    }
}

8. 切面优先级顺序错误

场景 : 如果你在同一个方法上既有 @Transactional,又有自定义的 AOP 切面(比如异常捕获切面)。
原因 : 如果自定义切面的优先级高于事务切面,且自定义切面在 catch 块中吞掉了异常 (没有抛出),事务切面就捕获不到异常,导致无法回滚。
解决:

  1. 确保异常在自定义切面中被抛出。
  2. 使用 @Order 控制切面顺序,让事务切面在最外层。

9. 传播行为设置错误

java 复制代码
@Service
public class UserService {
    
    // ❌ NOT_SUPPORTED表示不支持事务
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void addUser(User user) {
        userDao.insert(user);
    }
}

解决: 检查传播行为配置。

10. 多线程调用

java 复制代码
@Service
public class UserService {
    
    @Transactional
    public void addUser(User user) {
        userDao.insert(user);
        
        // ❌ 新线程里调用方法,事务失效
        new Thread(() -> {
            this.updateUser(user);  // 不在同一个线程,事务失效
        }).start();
    }
    
    @Transactional
    public void updateUser(User user) {
        userDao.update(user);
    }
}

原因 : Spring事务是通过ThreadLocal实现的,不同线程的事务是隔离的。

解决: 不要在事务方法里开新线程,或者在新线程里重新开启事务。

总结图表:

失效原因 解决办法
方法不是public 改成public
方法是final 去掉final
类内部调用 从Spring容器获取代理对象
异常被捕获 手动设置回滚或抛出异常
异常类型不对 指定rollbackFor
数据库不支持事务 改用InnoDB引擎
没有被Spring管理 加@Service等注解
传播行为设置错误 检查propagation配置
多线程调用 避免多线程或重新开启事务

💡 记忆口诀:

  • public方法才有效
  • final方法代理不了
  • 内部调用不走代理
  • 捕获异常要手动回滚
  • checked异常要配置
  • MyISAM不支持事务
  • 类要被Spring管理
  • 多线程事务隔离

📌 四、Spring MVC篇

4.1 Spring MVC的工作流程是什么?

✅ 正确回答思路:

SpringMVC是面试必考的,我画图加代码来说明:

一、SpringMVC的核心组件

  1. DispatcherServlet(前端控制器): 核心,负责调度整个流程
  2. HandlerMapping(处理器映射器): 根据URL找到对应的Handler(Controller)
  3. HandlerAdapter(处理器适配器): 执行Handler
  4. ViewResolver(视图解析器): 解析视图名称,返回View对象

二、完整的请求流程

复制代码
用户浏览器
    ↓ 1. 发送请求 http://localhost:8080/user/1
DispatcherServlet(前端控制器)
    ↓ 2. 询问: 哪个Handler处理这个请求?
HandlerMapping(处理器映射器)
    ↓ 3. 回答: UserController的getUser方法
DispatcherServlet
    ↓ 4. 调用Handler
HandlerAdapter(处理器适配器)
    ↓ 5. 执行UserController.getUser()
UserController
    ↓ 6. 返回ModelAndView(逻辑视图名"user", 模型数据user对象)
DispatcherServlet
    ↓ 7. 询问: "user"对应哪个视图文件?
ViewResolver(视图解析器)
    ↓ 8. 回答: /WEB-INF/views/user.jsp
DispatcherServlet
    ↓ 9. 渲染视图
View(JSP)
    ↓ 10. 返回HTML
用户浏览器
    ↓ 11. 显示页面

三、详细的9个步骤

步骤1: 用户发送请求

复制代码
GET http://localhost:8080/user/1

步骤2: DispatcherServlet拦截请求

DispatcherServlet是一个Servlet,配置在web.xml中拦截所有请求:

xml 复制代码
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

步骤3: HandlerMapping查找Handler

DispatcherServlet调用HandlerMapping,根据URL找到对应的Handler:

java 复制代码
// 伪代码
HandlerExecutionChain handler = handlerMapping.getHandler(request);
// 返回: UserController.getUser()方法 + 拦截器链

Spring MVC有多种HandlerMapping:

  • RequestMappingHandlerMapping: 处理@RequestMapping注解(最常用)
  • BeanNameUrlHandlerMapping: 根据Bean名称映射URL

步骤4: 选择HandlerAdapter

DispatcherServlet选择合适的HandlerAdapter来执行Handler:

java 复制代码
// 伪代码
HandlerAdapter adapter = getHandlerAdapter(handler);

常见的HandlerAdapter:

  • RequestMappingHandlerAdapter: 处理@RequestMapping注解的方法
  • HttpRequestHandlerAdapter: 处理HttpRequestHandler
  • SimpleControllerHandlerAdapter: 处理Controller接口

步骤5: 执行拦截器preHandle

HandlerInterceptor 的三个核心方法:

  • preHandle:方法执行前
  • postHandle:方法执行后、视图渲染前
  • afterCompletion:整个请求完成后(常用于资源清理)
java 复制代码
// 执行拦截器链的preHandle方法
for (HandlerInterceptor interceptor : interceptors) {
    if (!interceptor.preHandle(request, response, handler)) {
        return; // 返回false,中断请求
    }
}

步骤6: HandlerAdapter执行Handler

java 复制代码
// 伪代码
ModelAndView mav = adapter.handle(request, response, handler);

实际执行Controller的方法:

java 复制代码
@Controller
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public ModelAndView getUser(@PathVariable Long id) {
        User user = userService.getUser(id);
        
        ModelAndView mav = new ModelAndView();
        mav.setViewName("user");  // 逻辑视图名
        mav.addObject("user", user);  // 模型数据
        
        return mav;
    }
}

步骤7: 执行拦截器postHandle

java 复制代码
// 执行拦截器链的postHandle方法
for (HandlerInterceptor interceptor : interceptors) {
    interceptor.postHandle(request, response, handler, mav);
}

步骤8: ViewResolver解析视图

DispatcherServlet拿到逻辑视图名"user",调用ViewResolver解析:

java 复制代码
// 伪代码
View view = viewResolver.resolveViewName("user", locale);
// 返回: JstlView(/WEB-INF/views/user.jsp)

配置ViewResolver:

java 复制代码
@Bean
public ViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");  // 前缀
    resolver.setSuffix(".jsp");  // 后缀
    return resolver;
    // "user" → "/WEB-INF/views/user.jsp"
}

步骤9: 渲染视图

java 复制代码
// 把模型数据填充到视图
view.render(mav.getModelMap(), request, response);

JSP渲染HTML,返回给浏览器。

步骤10: 执行拦截器afterCompletion

java 复制代码
// 执行拦截器链的afterCompletion方法
for (HandlerInterceptor interceptor : interceptors) {
    interceptor.afterCompletion(request, response, handler, ex);
}

四、现代的RESTful API (最常用)

现在我们大多用 @RestController,它相当于 @Controller + @ResponseBody。流程的区别在于:

  1. 不走 ViewResolver: 方法返回的对象不被视为视图名称。
  2. HttpMessageConverter : DispatcherServlet 会利用 HttpMessageConverter (如 MappingJackson2HttpMessageConverter) 将对象序列化为 JSON。
  3. 直接写入响应 : 序列化后的 JSON 直接写入 HttpServletResponse 的输出流。

五、拦截器vs过滤器

对比项 过滤器(Filter) 拦截器(Interceptor)
所属 Servlet规范 Spring MVC
拦截范围 所有请求 只拦截Controller
执行时机 DispatcherServlet之前 DispatcherServlet之后,Handler之前
IOC容器 不能直接注入Spring Bean 可以注入Spring Bean
应用场景 编码、跨域、认证 权限校验、日志、参数校验

实际项目经验:

我们项目用拦截器实现了:

1. 登录校验

java 复制代码
@Component
public class LoginInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 从请求头获取token
        String token = request.getHeader("Authorization");
        
        if (token == null || !isValidToken(token)) {
            response.setStatus(401);
            return false;  // 拦截请求
        }
        
        // 把用户信息放入ThreadLocal
        User user = getUserByToken(token);
        UserContext.setCurrentUser(user);
        
        return true;  // 放行
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 清理ThreadLocal
        UserContext.clear();
    }
}

// 配置拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
            .addPathPatterns("/api/**")  // 拦截/api/**
            .excludePathPatterns("/api/login", "/api/register");  // 排除登录、注册接口
    }
}

💡 总结: SpringMVC的核心流程:

  1. DispatcherServlet拦截请求
  2. HandlerMapping找Handler
  3. HandlerAdapter执行Handler
  4. ViewResolver解析视图(RESTful API不需要)
  5. 渲染视图,返回响应

💡 面试技巧: 可以画个流程图,更直观!

📌 五、Spring Boot篇

5.1 Spring Boot的自动配置原理是什么?

✅ 正确回答思路:

Spring Boot的自动配置是它最核心的特性,我从原理到源码来说明:

一、什么是自动配置?

传统Spring项目:

xml 复制代码
<!-- 要配置数据源 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>

<!-- 要配置SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 要配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 配置很多很多... -->

Spring Boot项目:

yaml 复制代码
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456

就这么简单! Spring Boot自动帮我们配置了DataSource、SqlSessionFactory、TransactionManager等所有需要的Bean。

二、自动配置的核心原理

核心注解 : @SpringBootApplication

java 复制代码
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@SpringBootApplication是个组合注解:

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration  // 相当于@Configuration
@EnableAutoConfiguration  // 核心!自动配置的关键
@ComponentScan  // 扫描当前包及子包的@Component等注解
public @interface SpringBootApplication {
}

关键就是@EnableAutoConfiguration:

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage  // 自动配置包
@Import(AutoConfigurationImportSelector.class)  // 核心!导入自动配置类
public @interface EnableAutoConfiguration {
}

三、AutoConfigurationImportSelector做了什么?

这个类负责导入所有的自动配置类:

java 复制代码
public class AutoConfigurationImportSelector implements DeferredImportSelector {
    
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        // 1. 获取所有的自动配置类
        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
        
        // 2. 去重
        configurations = removeDuplicates(configurations);
        
        // 3. 排除不需要的
        configurations = exclude(configurations);
        
        // 4. 过滤(根据条件注解@ConditionalOnXxx)
        configurations = filter(configurations);
        
        // 5. 返回最终的自动配置类列表
        return configurations.toArray(new String[0]);
    }
    
    protected List<String> getCandidateConfigurations() {
        // 从META-INF/spring.factories文件读取配置类
        return SpringFactoriesLoader.loadFactoryNames(
            EnableAutoConfiguration.class, 
            classLoader
        );
    }
}

四、spring.factories文件

Spring Boot在各个starter的jar包里都有一个META-INF/spring.factories文件:

properties 复制代码
# spring-boot-autoconfigure-3.x.x.jar里的spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
... (一共130多个自动配置类)

Spring Boot启动时会读取所有jar包里的这个文件,加载所有的自动配置类。

五、条件注解(@ConditionalOnXxx)

但是!不是所有自动配置类都会生效,要看条件注解。

以DataSourceAutoConfiguration为例:

java 复制代码
@Configuration
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})  // 类路径有DataSource类才生效
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")  // 没有R2DBC的ConnectionFactory才生效
@EnableConfigurationProperties(DataSourceProperties.class)  // 启用配置属性
@Import({DataSourcePoolMetadataProvidersConfiguration.class})
public class DataSourceAutoConfiguration {
    
    @Configuration
    @Conditional(PooledDataSourceCondition.class)  // 有数据库连接池才生效
    @ConditionalOnMissingBean({DataSource.class, XADataSource.class})  // 用户没有自定义DataSource才生效
    @Import({HikariConfiguration.class, Tomcat.class, Dbcp2.class, ...})
    protected static class PooledDataSourceConfiguration {
    }
    
    @Configuration
    @ConditionalOnClass(HikariDataSource.class)  // 有HikariCP才生效
    @ConditionalOnMissingBean(DataSource.class)
    @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true)
    static class HikariConfiguration {
        
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.hikari")
        HikariDataSource dataSource(DataSourceProperties properties) {
            HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
            // ... 配置数据源
            return dataSource;
        }
    }
}

常用的条件注解:

注解 作用
@ConditionalOnClass 类路径有指定的类才生效
@ConditionalOnMissingClass 类路径没有指定的类才生效
@ConditionalOnBean 容器有指定的Bean才生效
@ConditionalOnMissingBean 容器没有指定的Bean才生效
@ConditionalOnProperty 配置文件有指定的属性才生效
@ConditionalOnResource 类路径有指定的资源才生效
@ConditionalOnWebApplication 是Web应用才生效
@ConditionalOnNotWebApplication 不是Web应用才生效

六、自动配置的完整流程

复制代码
1. @SpringBootApplication启动
   ↓
2. @EnableAutoConfiguration生效
   ↓
3. AutoConfigurationImportSelector加载META-INF/spring.factories
   ↓
4. 读取所有的自动配置类(130多个)
   ↓
5. 根据@ConditionalOnXxx条件注解过滤
   ↓
6. 生效的自动配置类创建Bean
   ↓
7. 读取application.yml的配置(@ConfigurationProperties)
   ↓
8. 注入到Bean的属性
   ↓
9. 自动配置完成!

七、自定义一个Starter

理解了原理,我们也可以自己写一个Starter:

1. 创建starter项目

xml 复制代码
<!-- my-spring-boot-starter/pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
</dependencies>

2. 创建配置属性类

java 复制代码
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
    private String name;
    private int timeout = 3000;
    
    // getter/setter
}

3. 创建Service

java 复制代码
public class MyService {
    private String name;
    private int timeout;
    
    public MyService(String name, int timeout) {
        this.name = name;
        this.timeout = timeout;
    }
    
    public void doSomething() {
        System.out.println("MyService: " + name + ", timeout: " + timeout);
    }
}

4. 创建自动配置类

java 复制代码
@Configuration
@ConditionalOnClass(MyService.class)  // 类路径有MyService才生效
@EnableConfigurationProperties(MyServiceProperties.class)  // 启用配置属性
public class MyServiceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean  // 用户没有自定义MyService才生效
    public MyService myService(MyServiceProperties properties) {
        return new MyService(properties.getName(), properties.getTimeout());
    }
}

5. 创建spring.factories

properties 复制代码
# src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.MyServiceAutoConfiguration

6. 使用Starter

其他项目引入我们的starter:

xml 复制代码
<dependency>
    <groupId>com.example</groupId>
    <artifactId>my-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

配置文件:

yaml 复制代码
my:
  service:
    name: MyService
    timeout: 5000

使用:

java 复制代码
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@RestController
public class TestController {
    @Autowired
    private MyService myService;  // 自动注入!
    
    @GetMapping("/test")
    public String test() {
        myService.doSomething();
        return "OK";
    }
}

八、实际项目经验

在我的项目中,我们自定义了一个统一日志Starter:

java 复制代码
@Configuration
@EnableConfigurationProperties(LogProperties.class)
public class LogAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public LogAspect logAspect(LogProperties properties) {
        return new LogAspect(properties);
    }
    
    @Bean
    @ConditionalOnProperty(name = "log.storage.enabled", havingValue = "true")
    public LogStorage logStorage() {
        return new LogStorage();
    }
}

所有微服务只需要引入这个starter,就自动具备了日志记录功能,不需要重复配置。

💡 总结: Spring Boot自动配置的核心:

  1. @EnableAutoConfiguration触发自动配置
  2. AutoConfigurationImportSelector加载配置类
  3. spring.factories文件列出所有配置类
  4. @ConditionalOnXxx条件注解决定是否生效
  5. @ConfigurationProperties读取配置文件

💡 面试加分项: "Spring Boot的自动配置体现了约定优于配置的思想,通过智能的默认配置,让开发者专注于业务逻辑,而不是配置文件。"


5.2 Spring Boot的启动流程是什么?

✅ 正确回答思路:

一、启动入口

java 复制代码
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

这一行代码做了很多事情!

二、SpringApplication的创建

java 复制代码
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return new SpringApplication(primarySource).run(args);
}

分两步:

  1. 创建SpringApplication对象
  2. 调用run方法

创建SpringApplication对象做了什么?

java 复制代码
public SpringApplication(Class<?>... primarySources) {
    // 1. 保存主配置类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    
    // 2. 判断应用类型(Servlet、Reactive、None)
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    // 3. 加载ApplicationContextInitializer(从spring.factories)
    setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class));
    
    // 4. 加载ApplicationListener(从spring.factories)
    setListeners(getSpringFactoriesInstances(ApplicationListener.class));
    
    // 5. 推断主类(有main方法的类)
    this.mainApplicationClass = deduceMainApplicationClass();
}

三、run方法的执行流程

java 复制代码
public ConfigurableApplicationContext run(String... args) {
    // 1. 创建StopWatch,用于统计启动耗时
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    
    // 2. 创建Bootstrap上下文
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    
    ConfigurableApplicationContext context = null;
    
    // 3. 配置Headless属性(java.awt.headless)
    configureHeadlessProperty();
    
    // 4. 获取SpringApplicationRunListeners(启动监听器)
    SpringApplicationRunListeners listeners = getRunListeners(args);
    
    // 5. 发布ApplicationStartingEvent事件
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    
    try {
        // 6. 封装命令行参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        
        // 7. 准备环境(Environment)
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        
        // 8. 打印Banner
        Banner printedBanner = printBanner(environment);
        
        // 9. 创建ApplicationContext(IOC容器)
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        
        // 10. 准备ApplicationContext
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        
        // 11. 刷新ApplicationContext(核心!)
        refreshContext(context);
        
        // 12. 刷新后的处理
        afterRefresh(context, applicationArguments);
        
        // 13. 停止计时
        stopWatch.stop();
        
        // 14. 打印启动日志
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        
        // 15. 发布ApplicationStartedEvent事件
        listeners.started(context);
        
        // 16. 调用ApplicationRunner和CommandLineRunner
        callRunners(context, applicationArguments);
        
    } catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    
    try {
        // 17. 发布ApplicationReadyEvent事件
        listeners.ready(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }
    
    // 18. 返回ApplicationContext
    return context;
}

详细说明几个关键步骤:

步骤7: 准备环境(prepareEnvironment)

java 复制代码
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ...) {
    // 1. 创建Environment对象
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    
    // 2. 配置Environment(命令行参数、系统属性)
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    
    // 3. 发布ApplicationEnvironmentPreparedEvent事件
    listeners.environmentPrepared(bootstrapContext, environment);
    
    // 4. 绑定环境到SpringApplication
    bindToSpringApplication(environment);
    
    return environment;
}

Environment是什么?

  • 包含所有的配置信息:系统属性、环境变量、配置文件(application.yml)
  • 通过@Value@ConfigurationProperties注入的值都来自Environment

步骤9: 创建ApplicationContext

根据应用类型创建不同的容器:

java 复制代码
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        switch (this.webApplicationType) {
            case SERVLET:
                // Web应用,创建AnnotationConfigServletWebServerApplicationContext
                contextClass = AnnotationConfigServletWebServerApplicationContext.class;
                break;
            case REACTIVE:
                // 响应式应用
                contextClass = AnnotationConfigReactiveWebServerApplicationContext.class;
                break;
            default:
                // 非Web应用
                contextClass = AnnotationConfigApplicationContext.class;
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

步骤10: 准备ApplicationContext

java 复制代码
private void prepareContext(...) {
    // 1. 设置Environment
    context.setEnvironment(environment);
    
    // 2. 后置处理ApplicationContext
    postProcessApplicationContext(context);
    
    // 3. 执行ApplicationContextInitializer
    applyInitializers(context);
    
    // 4. 发布ApplicationContextInitializedEvent事件
    listeners.contextPrepared(context);
    
    // 5. 注册启动参数Bean
    context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
    
    // 6. 加载配置源(主配置类)
    Set<Object> sources = getAllSources();
    load(context, sources.toArray(new Object[0]));
    
    // 7. 发布ApplicationPreparedEvent事件
    listeners.contextLoaded(context);
}

步骤11: 刷新ApplicationContext(核心!)

java 复制代码
private void refreshContext(ConfigurableApplicationContext context) {
    // 调用AbstractApplicationContext的refresh方法
    refresh(context);
}

这一步会:

  • 创建BeanFactory
  • 加载BeanDefinition
  • 实例化所有单例Bean
  • 初始化Bean
  • 自动配置生效
  • 启动内嵌的Tomcat

这就是IOC容器的启动流程!(前面讲过的1.3节)

步骤16: 调用Runner

Spring Boot支持在启动完成后执行一些初始化逻辑:

java 复制代码
@Component
@Order(1)  // 指定执行顺序
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("ApplicationRunner执行了!");
        // 可以在这里做一些初始化工作:预热缓存、加载数据等
    }
}

@Component
@Order(2)
public class MyCommandLineRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("CommandLineRunner执行了!");
    }
}

区别:

  • ApplicationRunner: 参数是ApplicationArguments(封装了命令行参数)
  • CommandLineRunner: 参数是String数组(原始命令行参数)

四、启动流程图

复制代码
1. 创建SpringApplication对象
   - 判断应用类型
   - 加载初始化器和监听器
   ↓
2. 执行run方法
   ↓
3. 准备Environment
   - 读取配置文件
   - 设置环境变量
   ↓
4. 打印Banner
   ↓
5. 创建ApplicationContext
   ↓
6. 准备ApplicationContext
   - 注册配置类
   - 执行初始化器
   ↓
7. 刷新ApplicationContext(核心!)
   - 创建BeanFactory
   - 加载BeanDefinition
   - 实例化Bean
   - 自动配置
   - 启动Tomcat
   ↓
8. 调用Runner
   ↓
9. 发布ApplicationReadyEvent
   ↓
10. 启动完成!

五、实际项目经验

1. 自定义Banner

复制代码
# src/main/resources/banner.txt
 __  __         _                 _           _   _             
|  \/  |_   _  / \   _ __  _ __  | |_   ___  | |_(_) ___  _ __  
| |\/| | | | |/ _ \ | '_ \| '_ \ | __| / __| | __| |/ _ \| '_ \ 
| |  | | |_| / ___ \| |_) | |_) || |_ | (__  | |_| | (_) | | | |
|_|  |_|\__, /_/   \_\ .__/| .__/  \__| \___|  \__|_|\___/|_| |_|
        |___/        |_|   |_|                                    
Application Version: ${application.version}
Spring Boot Version: ${spring-boot.version}

2. 监听启动完成事件

java 复制代码
@Component
public class StartupListener implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        System.out.println("应用启动完成,开始预热缓存...");
        // 预热缓存
        cacheService.warmUp();
    }
}

3. 启动失败诊断

Spring Boot提供了FailureAnalyzer接口,可以分析启动失败的原因:

java 复制代码
public class MyFailureAnalyzer extends AbstractFailureAnalyzer<MyException> {
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, MyException cause) {
        return new FailureAnalysis(
            "数据库连接失败: " + cause.getMessage(),
            "请检查application.yml中的数据库配置",
            cause
        );
    }
}

💡 总结: Spring Boot启动流程:

  1. 创建SpringApplication对象(加载初始化器、监听器)
  2. 准备Environment(读取配置文件)
  3. 创建ApplicationContext(IOC容器)
  4. 刷新ApplicationContext(实例化Bean、自动配置、启动Tomcat)
  5. 调用Runner(初始化逻辑)
  6. 启动完成

💡 面试技巧: 可以说"Spring Boot的启动流程体现了模板方法模式,通过监听器和Runner提供了多个扩展点,让开发者可以在启动的不同阶段插入自定义逻辑。"


📌 六、Bean生命周期篇

6.1 Spring Bean的生命周期是什么?

✅ 正确回答思路:

这是Spring面试的高频题,我从创建到销毁完整讲解:

一、Bean生命周期概览

复制代码
1. 实例化Bean(Instantiation)
2. 设置属性值(Populate Properties)
3. 检查Aware接口(Aware接口回调)
4. BeanPostProcessor前置处理
5. 初始化Bean(Initialization)
6. BeanPostProcessor后置处理
7. Bean可用(Ready to use)
8. 销毁Bean(Destruction)

二、详细的生命周期

阶段1: 实例化Bean

Spring通过反射调用构造器创建Bean对象:

java 复制代码
// 伪代码
Class<?> clazz = Class.forName("com.example.User");
Object bean = clazz.getDeclaredConstructor().newInstance();

可以通过InstantiationAwareBeanPostProcessor拦截:

java 复制代码
@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        System.out.println("实例化前: " + beanName);
        return null;  // 返回null表示继续正常流程
    }
    
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) {
        System.out.println("实例化后: " + beanName);
        return true;  // 返回false会跳过属性赋值
    }
}

阶段2: 设置属性值

Spring通过反射给Bean的属性赋值:

java 复制代码
@Component
public class UserService {
    @Autowired
    private UserDao userDao;  // Spring会通过反射注入
    
    @Value("${app.name}")
    private String appName;  // 从配置文件注入
}

阶段3: Aware接口回调

如果Bean实现了某些Aware接口,Spring会回调这些方法:

java 复制代码
@Component
public class User implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {
    
    private String beanName;
    private BeanFactory beanFactory;
    private ApplicationContext applicationContext;
    
    @Override
    public void setBeanName(String name) {
        System.out.println("1. BeanNameAware: " + name);
        this.beanName = name;
    }
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        System.out.println("2. BeanFactoryAware");
        this.beanFactory = beanFactory;
    }
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        System.out.println("3. ApplicationContextAware");
        this.applicationContext = applicationContext;
    }
}

常用的Aware接口:

Aware接口 回调方法 作用
BeanNameAware setBeanName(String name) 获取Bean的名称
BeanFactoryAware setBeanFactory(BeanFactory) 获取BeanFactory
ApplicationContextAware setApplicationContext(ApplicationContext) 获取ApplicationContext
EnvironmentAware setEnvironment(Environment) 获取Environment
ResourceLoaderAware setResourceLoader(ResourceLoader) 获取资源加载器

阶段4: BeanPostProcessor前置处理

java 复制代码
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("初始化前: " + beanName);
        // 可以返回代理对象
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("初始化后: " + beanName);
        // AOP代理就是在这里创建的!
        return bean;
    }
}

阶段5: 初始化Bean

有三种方式定义初始化方法,按顺序执行:

java 复制代码
@Component
public class User implements InitializingBean {
    
    // 方式1: @PostConstruct注解
    @PostConstruct
    public void postConstruct() {
        System.out.println("1. @PostConstruct");
    }
    
    // 方式2: 实现InitializingBean接口
    @Override
    public void afterPropertiesSet() {
        System.out.println("2. InitializingBean.afterPropertiesSet()");
    }
    
    // 方式3: 自定义init方法
    public void initMethod() {
        System.out.println("3. init-method");
    }
}

// 配置init-method
@Configuration
public class AppConfig {
    @Bean(initMethod = "initMethod")
    public User user() {
        return new User();
    }
}

执行顺序: @PostConstruct → afterPropertiesSet() → init-method

阶段6: BeanPostProcessor后置处理

java 复制代码
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    // AOP代理在这里创建
    if (需要AOP增强) {
        return 创建代理对象(bean);
    }
    return bean;
}

阶段7: Bean可用

初始化完成,Bean可以使用了!

阶段8: 销毁Bean

容器关闭时,销毁Bean:

java 复制代码
@Component
public class User implements DisposableBean {
    
    // 方式1: @PreDestroy注解
    @PreDestroy
    public void preDestroy() {
        System.out.println("1. @PreDestroy");
    }
    
    // 方式2: 实现DisposableBean接口
    @Override
    public void destroy() {
        System.out.println("2. DisposableBean.destroy()");
    }
    
    // 方式3: 自定义destroy方法
    public void destroyMethod() {
        System.out.println("3. destroy-method");
    }
}

// 配置destroy-method
@Bean(destroyMethod = "destroyMethod")
public User user() {
    return new User();
}

执行顺序: @PreDestroy → destroy() → destroy-method

三、完整的生命周期代码示例

java 复制代码
@Component
public class LifecycleBean implements 
        BeanNameAware, 
        BeanFactoryAware, 
        ApplicationContextAware,
        InitializingBean,
        DisposableBean {
    
    private String beanName;
    
    public LifecycleBean() {
        System.out.println("1. 构造器");
    }
    
    @Value("${app.name:DefaultApp}")
    public void setAppName(String appName) {
        System.out.println("2. 设置属性: appName = " + appName);
    }
    
    @Override
    public void setBeanName(String name) {
        System.out.println("3. BeanNameAware: " + name);
        this.beanName = name;
    }
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        System.out.println("4. BeanFactoryAware");
    }
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        System.out.println("5. ApplicationContextAware");
    }
    
    // BeanPostProcessor.postProcessBeforeInitialization在这里执行
    
    @PostConstruct
    public void postConstruct() {
        System.out.println("6. @PostConstruct");
    }
    
    @Override
    public void afterPropertiesSet() {
        System.out.println("7. InitializingBean.afterPropertiesSet()");
    }
    
    public void initMethod() {
        System.out.println("8. init-method");
    }
    
    // BeanPostProcessor.postProcessAfterInitialization在这里执行
    
    // Bean可用!
    
    @PreDestroy
    public void preDestroy() {
        System.out.println("9. @PreDestroy");
    }
    
    @Override
    public void destroy() {
        System.out.println("10. DisposableBean.destroy()");
    }
    
    public void destroyMethod() {
        System.out.println("11. destroy-method");
    }
}

// 配置
@Configuration
public class AppConfig {
    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public LifecycleBean lifecycleBean() {
        return new LifecycleBean();
    }
}

// BeanPostProcessor
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (bean instanceof LifecycleBean) {
            System.out.println("5.5 BeanPostProcessor.postProcessBeforeInitialization");
        }
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof LifecycleBean) {
            System.out.println("8.5 BeanPostProcessor.postProcessAfterInitialization");
        }
        return bean;
    }
}

输出:

复制代码
1. 构造器
2. 设置属性: appName = MyApp
3. BeanNameAware: lifecycleBean
4. BeanFactoryAware
5. ApplicationContextAware
5.5 BeanPostProcessor.postProcessBeforeInitialization
6. @PostConstruct
7. InitializingBean.afterPropertiesSet()
8. init-method
8.5 BeanPostProcessor.postProcessAfterInitialization
... Bean可用 ...
9. @PreDestroy
10. DisposableBean.destroy()
11. destroy-method

四、实际项目应用

1. 初始化连接池

java 复制代码
@Component
public class RedisConnectionPool {
    
    private JedisPool jedisPool;
    
    @PostConstruct
    public void init() {
        // 初始化Redis连接池
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(100);
        config.setMaxIdle(10);
        
        jedisPool = new JedisPool(config, "localhost", 6379);
        System.out.println("Redis连接池初始化完成");
    }
    
    @PreDestroy
    public void destroy() {
        // 关闭连接池
        if (jedisPool != null) {
            jedisPool.close();
            System.out.println("Redis连接池已关闭");
        }
    }
}

2. 预加载数据

java 复制代码
@Component
public class DictionaryService implements ApplicationListener<ContextRefreshedEvent> {
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 容器启动完成后,预加载字典数据到Redis
        System.out.println("预加载字典数据...");
        List<Dictionary> dictionaries = dictionaryDao.selectAll();
        for (Dictionary dict : dictionaries) {
            redisTemplate.opsForValue().set("dict:" + dict.getCode(), dict);
        }
        System.out.println("字典数据加载完成");
    }
}

3. 获取ApplicationContext

java 复制代码
@Component
public class SpringContextHolder implements ApplicationContextAware {
    
    private static ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        SpringContextHolder.applicationContext = applicationContext;
    }
    
    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }
    
    public static Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }
}

💡 总结: Bean生命周期核心流程:

  1. 实例化(通过构造器)
  2. 属性赋值(依赖注入)
  3. Aware接口回调(获取容器信息)
  4. BeanPostProcessor前置处理
  5. 初始化(@PostConstruct → afterPropertiesSet → init-method)
  6. BeanPostProcessor后置处理(AOP代理)
  7. Bean可用
  8. 销毁(@PreDestroy → destroy → destroy-method)

💡 记忆口诀: "实例化、赋属性、Aware回调、前置处理、初始化、后置处理、可用、销毁"

相关推荐
小钻风33662 小时前
Knife4j 文件上传 multipart/data 同时接受文件和对象,调试时上传文件失效
java·springboot·knife4j
马猴烧酒.2 小时前
【面试八股|Spring篇】Spring常见面试题详解笔记
笔记·spring·面试
草履虫建模2 小时前
Java面试应对思路和题库
java·jvm·spring boot·分布式·spring cloud·面试·mybatis
努力学算法的蒟蒻3 小时前
day86(2.15)——leetcode面试经典150
数据结构·leetcode·面试
利刃大大3 小时前
【SpringCloud】Nacos简介 && 安装 && 快速入手 && 负载均衡
spring·spring cloud·微服务·nacos·负载均衡
me8324 小时前
【Java面试】Java核心关键字解析(static_final_访问修饰符)小白易懂
java·开发语言·面试
那我掉的头发算什么5 小时前
【图书管理系统】基于Spring全家桶的图书管理系统(上)
java·服务器·数据库·spring boot·后端·spring·mybatis
weixin_421994785 小时前
MVC 模式初探
mvc·.net·.netcore
zheshiyangyang5 小时前
前端面试基础知识整理【Day-5】
前端·面试·职场和发展