SpringBoot扩展点全攻略:让你的代码像积木一样灵活组装
小李正在开发一个电商系统,老板突然说:"我们要在用户登录时发送短信通知,在订单支付后要积分奖励,在系统启动时要预热缓存..."小李一脸懵逼:这么多需求,到处改代码?不对!Spring Boot早就为我们准备好了"扩展点"这个神器,让你的代码像搭积木一样灵活!今天就让我们一起探索Spring Boot中那些强大又实用的扩展点!
一、什么是扩展点?为什么要用它?
扩展点的本质
扩展点就像是代码中预留的"插座",当你需要新功能时,直接"插"一个组件进去就行,而不用动原有代码。
传统做法 VS 扩展点做法:- // ❌ 传统做法:到处修改代码
- @Service
- public class UserService {
- public void login(User user) {
- // 登录逻辑
- validateUser(user);
- updateLoginTime(user);
-
- // 新需求来了,要发短信
- sendSMS(user); // 直接在这里加代码
-
- // 又来需求,要记录日志
- logLoginEvent(user); // 又在这里加代码
-
- // 再来需求,要统计登录次数
- updateLoginCount(user); // 继续加代码...
- }
- }
- // ✅ 扩展点做法:优雅解决
- @Service
- public class UserService {
- @Autowired
- private ApplicationEventPublisher eventPublisher;
-
- public void login(User user) {
- // 核心登录逻辑
- validateUser(user);
- updateLoginTime(user);
-
- // 发布事件,让扩展点处理其他逻辑
- eventPublisher.publishEvent(new UserLoginEvent(user));
- }
- }
- // 各种扩展功能独立实现
- @EventListener
- public void sendSmsOnLogin(UserLoginEvent event) {
- smsService.sendLoginNotification(event.getUser());
- }
- @EventListener
- public void logLoginEvent(UserLoginEvent event) {
- logService.recordLogin(event.getUser());
- }
复制代码 扩展点的优势
对比项传统做法扩展点做法代码耦合度高,功能混杂在一起低,各功能独立维护难度难,改一处影响多处简单,各司其职功能扩展需要修改原代码新增组件即可单元测试复杂,需要mock很多依赖简单,功能独立测试团队协作容易冲突可并行开发二、Spring Boot启动过程中的扩展点
Spring Boot在启动过程中为我们提供了多个时机来插入自定义逻辑,这些扩展点让我们能够在应用启动的不同阶段执行初始化操作,比如缓存预热、数据初始化、健康检查等。
应用启动监听器 - ApplicationListener
扩展点说明: ApplicationListener是Spring Boot中最常用的启动扩展点,它能监听应用启动过程中的各种事件。当应用完全启动并准备好接收请求时,我们可以通过监听ApplicationReadyEvent事件来执行一些初始化工作。
适用场景: 缓存预热、外部服务连接检查、定时任务启动、系统状态初始化等需要在应用完全启动后执行的操作。- /**
- * 应用启动完成后的扩展点
- * 常用于:缓存预热、定时任务启动、外部服务连接等
- */
- @Component
- public class ApplicationStartupListener implements ApplicationListener {
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- @Autowired
- private DataWarmupService dataWarmupService;
-
- @Override
- public void onApplicationEvent(ApplicationReadyEvent event) {
- log.info("应用启动完成,开始执行启动后任务...");
-
- // 1. 缓存预热
- warmupCache();
-
- // 2. 检查外部服务连接
- checkExternalServices();
-
- // 3. 启动后台任务
- startBackgroundTasks();
-
- log.info("应用启动后任务执行完成");
- }
-
- private void warmupCache() {
- try {
- // 预热热门商品数据
- List<Product> hotProducts = dataWarmupService.getHotProducts();
- hotProducts.forEach(product -> {
- String key = "product:" + product.getId();
- redisTemplate.opsForValue().set(key, product, Duration.ofHours(1));
- });
-
- log.info("缓存预热完成,预热商品数量:{}", hotProducts.size());
- } catch (Exception e) {
- log.error("缓存预热失败", e);
- }
- }
-
- private void checkExternalServices() {
- // 检查支付服务
- if (paymentService.isAvailable()) {
- log.info("支付服务连接正常");
- } else {
- log.warn("支付服务连接异常,请检查配置");
- }
-
- // 检查短信服务
- if (smsService.isAvailable()) {
- log.info("短信服务连接正常");
- } else {
- log.warn("短信服务连接异常,请检查配置");
- }
- }
- }
复制代码 CommandLineRunner - 命令行启动扩展
扩展点说明: CommandLineRunner接口允许我们在Spring Boot应用启动完成后立即执行一些代码。它的特点是可以接收命令行参数,并且支持通过@Order注解控制执行顺序。多个CommandLineRunner会按照@Order指定的顺序依次执行。
适用场景: 数据库初始化、系统配置检查、基础数据导入等需要在应用启动时一次性执行的任务。- /**
- * 命令行启动扩展点
- * 特点:按@Order顺序执行,可以接收命令行参数
- */
- @Component
- @Order(1) // 执行顺序,数字越小越先执行
- public class DatabaseInitCommandLineRunner implements CommandLineRunner {
-
- @Autowired
- private DataInitService dataInitService;
-
- @Override
- public void run(String... args) throws Exception {
- log.info("开始执行数据库初始化...");
-
- // 检查是否需要初始化数据
- if (shouldInitializeData(args)) {
- dataInitService.initializeDefaultData();
- log.info("数据库初始化完成");
- } else {
- log.info("跳过数据库初始化");
- }
- }
-
- private boolean shouldInitializeData(String[] args) {
- // 检查命令行参数
- return Arrays.asList(args).contains("--init-data");
- }
- }
- @Component
- @Order(2)
- public class HealthCheckCommandLineRunner implements CommandLineRunner {
-
- @Override
- public void run(String... args) throws Exception {
- log.info("执行系统健康检查...");
-
- // 检查数据库连接
- checkDatabaseConnection();
-
- // 检查Redis连接
- checkRedisConnection();
-
- log.info("系统健康检查完成");
- }
- }
复制代码 ApplicationRunner - 应用启动扩展
扩展点说明: ApplicationRunner与CommandLineRunner非常相似,主要区别在于它提供了更强大的参数解析功能。通过ApplicationArguments对象,我们可以更方便地处理命令行选项参数和非选项参数。
适用场景: 需要复杂命令行参数处理的启动任务,如根据不同参数执行不同的初始化逻辑。- /**
- * ApplicationRunner与CommandLineRunner类似
- * 区别:可以解析命令行参数为ApplicationArguments对象
- */
- @Component
- public class SystemConfigApplicationRunner implements ApplicationRunner {
-
- @Override
- public void run(ApplicationArguments args) throws Exception {
- log.info("开始加载系统配置...");
-
- // 检查特定参数
- if (args.containsOption("profile")) {
- String profile = args.getOptionValues("profile").get(0);
- log.info("使用指定环境配置: {}", profile);
- }
-
- // 检查非选项参数
- List<String> nonOptionArgs = args.getNonOptionArgs();
- if (!nonOptionArgs.isEmpty()) {
- log.info("接收到非选项参数: {}", nonOptionArgs);
- }
-
- // 加载配置
- loadSystemConfiguration();
- }
-
- private void loadSystemConfiguration() {
- // 加载系统级配置
- log.info("系统配置加载完成");
- }
- }
复制代码 三、Bean生命周期中的扩展点
Bean生命周期扩展点让我们能够在Spring容器创建和销毁Bean的过程中插入自定义逻辑。这些扩展点是实现AOP、依赖注入增强、Bean装饰等高级功能的基础。
BeanPostProcessor - Bean处理器
扩展点说明: BeanPostProcessor是Spring框架中最强大的扩展点之一,它允许我们在Bean初始化前后对Bean进行修改或包装。Spring的很多核心功能(如AOP、@Autowired注入等)都是通过BeanPostProcessor实现的。
适用场景: 实现AOP切面、Bean属性注入、性能监控、缓存代理、Bean验证等需要对多个Bean进行统一处理的场景。- /**
- * Bean后处理器:在Bean初始化前后进行扩展
- * 常用于:AOP、属性注入、Bean增强等
- */
- @Component
- public class CustomBeanPostProcessor implements BeanPostProcessor {
-
- /**
- * Bean初始化之前调用
- */
- @Override
- public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
-
- // 为带有@PerformanceMonitor注解的Bean添加性能监控
- if (bean.getClass().isAnnotationPresent(PerformanceMonitor.class)) {
- log.info("为Bean [{}] 添加性能监控", beanName);
- return createPerformanceProxy(bean);
- }
-
- return bean;
- }
-
- /**
- * Bean初始化之后调用
- */
- @Override
- public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
-
- // 为Service类添加缓存功能
- if (bean.getClass().getSimpleName().endsWith("Service")) {
- log.info("为Service Bean [{}] 添加缓存支持", beanName);
- return createCacheProxy(bean);
- }
-
- return bean;
- }
-
- private Object createPerformanceProxy(Object target) {
- return Proxy.newProxyInstance(
- target.getClass().getClassLoader(),
- target.getClass().getInterfaces(),
- (proxy, method, args) -> {
- long startTime = System.currentTimeMillis();
- try {
- Object result = method.invoke(target, args);
- long endTime = System.currentTimeMillis();
- log.info("方法 [{}] 执行耗时: {}ms", method.getName(), endTime - startTime);
- return result;
- } catch (Exception e) {
- throw e;
- }
- }
- );
- }
- }
- // 自定义注解
- @Target(ElementType.TYPE)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface PerformanceMonitor {
- }
- // 使用示例
- @Service
- @PerformanceMonitor
- public class OrderService {
- public void createOrder(Order order) {
- // 业务逻辑
- }
- }
复制代码 InitializingBean和DisposableBean
扩展点说明: 这两个接口提供了Bean生命周期的初始化和销毁钩子。InitializingBean的afterPropertiesSet方法在Bean的所有属性设置完成后调用,DisposableBean的destroy方法在Bean销毁前调用,主要用于资源清理。
适用场景: 连接池管理、定时任务启停、文件句柄释放、网络连接关闭等需要在Bean创建时初始化资源,销毁时清理资源的场景。- /**
- * Bean初始化和销毁的扩展点
- */
- @Component
- public class ConnectionPoolManager implements InitializingBean, DisposableBean {
-
- private ExecutorService executorService;
- private ScheduledExecutorService scheduledExecutor;
-
- /**
- * Bean属性设置完成后调用
- */
- @Override
- public void afterPropertiesSet() throws Exception {
- log.info("初始化连接池管理器...");
-
- // 创建线程池
- executorService = Executors.newFixedThreadPool(10);
- scheduledExecutor = Executors.newScheduledThreadPool(5);
-
- // 启动定时任务
- startScheduledTasks();
-
- log.info("连接池管理器初始化完成");
- }
-
- /**
- * Bean销毁前调用
- */
- @Override
- public void destroy() throws Exception {
- log.info("开始销毁连接池管理器...");
-
- // 优雅关闭线程池
- if (executorService != null) {
- executorService.shutdown();
- if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) {
- executorService.shutdownNow();
- }
- }
-
- if (scheduledExecutor != null) {
- scheduledExecutor.shutdown();
- if (!scheduledExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
- scheduledExecutor.shutdownNow();
- }
- }
-
- log.info("连接池管理器销毁完成");
- }
-
- private void startScheduledTasks() {
- // 定时清理任务
- scheduledExecutor.scheduleAtFixedRate(() -> {
- log.info("执行定时清理任务...");
- // 清理逻辑
- }, 0, 1, TimeUnit.HOURS);
- }
- }
复制代码 四、事件驱动扩展点
事件驱动扩展点是Spring Boot中实现业务解耦的重要机制。通过发布-订阅模式,我们可以将复杂的业务流程拆分成独立的事件处理器,提高代码的可维护性和可扩展性。
自定义事件和监听器
扩展点说明: Spring的事件机制允许我们定义自己的事件类型和对应的监听器。事件发布者通过ApplicationEventPublisher发布事件,事件监听器通过@EventListener注解自动接收并处理事件。这种方式可以很好地解耦业务逻辑。
适用场景: 订单处理后的库存扣减、积分增加、消息通知等需要在主业务完成后触发的后续操作,以及需要解耦的业务逻辑处理。- /**
- * 自定义事件
- */
- public class OrderCreatedEvent extends ApplicationEvent {
- private final Order order;
- private final User user;
-
- public OrderCreatedEvent(Object source, Order order, User user) {
- super(source);
- this.order = order;
- this.user = user;
- }
-
- // getter methods...
- }
- /**
- * 订单服务:发布事件
- */
- @Service
- public class OrderService {
-
- @Autowired
- private ApplicationEventPublisher eventPublisher;
-
- public Order createOrder(CreateOrderRequest request) {
- // 1. 创建订单
- Order order = buildOrder(request);
- orderRepository.save(order);
-
- // 2. 发布订单创建事件
- eventPublisher.publishEvent(new OrderCreatedEvent(this, order, request.getUser()));
-
- return order;
- }
- }
- /**
- * 库存服务:监听订单事件,扣减库存
- */
- @Component
- public class InventoryEventListener {
-
- @Autowired
- private InventoryService inventoryService;
-
- @EventListener
- @Async // 异步处理,不影响主流程
- public void handleOrderCreated(OrderCreatedEvent event) {
- log.info("订单创建事件:开始扣减库存,订单ID: {}", event.getOrder().getId());
-
- try {
- Order order = event.getOrder();
- order.getItems().forEach(item -> {
- inventoryService.reduceStock(item.getProductId(), item.getQuantity());
- });
-
- log.info("库存扣减完成,订单ID: {}", order.getId());
- } catch (Exception e) {
- log.error("库存扣减失败,订单ID: {}", event.getOrder().getId(), e);
- // 可以发布库存扣减失败事件,触发补偿逻辑
- }
- }
- }
- /**
- * 积分服务:监听订单事件,增加积分
- */
- @Component
- public class PointsEventListener {
-
- @Autowired
- private PointsService pointsService;
-
- @EventListener
- @Async
- public void handleOrderCreated(OrderCreatedEvent event) {
- log.info("订单创建事件:开始计算积分,订单ID: {}", event.getOrder().getId());
-
- try {
- Order order = event.getOrder();
- User user = event.getUser();
-
- // 计算积分(订单金额的1%)
- int points = (int) (order.getTotalAmount().doubleValue() * 0.01);
- pointsService.addPoints(user.getId(), points, "订单奖励");
-
- log.info("积分增加完成,用户ID: {}, 积分: {}", user.getId(), points);
- } catch (Exception e) {
- log.error("积分增加失败,订单ID: {}", event.getOrder().getId(), e);
- }
- }
- }
- /**
- * 通知服务:发送订单确认消息
- */
- @Component
- public class NotificationEventListener {
-
- @Autowired
- private EmailService emailService;
-
- @Autowired
- private SmsService smsService;
-
- @EventListener
- @Async
- public void handleOrderCreated(OrderCreatedEvent event) {
- log.info("订单创建事件:发送通知,订单ID: {}", event.getOrder().getId());
-
- try {
- Order order = event.getOrder();
- User user = event.getUser();
-
- // 发送邮件通知
- emailService.sendOrderConfirmation(user.getEmail(), order);
-
- // 发送短信通知
- smsService.sendOrderConfirmation(user.getPhone(), order);
-
- log.info("订单通知发送完成,订单ID: {}", order.getId());
- } catch (Exception e) {
- log.error("订单通知发送失败,订单ID: {}", event.getOrder().getId(), e);
- }
- }
- }
复制代码 事件监听器的高级用法
扩展点说明: Spring提供了多种高级事件监听功能,包括条件化监听(只处理满足特定条件的事件)和事务性监听(在事务的特定阶段执行)。这些功能让事件处理更加灵活和可靠。
适用场景: VIP用户特殊处理、大额订单风控、事务成功后的外部系统通知、事务失败后的补偿操作等需要根据条件或事务状态进行处理的场景。- /**
- * 条件化事件监听
- */
- @Component
- public class ConditionalEventListener {
-
- /**
- * 只处理VIP用户的订单
- */
- @EventListener(condition = "#event.user.vipLevel > 0")
- public void handleVipOrderCreated(OrderCreatedEvent event) {
- log.info("VIP用户订单处理,用户等级: {}", event.getUser().getVipLevel());
-
- // VIP专属处理逻辑
- processVipOrder(event.getOrder());
- }
-
- /**
- * 只处理大额订单(金额>1000)
- */
- @EventListener(condition = "#event.order.totalAmount > 1000")
- public void handleLargeOrderCreated(OrderCreatedEvent event) {
- log.info("大额订单处理,订单金额: {}", event.getOrder().getTotalAmount());
-
- // 大额订单特殊处理
- processLargeOrder(event.getOrder());
- }
- }
- /**
- * 事务性事件监听
- */
- @Component
- public class TransactionalEventListener {
-
- /**
- * 在事务提交后执行
- */
- @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
- public void handleOrderCreatedAfterCommit(OrderCreatedEvent event) {
- log.info("订单事务提交后处理,订单ID: {}", event.getOrder().getId());
-
- // 只有在订单真正保存成功后才执行
- sendOrderCreatedNotificationToExternalSystem(event.getOrder());
- }
-
- /**
- * 在事务回滚后执行
- */
- @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
- public void handleOrderCreatedAfterRollback(OrderCreatedEvent event) {
- log.info("订单事务回滚后处理,订单ID: {}", event.getOrder().getId());
-
- // 事务回滚时的补偿逻辑
- compensateForFailedOrder(event.getOrder());
- }
- }
复制代码 五、Web层扩展点
Web层扩展点专门用于处理HTTP请求的生命周期,让我们能够在请求处理的各个阶段插入自定义逻辑,实现统一的请求处理、权限控制、日志记录等功能。
拦截器 - HandlerInterceptor
扩展点说明: HandlerInterceptor是Spring MVC提供的请求拦截机制,它提供了三个时机:请求处理前(preHandle)、请求处理后视图渲染前(postHandle)、请求完全结束后(afterCompletion)。拦截器在Spring容器内工作,可以很方便地注入Spring Bean。
适用场景: 权限认证、请求日志记录、性能监控、参数校验、响应数据统一处理等需要对Web请求进行统一处理的场景。- /**
- * 请求日志拦截器
- */
- @Component
- public class RequestLoggingInterceptor implements HandlerInterceptor {
-
- private static final String START_TIME = "startTime";
- private static final String REQUEST_ID = "requestId";
-
- /**
- * 请求处理前
- */
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
- Object handler) throws Exception {
-
- String requestId = UUID.randomUUID().toString();
- long startTime = System.currentTimeMillis();
-
- // 设置请求ID和开始时间
- request.setAttribute(REQUEST_ID, requestId);
- request.setAttribute(START_TIME, startTime);
-
- // 设置MDC,方便日志追踪
- MDC.put("requestId", requestId);
-
- log.info("请求开始 - URL: {}, Method: {}, IP: {}, User-Agent: {}",
- request.getRequestURL(),
- request.getMethod(),
- getClientIpAddress(request),
- request.getHeader("User-Agent"));
-
- return true;
- }
-
- /**
- * 请求处理后,视图渲染前
- */
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response,
- Object handler, ModelAndView modelAndView) throws Exception {
-
- long startTime = (Long) request.getAttribute(START_TIME);
- long endTime = System.currentTimeMillis();
-
- log.info("请求处理完成 - 耗时: {}ms, Status: {}",
- endTime - startTime,
- response.getStatus());
- }
-
- /**
- * 请求完全结束后
- */
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
- Object handler, Exception ex) throws Exception {
-
- if (ex != null) {
- log.error("请求处理异常", ex);
- }
-
- // 清理MDC
- MDC.clear();
-
- log.info("请求结束");
- }
-
- private String getClientIpAddress(HttpServletRequest request) {
- String xForwardedFor = request.getHeader("X-Forwarded-For");
- if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
- return xForwardedFor.split(",")[0].trim();
- }
-
- String xRealIp = request.getHeader("X-Real-IP");
- if (xRealIp != null && !xRealIp.isEmpty()) {
- return xRealIp;
- }
-
- return request.getRemoteAddr();
- }
- }
- /**
- * 权限检查拦截器
- */
- @Component
- public class AuthenticationInterceptor implements HandlerInterceptor {
-
- @Autowired
- private JwtTokenUtil jwtTokenUtil;
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
- Object handler) throws Exception {
-
- // 检查是否需要权限验证
- if (handler instanceof HandlerMethod) {
- HandlerMethod handlerMethod = (HandlerMethod) handler;
-
- // 检查方法或类上是否有@NoAuth注解
- if (handlerMethod.hasMethodAnnotation(NoAuth.class) ||
- handlerMethod.getBeanType().isAnnotationPresent(NoAuth.class)) {
- return true;
- }
- }
-
- // 获取token
- String token = request.getHeader("Authorization");
- if (token == null || !token.startsWith("Bearer ")) {
- response.setStatus(HttpStatus.UNAUTHORIZED.value());
- response.getWriter().write("{"error":"未授权访问"}");
- return false;
- }
-
- token = token.substring(7); // 去掉"Bearer "
-
- // 验证token
- try {
- String userId = jwtTokenUtil.getUserIdFromToken(token);
- if (userId != null) {
- // 将用户信息存入request
- request.setAttribute("userId", userId);
- return true;
- }
- } catch (Exception e) {
- log.warn("Token验证失败", e);
- }
-
- response.setStatus(HttpStatus.UNAUTHORIZED.value());
- response.getWriter().write("{"error":"Token无效"}");
- return false;
- }
- }
- // 注册拦截器
- @Configuration
- public class WebConfig implements WebMvcConfigurer {
-
- @Autowired
- private RequestLoggingInterceptor requestLoggingInterceptor;
-
- @Autowired
- private AuthenticationInterceptor authenticationInterceptor;
-
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- // 请求日志拦截器:拦截所有请求
- registry.addInterceptor(requestLoggingInterceptor)
- .addPathPatterns("/**");
-
- // 权限拦截器:拦截API请求,排除登录等公开接口
- registry.addInterceptor(authenticationInterceptor)
- .addPathPatterns("/api/**")
- .excludePathPatterns("/api/login", "/api/register", "/api/public/**");
- }
- }
复制代码 过滤器 - Filter
扩展点说明: Filter是Servlet规范中的过滤器,它在请求到达Spring容器之前就开始工作。相比拦截器,Filter的执行时机更早,功能更底层,但无法直接使用Spring的依赖注入功能。
适用场景: 跨域处理、字符编码设置、请求体缓存、安全过滤等需要在最早期处理请求的场景,以及需要处理静态资源请求的情况。- /**
- * 跨域处理过滤器
- */
- @Component
- @Order(1)
- public class CorsFilter implements Filter {
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
-
- HttpServletResponse httpResponse = (HttpServletResponse) response;
- HttpServletRequest httpRequest = (HttpServletRequest) request;
-
- // 设置跨域头
- httpResponse.setHeader("Access-Control-Allow-Origin", "*");
- httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
- httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
- httpResponse.setHeader("Access-Control-Max-Age", "3600");
-
- // 处理预检请求
- if ("OPTIONS".equals(httpRequest.getMethod())) {
- httpResponse.setStatus(HttpServletResponse.SC_OK);
- return;
- }
-
- chain.doFilter(request, response);
- }
- }
- /**
- * 请求体包装过滤器
- */
- @Component
- @Order(2)
- public class RequestWrapperFilter implements Filter {
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response,
- FilterChain chain) throws IOException, ServletException {
-
- if (request instanceof HttpServletRequest) {
- HttpServletRequest httpRequest = (HttpServletRequest) request;
-
- // 只包装POST和PUT请求
- if ("POST".equals(httpRequest.getMethod()) || "PUT".equals(httpRequest.getMethod())) {
- // 使用自定义的请求包装器,支持多次读取请求体
- CachedBodyHttpServletRequest wrappedRequest =
- new CachedBodyHttpServletRequest(httpRequest);
- chain.doFilter(wrappedRequest, response);
- return;
- }
- }
-
- chain.doFilter(request, response);
- }
- }
- /**
- * 可重复读取的请求包装器
- */
- public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
-
- private byte[] cachedBody;
-
- public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
- super(request);
-
- // 缓存请求体
- InputStream requestInputStream = request.getInputStream();
- this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
- }
-
- @Override
- public ServletInputStream getInputStream() throws IOException {
- return new CachedBodyServletInputStream(this.cachedBody);
- }
-
- @Override
- public BufferedReader getReader() throws IOException {
- ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
- return new BufferedReader(new InputStreamReader(byteArrayInputStream));
- }
-
- public byte[] getCachedBody() {
- return cachedBody;
- }
- }
复制代码 六、自动配置扩展点
自动配置扩展点是Spring Boot的核心特性之一,它允许我们根据不同的条件(如类路径、配置属性、环境等)自动装配不同的Bean,实现"约定优于配置"的理念。
自定义Starter
扩展点说明: 自定义Starter是Spring Boot提供的模块化配置机制,通过@Conditional系列注解,我们可以根据类路径中是否存在特定类、是否有特定配置等条件来决定是否自动装配某些Bean。这是实现"开箱即用"的关键技术。
适用场景: 开发可复用的功能模块、第三方组件集成、企业内部通用组件封装等需要根据环境自动配置的场景。- /**
- * 自动配置类
- */
- @Configuration
- @ConditionalOnClass(RedisTemplate.class)
- @EnableConfigurationProperties(CacheProperties.class)
- public class CacheAutoConfiguration {
-
- @Bean
- @ConditionalOnMissingBean
- public CacheManager customCacheManager(CacheProperties properties) {
- return new CustomCacheManager(properties);
- }
-
- @Bean
- @ConditionalOnProperty(prefix = "custom.cache", name = "enabled", havingValue = "true")
- public CacheService cacheService(CacheManager cacheManager) {
- return new CacheService(cacheManager);
- }
- }
- /**
- * 配置属性
- */
- @ConfigurationProperties(prefix = "custom.cache")
- @Data
- public class CacheProperties {
-
- /**
- * 是否启用缓存
- */
- private boolean enabled = true;
-
- /**
- * 缓存过期时间(秒)
- */
- private int ttl = 3600;
-
- /**
- * 缓存前缀
- */
- private String prefix = "app:cache:";
-
- /**
- * 最大缓存数量
- */
- private int maxSize = 1000;
- }
- /**
- * 自定义缓存服务
- */
- public class CacheService {
-
- private final CacheManager cacheManager;
-
- public CacheService(CacheManager cacheManager) {
- this.cacheManager = cacheManager;
- }
-
- public void put(String key, Object value) {
- cacheManager.getCache("default").put(key, value);
- }
-
- public <T> T get(String key, Class<T> type) {
- Cache.ValueWrapper wrapper = cacheManager.getCache("default").get(key);
- return wrapper != null ? type.cast(wrapper.get()) : null;
- }
- }
复制代码 条件化配置
扩展点说明: Spring Boot提供了丰富的条件注解(@Profile、@ConditionalOnProperty等),让我们能够根据不同的环境、配置属性、Bean存在情况等条件来装配不同的Bean。这种机制让同一套代码能够适应不同的运行环境。
适用场景: 开发环境与生产环境的配置差异化、功能开关控制、可选组件的条件装配等需要根据运行环境动态调整配置的场景。- /**
- * 环境相关的自动配置
- */
- @Configuration
- public class EnvironmentSpecificConfiguration {
-
- /**
- * 开发环境配置
- */
- @Configuration
- @Profile("dev")
- static class DevelopmentConfiguration {
-
- @Bean
- public DataSource devDataSource() {
- // 开发环境数据源配置
- HikariDataSource dataSource = new HikariDataSource();
- dataSource.setJdbcUrl("jdbc:h2:mem:devdb");
- dataSource.setUsername("sa");
- dataSource.setPassword("");
- return dataSource;
- }
-
- @Bean
- public LogLevel devLogLevel() {
- return LogLevel.DEBUG;
- }
- }
-
- /**
- * 生产环境配置
- */
- @Configuration
- @Profile("prod")
- static class ProductionConfiguration {
-
- @Bean
- public DataSource prodDataSource(@Value("${spring.datasource.url}") String url,
- @Value("${spring.datasource.username}") String username,
- @Value("${spring.datasource.password}") String password) {
- HikariDataSource dataSource = new HikariDataSource();
- dataSource.setJdbcUrl(url);
- dataSource.setUsername(username);
- dataSource.setPassword(password);
- dataSource.setMaximumPoolSize(20);
- return dataSource;
- }
-
- @Bean
- public LogLevel prodLogLevel() {
- return LogLevel.INFO;
- }
- }
-
- /**
- * 基于属性的条件配置
- */
- @Bean
- @ConditionalOnProperty(name = "feature.async.enabled", havingValue = "true")
- public AsyncTaskExecutor asyncTaskExecutor() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(5);
- executor.setMaxPoolSize(10);
- executor.setQueueCapacity(100);
- executor.setThreadNamePrefix("async-");
- executor.initialize();
- return executor;
- }
-
- /**
- * 基于Bean存在与否的条件配置
- */
- @Bean
- @ConditionalOnMissingBean(name = "emailService")
- public EmailService defaultEmailService() {
- return new DefaultEmailService();
- }
- }
复制代码 七、扩展点最佳实践
扩展点设计原则
graph TD A[扩展点设计原则] --> B[单一职责] A --> C[开闭原则] A --> D[依赖倒置] A --> E[接口隔离] B --> B1[每个扩展点只负责一个功能] B --> B2[避免功能耦合] C --> C1[对扩展开放] C --> C2[对修改关闭] D --> D1[依赖抽象而非具体实现] D --> D2[通过接口定义扩展点] E --> E1[接口要精简] E --> E2[避免臃肿接口]扩展点使用建议
场景推荐扩展点原因应用启动初始化ApplicationListener生命周期清晰业务事件处理@EventListener解耦,异步处理跨切面功能BeanPostProcessor统一处理多个BeanWeb请求处理HandlerInterceptor专为Web设计全局请求过滤Filter在Spring容器外也能工作环境相关配置@Profile + @Conditional灵活的条件装配性能优化建议
- /**
- * 异步事件处理配置
- */
- @Configuration
- @EnableAsync
- public class AsyncEventConfiguration {
-
- /**
- * 事件处理专用线程池
- */
- @Bean("eventTaskExecutor")
- public TaskExecutor eventTaskExecutor() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(5);
- executor.setMaxPoolSize(20);
- executor.setQueueCapacity(500);
- executor.setThreadNamePrefix("event-");
- executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
- executor.initialize();
- return executor;
- }
-
- /**
- * 异步事件异常处理
- */
- @Bean
- public AsyncUncaughtExceptionHandler asyncExceptionHandler() {
- return new SimpleAsyncUncaughtExceptionHandler();
- }
- }
- /**
- * 高性能事件监听器示例
- */
- @Component
- public class HighPerformanceEventListener {
-
- private final TaskExecutor eventTaskExecutor;
-
- public HighPerformanceEventListener(@Qualifier("eventTaskExecutor") TaskExecutor eventTaskExecutor) {
- this.eventTaskExecutor = eventTaskExecutor;
- }
-
- /**
- * 使用专门的线程池处理事件
- */
- @EventListener
- @Async("eventTaskExecutor")
- public void handleOrderEvent(OrderCreatedEvent event) {
- // 业务处理逻辑
- processOrderEvent(event);
- }
-
- /**
- * 批量处理事件,提高吞吐量
- */
- @EventListener
- public void handleBatchEvent(List<OrderCreatedEvent> events) {
- eventTaskExecutor.execute(() -> {
- // 批量处理逻辑
- processBatchEvents(events);
- });
- }
- }
复制代码 常见问题与解决方案
问题原因解决方案事件监听器执行顺序不确定默认无序执行使用@Order注解指定顺序异步事件处理异常被忽略异常在子线程中配置AsyncUncaughtExceptionHandler扩展点过多导致启动缓慢初始化逻辑复杂使用@Lazy延迟初始化循环依赖问题Bean之间相互依赖使用@EventListener解耦八、总结
Spring Boot的扩展点就像是代码世界中的"瑞士军刀",每个扩展点都有其独特的用途:
核心扩展点一览
graph LR A[Spring Boot扩展点] --> B[启动阶段] A --> C[Bean生命周期] A --> D[事件驱动] A --> E[Web层] A --> F[自动配置] B --> B1[ApplicationListener] B --> B2[CommandLineRunner] B --> B3[ApplicationRunner] C --> C1[BeanPostProcessor] C --> C2[InitializingBean] C --> C3[DisposableBean] D --> D1[EventListener注解] D --> D2[ApplicationEventPublisher] D --> D3[TransactionalEventListener] E --> E1[HandlerInterceptor] E --> E2[Filter] E --> E3[ControllerAdvice注解] F --> F1[Conditional系列注解] F --> F2[Profile注解] F --> F3[AutoConfiguration]使用建议
1. 选择合适的扩展点:
- 应用启动时的初始化 → ApplicationListener
- 业务逻辑解耦 → @EventListener
- 横切关注点 → BeanPostProcessor
- Web请求处理 → HandlerInterceptor
2. 性能考虑:
- 使用异步处理避免阻塞主流程
- 合理配置线程池大小
- 避免在扩展点中执行耗时操作
3. 错误处理:
- 扩展点中的异常要妥善处理
- 使用try-catch避免影响主流程
- 记录详细的错误日志
4. 测试策略:
- 单独测试每个扩展点
- 使用@MockBean模拟依赖
- 验证扩展点的执行顺序
掌握了这些扩展点,你的Spring Boot应用就能像变形金刚一样灵活变换,轻松应对各种业务需求的变化。记住:好的架构不是一开始就设计完美,而是能够优雅地适应变化!
想要掌握更多Spring Boot高级技巧和企业级开发经验?欢迎关注我的微信公众号【一只划水的程序猿】,这里有最实用的Java技术干货,最贴近实战的编程经验,让你的代码水平快速提升!记得点赞收藏,分享给更多热爱编程的小伙伴!
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |