Detect generic type match behind interface-based proxy as well
Issue: SPR-14097
This commit is contained in:
@@ -528,6 +528,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||||||
}
|
}
|
||||||
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
|
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
|
||||||
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
|
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
|
||||||
|
mbd.resolvedTargetType = beanType;
|
||||||
|
|
||||||
// Allow post-processors to modify the merged bean definition.
|
// Allow post-processors to modify the merged bean definition.
|
||||||
synchronized (mbd.postProcessingLock) {
|
synchronized (mbd.postProcessingLock) {
|
||||||
|
|||||||
@@ -405,7 +405,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
|
||||||
// No singleton instance found -> check bean definition.
|
// No singleton instance found -> check bean definition.
|
||||||
BeanFactory parentBeanFactory = getParentBeanFactory();
|
BeanFactory parentBeanFactory = getParentBeanFactory();
|
||||||
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
|
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
|
||||||
@@ -432,7 +431,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
|
public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
|
||||||
@@ -449,7 +447,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||||||
// In case of FactoryBean, return singleton status of created object if not a dereference.
|
// In case of FactoryBean, return singleton status of created object if not a dereference.
|
||||||
return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName, mbd));
|
return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName, mbd));
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
// Singleton or scoped - not a prototype.
|
// Singleton or scoped - not a prototype.
|
||||||
// However, FactoryBean may still produce a prototype object...
|
// However, FactoryBean may still produce a prototype object...
|
||||||
if (BeanFactoryUtils.isFactoryDereference(name)) {
|
if (BeanFactoryUtils.isFactoryDereference(name)) {
|
||||||
@@ -475,7 +473,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
|
public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
|
||||||
@@ -493,16 +490,30 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||||||
return typeToMatch.isInstance(beanInstance);
|
return typeToMatch.isInstance(beanInstance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if (!BeanFactoryUtils.isFactoryDereference(name)) {
|
||||||
return (!BeanFactoryUtils.isFactoryDereference(name) && typeToMatch.isInstance(beanInstance));
|
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)) {
|
else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
|
||||||
// null instance registered
|
// null instance registered
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
|
||||||
// No singleton instance found -> check bean definition.
|
// No singleton instance found -> check bean definition.
|
||||||
BeanFactory parentBeanFactory = getParentBeanFactory();
|
BeanFactory parentBeanFactory = getParentBeanFactory();
|
||||||
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
|
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
|
||||||
@@ -565,7 +576,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||||||
}
|
}
|
||||||
return typeToMatch.isAssignableFrom(beanType);
|
return typeToMatch.isAssignableFrom(beanType);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
|
||||||
@@ -591,7 +601,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
|
||||||
// No singleton instance found -> check bean definition.
|
// No singleton instance found -> check bean definition.
|
||||||
BeanFactory parentBeanFactory = getParentBeanFactory();
|
BeanFactory parentBeanFactory = getParentBeanFactory();
|
||||||
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
|
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
|
||||||
@@ -628,7 +637,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||||||
return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
|
return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getAliases(String name) {
|
public String[] getAliases(String name) {
|
||||||
|
|||||||
@@ -26,9 +26,12 @@ import javax.annotation.PostConstruct;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
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.ScopedObject;
|
||||||
import org.springframework.aop.scope.ScopedProxyUtils;
|
import org.springframework.aop.scope.ScopedProxyUtils;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
|
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
@@ -524,6 +527,19 @@ public class ConfigurationClassPostProcessorTests {
|
|||||||
assertSame(beanFactory.getBean("genericRepo"), beanFactory.getBean("repoConsumer"));
|
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
|
@Test
|
||||||
public void genericsBasedInjectionWithLateGenericsMatching() {
|
public void genericsBasedInjectionWithLateGenericsMatching() {
|
||||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
||||||
@@ -539,9 +555,14 @@ public class ConfigurationClassPostProcessorTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void genericsBasedInjectionWithEarlyGenericsMatching() {
|
public void genericsBasedInjectionWithEarlyGenericsMatchingOnCglibProxy() {
|
||||||
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
beanFactory.registerBeanDefinition("configClass", new RootBeanDefinition(RepositoryConfiguration.class));
|
||||||
new ConfigurationClassPostProcessor().postProcessBeanFactory(beanFactory);
|
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);
|
String[] beanNames = beanFactory.getBeanNamesForType(Repository.class);
|
||||||
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
|
assertTrue(ObjectUtils.containsElement(beanNames, "stringRepo"));
|
||||||
@@ -549,6 +570,109 @@ public class ConfigurationClassPostProcessorTests {
|
|||||||
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
beanNames = beanFactory.getBeanNamesForType(ResolvableType.forClassWithGenerics(Repository.class, String.class));
|
||||||
assertEquals(1, beanNames.length);
|
assertEquals(1, beanNames.length);
|
||||||
assertEquals("stringRepo", beanNames[0]);
|
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
|
@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> {
|
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
|
@Configuration
|
||||||
public static class ScopedRepositoryConfiguration {
|
public static class ScopedRepositoryConfiguration {
|
||||||
|
|
||||||
|
|||||||
@@ -624,19 +624,19 @@ public class ResolvableType implements Serializable {
|
|||||||
* @see #resolveGenerics()
|
* @see #resolveGenerics()
|
||||||
*/
|
*/
|
||||||
public ResolvableType getGeneric(int... indexes) {
|
public ResolvableType getGeneric(int... indexes) {
|
||||||
try {
|
ResolvableType[] generics = getGenerics();
|
||||||
if (indexes == null || indexes.length == 0) {
|
if (indexes == null || indexes.length == 0) {
|
||||||
return getGenerics()[0];
|
return (generics.length == 0 ? NONE : generics[0]);
|
||||||
}
|
}
|
||||||
ResolvableType generic = this;
|
ResolvableType generic = this;
|
||||||
for (int index : indexes) {
|
for (int index : indexes) {
|
||||||
generic = generic.getGenerics()[index];
|
generics = generic.getGenerics();
|
||||||
}
|
if (index < 0 || index >= generics.length) {
|
||||||
return generic;
|
|
||||||
}
|
|
||||||
catch (IndexOutOfBoundsException ex) {
|
|
||||||
return NONE;
|
return NONE;
|
||||||
}
|
}
|
||||||
|
generic = generics[index];
|
||||||
|
}
|
||||||
|
return generic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -947,6 +947,10 @@ public class ResolvableType implements Serializable {
|
|||||||
*/
|
*/
|
||||||
public static ResolvableType forRawClass(Class<?> clazz) {
|
public static ResolvableType forRawClass(Class<?> clazz) {
|
||||||
return new ResolvableType(clazz) {
|
return new ResolvableType(clazz) {
|
||||||
|
@Override
|
||||||
|
public ResolvableType[] getGenerics() {
|
||||||
|
return EMPTY_TYPES_ARRAY;
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean isAssignableFrom(Class<?> other) {
|
public boolean isAssignableFrom(Class<?> other) {
|
||||||
return ClassUtils.isAssignable(getRawClass(), other);
|
return ClassUtils.isAssignable(getRawClass(), other);
|
||||||
|
|||||||
Reference in New Issue
Block a user