【AI问答】Java类中,一些变量设置了@NotNull,怎么在调用内部方法时校验变量是否为空

有问题问AI的时候,有一些回答比较准确和全面,由于没有时间再二次总结和精炼,直接分享记录一下。

一、Java类中,一些变量设置了@NotNull,怎么在调用内部方法时校验变量是否为空

Kimi: 在Java中,@NotNull 是一个注解,通常用于标记变量、参数或返回值不能为空。它本身并不直接提供运行时的校验功能,而是主要用于代码分析工具(如IDE、静态代码分析工具等)来检测潜在的空指针问题。如果你希望在运行时校验变量是否为空,可以通过以下几种方式实现:

1. 使用断言(assert

如果项目开启了断言功能(通过 -ea 参数启动JVM),可以在方法内部使用 assert 来校验变量是否为空。

public class Example {
    @NotNull
    private String name;

    public Example(@NotNull String name) {
        this.name = name;
    }

    public void doSomething() {
        assert name != null : "name should not be null";
        // 其他逻辑
    }
}

这种方式的优点是简单,但缺点是断言默认是关闭的,需要在运行时显式开启。

2. 手动校验

在方法内部手动校验变量是否为空,并抛出异常。

public class Example {
    @NotNull
    private String name;

    public Example(@NotNull String name) {
        this.name = name;
    }

    public void doSomething() {
        if (name == null) {
            throw new IllegalArgumentException("name should not be null");
        }
        // 其他逻辑
    }
}

这种方式的优点是明确且可控,但缺点是需要在每个需要校验的地方手动编写校验代码。

3. 使用工具类(如 Apache Commons Lang 或 Spring 的 Assert)

如果项目中使用了 Apache Commons Lang 或 Spring 框架,可以利用它们提供的工具类来简化校验。

使用 Apache Commons Lang

import org.apache.commons.lang3.Validate;

public class Example {
    @NotNull
    private String name;

    public Example(@NotNull String name) {
        this.name = name;
    }

    public void doSomething() {
        Validate.notNull(name, "name should not be null");
        // 其他逻辑
    }
}

使用 Spring 的 Assert

import org.springframework.util.Assert;

public class Example {
    @NotNull
    private String name;

    public Example(@NotNull String name) {
        this.name = name;
    }

    public void doSomething() {
        Assert.notNull(name, "name should not be null");
        // 其他逻辑
    }
}

这种方式的优点是代码简洁,且工具类通常会提供更丰富的校验功能。

4. 使用 AOP(面向切面编程)

如果项目中使用了 Spring 框架,可以通过 AOP 来实现自动校验。创建一个切面类,拦截需要校验的方法,并在方法执行前进行校验。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class NotNullAspect {
    @Before("execution(* com.example..*(..)) && @annotation(org.jetbrains.annotations.NotNull)")
    public void checkNotNull() {
        // 在这里可以获取方法参数或对象属性,并进行校验
        // 如果发现为空,抛出异常
    }
}

这种方式的优点是可以集中处理校验逻辑,减少重复代码,但缺点是实现相对复杂,且需要对AOP有一定的了解。

5. 使用 Lombok 的 @NonNull@Builder(如果使用 Lombok)

如果项目中使用了 Lombok,可以结合 @NonNull@Builder 来简化校验。

import lombok.Builder;
import lombok.NonNull;

@Builder
public class Example {
    @NonNull
    private String name;

    public void doSomething() {
        // 其他逻辑
    }
}

Lombok 会在生成的构造器中自动校验 @NonNull 标记的字段是否为空。这种方式的优点是代码简洁,但缺点是依赖于 Lombok 插件。

总结

  • 如果项目中已经使用了 Spring 或 Apache Commons Lang,推荐使用它们提供的工具类进行校验。
  • 如果项目中使用了 Lombok,可以结合 @NonNull@Builder 来简化校验。
  • 如果项目中没有使用上述框架或工具,可以手动校验或使用 AOP 来实现集中校验。

二、详细说明第五种

Kimi: 好的,下面详细说明第五种方法——使用 Lombok@NonNull@Builder 来实现变量的非空校验。

1. Lombok 简介

Lombok 是一个 Java 库,通过注解的方式简化 Java 代码的编写。它可以在编译时自动生成一些常见的代码,如构造器、Getter、Setter、toString 等,从而减少样板代码的编写。

2. 使用 @NonNull@Builder 实现非空校验

2.1 添加 Lombok 依赖

首先,确保你的项目中已经添加了 Lombok 的依赖。如果你使用的是 Maven,可以在 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.28</version>
    <scope>provided</scope>
</dependency>

如果你使用的是 Gradle,可以在 build.gradle 文件中添加以下依赖:

implementation 'org.projectlombok:lombok:1.18.28'
annotationProcessor 'org.projectlombok:lombok:1.18.28'

2.2 使用 @NonNull@Builder

接下来,我们通过一个示例来展示如何使用 @NonNull@Builder 来实现非空校验。

import lombok.Builder;
import lombok.NonNull;

public class Example {
    @NonNull
    private String name;

    @Builder
    public Example(@NonNull String name) {
        this.name = name;
    }

    public void doSomething() {
        // 其他逻辑
        System.out.println("Name: " + name);
    }

    public static void main(String[] args) {
        // 正常创建对象
        Example example = Example.builder().name("Kimi").build();
        example.doSomething();

        // 尝试创建对象时传入 null
        try {
            Example exampleWithNull = Example.builder().name(null).build();
        } catch (NullPointerException e) {
            System.out.println("Caught NullPointerException: " + e.getMessage());
        }
    }
}

3. 代码解析

3.1 @NonNull

  • @NonNull 是 Lombok 提供的一个注解,用于标记字段或方法参数不能为空。
  • 当 Lombok 生成代码时,会自动在构造器或方法中插入非空校验逻辑。如果传入的值为 null,会抛出 NullPointerException

3.2 @Builder

  • @Builder 是 Lombok 提供的一个注解,用于生成一个构建器(Builder)模式的类。
  • 构建器模式是一种设计模式,用于创建对象,特别是当对象的构造参数较多时,可以提供更清晰的代码。
  • 在上面的代码中,@Builder 会生成一个静态的 builder() 方法,以及一个 build() 方法,用于创建 Example 类的实例。

4. 运行结果

运行上述代码,输出如下:

Name: Kimi
Caught NullPointerException: name is marked non-null but is null

4.1 正常情况

当调用 Example.builder().name("Kimi").build() 时,name 参数为非空,对象创建成功,doSomething() 方法正常执行。

4.2 异常情况

当调用 Example.builder().name(null).build() 时,name 参数为 null,Lombok 生成的构造器会自动抛出 NullPointerException,提示 name 不能为空。

5. 优点

  • 代码简洁:使用 Lombok 的 @NonNull@Builder,可以减少大量的样板代码,使代码更加简洁。
  • 自动校验:Lombok 会在生成的构造器中自动插入非空校验逻辑,无需手动编写校验代码。
  • 易读性高:通过构建器模式创建对象,代码的可读性更高,特别是当对象的构造参数较多时。

6. 注意事项

  • IDE 支持:确保你的 IDE 支持 Lombok,并且已经安装了 Lombok 插件。例如,在 IntelliJ IDEA 中,需要安装 Lombok 插件并启用注解处理器。
  • 编译器配置:确保你的编译器配置正确,以便 Lombok 能够正常工作。例如,在 Maven 或 Gradle 项目中,需要正确配置 Lombok 的依赖和注解处理器。
  • 异常处理:虽然 Lombok 会自动抛出 NullPointerException,但你可能需要在调用代码中捕获并处理这些异常,以避免程序崩溃。

7. 总结

通过使用 Lombok 的 @NonNull@Builder,可以非常方便地实现变量的非空校验。这种方式不仅减少了代码量,还提高了代码的可读性和可维护性。如果你的项目中已经使用了 Lombok,这是一种非常推荐的实现方式。

### 一、解决MyBatisX无法识别MapStruct生成的VO属性问题 **根本原因**:MyBatisX在编译前扫描源码,MapStruct生成的VO尚未生成(需先执行`mvn compile`)。 **解决方案**: 1. **手动触发编译** ```bash mvn clean compile # 先编译生成VO ``` 2. **IDEA配置自动编译** - 开启设置:`Build → Compiler → Build project automatically` - 启用注解处理器:`Settings → Build → Compiler → Annotation Processors → ✔ Enable annotation processing` 3. **添加Lombok依赖**(若VO使用Lombok) ```xml <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> </dependency> ``` --- ### 二、Test表达式中的方法调用导航支持 **支持程度**: - ✅ **基础导航**:支持`<if test="obj.method()">`跳转到Java方法定义 - ❌ **链式调用**:不支持`obj.getA().getB()`的多级导航 - ⚠️ **静态方法**:需完整名(如 `@java.util.Objects@isNull(param)`) **最佳实践**: 1. 简化表达式逻辑,避免多层方法调用 2. 复杂逻辑转移到Java代码中用`@Param`传递: ```java // Mapper接口 List<User> selectByLogic(@Param("valid") boolean isValid); ``` ```xml <!-- XML使用简单表达式 --> <if test="valid"> ... </if> ``` --- ### 三、多模块项目中VO的配置方法 **项目结构示例**: ``` my-project ├── core-module (实体定义) └── web-module (VO定义) ``` **配置步骤**: 1. **添加模块依赖** 在`web-module/pom.xml`中引入core-module: ```xml <dependency> <groupId>com.example</groupId> <artifactId>core-module</artifactId> <version>1.0.0</version> </dependency> ``` 2. **MyBatisX生成配置** - 在数据库表右键 → `MybatisX-Generator` - 设置`Model Package`指向VO所在包: ```markdown Entity Package: com.example.web.vo # VO路径 ``` 3. **启用跨模块扫描** 在MyBatis配置文件中添加: ```xml <typeAliases> <package name="com.example.core.entity"/> <!-- 实体 --> <package name="com.example.web.vo"/> <!-- VO --> </typeAliases> ``` --- ### 四、OGNL表达式逻辑验证能力 **当前能力限制**: - ✅ **语法检查**:验证基础语法错误(如缺少括号) - ❌ **逻辑验证**:无法验证运行逻辑(如`test="id != null and id == 0"`) - ⚠️ **部分提示**:仅对MyBatis内置方法(如`_parameter`)有代码补全 **调试建议**: 1. **单元测试覆盖**: ```java @Test void testOgnlLogic() { Map<String, Object> params = new HashMap<>(); params.put("id", 0); boolean result = new Ognl().parseExpression("id != null and id == 0"); // 手动验证逻辑 } ``` 2. **日志输出**: ```xml <select id="selectByCondition"> SELECT * FROM user <where> <if test="id != null"> AND id = #{id} <!-- 临日志输出 --> ${@System.out@println("ID condition triggered")} </if> </where> </select> ``` --- ### 总结对比表 | 问题型 | 解决方式 | 工具支持度 | |------------------------|----------------------------------|-----------| | VO属性识别 | 预编译+注解处理器 | ✅ | | 方法调用导航 | 简化表达式+静态方法全限定名 | ️(部分) | | 多模块VO配置 | 模块依赖+包扫描 | ✅ | | OGNL逻辑验证 | 单元测试+日志调试 | ❌ | > 💡 **关键建议**: > - 对于复杂OGNL逻辑,使用`<script>`标签嵌入Java代码 > - 定期升级MyBatisX(当前最新版v1.6.3修复了多模块兼容性问题) ---
10-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荔枝吻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值