spring-data-redis icon indicating copy to clipboard operation
spring-data-redis copied to clipboard

Polymorphism doesn't work when Spring Boot Dev Tools are loaded

Open Guerric-P opened this issue 3 years ago • 2 comments

I recently encountered a problem while developing locally with Spring Boot Dev Tools enabled (reproduced from 2.6.4 to 2.7.1).

I'm not sure if you want to fix this, I'm not even sure if it's a Spring Data related issue or not.

Given this code:

@Getter
@Setter
@RedisHash
class Root {
    private Base base;
}
    
public interface Base {

}
    
@Getter
@Setter
public class Implementor implements Base {
    // ...
}

public class CustomKeyspaceConfiguration extends KeyspaceConfiguration {
	private final String prefix;
	
	public CustomKeyspaceConfiguration(String prefix) {
		super();
		
		this.prefix = prefix;
	}
	
	@Override
	public boolean hasSettingsFor(Class<?> type) {
		if (super.hasSettingsFor(type) == false) {
			KeyspaceSettings settings = new KeyspaceSettings(type, prefix + ClassUtils.getUserClass(type).getName());
			addKeyspaceSettings(settings);
		}
		
		return true;
	}
}

I have a CrudRepository for the Root class which allows me to successfully store objects of that type in Redis.

But when I retrieve an object with a simple findById, I get this error:

Failed to instantiate some.package.name.Base using constructor NO_CONSTRUCTOR with arguments

However, the _class attribute is correctly set in Redis for that property:

base._class: some.package.name.Implementor

Explanation:

Spring Data has this piece of code:

Class<?> documentsTargetType = getDefaultedTypeToBeUsed(source);
    
if (documentsTargetType == null) {
    return basicType;
}

Class<T> rawType = basicType.getType();
    
boolean isMoreConcreteCustomType = (rawType == null)
    || (rawType.isAssignableFrom(documentsTargetType) && !rawType.equals(documentsTargetType));
    
if (!isMoreConcreteCustomType) {
    return basicType;
}

In my example, documentsTargetType contains Implementor and basicType contains Base. The rawType.isAssignableFrom(documentsTargetType) condition is supposed to return true, but it actually returns false, because Base and Implementor are loaded by different classloaders.

This results in an attempt to call the constructor of an interface which makes the deserialization of the entity fail.

Guerric-P avatar Jun 28 '22 19:06 Guerric-P

I have difficulties reproducing the issue. Can you please provide a reproducer, ideally through a GitHub repository?

mp911de avatar Jul 27 '22 09:07 mp911de

I was pretty sure that the specificity causing this bug in my project was the CustomKeyspaceConfiguration but indeed that minimal setup does not generate the bug.

I'll let you know if I find an actual minimal reproducible example. If you have any feeling about the possible cause, please let me know.

Guerric-P avatar Aug 01 '22 06:08 Guerric-P

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-projects-issues avatar Aug 30 '22 13:08 spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.

spring-projects-issues avatar Sep 06 '22 13:09 spring-projects-issues