Detect generic type match behind interface-based proxy as well

Issue: SPR-14097
This commit is contained in:
Juergen Hoeller 2016-12-26 11:23:07 +01:00
parent 0208198804
commit f805427629
4 changed files with 297 additions and 140 deletions

View File

@ -528,6 +528,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
}
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {

View File

@ -405,33 +405,31 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
return true;
}
else {
// No singleton instance found -> check bean definition.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// No bean definition found in this factory -> delegate to parent.
return parentBeanFactory.isSingleton(originalBeanName(name));
}
// No singleton instance found -> check bean definition.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// No bean definition found in this factory -> delegate to parent.
return parentBeanFactory.isSingleton(originalBeanName(name));
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// In case of FactoryBean, return singleton status of created object if not a dereference.
if (mbd.isSingleton()) {
if (isFactoryBean(beanName, mbd)) {
if (BeanFactoryUtils.isFactoryDereference(name)) {
return true;
}
FactoryBean<?> factoryBean = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
return factoryBean.isSingleton();
}
else {
return !BeanFactoryUtils.isFactoryDereference(name);
// In case of FactoryBean, return singleton status of created object if not a dereference.
if (mbd.isSingleton()) {
if (isFactoryBean(beanName, mbd)) {
if (BeanFactoryUtils.isFactoryDereference(name)) {
return true;
}
FactoryBean<?> factoryBean = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
return factoryBean.isSingleton();
}
else {
return false;
return !BeanFactoryUtils.isFactoryDereference(name);
}
}
else {
return false;
}
}
@Override
@ -449,32 +447,31 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
// In case of FactoryBean, return singleton status of created object if not a dereference.
return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName, mbd));
}
else {
// Singleton or scoped - not a prototype.
// However, FactoryBean may still produce a prototype object...
if (BeanFactoryUtils.isFactoryDereference(name)) {
return false;
}
if (isFactoryBean(beanName, mbd)) {
final FactoryBean<?> fb = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean<?>) fb).isPrototype()) ||
!fb.isSingleton());
}
}, getAccessControlContext());
}
else {
return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean<?>) fb).isPrototype()) ||
!fb.isSingleton());
}
// Singleton or scoped - not a prototype.
// However, FactoryBean may still produce a prototype object...
if (BeanFactoryUtils.isFactoryDereference(name)) {
return false;
}
if (isFactoryBean(beanName, mbd)) {
final FactoryBean<?> fb = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean<?>) fb).isPrototype()) ||
!fb.isSingleton());
}
}, getAccessControlContext());
}
else {
return false;
return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean<?>) fb).isPrototype()) ||
!fb.isSingleton());
}
}
else {
return false;
}
}
@Override
@ -493,78 +490,91 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
return typeToMatch.isInstance(beanInstance);
}
}
else {
return (!BeanFactoryUtils.isFactoryDereference(name) && typeToMatch.isInstance(beanInstance));
else if (!BeanFactoryUtils.isFactoryDereference(name)) {
if (typeToMatch.isInstance(beanInstance)) {
// Direct match for exposed instance?
return true;
}
else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
// Generics potentially only match on the target class, not on the proxy...
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
Class<?> targetType = mbd.getTargetType();
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
typeToMatch.isAssignableFrom(targetType)) {
// Check raw class match as well, making sure it's exposed on the proxy.
Class<?> classToMatch = typeToMatch.resolve();
return (classToMatch == null || classToMatch.isInstance(beanInstance));
}
}
}
return false;
}
else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
// null instance registered
return false;
}
else {
// No singleton instance found -> check bean definition.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// No bean definition found in this factory -> delegate to parent.
return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
}
// No singleton instance found -> check bean definition.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// No bean definition found in this factory -> delegate to parent.
return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
}
// Retrieve corresponding bean definition.
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Retrieve corresponding bean definition.
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
Class<?> classToMatch = typeToMatch.resolve();
if (classToMatch == null) {
classToMatch = FactoryBean.class;
}
Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});
Class<?> classToMatch = typeToMatch.resolve();
if (classToMatch == null) {
classToMatch = FactoryBean.class;
}
Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});
// Check decorated bean definition, if any: We assume it'll be easier
// to determine the decorated bean's type than the proxy's type.
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
return typeToMatch.isAssignableFrom(targetClass);
}
// Check decorated bean definition, if any: We assume it'll be easier
// to determine the decorated bean's type than the proxy's type.
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
return typeToMatch.isAssignableFrom(targetClass);
}
}
Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
if (beanType == null) {
return false;
}
Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
if (beanType == null) {
return false;
}
// Check bean class whether we're dealing with a FactoryBean.
if (FactoryBean.class.isAssignableFrom(beanType)) {
if (!BeanFactoryUtils.isFactoryDereference(name)) {
// If it's a FactoryBean, we want to look at what it creates, not the factory class.
beanType = getTypeForFactoryBean(beanName, mbd);
if (beanType == null) {
return false;
}
}
}
else if (BeanFactoryUtils.isFactoryDereference(name)) {
// Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
// type but we nevertheless are being asked to dereference a FactoryBean...
// Let's check the original bean class and proceed with it if it is a FactoryBean.
beanType = predictBeanType(beanName, mbd, FactoryBean.class);
if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
// Check bean class whether we're dealing with a FactoryBean.
if (FactoryBean.class.isAssignableFrom(beanType)) {
if (!BeanFactoryUtils.isFactoryDereference(name)) {
// If it's a FactoryBean, we want to look at what it creates, not the factory class.
beanType = getTypeForFactoryBean(beanName, mbd);
if (beanType == null) {
return false;
}
}
ResolvableType resolvableType = mbd.targetType;
if (resolvableType == null) {
resolvableType = mbd.factoryMethodReturnType;
}
if (resolvableType != null && resolvableType.resolve() == beanType) {
return typeToMatch.isAssignableFrom(resolvableType);
}
return typeToMatch.isAssignableFrom(beanType);
}
else if (BeanFactoryUtils.isFactoryDereference(name)) {
// Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
// type but we nevertheless are being asked to dereference a FactoryBean...
// Let's check the original bean class and proceed with it if it is a FactoryBean.
beanType = predictBeanType(beanName, mbd, FactoryBean.class);
if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
return false;
}
}
ResolvableType resolvableType = mbd.targetType;
if (resolvableType == null) {
resolvableType = mbd.factoryMethodReturnType;
}
if (resolvableType != null && resolvableType.resolve() == beanType) {
return typeToMatch.isAssignableFrom(resolvableType);
}
return typeToMatch.isAssignableFrom(beanType);
}
@Override
@ -591,43 +601,41 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
return null;
}
else {
// No singleton instance found -> check bean definition.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// No bean definition found in this factory -> delegate to parent.
return parentBeanFactory.getType(originalBeanName(name));
// No singleton instance found -> check bean definition.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// No bean definition found in this factory -> delegate to parent.
return parentBeanFactory.getType(originalBeanName(name));
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Check decorated bean definition, if any: We assume it'll be easier
// to determine the decorated bean's type than the proxy's type.
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd);
if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
return targetClass;
}
}
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
Class<?> beanClass = predictBeanType(beanName, mbd);
// Check decorated bean definition, if any: We assume it'll be easier
// to determine the decorated bean's type than the proxy's type.
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd);
if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
return targetClass;
}
}
Class<?> beanClass = predictBeanType(beanName, mbd);
// Check bean class whether we're dealing with a FactoryBean.
if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
if (!BeanFactoryUtils.isFactoryDereference(name)) {
// If it's a FactoryBean, we want to look at what it creates, not at the factory class.
return getTypeForFactoryBean(beanName, mbd);
}
else {
return beanClass;
}
// Check bean class whether we're dealing with a FactoryBean.
if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
if (!BeanFactoryUtils.isFactoryDereference(name)) {
// If it's a FactoryBean, we want to look at what it creates, not at the factory class.
return getTypeForFactoryBean(beanName, mbd);
}
else {
return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
return beanClass;
}
}
else {
return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
}
}
@Override

View File

@ -26,9 +26,12 @@ import javax.annotation.PostConstruct;
import org.junit.Before;
import org.junit.Test;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.aop.interceptor.SimpleTraceInterceptor;
import org.springframework.aop.scope.ScopedObject;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.FactoryBean;
@ -524,6 +527,19 @@ public class ConfigurationClassPostProcessorTests {
assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer"));
}
@Test
public void genericsBasedInjectionWithEarlyGenericsMatching() {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
}
@Test
public void genericsBasedInjectionWithLateGenericsMatching() {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
@ -539,9 +555,14 @@ public class ConfigurationClassPostProcessorTests {
}
@Test
public void genericsBasedInjectionWithEarlyGenericsMatching() {
public void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
autoProxyCreator.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(autoProxyCreator);
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
@ -549,6 +570,109 @@ public class ConfigurationClassPostProcessorTests {
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
}
@Test
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxy() {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
autoProxyCreator.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(autoProxyCreator);
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
beanFactory.preInstantiateSingletons();
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
}
@Test
public void genericsBasedInjectionWithLateGenericsMatchingOnCglibProxyAndRawFactoryMethod() {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setProxyTargetClass(true);
autoProxyCreator.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(autoProxyCreator);
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
beanFactory.preInstantiateSingletons();
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
assertTrue(AopUtils.isCglibProxy(beanFactory.getBean("stringRepo")));
}
@Test
public void genericsBasedInjectionWithEarlyGenericsMatchingOnJdkProxy() {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(autoProxyCreator);
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
}
@Test
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxy() {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(autoProxyCreator);
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
beanFactory.preInstantiateSingletons();
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
}
@Test
public void genericsBasedInjectionWithLateGenericsMatchingOnJdkProxyAndRawFactoryMethod() {
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RawRepositoryConfiguration.class));
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
autoProxyCreator.setBeanFactory(beanFactory);
beanFactory.addBeanPostProcessor(autoProxyCreator);
beanFactory.registerSingleton("traceInterceptor", new DefaultPointcutAdvisor(new SimpleTraceInterceptor()));
beanFactory.preInstantiateSingletons();
String[] beanNames = beanFactory.getBeanNamesForType(RepositoryInterface.class);
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(RepositoryInterface.class, String.class));
assertEquals(1, beanNames.length);
assertEquals("stringRepo", beanNames[0]);
assertTrue(AopUtils.isJdkDynamicProxy(beanFactory.getBean("stringRepo")));
}
@Test
@ -752,7 +876,12 @@ public class ConfigurationClassPostProcessorTests {
}
}
public static class Repository<T> {
public interface RepositoryInterface<T> {
String toString();
}
public static class Repository<T> implements RepositoryInterface<T> {
}
public static class GenericRepository<T> extends Repository<T> {
@ -819,6 +948,21 @@ public class ConfigurationClassPostProcessorTests {
}
}
@Configuration
public static class RawRepositoryConfiguration {
@Bean
public Repository stringRepo() {
return new Repository<String>() {
@Override
public String toString() {
return "Repository<String>";
}
};
}
}
@Configuration
public static class ScopedRepositoryConfiguration {

View File

@ -624,19 +624,19 @@ public class ResolvableType implements Serializable {
* @see #resolveGenerics()
*/
public ResolvableType getGeneric(int... indexes) {
try {
if (indexes == null || indexes.length == 0) {
return getGenerics()[0];
}
ResolvableType generic = this;
for (int index : indexes) {
generic = generic.getGenerics()[index];
}
return generic;
ResolvableType[] generics = getGenerics();
if (indexes == null || indexes.length == 0) {
return (generics.length == 0 ? NONE : generics[0]);
}
catch (IndexOutOfBoundsException ex) {
return NONE;
ResolvableType generic = this;
for (int index : indexes) {
generics = generic.getGenerics();
if (index < 0 || index >= generics.length) {
return NONE;
}
generic = generics[index];
}
return generic;
}
/**
@ -947,6 +947,10 @@ public class ResolvableType implements Serializable {
*/
public static ResolvableType forRawClass(Class<?> clazz) {
return new ResolvableType(clazz) {
@Override
public ResolvableType[] getGenerics() {
return EMPTY_TYPES_ARRAY;
}
@Override
public boolean isAssignableFrom(Class<?> other) {
return ClassUtils.isAssignable(getRawClass(), other);