• 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

81.06
today-context/src/main/java/infra/context/annotation/ContextAnnotationAutowireCandidateResolver.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.io.Serializable;
23
import java.lang.annotation.Annotation;
24
import java.lang.reflect.Method;
25
import java.util.Collection;
26
import java.util.Collections;
27
import java.util.LinkedHashSet;
28
import java.util.List;
29
import java.util.Map;
30
import java.util.Set;
31

32
import infra.aop.TargetSource;
33
import infra.aop.framework.ProxyFactory;
34
import infra.beans.factory.NoSuchBeanDefinitionException;
35
import infra.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver;
36
import infra.beans.factory.config.DependencyDescriptor;
37
import infra.beans.factory.support.AutowireCandidateResolver;
38
import infra.beans.factory.support.StandardBeanFactory;
39
import infra.core.MethodParameter;
40
import infra.core.annotation.AnnotationUtils;
41

42
/**
43
 * Complete implementation of the {@link AutowireCandidateResolver} strategy
44
 * interface, providing support for qualifier annotations as well as for lazy resolution
45
 * driven by the {@link Lazy} annotation in the {@code context.annotation} package.
46
 *
47
 * @author Juergen Hoeller
48
 * @author <a href="https://github.com/TAKETODAY">Harry Yang</a>
49
 * @since 4.0 2021/12/22 22:11
50
 */
51
public class ContextAnnotationAutowireCandidateResolver extends QualifierAnnotationAutowireCandidateResolver {
3✔
52

53
  @Override
54
  @Nullable
55
  public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
56
    return isLazy(descriptor)
5✔
57
            ? buildLazyResolutionProxy(descriptor, beanName)
5✔
58
            : null;
1✔
59
  }
60

61
  @Override
62
  @Nullable
63
  public Class<?> getLazyResolutionProxyClass(DependencyDescriptor descriptor, @Nullable String beanName) {
64
    return (isLazy(descriptor) ? (Class<?>) buildLazyResolutionProxy(descriptor, beanName, true) : null);
13✔
65
  }
66

67
  protected boolean isLazy(DependencyDescriptor descriptor) {
68
    for (Annotation ann : descriptor.getAnnotations()) {
17✔
69
      Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class);
5✔
70
      if (lazy != null && lazy.value()) {
5!
71
        return true;
2✔
72
      }
73
    }
74
    MethodParameter methodParam = descriptor.getMethodParameter();
3✔
75
    if (methodParam != null) {
2✔
76
      Method method = methodParam.getMethod();
3✔
77
      if (method == null || void.class == method.getReturnType()) {
6✔
78
        Lazy lazy = AnnotationUtils.getAnnotation(methodParam.getAnnotatedElement(), Lazy.class);
6✔
79
        return lazy != null && lazy.value();
9!
80
      }
81
    }
82
    return false;
2✔
83
  }
84

85
  protected Object buildLazyResolutionProxy(DependencyDescriptor descriptor, @Nullable String beanName) {
86
    return buildLazyResolutionProxy(descriptor, beanName, false);
6✔
87
  }
88

89
  private Object buildLazyResolutionProxy(DependencyDescriptor descriptor, @Nullable String beanName, boolean classOnly) {
90

91
    if (!(getBeanFactory() instanceof StandardBeanFactory stdF)) {
10!
92
      throw new IllegalStateException("Lazy resolution only supported with StandardBeanFactory");
×
93
    }
94

95
    TargetSource ts = new LazyDependencyTargetSource(stdF, descriptor, beanName);
7✔
96

97
    ProxyFactory pf = new ProxyFactory();
4✔
98
    pf.setTargetSource(ts);
3✔
99
    Class<?> dependencyType = descriptor.getDependencyType();
3✔
100
    if (dependencyType.isInterface()) {
3✔
101
      pf.addInterface(dependencyType);
3✔
102
    }
103
    ClassLoader classLoader = stdF.getBeanClassLoader();
3✔
104
    return classOnly ? pf.getProxyClass(classLoader) : pf.getProxy(classLoader);
10✔
105
  }
106

107
  @SuppressWarnings("serial")
108
  private static class LazyDependencyTargetSource implements TargetSource, Serializable {
109

110
    private final StandardBeanFactory beanFactory;
111

112
    private final DependencyDescriptor descriptor;
113

114
    @Nullable
115
    private final String beanName;
116

117
    @Nullable
118
    private transient volatile Object cachedTarget;
119

120
    public LazyDependencyTargetSource(StandardBeanFactory beanFactory,
121
            DependencyDescriptor descriptor, @Nullable String beanName) {
2✔
122

123
      this.beanFactory = beanFactory;
3✔
124
      this.descriptor = descriptor;
3✔
125
      this.beanName = beanName;
3✔
126
    }
1✔
127

128
    @Override
129
    public Class<?> getTargetClass() {
130
      return this.descriptor.getDependencyType();
4✔
131
    }
132

133
    @Override
134
    @SuppressWarnings("NullAway")
135
    public Object getTarget() {
136
      Object cachedTarget = this.cachedTarget;
3✔
137
      if (cachedTarget != null) {
2✔
138
        return cachedTarget;
2✔
139
      }
140

141
      Set<String> autowiredBeanNames = new LinkedHashSet<>(2);
5✔
142
      Object target = this.beanFactory.doResolveDependency(
10✔
143
              this.descriptor, this.beanName, autowiredBeanNames, null);
144

145
      if (target == null) {
2✔
146
        Class<?> type = getTargetClass();
3✔
147
        if (Map.class == type) {
3!
148
          target = Collections.emptyMap();
×
149
        }
150
        else if (List.class == type) {
3✔
151
          target = Collections.emptyList();
3✔
152
        }
153
        else if (Set.class == type || Collection.class == type) {
6!
154
          target = Collections.emptySet();
×
155
        }
156
        else {
157
          throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType(),
8✔
158
                  "Optional dependency not present for lazy injection point");
159
        }
160
      }
1✔
161
      else {
162
        if (target instanceof Map<?, ?> map && Map.class == getTargetClass()) {
3!
163
          target = Collections.unmodifiableMap(map);
×
164
        }
165
        else if (target instanceof List<?> list && List.class == getTargetClass()) {
10!
166
          target = Collections.unmodifiableList(list);
4✔
167
        }
168
        else if (target instanceof Set<?> set && Set.class == getTargetClass()) {
3!
169
          target = Collections.unmodifiableSet(set);
×
170
        }
171
        else if (target instanceof Collection<?> coll && Collection.class == getTargetClass()) {
3!
172
          target = Collections.unmodifiableCollection(coll);
×
173
        }
174
      }
175

176
      boolean cacheable = true;
2✔
177
      for (String autowiredBeanName : autowiredBeanNames) {
10✔
178
        if (!this.beanFactory.containsBean(autowiredBeanName)) {
5!
179
          cacheable = false;
×
180
        }
181
        else {
182
          if (this.beanName != null) {
3!
183
            this.beanFactory.registerDependentBean(autowiredBeanName, this.beanName);
6✔
184
          }
185
          if (!this.beanFactory.isSingleton(autowiredBeanName)) {
5✔
186
            cacheable = false;
2✔
187
          }
188
        }
189
        if (cacheable) {
2✔
190
          this.cachedTarget = target;
3✔
191
        }
192
      }
1✔
193

194
      return target;
2✔
195
    }
196
  }
197

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