• 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

56.13
today-context/src/main/java/infra/scripting/support/ScriptFactoryPostProcessor.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
package infra.scripting.support;
18

19
import org.jspecify.annotations.Nullable;
20

21
import java.util.Map;
22
import java.util.concurrent.ConcurrentHashMap;
23

24
import infra.aop.AopInfrastructureBean;
25
import infra.aop.TargetSource;
26
import infra.aop.framework.ProxyFactory;
27
import infra.aop.support.DelegatingIntroductionInterceptor;
28
import infra.beans.BeanUtils;
29
import infra.beans.PropertyValue;
30
import infra.beans.PropertyValues;
31
import infra.beans.factory.BeanClassLoaderAware;
32
import infra.beans.factory.BeanCreationException;
33
import infra.beans.factory.BeanCurrentlyInCreationException;
34
import infra.beans.factory.BeanDefinitionStoreException;
35
import infra.beans.factory.BeanDefinitionValidationException;
36
import infra.beans.factory.BeanFactory;
37
import infra.beans.factory.BeanFactoryAware;
38
import infra.beans.factory.DisposableBean;
39
import infra.beans.factory.FactoryBean;
40
import infra.beans.factory.config.BeanDefinition;
41
import infra.beans.factory.config.BeanPostProcessor;
42
import infra.beans.factory.config.ConfigurableBeanFactory;
43
import infra.beans.factory.config.ConstructorArgumentValues;
44
import infra.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
45
import infra.beans.factory.support.AbstractBeanDefinition;
46
import infra.beans.factory.support.GenericBeanDefinition;
47
import infra.beans.factory.support.StandardBeanFactory;
48
import infra.bytecode.Type;
49
import infra.bytecode.commons.MethodSignature;
50
import infra.bytecode.proxy.InterfaceMaker;
51
import infra.context.ResourceLoaderAware;
52
import infra.core.AttributeAccessor;
53
import infra.core.Conventions;
54
import infra.core.Ordered;
55
import infra.core.io.DefaultResourceLoader;
56
import infra.core.io.Resource;
57
import infra.core.io.ResourceLoader;
58
import infra.lang.Assert;
59
import infra.logging.Logger;
60
import infra.logging.LoggerFactory;
61
import infra.scripting.ScriptFactory;
62
import infra.scripting.ScriptSource;
63
import infra.util.ClassUtils;
64
import infra.util.ObjectUtils;
65
import infra.util.StringUtils;
66

67
/**
68
 * {@link BeanPostProcessor} that
69
 * handles {@link ScriptFactory} definitions,
70
 * replacing each factory with the actual scripted Java object generated by it.
71
 *
72
 * <p>This is similar to the
73
 * {@link FactoryBean} mechanism, but is
74
 * specifically tailored for scripts and not built into Framework's core
75
 * container itself but rather implemented as an extension.
76
 *
77
 * <p><b>NOTE:</b> The most important characteristic of this post-processor
78
 * is that constructor arguments are applied to the
79
 * {@link ScriptFactory} instance
80
 * while bean property values are applied to the generated scripted object.
81
 * Typically, constructor arguments include a script source locator and
82
 * potentially script interfaces, while bean property values include
83
 * references and config values to inject into the scripted object itself.
84
 *
85
 * <p>The following {@link ScriptFactoryPostProcessor} will automatically
86
 * be applied to the two
87
 * {@link ScriptFactory} definitions below.
88
 * At runtime, the actual scripted objects will be exposed for
89
 * "bshMessenger" and "groovyMessenger", rather than the
90
 * {@link ScriptFactory} instances. Both of
91
 * those are supposed to be castable to the example's {@code Messenger}
92
 * interfaces here.
93
 *
94
 * <pre class="code">&lt;bean class="infra.scripting.support.ScriptFactoryPostProcessor"/&gt;
95
 *
96
 * &lt;bean id="bshMessenger" class="infra.scripting.bsh.BshScriptFactory"&gt;
97
 *   &lt;constructor-arg value="classpath:mypackage/Messenger.bsh"/&gt;
98
 *   &lt;constructor-arg value="mypackage.Messenger"/&gt;
99
 *   &lt;property name="message" value="Hello World!"/&gt;
100
 * &lt;/bean&gt;
101
 *
102
 * &lt;bean id="groovyMessenger" class="infra.scripting.groovy.GroovyScriptFactory"&gt;
103
 *   &lt;constructor-arg value="classpath:mypackage/Messenger.groovy"/&gt;
104
 *   &lt;property name="message" value="Hello World!"/&gt;
105
 * &lt;/bean&gt;</pre>
106
 *
107
 * <p><b>NOTE:</b> Please note that the above excerpt from a Framework
108
 * XML bean definition file uses just the &lt;bean/&gt;-style syntax
109
 * (in an effort to illustrate using the {@link ScriptFactoryPostProcessor} itself).
110
 * In reality, you would never create a &lt;bean/&gt; definition for a
111
 * {@link ScriptFactoryPostProcessor} explicitly; rather you would import the
112
 * tags from the {@code 'lang'} namespace and simply create scripted
113
 * beans using the tags in that namespace... as part of doing so, a
114
 * {@link ScriptFactoryPostProcessor} will implicitly be created for you.
115
 *
116
 * @author Juergen Hoeller
117
 * @author Rob Harrop
118
 * @author Rick Evans
119
 * @author Mark Fisher
120
 * @author Sam Brannen
121
 * @author <a href="https://github.com/TAKETODAY">海子 Yang</a>
122
 * @since 4.0
123
 */
124
public class ScriptFactoryPostProcessor implements SmartInstantiationAwareBeanPostProcessor,
2✔
125
        BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware, DisposableBean, Ordered {
126

127
  /**
128
   * The {@link Resource}-style prefix that denotes
129
   * an inline script.
130
   * <p>An inline script is a script that is defined right there in the (typically XML)
131
   * configuration, as opposed to being defined in an external file.
132
   */
133
  public static final String INLINE_SCRIPT_PREFIX = "inline:";
134

135
  /**
136
   * The {@code refreshCheckDelay} attribute.
137
   */
138
  public static final String REFRESH_CHECK_DELAY_ATTRIBUTE = Conventions.getQualifiedAttributeName(
4✔
139
          ScriptFactoryPostProcessor.class, "refreshCheckDelay");
140

141
  /**
142
   * The {@code proxyTargetClass} attribute.
143
   */
144
  public static final String PROXY_TARGET_CLASS_ATTRIBUTE = Conventions.getQualifiedAttributeName(
4✔
145
          ScriptFactoryPostProcessor.class, "proxyTargetClass");
146

147
  /**
148
   * The {@code language} attribute.
149
   */
150
  public static final String LANGUAGE_ATTRIBUTE = Conventions.getQualifiedAttributeName(
5✔
151
          ScriptFactoryPostProcessor.class, "language");
152

153
  private static final String SCRIPT_FACTORY_NAME_PREFIX = "scriptFactory.";
154

155
  private static final String SCRIPTED_OBJECT_NAME_PREFIX = "scriptedObject.";
156

157
  /** Logger available to subclasses. */
158
  protected final Logger logger = LoggerFactory.getLogger(getClass());
5✔
159

160
  private long defaultRefreshCheckDelay = -1;
3✔
161

162
  private boolean defaultProxyTargetClass = false;
3✔
163

164
  @Nullable
1✔
165
  private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
2✔
166

167
  @Nullable
168
  private ConfigurableBeanFactory beanFactory;
169

170
  private ResourceLoader resourceLoader = new DefaultResourceLoader();
5✔
171

172
  final StandardBeanFactory scriptBeanFactory = new StandardBeanFactory();
5✔
173

174
  /** Map from bean name String to ScriptSource object. */
175
  private final Map<String, ScriptSource> scriptSourceCache = new ConcurrentHashMap<>();
6✔
176

177
  /**
178
   * Set the delay between refresh checks, in milliseconds.
179
   * Default is -1, indicating no refresh checks at all.
180
   * <p>Note that an actual refresh will only happen when
181
   * the {@link ScriptSource} indicates
182
   * that it has been modified.
183
   *
184
   * @see ScriptSource#isModified()
185
   */
186
  public void setDefaultRefreshCheckDelay(long defaultRefreshCheckDelay) {
187
    this.defaultRefreshCheckDelay = defaultRefreshCheckDelay;
3✔
188
  }
1✔
189

190
  /**
191
   * Flag to signal that refreshable proxies should be created to proxy the target class not its interfaces.
192
   *
193
   * @param defaultProxyTargetClass the flag value to set
194
   */
195
  public void setDefaultProxyTargetClass(boolean defaultProxyTargetClass) {
196
    this.defaultProxyTargetClass = defaultProxyTargetClass;
×
197
  }
×
198

199
  @Override
200
  public void setBeanClassLoader(ClassLoader classLoader) {
201
    this.beanClassLoader = classLoader;
3✔
202
  }
1✔
203

204
  @Override
205
  public void setBeanFactory(BeanFactory beanFactory) {
206
    if (!(beanFactory instanceof ConfigurableBeanFactory)) {
3✔
207
      throw new IllegalStateException("ScriptFactoryPostProcessor doesn't work with " +
3✔
208
              "non-ConfigurableBeanFactory: " + beanFactory.getClass());
5✔
209
    }
210
    this.beanFactory = (ConfigurableBeanFactory) beanFactory;
4✔
211

212
    // Required so that references (up container hierarchies) are correctly resolved.
213
    this.scriptBeanFactory.setParentBeanFactory(this.beanFactory);
5✔
214

215
    // Required so that all BeanPostProcessors, Scopes, etc become available.
216
    this.scriptBeanFactory.copyConfigurationFrom(this.beanFactory);
5✔
217

218
    // Filter out BeanPostProcessors that are part of the AOP infrastructure,
219
    // since those are only meant to apply to beans defined in the original factory.
220
    this.scriptBeanFactory.getBeanPostProcessors()
4✔
221
            .removeIf(beanPostProcessor -> beanPostProcessor instanceof AopInfrastructureBean);
5✔
222
  }
1✔
223

224
  @Override
225
  public void setResourceLoader(ResourceLoader resourceLoader) {
226
    this.resourceLoader = resourceLoader;
3✔
227
  }
1✔
228

229
  @Override
230
  public int getOrder() {
231
    return Integer.MIN_VALUE;
×
232
  }
233

234
  @Override
235
  @Nullable
236
  @SuppressWarnings("NullAway")
237
  public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
238
    // We only apply special treatment to ScriptFactory implementations here.
239
    if (!ScriptFactory.class.isAssignableFrom(beanClass)) {
4✔
240
      return null;
2✔
241
    }
242

243
    Assert.state(this.beanFactory != null, "No BeanFactory set");
7!
244
    BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
5✔
245

246
    try {
247
      String scriptFactoryBeanName = SCRIPT_FACTORY_NAME_PREFIX + beanName;
3✔
248
      String scriptedObjectBeanName = SCRIPTED_OBJECT_NAME_PREFIX + beanName;
3✔
249
      prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName);
5✔
250

251
      ScriptFactory scriptFactory = scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class);
7✔
252
      ScriptSource scriptSource = getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator());
6✔
253
      Class<?>[] interfaces = scriptFactory.getScriptInterfaces();
3✔
254

255
      Class<?> scriptedType = scriptFactory.getScriptedObjectType(scriptSource);
4✔
256
      if (scriptedType != null) {
2!
257
        return scriptedType;
2✔
258
      }
259
      else if (ObjectUtils.isNotEmpty(interfaces)) {
×
260
        return (interfaces.length == 1 ? interfaces[0] : createCompositeInterface(interfaces));
×
261
      }
262
      else {
263
        if (bd.isSingleton()) {
×
264
          return scriptBeanFactory.getBean(scriptedObjectBeanName).getClass();
×
265
        }
266
      }
267
    }
268
    catch (Exception ex) {
×
269
      if (ex instanceof BeanCreationException
×
270
              && ((BeanCreationException) ex).getMostSpecificCause() instanceof BeanCurrentlyInCreationException) {
×
271
        if (logger.isTraceEnabled()) {
×
272
          logger.trace("Could not determine scripted object type for bean '{}': {}", beanName, ex.getMessage());
×
273
        }
274
      }
275
      else {
276
        if (logger.isDebugEnabled()) {
×
277
          logger.debug("Could not determine scripted object type for bean '{}'", beanName, ex);
×
278
        }
279
      }
280
    }
×
281

282
    return null;
×
283
  }
284

285
  @Nullable
286
  @Override
287
  public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
288
    // We only apply special treatment to ScriptFactory implementations here.
289
    if (!ScriptFactory.class.isAssignableFrom(beanClass)) {
4✔
290
      return null;
2✔
291
    }
292
    Assert.state(this.beanFactory != null, "No BeanFactory set");
7!
293
    BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
5✔
294

295
    String scriptFactoryBeanName = SCRIPT_FACTORY_NAME_PREFIX + beanName;
3✔
296
    String scriptedObjectBeanName = SCRIPTED_OBJECT_NAME_PREFIX + beanName;
3✔
297
    prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName);
5✔
298

299
    ScriptFactory scriptFactory = scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class);
7✔
300
    ScriptSource scriptSource = getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator());
6✔
301
    boolean isFactoryBean = false;
2✔
302
    try {
303
      Class<?> scriptedObjectType = scriptFactory.getScriptedObjectType(scriptSource);
4✔
304
      // Returned type may be null if the factory is unable to determine the type.
305
      if (scriptedObjectType != null) {
2!
306
        isFactoryBean = FactoryBean.class.isAssignableFrom(scriptedObjectType);
4✔
307
      }
308
    }
309
    catch (Exception ex) {
×
310
      throw new BeanCreationException(beanName,
×
311
              "Could not determine scripted object type for " + scriptFactory, ex);
312
    }
1✔
313

314
    long refreshCheckDelay = resolveRefreshCheckDelay(bd);
4✔
315
    if (refreshCheckDelay >= 0) {
4✔
316
      Class<?>[] interfaces = scriptFactory.getScriptInterfaces();
3✔
317
      RefreshableScriptTargetSource ts = new RefreshableScriptTargetSource(this.scriptBeanFactory,
10✔
318
              scriptedObjectBeanName, scriptFactory, scriptSource, isFactoryBean);
319
      boolean proxyTargetClass = resolveProxyTargetClass(bd);
4✔
320
      String language = (String) bd.getAttribute(LANGUAGE_ATTRIBUTE);
5✔
321
      if (proxyTargetClass && (language == null || !language.equals("groovy"))) {
2!
322
        throw new BeanDefinitionValidationException(
×
323
                "Cannot use proxyTargetClass=true with script beans where language is not 'groovy': '" +
324
                        language + "'");
325
      }
326
      ts.setRefreshCheckDelay(refreshCheckDelay);
3✔
327
      return createRefreshableProxy(ts, interfaces, proxyTargetClass);
6✔
328
    }
329

330
    if (isFactoryBean) {
2!
331
      scriptedObjectBeanName = BeanFactory.FACTORY_BEAN_PREFIX + scriptedObjectBeanName;
×
332
    }
333
    return this.scriptBeanFactory.getBean(scriptedObjectBeanName);
5✔
334
  }
335

336
  /**
337
   * Prepare the script beans in the internal BeanFactory that this
338
   * post-processor uses. Each original bean definition will be split
339
   * into a ScriptFactory definition and a scripted object definition.
340
   *
341
   * @param bd the original bean definition in the main BeanFactory
342
   * @param scriptFactoryBeanName the name of the internal ScriptFactory bean
343
   * @param scriptedObjectBeanName the name of the internal scripted object bean
344
   */
345
  protected void prepareScriptBeans(BeanDefinition bd, String scriptFactoryBeanName, String scriptedObjectBeanName) {
346
    // Avoid recreation of the script bean definition in case of a prototype.
347
    synchronized(scriptBeanFactory) {
5✔
348
      if (!scriptBeanFactory.containsBeanDefinition(scriptedObjectBeanName)) {
5✔
349

350
        scriptBeanFactory.registerBeanDefinition(
6✔
351
                scriptFactoryBeanName, createScriptFactoryBeanDefinition(bd));
1✔
352

353
        ScriptFactory scriptFactory = scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class);
7✔
354

355
        ScriptSource scriptSource =
3✔
356
                getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator());
3✔
357
        Class<?>[] interfaces = scriptFactory.getScriptInterfaces();
3✔
358

359
        Class<?>[] scriptedInterfaces = interfaces;
2✔
360
        if (scriptFactory.requiresConfigInterface() && bd.hasPropertyValues()) {
3!
361
          Class<?> configInterface = createConfigInterface(bd, interfaces);
×
362
          scriptedInterfaces = ObjectUtils.addObjectToArray(interfaces, configInterface);
×
363
        }
364

365
        BeanDefinition objectBd = createScriptedObjectBeanDefinition(
7✔
366
                bd, scriptFactoryBeanName, scriptSource, scriptedInterfaces);
367
        long refreshCheckDelay = resolveRefreshCheckDelay(bd);
4✔
368
        if (refreshCheckDelay >= 0) {
4✔
369
          objectBd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
3✔
370
        }
371

372
        this.scriptBeanFactory.registerBeanDefinition(scriptedObjectBeanName, objectBd);
5✔
373
      }
374
    }
3✔
375
  }
1✔
376

377
  /**
378
   * Get the refresh check delay for the given {@link ScriptFactory} {@link BeanDefinition}.
379
   * If the {@link BeanDefinition} has a
380
   * {@link AttributeAccessor metadata attribute}
381
   * under the key {@link #REFRESH_CHECK_DELAY_ATTRIBUTE} which is a valid {@link Number}
382
   * type, then this value is used. Otherwise, the {@link #defaultRefreshCheckDelay}
383
   * value is used.
384
   *
385
   * @param beanDefinition the BeanDefinition to check
386
   * @return the refresh check delay
387
   */
388
  protected long resolveRefreshCheckDelay(BeanDefinition beanDefinition) {
389
    long refreshCheckDelay = this.defaultRefreshCheckDelay;
3✔
390
    Object attributeValue = beanDefinition.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE);
4✔
391
    if (attributeValue instanceof Number) {
3!
392
      refreshCheckDelay = ((Number) attributeValue).longValue();
×
393
    }
394
    else if (attributeValue instanceof String) {
3!
395
      refreshCheckDelay = Long.parseLong((String) attributeValue);
×
396
    }
397
    else if (attributeValue != null) {
2!
398
      throw new BeanDefinitionStoreException("Invalid refresh check delay attribute [" +
×
399
              REFRESH_CHECK_DELAY_ATTRIBUTE + "] with value '" + attributeValue +
400
              "': needs to be of type Number or String");
401
    }
402
    return refreshCheckDelay;
2✔
403
  }
404

405
  protected boolean resolveProxyTargetClass(BeanDefinition beanDefinition) {
406
    boolean proxyTargetClass = this.defaultProxyTargetClass;
3✔
407
    Object attributeValue = beanDefinition.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE);
4✔
408
    if (attributeValue instanceof Boolean) {
3!
409
      proxyTargetClass = (Boolean) attributeValue;
×
410
    }
411
    else if (attributeValue instanceof String) {
3!
412
      proxyTargetClass = Boolean.parseBoolean((String) attributeValue);
×
413
    }
414
    else if (attributeValue != null) {
2!
415
      throw new BeanDefinitionStoreException("Invalid proxy target class attribute [%s] with value '%s': needs to be of type Boolean or String"
×
416
              .formatted(PROXY_TARGET_CLASS_ATTRIBUTE, attributeValue));
×
417
    }
418
    return proxyTargetClass;
2✔
419
  }
420

421
  /**
422
   * Create a ScriptFactory bean definition based on the given script definition,
423
   * extracting only the definition data that is relevant for the ScriptFactory
424
   * (that is, only bean class and constructor arguments).
425
   *
426
   * @param bd the full script bean definition
427
   * @return the extracted ScriptFactory bean definition
428
   * @see ScriptFactory
429
   */
430
  protected BeanDefinition createScriptFactoryBeanDefinition(BeanDefinition bd) {
431
    GenericBeanDefinition scriptBd = new GenericBeanDefinition();
4✔
432
    scriptBd.setBeanClassName(bd.getBeanClassName());
4✔
433
    scriptBd.getConstructorArgumentValues().addArgumentValues(bd.getConstructorArgumentValues());
5✔
434
    return scriptBd;
2✔
435
  }
436

437
  /**
438
   * Obtain a ScriptSource for the given bean, lazily creating it
439
   * if not cached already.
440
   *
441
   * @param beanName the name of the scripted bean
442
   * @param scriptSourceLocator the script source locator associated with the bean
443
   * @return the corresponding ScriptSource instance
444
   * @see #convertToScriptSource
445
   */
446
  protected ScriptSource getScriptSource(String beanName, String scriptSourceLocator) {
447
    return this.scriptSourceCache.computeIfAbsent(beanName, key ->
10✔
448
            convertToScriptSource(beanName, scriptSourceLocator, this.resourceLoader));
7✔
449
  }
450

451
  /**
452
   * Convert the given script source locator to a ScriptSource instance.
453
   * <p>By default, supported locators are Framework resource locations
454
   * (such as "file:C:/myScript.bsh" or "classpath:myPackage/myScript.bsh")
455
   * and inline scripts ("inline:myScriptText...").
456
   *
457
   * @param beanName the name of the scripted bean
458
   * @param scriptSourceLocator the script source locator
459
   * @param resourceLoader the ResourceLoader to use (if necessary)
460
   * @return the ScriptSource instance
461
   */
462
  protected ScriptSource convertToScriptSource(String beanName, String scriptSourceLocator, ResourceLoader resourceLoader) {
463
    if (scriptSourceLocator.startsWith(INLINE_SCRIPT_PREFIX)) {
4!
464
      return new StaticScriptSource(scriptSourceLocator.substring(INLINE_SCRIPT_PREFIX.length()), beanName);
9✔
465
    }
466
    else {
467
      return new ResourceScriptSource(resourceLoader.getResource(scriptSourceLocator));
×
468
    }
469
  }
470

471
  /**
472
   * Create a config interface for the given bean definition, defining setter
473
   * methods for the defined property values as well as an init method and
474
   * a destroy method (if defined).
475
   * <p>This implementation creates the interface via CGLIB's InterfaceMaker,
476
   * determining the property types from the given interfaces (as far as possible).
477
   *
478
   * @param bd the bean definition (property values etc) to create a
479
   * config interface for
480
   * @param interfaces the interfaces to check against (might define
481
   * getters corresponding to the setters we're supposed to generate)
482
   * @return the config interface
483
   * @see InterfaceMaker
484
   */
485
  protected Class<?> createConfigInterface(BeanDefinition bd, Class<?> @Nullable [] interfaces) {
486
    InterfaceMaker maker = new InterfaceMaker();
×
487
    PropertyValues propertyValues = bd.getPropertyValues();
×
488
    if (propertyValues != null) {
×
489
      for (PropertyValue pv : propertyValues) {
×
490
        String propertyName = pv.getName();
×
491
        Class<?> propertyType = BeanUtils.findPropertyType(propertyName, interfaces);
×
492
        String setterName = "set" + StringUtils.capitalize(propertyName);
×
493
        MethodSignature signature = new MethodSignature(
×
494
                Type.VOID_TYPE, setterName, Type.forClass(propertyType));
×
495
        maker.add(signature, Type.EMPTY_ARRAY);
×
496
      }
×
497
    }
498

499
    if (bd instanceof AbstractBeanDefinition abd) {
×
500
      if (ObjectUtils.isNotEmpty(abd.getInitMethodNames())) {
×
501
        for (String initMethodName : abd.getInitMethodNames()) {
×
502
          MethodSignature signature = new MethodSignature(Type.VOID_TYPE, initMethodName);
×
503
          maker.add(signature, Type.EMPTY_ARRAY);
×
504
        }
505
      }
506
      if (ObjectUtils.isNotEmpty(abd.getDestroyMethodNames())) {
×
507
        for (String destroyMethodName : abd.getDestroyMethodNames()) {
×
508
          MethodSignature signature = new MethodSignature(Type.VOID_TYPE, destroyMethodName);
×
509
          maker.add(signature, Type.EMPTY_ARRAY);
×
510
        }
511
      }
512
    }
513
    else {
514
      if (StringUtils.hasText(bd.getDestroyMethodName())) {
×
515
        MethodSignature signature = new MethodSignature(Type.VOID_TYPE, bd.getDestroyMethodName());
×
516
        maker.add(signature, Type.EMPTY_ARRAY);
×
517
      }
518

519
      if (StringUtils.hasText(bd.getDestroyMethodName())) {
×
520
        MethodSignature signature = new MethodSignature(Type.VOID_TYPE, bd.getDestroyMethodName());
×
521
        maker.add(signature, Type.EMPTY_ARRAY);
×
522
      }
523
    }
524

525
    return maker.create();
×
526
  }
527

528
  /**
529
   * Create a composite interface Class for the given interfaces,
530
   * implementing the given interfaces in one single Class.
531
   * <p>The default implementation builds a JDK proxy class
532
   * for the given interfaces.
533
   *
534
   * @param interfaces the interfaces to merge
535
   * @return the merged interface as Class
536
   * @see java.lang.reflect.Proxy#getProxyClass
537
   */
538
  protected Class<?> createCompositeInterface(Class<?>[] interfaces) {
539
    return ClassUtils.createCompositeInterface(interfaces, this.beanClassLoader);
×
540
  }
541

542
  /**
543
   * Create a bean definition for the scripted object, based on the given script
544
   * definition, extracting the definition data that is relevant for the scripted
545
   * object (that is, everything but bean class and constructor arguments).
546
   *
547
   * @param bd the full script bean definition
548
   * @param scriptFactoryBeanName the name of the internal ScriptFactory bean
549
   * @param scriptSource the ScriptSource for the scripted bean
550
   * @param interfaces the interfaces that the scripted bean is supposed to implement
551
   * @return the extracted ScriptFactory bean definition
552
   * @see ScriptFactory#getScriptedObject
553
   */
554
  protected BeanDefinition createScriptedObjectBeanDefinition(BeanDefinition bd,
555
          String scriptFactoryBeanName, ScriptSource scriptSource, Class<?> @Nullable [] interfaces) {
556

557
    BeanDefinition objectBd = bd.cloneBeanDefinition();
3✔
558
    objectBd.setFactoryBeanName(scriptFactoryBeanName);
3✔
559
    objectBd.setFactoryMethodName("getScriptedObject");
3✔
560
    ConstructorArgumentValues argumentValues = objectBd.getConstructorArgumentValues();
3✔
561
    argumentValues.clear();
2✔
562
    argumentValues.addIndexedArgumentValue(0, scriptSource);
4✔
563
    argumentValues.addIndexedArgumentValue(1, interfaces);
4✔
564
    return objectBd;
2✔
565
  }
566

567
  /**
568
   * Create a refreshable proxy for the given AOP TargetSource.
569
   *
570
   * @param ts the refreshable TargetSource
571
   * @param interfaces the proxy interfaces (may be {@code null} to
572
   * indicate proxying of all interfaces implemented by the target class)
573
   * @return the generated proxy
574
   * @see RefreshableScriptTargetSource
575
   */
576
  protected Object createRefreshableProxy(TargetSource ts, Class<?> @Nullable [] interfaces, boolean proxyTargetClass) {
577
    ProxyFactory proxyFactory = new ProxyFactory();
4✔
578
    proxyFactory.setTargetSource(ts);
3✔
579
    ClassLoader classLoader = this.beanClassLoader;
3✔
580

581
    if (interfaces != null) {
2!
582
      proxyFactory.setInterfaces(interfaces);
×
583
    }
584
    else {
585
      Class<?> targetClass = ts.getTargetClass();
3✔
586
      if (targetClass != null) {
2!
587
        proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.beanClassLoader));
6✔
588
      }
589
    }
590

591
    if (proxyTargetClass) {
2!
592
      classLoader = null;  // force use of Class.getClassLoader()
×
593
      proxyFactory.setProxyTargetClass(true);
×
594
    }
595

596
    DelegatingIntroductionInterceptor introduction = new DelegatingIntroductionInterceptor(ts);
5✔
597
    introduction.suppressInterface(TargetSource.class);
3✔
598
    proxyFactory.addAdvice(introduction);
3✔
599

600
    return proxyFactory.getProxy(classLoader);
4✔
601
  }
602

603
  /**
604
   * Destroy the inner bean factory (used for scripts) on shutdown.
605
   */
606
  @Override
607
  public void destroy() {
608
    this.scriptBeanFactory.destroySingletons();
×
609
  }
×
610

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