简体   繁体   English

如何在不使用“setAccessible()”的情况下使用反射调用私有字段

[英]How to use reflection to invoke a private field without using "setAccesible()"

I've encountered this problem multiple times.我多次遇到这个问题。

The problem: I have some class with private Fields (For example User information which is a Dto):问题:我有一些带有私有字段的类(例如用户信息,它是一个 Dto):

public class RegisterRequest {
    private String firstName;
    private String lastName;
    private String username;
    private String email;
    private String fieldOfStudy;
    private String password;
}

After searching the community on how to read the values of these fields (when for example doing a post request), I saw a lot of answers that said the solution is Reflection.在社区中搜索了如何读取这些字段的值后(例如在执行发布请求时),我看到很多答案都说解决方案是反射。

Lets say I want to check if any field is null (in another class), then my reflection-method would be the following:假设我想检查是否有任何字段为空(在另一个类中),那么我的反射方法如下:

for (Field f : registerRequest.getClass().getDeclaredFields()) {
    try {
        Field field = registerRequest.getClass().getDeclaredField(f.getName());
        field.setAccessible(true);
        Object value = field.get(registerRequest);
        if (value == null) {
            throw new AccountException("Vul alle velden in.");
        }
    }
    catch (NoSuchFieldException | IllegalAccessException e) {
        e.printStackTrace();
    }
}

Now I'm wondering if there is a way to do this without using field.setAccesible(true) , Since bypassing the accessibility of fields could lead to run-time errors.现在我想知道是否有一种方法可以在不使用field.setAccessible(true)的情况下执行此操作,因为绕过字段的可访问性可能会导致运行时错误。 Ie We shouldn't use reflection to change the visibility of a field.即我们不应该使用反射来改变字段的可见性。

Thank you for your time!感谢您的时间!

Do the validity check in the class that contains the private Data:在包含私有数据的类中进行有效性检查:

public boolean isValidForm() {
    for (Field field : this.getClass().getDeclaredFields()) {
        try {
            if (field.get(this) == null) {
                return false;
            }
        } catch (IllegalAccessException e) {
            throw new AccountException("Something went wrong");
        }
    }
    return true;
}

Use this function in your other class by doing:通过执行以下操作在您的其他类中使用此功能:

if(!registerRequest.isValidForm) {
   throw new AccountException("...")
}

Credits to Geocodezip Geocodezip 的积分

Maybe there's something I didn't undertsand in your post but I don't think you should use reflection to do validation on your DTO .也许您的帖子中有一些我没有理解的内容,但我认为您不应该使用reflection来对您的DTO进行验证。 Here's a solution on how you could handle validation using javax :这是一个关于如何使用javax处理验证的解决方案:

@Data
@Value
class RegisterRequest {
    @NotNull(message = "First name should not be null")
    String firstName;
    @NotNull(message = "Last name should not be null")
    String lastName;
    @NotNull(message = "Username should not be null")
    String username;
    @NotNull(message = "Email should not be null")
    String email;
    @NotNull(message = "Field of study should not be null")
    String fieldOfStudy;
    @NotNull(message = "Password should not be null")
    String password;
}

If you use java17+ you can use a record like this (and get rid of lombok ):如果您使用 java17+,则可以使用这样的record (并摆脱lombok ):

public record RegisterRequest(
        @NotNull(message = "First name should not be null")
        String firstName,
        @NotNull(message = "Last name should not be null")
        String lastName,
        @NotNull(message = "Username should not be null")
        String username,
        @NotNull(message = "Email should not be null")
        String email,
        @NotNull(message = "Field of study should not be null")
        String fieldOfStudy,
        @NotNull(message = "Password should not be null")
        String password
) {
}

Then you can make a unit test on your validation like this:然后你可以像这样对你的验证进行单元测试:

class RegisterRequestTest {

    private Validator validator;

    @BeforeEach
    public void setUp() {
        try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
            validator = factory.getValidator();
        }
    }

    @ParameterizedTest
    @MethodSource("invalidRegisterRequest")
    void validation_should_be_invalid(RegisterRequest registerRequest) {
        assertThat(validator.validate(registerRequest)).isNotEmpty();
    }

    private static List<RegisterRequest> invalidRegisterRequest() {
        return List.of(
                new RegisterRequest(null, "lasstname", "username", "email", "fieldOfStudy", "password"),
                new RegisterRequest("firstName", null, "username", "email", "fieldOfStudy", "password"),
                new RegisterRequest("firstName", "lasstname", null, "email", "fieldOfStudy", "password"),
                new RegisterRequest("firstName", "lasstname", "username", null, "fieldOfStudy", "password"),
                new RegisterRequest("firstName", "lasstname", "username", "email", null, "password"),
                new RegisterRequest("firstName", "lasstname", "username", "email", "fieldOfStudy", null)
        );
    }

}

In my examples I used @NotNull like in your post but I would recommand to use @NotBlank because in addition to checking for nullity, it checks if the property contains at least one non-blank character.在我的示例中,我像在您的帖子中一样使用了@NotNull ,但我建议使用@NotBlank ,因为除了检查是否为空之外,它还会检查属性是否包含至少一个非空白字符。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM