别再到处try-catch了!SpringBoot全局异常处理这样设计

《别再到处try-catch了!SpringBoot全局异常处理这样设计》

每天5分钟,掌握一个SpringBoot核心知识点。大家好,我是SpringBoot指南的创始人小明。昨天我们讲解了自动配置,今天来聊聊异常处理。 零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

资源获取关注公众号:小坏睡说Java

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

引言

想象一下这样的场景:你的项目有50个Controller,每个Controller方法里都有类似的try-catch代码,而且每个开发者处理异常的方式都不一样。线上出了问题,日志里要么没有记录,要么记录得乱七八糟...

这就是为什么我们需要统一的全局异常处理。今天,我将带你设计一个优雅的全局异常处理方案,让你的代码干净、统一、易于维护

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

先看两个对比:

❌ 混乱的异常处理方式

java 复制代码
@RestController
public class UserController {
    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody UserDTO user) {
        try {
            User registered = userService.register(user);
            return ResponseEntity.ok(registered);
        } catch (ValidationException e) {
            return ResponseEntity.badRequest().body("参数错误:" + e.getMessage());
        } catch (DuplicateException e) {
            return ResponseEntity.status(409).body("用户已存在");
        } catch (Exception e) {
            log.error("注册失败", e);
            return ResponseEntity.status(500).body("系统繁忙");
        }
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<?> getUser(@PathVariable Long id) {
        try {
            User user = userService.findById(id);
            return ResponseEntity.ok(user);
        } catch (NotFoundException e) {
            return ResponseEntity.status(404).body("用户不存在");
        } catch (Exception e) {
            log.error("查询用户失败", e);
            return ResponseEntity.status(500).body("系统错误");
        }
    }
    // ... 其他方法重复类似的模式
}

✅ 优雅的全局异常处理

java 复制代码
@RestController
public class UserController {
    @PostMapping("/register")
    public ResponseResult<User> register(@RequestBody UserDTO user) {
        User registered = userService.register(user);
        return ResponseResult.success(registered);
    }
    
    @GetMapping("/{id}")
    public ResponseResult<User> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        return ResponseResult.success(user);
    }
    // ... 干净的业务代码,不包含任何异常处理逻辑
}

所有的异常都在一个地方统一处理!下面,让我们一步步实现这个目标。

一、SpringBoot异常处理核心注解

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

1. @ControllerAdvice:全局异常处理的基石

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
    // 可以指定要处理的包
    String[] value() default {};
    
    // 指定要处理的Controller类型
    Class<?>[] basePackageClasses() default {};
    
    // 指定要处理的注解
    Class<? extends Annotation>[] annotations() default {};
}

三种常见用法

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

java 复制代码
// 方式1:处理所有Controller的异常(最常用)
@ControllerAdvice
public class GlobalExceptionHandler {
    // 处理逻辑...
}

// 方式2:只处理指定包下的Controller
@ControllerAdvice("com.example.user")
public class UserExceptionHandler {
    // 只处理user模块的异常
}

// 方式3:只处理带有特定注解的Controller
@ControllerAdvice(annotations = RestController.class)
public class RestExceptionHandler {
    // 只处理@RestController注解的类
}

2. @RestControllerAdvice:更便捷的选择

@RestControllerAdvice@ControllerAdvice@ResponseBody 的组合,专门用于REST API开发。

java 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
    // 同@ControllerAdvice
}

3. @ExceptionHandler:指定处理什么异常

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
    // 指定要处理的异常类型
    Class<? extends Throwable>[] value() default {};
}

二、设计优雅的异常体系

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

1. 异常分类策略

php 复制代码
Throwable
├── Error(系统错误,如OutOfMemoryError,通常不捕获)
└── Exception(异常)
    ├── RuntimeException(运行时异常)
    │   ├── BusinessException(自定义业务异常)
    │   │   ├── ValidationException(参数校验异常)
    │   │   ├── DuplicateException(数据重复异常)
    │   │   ├── NotFoundException(数据不存在异常)
    │   │   └── PermissionException(权限异常)
    │   └── SystemException(自定义系统异常)
    └── 其他Checked Exception(已检查异常)

2. 基础异常类设计

java 复制代码
// 基础业务异常
public class BusinessException extends RuntimeException {
    private final int code;
    private final String message;
    
    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
        this.message = message;
    }
    
    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
        this.message = errorCode.getMessage();
    }
    
    // getters...
}

// 错误码枚举
public enum ErrorCode {
    // 系统错误
    SUCCESS(200, "成功"),
    SYSTEM_ERROR(500, "系统错误"),
    
    // 业务错误
    PARAM_VALID_ERROR(10001, "参数校验失败"),
    DATA_NOT_FOUND(10002, "数据不存在"),
    DATA_DUPLICATE(10003, "数据已存在"),
    PERMISSION_DENIED(10004, "权限不足"),
    USER_NOT_LOGIN(10005, "用户未登录"),
    
    // 第三方服务错误
    THIRD_PARTY_ERROR(20001, "第三方服务异常");
    
    private final int code;
    private final String message;
    
    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }
    
    // getters...
}

// 具体的业务异常
public class ValidationException extends BusinessException {
    public ValidationException(String message) {
        super(ErrorCode.PARAM_VALID_ERROR.getCode(), message);
    }
}

public class NotFoundException extends BusinessException {
    public NotFoundException(String message) {
        super(ErrorCode.DATA_NOT_FOUND.getCode(), message);
    }
}

三、统一响应体设计

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

1. 响应体基础结构

java 复制代码
// 统一响应体
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResponseResult<T> {
    private int code;        // 状态码
    private String message;  // 消息
    private T data;          // 数据
    private long timestamp;  // 时间戳
    
    public static <T> ResponseResult<T> success() {
        return success(null);
    }
    
    public static <T> ResponseResult<T> success(T data) {
        return ResponseResult.<T>builder()
                .code(ErrorCode.SUCCESS.getCode())
                .message(ErrorCode.SUCCESS.getMessage())
                .data(data)
                .timestamp(System.currentTimeMillis())
                .build();
    }
    
    public static <T> ResponseResult<T> fail(int code, String message) {
        return ResponseResult.<T>builder()
                .code(code)
                .message(message)
                .timestamp(System.currentTimeMillis())
                .build();
    }
    
    public static <T> ResponseResult<T> fail(ErrorCode errorCode) {
        return fail(errorCode.getCode(), errorCode.getMessage());
    }
}

四、实现全局异常处理器

1. 完整异常处理器实现

java 复制代码
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    /**
     * 处理业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public ResponseResult<Void> handleBusinessException(BusinessException e) {
        log.warn("业务异常:code={}, message={}", e.getCode(), e.getMessage());
        return ResponseResult.fail(e.getCode(), e.getMessage());
    }
    
    /**
     * 处理参数校验异常(@Validated触发的异常)
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseResult<Void> handleMethodArgumentNotValidException(
            MethodArgumentNotValidException e) {
        String message = e.getBindingResult().getAllErrors().stream()
                .map(DefaultMessageSourceResolvable::getDefaultMessage)
                .collect(Collectors.joining(", "));
        log.warn("参数校验失败:{}", message);
        return ResponseResult.fail(ErrorCode.PARAM_VALID_ERROR.getCode(), message);
    }
    
    /**
     * 处理请求参数绑定异常(类型转换错误等)
     */
    @ExceptionHandler(BindException.class)
    public ResponseResult<Void> handleBindException(BindException e) {
        String message = e.getBindingResult().getAllErrors().stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining(", "));
        log.warn("参数绑定失败:{}", message);
        return ResponseResult.fail(ErrorCode.PARAM_VALID_ERROR.getCode(), message);
    }
    
    /**
     * 处理参数解析异常(JSON解析错误等)
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseResult<Void> handleHttpMessageNotReadableException(
            HttpMessageNotReadableException e) {
        log.warn("请求参数解析失败", e);
        return ResponseResult.fail(ErrorCode.PARAM_VALID_ERROR.getCode(), "请求参数格式错误");
    }
    
    /**
     * 处理404异常
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseResult<Void> handleNoHandlerFoundException(
            NoHandlerFoundException e) {
        log.warn("接口不存在:{} {}", e.getHttpMethod(), e.getRequestURL());
        return ResponseResult.fail(404, "接口不存在");
    }
    
    /**
     * 处理请求方法不支持异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseResult<Void> handleHttpRequestMethodNotSupportedException(
            HttpRequestMethodNotSupportedException e) {
        log.warn("请求方法不支持:{},支持的方法:{}", 
                e.getMethod(), e.getSupportedHttpMethods());
        return ResponseResult.fail(405, "请求方法不支持");
    }
    
    /**
     * 处理所有未捕获的异常
     */
    @ExceptionHandler(Exception.class)
    public ResponseResult<Void> handleException(Exception e) {
        log.error("系统异常", e);
        
        // 生产环境返回模糊的错误信息,开发环境返回详细错误
        String message = isProduction() ? "系统繁忙,请稍后重试" : e.getMessage();
        return ResponseResult.fail(ErrorCode.SYSTEM_ERROR.getCode(), message);
    }
    
    private boolean isProduction() {
        String env = System.getProperty("spring.profiles.active");
        return "prod".equals(env);
    }
}

2. 增强版:带异常日志记录的处理器

java 复制代码
@Slf4j
@RestControllerAdvice
public class EnhancedGlobalExceptionHandler {
    
    /**
     * 异常信息上下文,用于记录额外信息
     */
    private static final ThreadLocal<Map<String, Object>> exceptionContext = 
            ThreadLocal.withInitial(HashMap::new);
    
    /**
     * 添加上下文信息(可在业务代码中调用)
     */
    public static void addContext(String key, Object value) {
        exceptionContext.get().put(key, value);
    }
    
    /**
     * 清理上下文(在过滤器或拦截器中调用)
     */
    public static void clearContext() {
        exceptionContext.remove();
    }
    
    @ExceptionHandler(BusinessException.class)
    public ResponseResult<Void> handleBusinessException(
            BusinessException e, HttpServletRequest request) {
        
        Map<String, Object> context = exceptionContext.get();
        log.warn("业务异常 | 路径:{} | 参数:{} | 错误码:{} | 消息:{} | 上下文:{}",
                request.getRequestURI(),
                getRequestParams(request),
                e.getCode(),
                e.getMessage(),
                context);
        
        clearContext();
        return ResponseResult.fail(e.getCode(), e.getMessage());
    }
    
    @ExceptionHandler(Exception.class)
    public ResponseResult<Void> handleException(
            Exception e, HttpServletRequest request) {
        
        Map<String, Object> context = exceptionContext.get();
        log.error("系统异常 | 路径:{} | 参数:{} | 上下文:{}",
                request.getRequestURI(),
                getRequestParams(request),
                context,
                e);
        
        clearContext();
        return ResponseResult.fail(ErrorCode.SYSTEM_ERROR.getCode(), 
                isProduction() ? "系统繁忙" : e.getMessage());
    }
    
    private String getRequestParams(HttpServletRequest request) {
        Map<String, String[]> params = request.getParameterMap();
        return params.entrySet().stream()
                .map(entry -> entry.getKey() + "=" + 
                        Arrays.toString(entry.getValue()))
                .collect(Collectors.joining(", "));
    }
    
    // ... 其他异常处理方法
}

五、最佳实践与高级用法

1. 参数校验的优雅处理

传统方式

java 复制代码
@RestController
public class UserController {
    @PostMapping("/user")
    public ResponseResult<User> createUser(@Valid @RequestBody UserDTO user) {
        // 需要手动处理BindingResult
        // 或者依赖全局异常处理器
    }
}

增强方式:自定义校验注解

java 复制代码
// 自定义手机号校验注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
    String message() default "手机号格式错误";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

// 实现校验逻辑
public class PhoneValidator implements ConstraintValidator<Phone, String> {
    private static final Pattern PHONE_PATTERN = 
            Pattern.compile("^1[3-9]\\d{9}$");
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true; // 由@NotNull处理空值
        }
        return PHONE_PATTERN.matcher(value).matches();
    }
}

// 使用方式
@Data
public class UserDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度2-20")
    private String username;
    
    @Phone  // 使用自定义注解
    private String phone;
}

2. 多环境差异化处理

yaml 复制代码
# application.yml
spring:
  profiles:
    active: dev

app:
  exception:
    # 开发环境返回详细错误
    detail-in-dev: true
    # 生产环境屏蔽堆栈
    hide-stack-in-prod: true
java 复制代码
@ConfigurationProperties(prefix = "app.exception")
@Data
public class ExceptionProperties {
    private boolean detailInDev = true;
    private boolean hideStackInProd = true;
}

@RestControllerAdvice
public class EnvironmentAwareExceptionHandler {
    
    @Autowired
    private ExceptionProperties properties;
    
    @ExceptionHandler(Exception.class)
    public ResponseResult<Void> handleException(Exception e, 
            HttpServletRequest request) {
        
        // 记录异常日志(生产环境记录简要,开发环境记录详细)
        if (isProduction()) {
            log.error("系统异常 | 路径:{} | 错误:{}", 
                    request.getRequestURI(), e.getMessage());
        } else {
            log.error("系统异常 | 路径:{}", request.getRequestURI(), e);
        }
        
        // 构建响应
        ErrorResponse error = new ErrorResponse();
        error.setCode(ErrorCode.SYSTEM_ERROR.getCode());
        error.setMessage(buildErrorMessage(e));
        
        if (!isProduction() && properties.isDetailInDev()) {
            error.setDetail(e.getMessage());
            error.setPath(request.getRequestURI());
            error.setException(e.getClass().getName());
        }
        
        if (!isProduction() || !properties.isHideStackInProd()) {
            error.setTrace(getStackTrace(e));
        }
        
        return ResponseResult.fail(error);
    }
    
    private String buildErrorMessage(Exception e) {
        if (isProduction()) {
            return "系统繁忙,请稍后重试";
        }
        return e.getMessage();
    }
}

3. 集成Sentinel进行流控异常处理

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

java 复制代码
@Component
public class SentinelExceptionHandler {
    
    @ExceptionHandler(BlockException.class)
    public ResponseResult<Void> handleBlockException(BlockException e) {
        String message = "系统繁忙,请稍后重试";
        
        if (e instanceof FlowException) {
            message = "接口限流,请稍后重试";
        } else if (e instanceof DegradeException) {
            message = "接口降级,请稍后重试";
        } else if (e instanceof ParamFlowException) {
            message = "热点参数限流";
        } else if (e instanceof SystemBlockException) {
            message = "系统保护(CPU/负载)";
        } else if (e instanceof AuthorityException) {
            message = "授权规则不通过";
        }
        
        log.warn("Sentinel流控:{}", message);
        return ResponseResult.fail(429, message);
    }
}

六、完整示例:电商系统异常处理实战

1. 业务异常定义

java 复制代码
// 库存不足异常
public class InsufficientStockException extends BusinessException {
    private final Long productId;
    private final Integer requested;
    private final Integer available;
    
    public InsufficientStockException(Long productId, 
            Integer requested, Integer available) {
        super(ErrorCode.INSUFFICIENT_STOCK);
        this.productId = productId;
        this.requested = requested;
        this.available = available;
    }
    
    @Override
    public String getMessage() {
        return String.format("商品[%d]库存不足,需要%d,可用%d", 
                productId, requested, available);
    }
}

// 订单状态异常
public class OrderStateException extends BusinessException {
    private final Long orderId;
    private final String currentState;
    private final String requiredState;
    
    public OrderStateException(Long orderId, 
            String currentState, String requiredState) {
        super(ErrorCode.ORDER_STATE_ERROR);
        this.orderId = orderId;
        this.currentState = currentState;
        this.requiredState = requiredState;
    }
}

2. 业务使用示例

java 复制代码
@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    
    @Override
    @Transactional
    public Order createOrder(OrderCreateDTO dto) {
        // 参数校验(会自动抛出MethodArgumentNotValidException)
        validateOrder(dto);
        
        try {
            // 检查库存
            checkStock(dto);
            
            // 创建订单
            Order order = buildOrder(dto);
            orderRepository.save(order);
            
            // 扣减库存
            reduceStock(dto);
            
            // 发送创建订单事件
            eventPublisher.publishEvent(new OrderCreatedEvent(order));
            
            return order;
            
        } catch (InsufficientStockException e) {
            // 添加上下文信息,便于日志记录
            EnhancedGlobalExceptionHandler.addContext("orderDTO", dto);
            throw e;
        }
    }
    
    private void checkStock(OrderCreateDTO dto) {
        dto.getItems().forEach(item -> {
            Integer available = stockService.getAvailableStock(item.getProductId());
            if (available < item.getQuantity()) {
                throw new InsufficientStockException(
                        item.getProductId(), 
                        item.getQuantity(), 
                        available);
            }
        });
    }
    
    private void validateOrder(OrderCreateDTO dto) {
        if (dto.getItems() == null || dto.getItems().isEmpty()) {
            throw new ValidationException("订单商品不能为空");
        }
        
        if (dto.getTotalAmount() == null || dto.getTotalAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new ValidationException("订单金额必须大于0");
        }
    }
}

3. Controller层示例

java 复制代码
@RestController
@RequestMapping("/orders")
@Validated
@Slf4j
public class OrderController {
    
    @Autowired
    private OrderService orderService;
    
    @PostMapping
    public ResponseResult<Order> createOrder(
            @Valid @RequestBody OrderCreateDTO dto) {
        
        log.info("创建订单,用户:{},商品数量:{}", 
                dto.getUserId(), dto.getItems().size());
        
        Order order = orderService.createOrder(dto);
        return ResponseResult.success(order);
    }
    
    @PostMapping("/{orderId}/cancel")
    public ResponseResult<Void> cancelOrder(@PathVariable Long orderId) {
        orderService.cancelOrder(orderId);
        return ResponseResult.success();
    }
    
    @GetMapping("/{orderId}")
    public ResponseResult<Order> getOrder(@PathVariable Long orderId) {
        Order order = orderService.getOrder(orderId);
        return ResponseResult.success(order);
    }
}

七、测试你的异常处理器

零基础全栈开发Java微服务版本实战-后端-前端-运维-实战企业级三个实战项目

1. 单元测试

java 复制代码
@SpringBootTest
@AutoConfigureMockMvc
class GlobalExceptionHandlerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void testBusinessException() throws Exception {
        mockMvc.perform(get("/api/test/business-error"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(10001))
                .andExpect(jsonPath("$.message").value("业务异常测试"));
    }
    
    @Test
    void testValidationException() throws Exception {
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"username\":\"\"}"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(10001))
                .andExpect(jsonPath("$.message").contains("不能为空"));
    }
    
    @Test
    void testSystemException() throws Exception {
        mockMvc.perform(get("/api/test/system-error"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(500));
    }
}

2. 集成测试异常处理器

java 复制代码
@WebMvcTest(OrderController.class)
@Import(GlobalExceptionHandler.class)
class OrderControllerExceptionTest {
    
    @MockBean
    private OrderService orderService;
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void createOrder_InsufficientStock() throws Exception {
        // 模拟库存不足异常
        when(orderService.createOrder(any()))
                .thenThrow(new InsufficientStockException(1L, 10, 5));
        
        mockMvc.perform(post("/orders")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{...}"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(ErrorCode.INSUFFICIENT_STOCK.getCode()))
                .andExpect(jsonPath("$.message").value("商品[1]库存不足,需要10,可用5"));
    }
}

八、个人建议性能与注意事项

资源获取关注公众号:小坏睡说Java

1. 性能考虑

  • 异常创建开销:异常对象的创建比普通对象开销大
  • 栈追踪开销:fillInStackTrace()方法比较耗时
  • 建议:对于频繁发生的业务异常,考虑使用错误码而非异常

总结

今天我们一起构建了一个完整的全局异常处理体系:

  1. 核心机制@RestControllerAdvice + @ExceptionHandler
  2. 异常体系:业务异常与系统异常分离
  3. 统一响应:标准化的响应格式
  4. 日志记录:完整的异常上下文信息
  5. 多环境适配:开发/生产环境差异化处理
  6. 最佳实践:参数校验、Sentinel集成等

通过全局异常处理,我们实现了:

  • 代码整洁:Controller层只有业务逻辑
  • 统一格式:所有异常响应格式一致
  • 易于维护:异常处理逻辑集中管理
  • 便于监控:异常日志完整记录

今日思考题

在你的项目中,有没有遇到过以下情况:

  1. 某个第三方接口频繁超时,需要特殊处理
  2. 不同微服务之间的异常需要统一处理
  3. 需要根据用户类型返回不同的错误信息

你是如何解决这些问题的?欢迎在评论区分享你的经验!


下期预告:明天我们将探讨《SpringBoot配置文件:一个注解搞定多环境配置!》,学习如何优雅管理不同环境的配置。

互动时间:你在异常处理中遇到过哪些"坑"?或者有哪些更好的实践建议?

资源获取关注公众号:小坏睡说Java 回复"异常处理源码",获取本文所有示例代码及完整项目。

相关推荐
李少兄2 小时前
深入理解 Java Web 开发中的 HttpServletRequest 与 HttpServletResponse
java·开发语言·前端
梁同学与Android2 小时前
Android ---【经验篇】阿里云 CentOS 服务器环境搭建 + SpringBoot项目部署(二)
android·spring boot·后端
用户2190326527352 小时前
SpringBoot自动配置:为什么你的应用能“开箱即用
java·spring boot·后端
爱笑的眼睛112 小时前
TensorFlow Hub:解锁预训练模型的无限可能,超越基础分类任务
java·人工智能·python·ai
shehuiyuelaiyuehao2 小时前
7类和对象
java·开发语言
汤姆Tom2 小时前
前端转战后端:JavaScript 与 Java 对照学习指南(第五篇 —— 面向对象:类、接口与多态)
java·前端·后端
凤凰战士芭比Q2 小时前
Jenkins(Pipeline job)
java·servlet·jenkins