• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TAKETODAY / today-infrastructure / 18154768944

01 Oct 2025 07:26AM UTC coverage: 81.882% (-0.005%) from 81.887%
18154768944

push

github

web-flow
Merge pull request #290 from TAKETODAY/dev/jspecify

jspecify

59788 of 78013 branches covered (76.64%)

Branch coverage included in aggregate %.

141239 of 167496 relevant lines covered (84.32%)

3.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.97
today-context/src/main/java/infra/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessor.java
1
/*
2
 * Copyright 2017 - 2025 the original author or authors.
3
 *
4
 * This program is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see [https://www.gnu.org/licenses/]
16
 */
17

18
package infra.validation.beanvalidation;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.util.Collection;
23
import java.util.HashSet;
24
import java.util.Map;
25
import java.util.Optional;
26
import java.util.Set;
27

28
import infra.aot.generate.GenerationContext;
29
import infra.aot.hint.MemberCategory;
30
import infra.aot.hint.ReflectionHints;
31
import infra.beans.factory.aot.BeanRegistrationAotContribution;
32
import infra.beans.factory.aot.BeanRegistrationAotProcessor;
33
import infra.beans.factory.aot.BeanRegistrationCode;
34
import infra.beans.factory.support.RegisteredBean;
35
import infra.core.ResolvableType;
36
import infra.lang.Assert;
37
import infra.logging.Logger;
38
import infra.logging.LoggerFactory;
39
import infra.util.ClassUtils;
40
import infra.util.ReflectionUtils;
41
import jakarta.validation.ConstraintValidator;
42
import jakarta.validation.NoProviderFoundException;
43
import jakarta.validation.Validation;
44
import jakarta.validation.Validator;
45
import jakarta.validation.ValidatorFactory;
46
import jakarta.validation.metadata.BeanDescriptor;
47
import jakarta.validation.metadata.ConstraintDescriptor;
48
import jakarta.validation.metadata.ContainerElementTypeDescriptor;
49
import jakarta.validation.metadata.ExecutableDescriptor;
50
import jakarta.validation.metadata.MethodType;
51
import jakarta.validation.metadata.ParameterDescriptor;
52
import jakarta.validation.metadata.PropertyDescriptor;
53

54
/**
55
 * AOT {@code BeanRegistrationAotProcessor} that adds additional hints
56
 * required for {@link ConstraintValidator}s.
57
 *
58
 * @author Sebastien Deleuze
59
 * @author Juergen Hoeller
60
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
61
 * @since 4.0 2023/6/24 14:06
62
 */
63
class BeanValidationBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
3✔
64

65
  private static final boolean beanValidationPresent = ClassUtils.isPresent(
4✔
66
          "jakarta.validation.Validation", BeanValidationBeanRegistrationAotProcessor.class.getClassLoader());
1✔
67

68
  private static final Logger logger = LoggerFactory.getLogger(BeanValidationBeanRegistrationAotProcessor.class);
4✔
69

70
  @Override
71
  @Nullable
72
  public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
73
    if (beanValidationPresent) {
2!
74
      return BeanValidationDelegate.processAheadOfTime(registeredBean);
3✔
75
    }
76
    return null;
×
77
  }
78

79
  /**
80
   * Inner class to avoid a hard dependency on the Bean Validation API at runtime.
81
   */
82
  private static final class BeanValidationDelegate {
83

84
    @Nullable
85
    private static final Validator validator = getValidatorIfAvailable();
3✔
86

87
    @Nullable
88
    private static Validator getValidatorIfAvailable() {
89
      try (ValidatorFactory validator = Validation.buildDefaultValidatorFactory()) {
2✔
90
        return validator.getValidator();
5✔
91
      }
92
      catch (NoProviderFoundException ex) {
×
93
        logger.info("No Bean Validation provider available - skipping validation constraint hint inference");
×
94
        return null;
×
95
      }
96
    }
97

98
    @Nullable
99
    public static BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
100
      if (validator == null) {
2!
101
        return null;
×
102
      }
103

104
      Class<?> beanClass = registeredBean.getBeanClass();
3✔
105
      Set<Class<?>> validatedClasses = new HashSet<>();
4✔
106
      Set<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses = new HashSet<>();
4✔
107

108
      processAheadOfTime(beanClass, new HashSet<>(), validatedClasses, constraintValidatorClasses);
7✔
109

110
      if (!validatedClasses.isEmpty() || !constraintValidatorClasses.isEmpty()) {
6!
111
        return new AotContribution(validatedClasses, constraintValidatorClasses);
6✔
112
      }
113
      return null;
2✔
114
    }
115

116
    private static void processAheadOfTime(Class<?> clazz, Set<Class<?>> visitedClasses, Set<Class<?>> validatedClasses,
117
            Set<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses) {
118

119
      Assert.notNull(validator, "Validator cannot be null");
3✔
120

121
      if (!visitedClasses.add(clazz)) {
4✔
122
        return;
1✔
123
      }
124

125
      BeanDescriptor descriptor;
126
      try {
127
        descriptor = validator.getConstraintsForClass(clazz);
4✔
128
      }
129
      catch (RuntimeException | LinkageError ex) {
1✔
130
        String className = clazz.getName();
3✔
131
        if (ex instanceof TypeNotPresentException || ex instanceof NoClassDefFoundError) {
6!
132
          if (logger.isDebugEnabled()) {
3!
133
            logger.debug("Skipping validation constraint hint inference for class %s due to a %s for %s"
×
134
                    .formatted(className, ex.getClass().getSimpleName(), ex.getMessage()));
×
135
          }
136
        }
137
        else {
138
          if (logger.isWarnEnabled()) {
×
139
            logger.warn("Skipping validation constraint hint inference for class {}", className, ex);
×
140
          }
141
        }
142
        return;
1✔
143
      }
1✔
144

145
      processExecutableDescriptor(descriptor.getConstrainedMethods(MethodType.NON_GETTER, MethodType.GETTER), constraintValidatorClasses);
11✔
146
      processExecutableDescriptor(descriptor.getConstrainedConstructors(), constraintValidatorClasses);
4✔
147
      processPropertyDescriptors(descriptor.getConstrainedProperties(), constraintValidatorClasses);
4✔
148
      if (!constraintValidatorClasses.isEmpty() && shouldProcess(clazz)) {
6!
149
        validatedClasses.add(clazz);
4✔
150
      }
151

152
      ReflectionUtils.doWithFields(clazz, field -> {
7✔
153
        Class<?> type = field.getType();
3✔
154
        if (Iterable.class.isAssignableFrom(type) || Optional.class.isAssignableFrom(type)) {
8✔
155
          ResolvableType resolvableType = ResolvableType.forField(field);
3✔
156
          Class<?> genericType = resolvableType.getGeneric(0).toClass();
10✔
157
          if (shouldProcess(genericType)) {
3✔
158
            validatedClasses.add(clazz);
4✔
159
            processAheadOfTime(genericType, visitedClasses, validatedClasses, constraintValidatorClasses);
5✔
160
          }
161
        }
162
        if (Map.class.isAssignableFrom(type)) {
4✔
163
          ResolvableType resolvableType = ResolvableType.forField(field);
3✔
164
          Class<?> keyGenericType = resolvableType.getGeneric(0).toClass();
10✔
165
          Class<?> valueGenericType = resolvableType.getGeneric(1).toClass();
10✔
166
          if (shouldProcess(keyGenericType)) {
3✔
167
            validatedClasses.add(clazz);
4✔
168
            processAheadOfTime(keyGenericType, visitedClasses, validatedClasses, constraintValidatorClasses);
5✔
169
          }
170
          if (shouldProcess(valueGenericType)) {
3✔
171
            validatedClasses.add(clazz);
4✔
172
            processAheadOfTime(valueGenericType, visitedClasses, validatedClasses, constraintValidatorClasses);
5✔
173
          }
174
        }
175
      });
1✔
176
    }
1✔
177

178
    private static boolean shouldProcess(Class<?> clazz) {
179
      return !clazz.getCanonicalName().startsWith("java.");
9✔
180
    }
181

182
    private static void processExecutableDescriptor(Set<? extends ExecutableDescriptor> executableDescriptors,
183
            Collection<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses) {
184

185
      for (ExecutableDescriptor executableDescriptor : executableDescriptors) {
10✔
186
        for (ParameterDescriptor parameterDescriptor : executableDescriptor.getParameterDescriptors()) {
11✔
187
          for (ConstraintDescriptor<?> constraintDescriptor : parameterDescriptor.getConstraintDescriptors()) {
11✔
188
            constraintValidatorClasses.addAll(constraintDescriptor.getConstraintValidatorClasses());
5✔
189
          }
1✔
190
          for (ContainerElementTypeDescriptor typeDescriptor : parameterDescriptor.getConstrainedContainerElementTypes()) {
7!
191
            for (ConstraintDescriptor<?> constraintDescriptor : typeDescriptor.getConstraintDescriptors()) {
×
192
              constraintValidatorClasses.addAll(constraintDescriptor.getConstraintValidatorClasses());
×
193
            }
×
194
          }
×
195
        }
1✔
196
      }
1✔
197
    }
1✔
198

199
    private static void processPropertyDescriptors(Set<PropertyDescriptor> propertyDescriptors,
200
            Collection<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses) {
201

202
      for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
10✔
203
        for (ConstraintDescriptor<?> constraintDescriptor : propertyDescriptor.getConstraintDescriptors()) {
11✔
204
          constraintValidatorClasses.addAll(constraintDescriptor.getConstraintValidatorClasses());
5✔
205
        }
1✔
206
        for (ContainerElementTypeDescriptor typeDescriptor : propertyDescriptor.getConstrainedContainerElementTypes()) {
11✔
207
          for (ConstraintDescriptor<?> constraintDescriptor : typeDescriptor.getConstraintDescriptors()) {
11✔
208
            constraintValidatorClasses.addAll(constraintDescriptor.getConstraintValidatorClasses());
5✔
209
          }
1✔
210
        }
1✔
211
      }
1✔
212
    }
1✔
213
  }
214

215
  private static class AotContribution implements BeanRegistrationAotContribution {
216

217
    private final Collection<Class<?>> validatedClasses;
218

219
    private final Collection<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses;
220

221
    public AotContribution(Collection<Class<?>> validatedClasses,
222
            Collection<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses) {
2✔
223

224
      this.validatedClasses = validatedClasses;
3✔
225
      this.constraintValidatorClasses = constraintValidatorClasses;
3✔
226
    }
1✔
227

228
    @Override
229
    public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
230
      ReflectionHints hints = generationContext.getRuntimeHints().reflection();
4✔
231
      for (Class<?> validatedClass : this.validatedClasses) {
11✔
232
        hints.registerType(validatedClass, MemberCategory.ACCESS_DECLARED_FIELDS);
10✔
233
      }
1✔
234
      for (Class<? extends ConstraintValidator<?, ?>> constraintValidatorClass : this.constraintValidatorClasses) {
11✔
235
        hints.registerType(constraintValidatorClass, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
10✔
236
      }
1✔
237
    }
1✔
238
  }
239

240
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc