Skip to content

Commit

Permalink
Revise TargetSource implementations for proper nullability
Browse files Browse the repository at this point in the history
Includes hashCode optimization in AbstractBeanFactoryBasedTargetSource.
Includes ThreadLocal naming fix in ThreadLocalTargetSource.

Closes gh-30576
Closes gh-30581
  • Loading branch information
jhoeller committed Jun 2, 2023
1 parent b738a20 commit c685525
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 24 deletions.
Expand Up @@ -34,6 +34,7 @@
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
* Convenient superclass for
Expand Down Expand Up @@ -82,6 +83,11 @@ protected final BeanFactory getBeanFactory() {
return this.beanFactory;
}

private ConfigurableBeanFactory getConfigurableBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory not set");
return this.beanFactory;
}


//---------------------------------------------------------------------
// Implementation of the TargetSourceCreator interface
Expand All @@ -105,7 +111,7 @@ public final TargetSource getTargetSource(Class<?> beanClass, String beanName) {
// We need to override just this bean definition, as it may reference other beans
// and we're happy to take the parent's definition for those.
// Always use prototype scope if demanded.
BeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName);
BeanDefinition bd = getConfigurableBeanFactory().getMergedBeanDefinition(beanName);
GenericBeanDefinition bdCopy = new GenericBeanDefinition(bd);
if (isPrototypeBased()) {
bdCopy.setScope(BeanDefinition.SCOPE_PROTOTYPE);
Expand All @@ -127,7 +133,7 @@ public final TargetSource getTargetSource(Class<?> beanClass, String beanName) {
protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) {
synchronized (this.internalBeanFactories) {
return this.internalBeanFactories.computeIfAbsent(beanName,
name -> buildInternalBeanFactory(this.beanFactory));
name -> buildInternalBeanFactory(getConfigurableBeanFactory()));
}
}

Expand Down
Expand Up @@ -25,6 +25,7 @@
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

/**
Expand Down Expand Up @@ -58,16 +59,18 @@ public abstract class AbstractBeanFactoryBasedTargetSource implements TargetSour
protected final transient Log logger = LogFactory.getLog(getClass());

/** Name of the target bean we will create on each invocation. */
@Nullable
private String targetBeanName;

/** Class of the target. */
@Nullable
private volatile Class<?> targetClass;

/**
* BeanFactory that owns this TargetSource. We need to hold onto this
* reference so that we can create new prototype instances as necessary.
*/
@SuppressWarnings("serial")
@Nullable
private BeanFactory beanFactory;


Expand All @@ -88,6 +91,7 @@ public void setTargetBeanName(String targetBeanName) {
* Return the name of the target bean in the factory.
*/
public String getTargetBeanName() {
Assert.state(this.targetBeanName != null, "Target bean name not set");
return this.targetBeanName;
}

Expand Down Expand Up @@ -117,11 +121,13 @@ public void setBeanFactory(BeanFactory beanFactory) {
* Return the owning BeanFactory.
*/
public BeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "BeanFactory not set");
return this.beanFactory;
}


@Override
@Nullable
public Class<?> getTargetClass() {
Class<?> targetClass = this.targetClass;
if (targetClass != null) {
Expand All @@ -130,7 +136,7 @@ public Class<?> getTargetClass() {
synchronized (this) {
// Full check within synchronization, entering the BeanFactory interaction algorithm only once...
targetClass = this.targetClass;
if (targetClass == null && this.beanFactory != null) {
if (targetClass == null && this.beanFactory != null && this.targetBeanName != null) {
// Determine type of the target bean.
targetClass = this.beanFactory.getType(this.targetBeanName);
if (targetClass == null) {
Expand Down Expand Up @@ -184,18 +190,16 @@ public boolean equals(@Nullable Object other) {

@Override
public int hashCode() {
int hashCode = getClass().hashCode();
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.beanFactory);
hashCode = 13 * hashCode + ObjectUtils.nullSafeHashCode(this.targetBeanName);
return hashCode;
return getClass().hashCode() * 13 + ObjectUtils.nullSafeHashCode(this.targetBeanName);
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder(getClass().getSimpleName());
sb.append(" for target bean '").append(this.targetBeanName).append('\'');
if (this.targetClass != null) {
sb.append(" of type [").append(this.targetClass.getName()).append(']');
Class<?> targetClass = this.targetClass;
if (targetClass != null) {
sb.append(" of type [").append(targetClass.getName()).append(']');
}
return sb.toString();
}
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -46,6 +46,7 @@ public abstract class AbstractLazyCreationTargetSource implements TargetSource {
protected final Log logger = LogFactory.getLog(getClass());

/** The lazily initialized target object. */
@Nullable
private Object lazyTarget;


Expand Down
Expand Up @@ -70,6 +70,7 @@ public static EmptyTargetSource forClass(@Nullable Class<?> targetClass, boolean
// Instance implementation
//---------------------------------------------------------------------

@Nullable
private final Class<?> targetClass;

private final boolean isStatic;
Expand Down
Expand Up @@ -97,12 +97,11 @@ public synchronized Object swap(Object newTarget) throws IllegalArgumentExceptio


/**
* Two HotSwappableTargetSources are equal if the current target
* objects are equal.
* Two HotSwappableTargetSources are equal if the current target objects are equal.
*/
@Override
public boolean equals(@Nullable Object obj) {
return (this == obj || (obj instanceof HotSwappableTargetSource that &&
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof HotSwappableTargetSource that &&
this.target.equals(that.target)));
}

Expand Down
Expand Up @@ -84,13 +84,8 @@ public boolean isStatic() {
*/
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof SingletonTargetSource otherTargetSource)) {
return false;
}
return this.target.equals(otherTargetSource.target);
return (this == other || (other instanceof SingletonTargetSource that &&
this.target.equals(that.target)));
}

/**
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -58,7 +58,12 @@ public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
* is meant to be per thread per instance of the ThreadLocalTargetSource class.
*/
private final ThreadLocal<Object> targetInThread =
new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'");
new NamedThreadLocal<>("Thread-local instance of bean") {
@Override
public String toString() {
return super.toString() + " '" + getTargetBeanName() + "'";
}
};

/**
* Set of managed targets, enabling us to keep track of the targets we've created.
Expand Down

0 comments on commit c685525

Please sign in to comment.