告別舊版本!帶你玩轉(zhuǎn) Java 23、Spring Boot 3.3.4 與 Jakarta 10
在現(xiàn)代應(yīng)用開發(fā)中,微服務(wù)架構(gòu)與云原生設(shè)計(jì)已成為行業(yè)的主流方向。為了應(yīng)對(duì)日益復(fù)雜的業(yè)務(wù)場(chǎng)景,開發(fā)者們需要一個(gè)既簡(jiǎn)潔又強(qiáng)大的基礎(chǔ)模板,能夠在保證可維護(hù)性和可擴(kuò)展性的同時(shí),加速項(xiàng)目的落地。
本文將帶你深入理解一個(gè)基于 Java 23、Spring Boot 3.3.4 與 Jakarta 10 打造的快速啟動(dòng)工程,它采用了 六邊形架構(gòu)(Hexagonal Architecture),并集成了 JWT 安全認(rèn)證、日志統(tǒng)一管理、Spring Profiles 環(huán)境切換、Docker 容器化部署 等關(guān)鍵特性,幫助開發(fā)團(tuán)隊(duì)輕松應(yīng)對(duì) 微服務(wù)與云原生應(yīng)用開發(fā)的挑戰(zhàn)。
模板核心特性概覽
這個(gè)工程模板的設(shè)計(jì)目標(biāo)是:清晰分層、易于擴(kuò)展、貼合微服務(wù)場(chǎng)景。其關(guān)鍵特性包括:
- 六邊形架構(gòu)(Ports & Adapters):核心業(yè)務(wù)邏輯與外部實(shí)現(xiàn)解耦,保證代碼高內(nèi)聚、低耦合。
 - AOP 切面編程:統(tǒng)一處理日志、異常、安全校驗(yàn)等橫切關(guān)注點(diǎn)。
 - JWT 認(rèn)證與授權(quán):通過令牌機(jī)制實(shí)現(xiàn)安全訪問控制。
 - 完善的安全機(jī)制:請(qǐng)求與響應(yīng)全流程攔截與校驗(yàn)。
 - 日志體系:支持文件滾動(dòng)、統(tǒng)一日志格式與關(guān)鍵事件記錄。
 - 多環(huán)境配置:內(nèi)置 dev、staging、prod 三套配置,開發(fā)環(huán)境使用 H2,測(cè)試/生產(chǎn)使用 PostgreSQL。
 - Docker 容器化:內(nèi)置構(gòu)建與測(cè)試腳本,便于云端部署與集成。
 
六邊形架構(gòu)簡(jiǎn)介
六邊形架構(gòu)(Hexagonal Architecture),又稱 端口與適配器模式,由 Alistair Cockburn 在 2005 年提出。它的核心思想是:
- Domain/Core Logic(核心領(lǐng)域邏輯):不依賴外部系統(tǒng),只關(guān)注業(yè)務(wù)規(guī)則。
 - Ports(端口):定義交互契約,區(qū)分輸入(驅(qū)動(dòng)端口)與輸出(被驅(qū)動(dòng)端口)。
 - Adapters(適配器):具體實(shí)現(xiàn)端口,負(fù)責(zé)數(shù)據(jù)庫、消息隊(duì)列、外部 API 等集成。
 
這種架構(gòu)模式特別適合微服務(wù),因?yàn)樗茏尫?wù)保持 模塊化、易擴(kuò)展、可替換技術(shù)棧。
目錄結(jié)構(gòu)與包劃分
/src/main/java/com/icoderoad/ms-vanilla
 ├── adapters        # 外部適配層(REST 接口、Repository、AOP 過濾器)
 ├── domain          # 領(lǐng)域?qū)樱ń涌?、?shí)體、異常定義)
 ├── server          # 服務(wù)配置層(數(shù)據(jù)庫、緩存、消息隊(duì)列配置)
 ├── security        # 安全認(rèn)證層(JWT、簽名、加密組件)
 ├── utils           # 工具類封裝(日期、校驗(yàn)、字符串處理)
 └── ServiceBootStrap.java   # 啟動(dòng)入口服務(wù)啟動(dòng)與環(huán)境配置
package com.icoderoad.msvanilla;
@SpringBootApplication
public class ServiceBootStrap {
    public static void main(String[] args) {
        SpringApplication.run(ServiceBootStrap.class, args);
    }
}- 啟動(dòng)時(shí)會(huì)根據(jù) spring.profiles.active 加載對(duì)應(yīng)配置:
 
application-dev.yml → H2 內(nèi)存數(shù)據(jù)庫
application-staging.yml → PostgreSQL
application-prod.yml → PostgreSQL
Swagger + OpenAPI 集成:
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.6.0</version>
</dependency>API 分組配置:
@Bean
public GroupedOpenApi productApi() {
    return GroupedOpenApi.builder()
        .group("product-service")
        .pathsToMatch("/api/v1/product/**")
        .build();
}訪問接口文檔:http://localhost:9334/ms-vanilla/api/v1/swagger-ui/index.html
示例:產(chǎn)品接口 ProductController
package com.icoderoad.msvanilla.adapters.controller;
@RestController
@RequestMapping("${service.api.path}/product")
@Tag(name = "Product API", description = "產(chǎn)品查詢、創(chuàng)建、啟用/停用、刪除與更新")
public class ProductController {
    @Operation(summary = "通過 UUID 獲取產(chǎn)品狀態(tài)")
    @ApiResponses({
        @ApiResponse(responseCode = "200", description = "成功返回產(chǎn)品狀態(tài)"),
        @ApiResponse(responseCode = "400", description = "無效的產(chǎn)品 ID")
    })
    @GetMapping("/status/{productId}")
    public ResponseEntity<StandardResponse> getProductStatus(
            @PathVariable UUID productId) {
        ProductEntity product = productService.getProductById(productId);
        return ResponseEntity.ok(StandardResponse.success(product));
    }
}擴(kuò)展:異常處理、日志、JWT 安全與過濾器
統(tǒng)一異常處理(GlobalExceptionHandler)
package com.icoderoad.msvanilla.adapters.exception;
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @ExceptionHandler(EntityNotFoundException.class)
    public ResponseEntity<StandardResponse> handleNotFound(EntityNotFoundException ex) {
        log.error("Entity not found: {}", ex.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(StandardResponse.error("NOT_FOUND", ex.getMessage()));
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<StandardResponse> handleGeneric(Exception ex) {
        log.error("Unexpected error: ", ex);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(StandardResponse.error("INTERNAL_ERROR", "服務(wù)器內(nèi)部錯(cuò)誤"));
    }
}統(tǒng)一日志攔截(AOP)
package com.icoderoad.msvanilla.adapters.aop;
@Aspect
@Component
public class LoggingAspect {
    private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
    @Pointcut("execution(* com.icoderoad.msvanilla.adapters.controller..*(..))")
    public void controllerMethods() {}
    @Around("controllerMethods()")
    public Object logController(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        log.info("調(diào)用方法: {} 參數(shù): {}", joinPoint.getSignature(), Arrays.toString(joinPoint.getArgs()));
        Object result = joinPoint.proceed();
        log.info("方法 {} 執(zhí)行完成,耗時(shí): {}ms", joinPoint.getSignature(), System.currentTimeMillis() - start);
        return result;
    }
}JWT 工具類
package com.icoderoad.msvanilla.security;
@Component
public class JwtUtil {
    private static final String SECRET_KEY = "ChangeThisSecretKey";
    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setExpiration(Date.from(Instant.now().plus(1, ChronoUnit.HOURS)))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }
    public String extractUsername(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY)
                .parseClaimsJws(token).getBody().getSubject();
    }
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }
}JWT 過濾器
package com.icoderoad.msvanilla.security;
@Component
public class JwtFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtil jwtUtil;
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            if (jwtUtil.validateToken(token)) {
                String username = jwtUtil.extractUsername(token);
                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(username, null, List.of());
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        filterChain.doFilter(request, response);
    }
}Spring Security 配置
package com.icoderoad.msvanilla.security;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private JwtFilter jwtFilter;
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/api/v1/auth/**").permitAll()
                        .anyRequest().authenticated())
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
                .build();
    }
}結(jié)論
在 Java 23、Spring Boot 3.3.4 與 Jakarta 10 的加持下,這一快速啟動(dòng)模板不僅在工程結(jié)構(gòu)上體現(xiàn)了 六邊形架構(gòu)的優(yōu)雅設(shè)計(jì),還通過 統(tǒng)一異常處理、日志體系、JWT 安全認(rèn)證與過濾器機(jī)制,打造了一個(gè) 可擴(kuò)展、可維護(hù)、適合云原生環(huán)境 的微服務(wù)工程。
未來,隨著更多微服務(wù)的加入,團(tuán)隊(duì)能夠在不破壞核心邏輯的前提下,靈活替換數(shù)據(jù)庫、消息隊(duì)列、認(rèn)證機(jī)制等外部實(shí)現(xiàn),從而真正做到 技術(shù)無關(guān)、業(yè)務(wù)驅(qū)動(dòng)。
這不僅是一份模板,更是一份現(xiàn)代化應(yīng)用開發(fā)的 最佳實(shí)踐指南。















 
 
 









 
 
 
 