• 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

87.5
today-context/src/main/java/infra/context/annotation/ResourceElementResolver.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.context.annotation;
19

20
import org.jspecify.annotations.Nullable;
21

22
import java.lang.reflect.AnnotatedElement;
23
import java.lang.reflect.Field;
24
import java.lang.reflect.Method;
25
import java.util.Collections;
26
import java.util.LinkedHashSet;
27
import java.util.Set;
28

29
import infra.aop.TargetSource;
30
import infra.aop.framework.ProxyFactory;
31
import infra.beans.factory.NoSuchBeanDefinitionException;
32
import infra.beans.factory.config.ConfigurableBeanFactory;
33
import infra.beans.factory.config.DependencyDescriptor;
34
import infra.beans.factory.support.RegisteredBean;
35
import infra.core.MethodParameter;
36
import infra.lang.Assert;
37
import infra.util.ReflectionUtils;
38
import infra.util.StringUtils;
39

40
/**
41
 * Resolver for the injection of named beans on a field or method element,
42
 * following the rules of the {@link jakarta.annotation.Resource} annotation
43
 * but without any JNDI support. This is primarily intended for AOT processing.
44
 *
45
 * @author Stephane Nicoll
46
 * @author Juergen Hoeller
47
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
48
 * @see CommonAnnotationBeanPostProcessor
49
 * @see jakarta.annotation.Resource
50
 * @since 5.0
51
 */
52
public abstract class ResourceElementResolver {
53

54
  private final String name;
55

56
  private final boolean defaultName;
57

58
  ResourceElementResolver(String name, boolean defaultName) {
2✔
59
    this.name = name;
3✔
60
    this.defaultName = defaultName;
3✔
61
  }
1✔
62

63
  /**
64
   * Create a new {@link ResourceFieldResolver} for the specified field.
65
   *
66
   * @param fieldName the field name
67
   * @return a new {@link ResourceFieldResolver} instance
68
   */
69
  public static ResourceElementResolver forField(String fieldName) {
70
    return new ResourceFieldResolver(fieldName, true, fieldName);
7✔
71
  }
72

73
  /**
74
   * Create a new {@link ResourceFieldResolver} for the specified field and resource name.
75
   *
76
   * @param fieldName the field name
77
   * @param resourceName the resource name
78
   * @return a new {@link ResourceFieldResolver} instance
79
   */
80
  public static ResourceElementResolver forField(String fieldName, String resourceName) {
81
    return new ResourceFieldResolver(resourceName, false, fieldName);
×
82
  }
83

84
  /**
85
   * Create a new {@link ResourceMethodResolver} for the specified method
86
   * using a resource name that infers from the method name.
87
   *
88
   * @param methodName the method name
89
   * @param parameterType the parameter type.
90
   * @return a new {@link ResourceMethodResolver} instance
91
   */
92
  public static ResourceElementResolver forMethod(String methodName, Class<?> parameterType) {
93
    return new ResourceMethodResolver(defaultResourceNameForMethod(methodName), true,
9✔
94
            methodName, parameterType);
95
  }
96

97
  /**
98
   * Create a new {@link ResourceMethodResolver} for the specified method
99
   * and resource name.
100
   *
101
   * @param methodName the method name
102
   * @param parameterType the parameter type
103
   * @param resourceName the resource name
104
   * @return a new {@link ResourceMethodResolver} instance
105
   */
106
  public static ResourceElementResolver forMethod(String methodName, Class<?> parameterType, String resourceName) {
107
    return new ResourceMethodResolver(resourceName, false, methodName, parameterType);
8✔
108
  }
109

110
  private static String defaultResourceNameForMethod(String methodName) {
111
    if (methodName.startsWith("set") && methodName.length() > 3) {
8!
112
      return StringUtils.uncapitalizeAsProperty(methodName.substring(3));
5✔
113
    }
114
    return methodName;
×
115
  }
116

117
  /**
118
   * Resolve the value for the specified registered bean.
119
   *
120
   * @param registeredBean the registered bean
121
   * @return the resolved field or method parameter value
122
   */
123
  @Nullable
124
  @SuppressWarnings("unchecked")
125
  public <T> T resolve(RegisteredBean registeredBean) {
126
    Assert.notNull(registeredBean, "'registeredBean' is required");
3✔
127
    return (T) (isLazyLookup(registeredBean) ? buildLazyResourceProxy(registeredBean) :
9✔
128
            resolveValue(registeredBean));
3✔
129
  }
130

131
  /**
132
   * Resolve the value for the specified registered bean and set it using reflection.
133
   *
134
   * @param registeredBean the registered bean
135
   * @param instance the bean instance
136
   */
137
  public abstract void resolveAndSet(RegisteredBean registeredBean, Object instance);
138

139
  /**
140
   * Create a suitable {@link DependencyDescriptor} for the specified bean.
141
   *
142
   * @param registeredBean the registered bean
143
   * @return a descriptor for that bean
144
   */
145
  abstract DependencyDescriptor createDependencyDescriptor(RegisteredBean registeredBean);
146

147
  abstract Class<?> getLookupType(RegisteredBean registeredBean);
148

149
  abstract AnnotatedElement getAnnotatedElement(RegisteredBean registeredBean);
150

151
  boolean isLazyLookup(RegisteredBean registeredBean) {
152
    AnnotatedElement ae = getAnnotatedElement(registeredBean);
4✔
153
    Lazy lazy = ae.getAnnotation(Lazy.class);
5✔
154
    return (lazy != null && lazy.value());
9!
155
  }
156

157
  private Object buildLazyResourceProxy(RegisteredBean registeredBean) {
158
    Class<?> lookupType = getLookupType(registeredBean);
4✔
159

160
    TargetSource ts = new TargetSource() {
19✔
161
      @Override
162
      public Class<?> getTargetClass() {
163
        return lookupType;
×
164
      }
165

166
      @Override
167
      public Object getTarget() {
168
        return resolveValue(registeredBean);
×
169
      }
170
    };
171

172
    ProxyFactory pf = new ProxyFactory();
4✔
173
    pf.setTargetSource(ts);
3✔
174
    if (lookupType.isInterface()) {
3!
175
      pf.addInterface(lookupType);
3✔
176
    }
177
    return pf.getProxy(registeredBean.getBeanFactory().getBeanClassLoader());
6✔
178
  }
179

180
  /**
181
   * Resolve the value to inject for this instance.
182
   *
183
   * @param registeredBean the bean registration
184
   * @return the value to inject
185
   */
186
  private Object resolveValue(RegisteredBean registeredBean) {
187
    ConfigurableBeanFactory factory = registeredBean.getBeanFactory();
3✔
188

189
    Object resource;
190
    Set<String> autowiredBeanNames;
191
    DependencyDescriptor descriptor = createDependencyDescriptor(registeredBean);
4✔
192
    if (this.defaultName && !factory.containsBean(this.name)) {
8✔
193
      autowiredBeanNames = new LinkedHashSet<>();
4✔
194
      resource = factory.resolveDependency(descriptor, registeredBean.getBeanName(), autowiredBeanNames, null);
8✔
195
      if (resource == null) {
2!
196
        throw new NoSuchBeanDefinitionException(descriptor.getDependencyType(), "No resolvable resource object");
×
197
      }
198
    }
199
    else {
200
      resource = factory.resolveBeanByName(this.name, descriptor);
6✔
201
      autowiredBeanNames = Collections.singleton(this.name);
4✔
202
    }
203

204
    for (String autowiredBeanName : autowiredBeanNames) {
10✔
205
      if (factory.containsBean(autowiredBeanName)) {
4✔
206
        factory.registerDependentBean(autowiredBeanName, registeredBean.getBeanName());
5✔
207
      }
208
    }
1✔
209
    return resource;
2✔
210
  }
211

212
  private static final class ResourceFieldResolver extends ResourceElementResolver {
213

214
    private final String fieldName;
215

216
    public ResourceFieldResolver(String name, boolean defaultName, String fieldName) {
217
      super(name, defaultName);
4✔
218
      this.fieldName = fieldName;
3✔
219
    }
1✔
220

221
    @Override
222
    public void resolveAndSet(RegisteredBean registeredBean, Object instance) {
223
      Assert.notNull(registeredBean, "'registeredBean' is required");
3✔
224
      Assert.notNull(instance, "'instance' is required");
3✔
225
      Field field = getField(registeredBean);
4✔
226
      Object resolved = resolve(registeredBean);
4✔
227
      ReflectionUtils.makeAccessible(field);
3✔
228
      ReflectionUtils.setField(field, instance, resolved);
4✔
229
    }
1✔
230

231
    @Override
232
    protected DependencyDescriptor createDependencyDescriptor(RegisteredBean registeredBean) {
233
      Field field = getField(registeredBean);
4✔
234
      return new LookupDependencyDescriptor(field, field.getType(), isLazyLookup(registeredBean));
10✔
235
    }
236

237
    @Override
238
    protected Class<?> getLookupType(RegisteredBean registeredBean) {
239
      return getField(registeredBean).getType();
5✔
240
    }
241

242
    @Override
243
    protected AnnotatedElement getAnnotatedElement(RegisteredBean registeredBean) {
244
      return getField(registeredBean);
4✔
245
    }
246

247
    private Field getField(RegisteredBean registeredBean) {
248
      Field field = ReflectionUtils.findField(registeredBean.getBeanClass(), this.fieldName);
6✔
249
      Assert.notNull(field,
5✔
250
              () -> "No field '" + this.fieldName + "' found on " + registeredBean.getBeanClass().getName());
×
251
      return field;
2✔
252
    }
253
  }
254

255
  private static final class ResourceMethodResolver extends ResourceElementResolver {
256

257
    private final String methodName;
258

259
    private final Class<?> lookupType;
260

261
    private ResourceMethodResolver(String name, boolean defaultName, String methodName, Class<?> lookupType) {
262
      super(name, defaultName);
4✔
263
      this.methodName = methodName;
3✔
264
      this.lookupType = lookupType;
3✔
265
    }
1✔
266

267
    @Override
268
    public void resolveAndSet(RegisteredBean registeredBean, Object instance) {
269
      Assert.notNull(registeredBean, "'registeredBean' is required");
3✔
270
      Assert.notNull(instance, "'instance' is required");
3✔
271
      Method method = getMethod(registeredBean);
4✔
272
      Object resolved = resolve(registeredBean);
4✔
273
      ReflectionUtils.makeAccessible(method);
3✔
274
      ReflectionUtils.invokeMethod(method, instance, resolved);
10✔
275
    }
1✔
276

277
    @Override
278
    protected DependencyDescriptor createDependencyDescriptor(RegisteredBean registeredBean) {
279
      return new LookupDependencyDescriptor(
5✔
280
              getMethod(registeredBean), this.lookupType, isLazyLookup(registeredBean));
7✔
281
    }
282

283
    @Override
284
    protected Class<?> getLookupType(RegisteredBean bean) {
285
      return this.lookupType;
3✔
286
    }
287

288
    @Override
289
    protected AnnotatedElement getAnnotatedElement(RegisteredBean registeredBean) {
290
      return getMethod(registeredBean);
4✔
291
    }
292

293
    private Method getMethod(RegisteredBean registeredBean) {
294
      Method method = ReflectionUtils.findMethod(registeredBean.getBeanClass(), this.methodName, this.lookupType);
13✔
295
      Assert.notNull(method,
5✔
296
              () -> "Method '%s' with parameter type '%s' declared on %s could not be found.".formatted(
×
297
                      this.methodName, this.lookupType.getName(), registeredBean.getBeanClass().getName()));
×
298
      return method;
2✔
299
    }
300
  }
301

302
  /**
303
   * Extension of the DependencyDescriptor class,
304
   * overriding the dependency type with the specified resource type.
305
   */
306
  @SuppressWarnings("serial")
307
  static class LookupDependencyDescriptor extends DependencyDescriptor {
308

309
    private final Class<?> lookupType;
310

311
    private final boolean lazyLookup;
312

313
    public LookupDependencyDescriptor(Field field, Class<?> lookupType, boolean lazyLookup) {
314
      super(field, true);
4✔
315
      this.lookupType = lookupType;
3✔
316
      this.lazyLookup = lazyLookup;
3✔
317
    }
1✔
318

319
    public LookupDependencyDescriptor(Method method, Class<?> lookupType, boolean lazyLookup) {
320
      super(new MethodParameter(method, 0), true);
8✔
321
      this.lookupType = lookupType;
3✔
322
      this.lazyLookup = lazyLookup;
3✔
323
    }
1✔
324

325
    @Override
326
    public Class<?> getDependencyType() {
327
      return this.lookupType;
3✔
328
    }
329

330
    @Override
331
    public boolean supportsLazyResolution() {
332
      return !this.lazyLookup;
6!
333
    }
334
  }
335

336
}
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