SuperAPI AI Directory

cc-java-dev

Coding & development

View repository

Updated 10-15 21:11

About this skill

Java Development Standards

References: Google Java Style Guide, Alibaba Java Development Manual


Toolchain

# Maven
mvn clean compile                    # 编译
mvn test                             # 运行测试
mvn verify                           # 运行所有检查

# Gradle
./gradlew build                      # 构建
./gradlew test                       # 运行测试

Naming Conventions

Type Rule Example
Package name All lowercase, reversed domain com.example.project
Class name UpperCamelCase, noun/noun phrase UserService, HttpClient
Method name lowerCamelCase, starts with a verb findById, isValid
Constant All uppercase with underscore separators MAX_RETRY_COUNT
Boolean return value is/has/can prefix isActive(), hasPermission()

Class Member Order

public class Example {
    // 1. 静态常量
    public static final String CONSTANT = "value";

    // 2. 静态变量
    private static Logger logger = LoggerFactory.getLogger(Example.class);

    // 3. 实例变量
    private Long id;

    // 4. 构造函数
    public Example() { }

    // 5. 静态方法
    public static Example create() { return new Example(); }

    // 6. 实例方法(公共 → 私有)
    public void doSomething() { }
    private void helperMethod() { }

    // 7. getter/setter(或使用 Lombok)
}

DTO/VO Class Standards

Rule Description
❌ Do not hand-write getters/setters DTO, VO, Request, and Response classes must all use Lombok
✅ Use @Data ordinary DTO
✅ Use @Value immutable DTO
✅ Use @Builder used together when there are many fields
⚠️ Use with caution on Entity classes @Data JPA Entity's equals/hashCode can affect Hibernate proxies
// ❌ 手写 getter/setter
public class UserDTO {
    private Long id;
    private String name;
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    // ... 大量样板代码
}

// ✅ 使用 Lombok
@Data
public class UserDTO {
    private Long id;
    private String name;
}

Batch Query Standards

Rule Description
❌ Do not use IN clauses with more than 500 parameters SQL parsing overhead is high, and the execution plan is unstable
✅ Query in batches when exceeded 500 per batch, merge the results
✅ Encapsulate common utility methods Avoid hand-writing batching logic everywhere
// ❌ 1700 个 ID 一次查询
List<User> users = userRepository.findByIdIn(allIds); // IN 子句过长

// ✅ 分批查询工具方法
public static <T, R> List<R> batchQuery(List<T> params, int batchSize,
                                         Function<List<T>, List<R>> queryFn) {
    List<R> result = new ArrayList<>();
    for (int i = 0; i < params.size(); i += batchSize) {
        List<T> batch = params.subList(i, Math.min(i + batchSize, params.size()));
        result.addAll(queryFn.apply(batch));
    }
    return result;
}

// 使用
List<User> users = batchQuery(allIds, 500, ids -> userRepository.findByIdIn(ids));

N+1 Query Prevention

Rule Description
❌ Do not call Repository/Mapper inside a loop Each iteration inside stream/forEach/for triggers one query
✅ Batch query outside the loop, convert results to a Map Number of queries drops from N to 1 (or the distinct count)
// ❌ N+1:循环内逐行查询 count
records.forEach(record -> {
    long count = deviceRepo.countByDeviceId(record.getDeviceId()); // 每条触发一次查询
    record.setDeviceCount(count);
});

// ✅ 循环外批量查询 + Map 查找
List<String> deviceIds = records.stream()
    .map(Record::getDeviceId).distinct().collect(Collectors.toList());
Map<String, Long> countMap = deviceRepo.countByDeviceIdIn(deviceIds).stream()
    .collect(Collectors.toMap(CountDTO::getDeviceId, CountDTO::getCount));
records.forEach(r -> r.setDeviceCount(countMap.getOrDefault(r.getDeviceId(), 0L)));

Common N+1 scenarios and fix patterns:

Scenario Inside the loop (❌) Outside the loop (✅)
count repo.countByXxx(id) repo.countByXxxIn(ids) → Map<id, count>
findById repo.findById(id) repo.findByIdIn(ids) → Map<id, entity>
exists repo.existsByXxx(id) repo.findXxxIn(ids) → Set<id> + set.contains()

Concurrency Safety Standards

Rule Description
❌ Do not read-modify-write Read the balance then write it back; concurrent access causes lost updates
❌ Do not check-then-act without fallback Check first then act; conditions fail under concurrency
✅ Use atomic update SQL UPDATE SET balance = balance + :delta WHERE id = :id
✅ Or use optimistic locking @Version field + retry mechanism
✅ Unique index as a fallback The last line of defense against duplicate inserts
// ❌ read-modify-write 竞态条件
PointsAccount account = accountRepo.findById(id);
account.setBalance(account.getBalance() + points); // 并发时丢失更新
accountRepo.save(account);

// ✅ 方案一:原子更新 SQL
@Modifying
@Query("UPDATE PointsAccount SET balance = balance + :points WHERE id = :id")
int addBalance(@Param("id") Long id, @Param("points") int points);

// ✅ 方案二:乐观锁
@Version
private Long version; // Entity 中添加版本字段
// ❌ check-then-act 无兜底(并发下可能重复结算)
if (!rewardRepo.existsByTenantIdAndPeriod(tenantId, period)) {
    rewardRepo.save(new RankingReward(...));
}

// ✅ 唯一索引兜底 + 异常捕获
// DDL: UNIQUE INDEX uk_tenant_period (tenant_id, ranking_type, period, rank_position)
try {
    rewardRepo.save(new RankingReward(...));
} catch (DataIntegrityViolationException e) {
    log.warn("重复结算已被唯一索引拦截: tenantId={}, period={}", tenantId, period);
}

Exception Handling

// ✅ 好:捕获具体异常,添加上下文
try {
    user = userRepository.findById(id);
} catch (DataAccessException e) {
    throw new ServiceException("Failed to find user: " + id, e);
}

// ✅ 好:资源自动关闭
try (InputStream is = new FileInputStream(file)) {
    // 使用资源
}

// ❌ 差:捕获过宽
catch (Exception e) { e.printStackTrace(); }

Null Value Handling

// ✅ 使用 Optional
public Optional<User> findById(Long id) {
    return userRepository.findById(id);
}

// ✅ 参数校验
public void updateUser(User user) {
    Objects.requireNonNull(user, "user must not be null");
}

// ✅ 安全的空值处理
String name = Optional.ofNullable(user)
    .map(User::getName)
    .orElse("Unknown");

Concurrent Programming

// ✅ 使用 ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(10);
Future<Result> future = executor.submit(() -> doWork());

// ✅ 使用 CompletableFuture
CompletableFuture<User> future = CompletableFuture
    .supplyAsync(() -> findUser(id))
    .thenApply(user -> enrichUser(user));

// ❌ 差:直接创建线程
new Thread(() -> doWork()).start();

Testing Standards (JUnit 5)

class UserServiceTest {
    @Test
    @DisplayName("根据 ID 查找用户 - 用户存在时返回用户")
    void findById_whenUserExists_returnsUser() {
        // given
        when(userRepository.findById(1L)).thenReturn(Optional.of(expected));

        // when
        Optional<User> result = userService.findById(1L);

        // then
        assertThat(result).isPresent();
        assertThat(result.get().getName()).isEqualTo("test");
    }
}

Spring Boot Standards

// ✅ 构造函数注入
@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;
}
Topic One-sentence rule
Injection method Use @RequiredArgsConstructor constructor injection, do not use @Autowired field injection
Auth downgrade When an optional-auth path encounters an invalid/expired token, downgrade to anonymous instead of returning 401/403
Circular dependency Spring Boot 3.x prohibits constructor circular dependencies by default. When splitting a large Service into sub-Services, first use a utility class to share methods, then consider @Lazy
self-invocation this.method() does not go through the proxy,@Transactional/@Async/@Cacheable all become ineffective; use self-injection or split the Bean
@Modifying JPA must be called within @Transactional , otherwise it throws Executing an update/delete query
Pagination parameters Full stack uniformly 0-based, Controller defaultValue = "0", Service directly PageRequest.of(page, size)

For detailed rules, fix templates, and checklists, see references/spring.md


Input Validation Specification

Rule Description
❌ Prohibited @RequestBody without @Valid All request bodies must be validated
✅ Add constraint annotations to DTO fields @NotBlank、@Size、@Pattern , etc.
✅ Add range constraints to numeric fields @Min、@Max、@Positive , etc.
✅ Add an upper limit to pagination parameters size must @Max(100) to prevent massive queries
✅ Whitelist validation for enum/status fields Custom validator or @Pattern

Common DTO Field Validation Quick Reference:

Field Type Required Annotation Description
Quantity quantity @NotNull @Min(1) Prevent 0 or negative numbers (negative numbers can cause reverse operations)
Amount amount/price @NotNull @Positive or @DecimalMin("0.01")
Pagination size @Min(1) @Max(100) Prevent size=999999 from overwhelming the database
Pagination page @Min(1) Page numbers start from 1
Percentage rate @Min(0) @Max(100) Define the range according to business needs
// ❌ 无校验,任意输入直接进入业务逻辑
@PostMapping("/ship")
public Result ship(@RequestBody ShippingRequest request) { ... }

// ✅ 完整校验
@PostMapping("/ship")
public Result ship(@RequestBody @Valid ShippingRequest request) { ... }

public record ShippingRequest(
    @NotNull Long orderId,
    @NotBlank @Size(max = 500) String shippingInfo,
    @Pattern(regexp = "pending|shipped|delivered") String giftStatus
) {}

// ❌ quantity 只有 @NotNull,负数会导致 Redis DECRBY 反向加库存
public record CreateOrderRequest(
    @NotNull Integer quantity  // 可提交 0 或负数
) {}

// ✅ 数量必须 >= 1
public record CreateOrderRequest(
    @NotNull @Min(1) Integer quantity
) {}

// ❌ 分页无上限,用户可传 size=999999
@GetMapping("/orders")
public Result list(@RequestParam int page, @RequestParam int size) { ... }

// ✅ 分页参数加约束
@GetMapping("/orders")
public Result list(@RequestParam @Min(1) int page,
                   @RequestParam @Min(1) @Max(100) int size) { ... }

Performance Optimization

Pitfall Solution
N+1 query See the "N+1 Query Prevention" chapter
Concatenating strings in a loop Use StringBuilder
Frequent boxing and unboxing Use primitive type streams
Collection initial capacity not specified new ArrayList<>(size)

Third-Party API HTTP Client Selection

Rule Description
❌ Avoid RestTemplate default client when calling domestic platform APIs By default, HttpURLConnection POST requests have compatibility issues with CDNs such as WeChat/Alipay (known to trigger 412/403)
✅ Prefer java.net.http.HttpClient(JDK 11+) Modern HTTP client, no CDN compatibility issues
✅ Or configure HttpComponentsClientHttpRequestFactory to make RestTemplate use Apache HttpClient at the underlying level

Diagnostic characteristics: HTTP error + empty body + extremely minimal response headers (only Connection/Content-Length) = CDN-layer interception, not the API's own response. When the same API's GET works normally but POST fails, first suspect HTTP client compatibility.


Native SQL Specification

Avoid MySQL reserved words in aliases

@Query(nativeQuery = true) If a column alias in is a MySQL reserved word, it will cause a syntax error.

High-Frequency Reserved Word Pitfalls:year_month, order, status, key, value, name, type, date, time, rank, range, rows, column, user, role, group

Rule Description
✅ Use short aliases or abbreviations ym, ord_status, cnt
✅ Or escape with backticks `year_month`
❌ Prohibited to use reserved words directly as aliases as year_month、as order、as rank
// ❌ year_month 是 MySQL 保留字(INTERVAL YEAR_MONTH)
@Query(value = """
    SELECT DATE_FORMAT(o.order_date, '%Y-%m') as year_month,
           COUNT(*) as cnt
    FROM orders o
    GROUP BY year_month
    """, nativeQuery = true)

// ✅ 改用非保留字别名
@Query(value = """
    SELECT DATE_FORMAT(o.order_date, '%Y-%m') as ym,
           COUNT(*) as cnt
    FROM orders o
    GROUP BY ym
    """, nativeQuery = true)

Logging Specification

// ✅ 参数化日志
log.debug("Finding user by id: {}", userId);
log.info("User {} logged in successfully", username);
log.error("Failed to process order {}", orderId, exception);

// ❌ 差:字符串拼接
log.debug("Finding user by id: " + userId);

Spring Bean Naming Conflicts

Scenario: At Spring startup BeanDefinitionStoreException, or "no qualifying bean of type X found" where it seems the Bean cannot be found.

Root Cause

Spring generates the bean name by default using the class name with the first letter lowercased. There are two types of conflicts:

  • Sub-scenario A: Same name, same type — two @Service/@Controller classes with the same name (different packages)
  • Sub-scenario B: Same name, different type —— @Bean(name=X) The registered object collides with the default bean name of the @Component/@Service class ⚠️ More insidious

Sub-scenario A: Two Controllers / Services with the same name

// ❌ 两个包下同名类,bean name 都是 miniAppConfigController
// com.trade.controller.MiniAppConfigController
@RestController
public class MiniAppConfigController { }

// com.trade.controller.mini.MiniAppConfigController
@RestController
public class MiniAppConfigController { }
// → 启动报 BeanDefinitionStoreException

Fix:

// ✅ 方案1: 改类名(推荐)
@RestController
public class MiniPublicConfigController { }

// ✅ 方案2: 显式指定 bean name
@RestController("miniPublicConfigController")
public class MiniAppConfigController { }

Sub-scenario B:@Bean and @Service different types with the same name ⚠️

The most treacherous kind — the error message may appear as "Bean not found", but the root cause is naming override.

// ❌ AsyncConfig 注册线程池
@Configuration
public class AsyncConfig {
    @Bean(name = "priceBatchUpdateExecutor")  // 名字和下面那个 Service 冲突
    public Executor priceBatchUpdateExecutor() { ... }
}

// ❌ 同时 @Service 类的默认 bean name 就是 priceBatchUpdateExecutor
@Service
public class PriceBatchUpdateExecutor { ... }

// ❌ 业务代码注入
@RequiredArgsConstructor
public class PriceBatchUpdateService {
    private final PriceBatchUpdateExecutor priceBatchUpdateExecutor;
    // 启动时报: no qualifying bean of type PriceBatchUpdateExecutor
}

Two consequences, both bad:

State Behavior Probability of misdiagnosis
overriding not enabled BeanDefinitionStoreException, clear localization Low
overriding enabled Thread pool Bean overrides Service Bean → when injecting PriceBatchUpdateExecutor what is found is of type Executor → reports no qualifying bean of type, the root cause is masked High, often takes hours to troubleshoot

Never fix it this way:

# ❌ AI 反射式开全局开关,掩盖问题
spring.main.allow-bean-definition-overriding=true

See .codex/global/AGENTS.md quality requirements: do not mask errors by enabling a global permissive switch.

Correct Fix:

// ✅ 区分语义:线程池叫 ThreadPool / TaskExecutor,业务类保持类名
@Bean(name = "priceBatchUpdateThreadPool")
public Executor priceBatchUpdateThreadPool() { ... }

@Service
public class PriceBatchUpdateExecutor { ... }  // 业务类不动

// 调用方同步改 @Qualifier
@RequiredArgsConstructor
public class PriceBatchUpdateTaskScheduler {
    @Qualifier("priceBatchUpdateThreadPool")
    private final Executor threadPool;
    private final PriceBatchUpdateExecutor executor;  // 注入业务 Bean
}

Bean naming conventions (avoid conflicts)

Type Suffix / naming convention
Business service XxxService / XxxManager / XxxExecutor(if "executor" is a business concept)
Thread pool Bean XxxThreadPool / XxxTaskExecutor
Configuration Bean XxxConfig / XxxProperties
Data source Bean XxxDataSource / XxxJdbcTemplate
HTTP client XxxRestTemplate / XxxHttpClient
MQ Bean XxxListenerContainer / XxxTemplate

Iron rule:@Bean(name=) naming must not collide with the default Bean name of any @Component/@Service/@Controller/@Repository class.

Troubleshooting commands

# 在项目里搜索是否有重名
rg '@Bean\(name = "priceBatchUpdateExecutor"' src/
rg 'class PriceBatchUpdateExecutor' src/

# 启动时打开 DEBUG 看 Bean 注册
# logging.level.org.springframework.beans.factory=DEBUG

Checklist

  • When adding a new Controller/Service, do you check whether a class with the same name already exists in the project
  • When adding a new @Bean(name=X) , do you check whether class X exists
  • Do classes with the same name under different packages use different class names or explicit bean names
  • When startup reports BeanDefinitionStoreException or no qualifying bean of type ,prioritize checking for same-named Beans
  • After temporarily enabling allow-bean-definition-overriding=true , do youremove it in timeafter fixing the root cause (in the vast majority of cases it should not be enabled)

Detailed reference

File Content
references/java-style.md Naming conventions, exception handling, Spring Boot, testing standards
references/spring.md Complete Spring Boot standards: injection, Auth degradation, circular dependencies, self-invocation pitfalls, pagination parameters
references/collections.md Immutable collections (Guava), string splitting
references/concurrency.md Thread pool configuration, CompletableFuture timeout
references/concurrency-db-patterns.md Get-Or-Create concurrency, N+1 prevention, atomic updates, Redis+DB consistency
references/code-patterns.md Guard clauses, enum optimization, strategy factory pattern
references/date-time.md Date arithmetic, accounting period calculation, no month-end alignment
references/http-client.md Third-party API HTTP client selection, CDN compatibility issues

📋 This reply follows:cc-java-dev - [specific section]