Spring:EnclosingClass工具类分辨

Spring:EnclosingClass工具类分辨

1 前言

通过Spring的工具分辨EnclosingClass类。

测试类如下:

package com.xiaoxu.test.enclosingClass;

/**
 * @author xiaoxu
 * @date 2024-01-18
 * java_demo2:com.xiaoxu.test.enclosingClass.Outter
 */
public class Outter {

    public static class Inner{}

    public class Jug{}

    private static class Real{}

    private class Fal{}

    interface Apple{}

    private static interface Apple2{}

    private interface Apple3{}

}

测试demo:

package com.xiaoxu.test.enclosingClass;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

/**
 * @author xiaoxu
 * @date 2024-01-18
 * java_demo2:com.xiaoxu.test.enclosingClass.TestEnclosingClass
 */
public class TestEnclosingClass {

    public static void main(String[] args) throws Exception {
        System.out.println(Outter.class.getEnclosingClass());
        System.out.println(Outter.Inner.class.getEnclosingClass());
        System.out.println(Outter.Jug.class.getEnclosingClass());

        System.out.println(Outter.class.isMemberClass());
        System.out.println(Outter.Inner.class.isMemberClass());
        System.out.println(Outter.Jug.class.isMemberClass());

        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = pathMatchingResourcePatternResolver.getResources("classpath*:com/xiaoxu/test/enclosingClass/*.class");
        for (Resource resource : resources) {
            System.out.println(resource);
            if(resource.isReadable()) {
                System.out.println("哈哈");
                MetadataReader metadataReader = new CachingMetadataReaderFactory().getMetadataReader(resource);
                System.out.println(metadataReader);
                AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                System.out.println(annotationMetadata.getEnclosingClassName());
                System.out.println(annotationMetadata.getClassName());
                System.out.println(annotationMetadata.isIndependent());

                System.out.println("----------------------------------------------------------\n");
            }
        }
    }

}

Spring-core的MetadataReader,是通过字节码分析一个类的,其中如果是内部类(或者内部接口),那么就有EnclosingClassName的属性。

上面调用的是Spring-core的SimpleAnnotationMetadata的isIndependent方法,independent意即独立的:

如果是内部类,那么只有静态内部类isIndependent才返回true,无论公私有类,非静态的内部类有EnclosingClassName属性,即上述定义的Outter类,但是isIndependent为false。

因为内部类接口,默认就是static的,所以内部接口,即便有EnclosingClassName属性,但isIndependent依然返回true。

@Override
public boolean isIndependent() {
	return (this.enclosingClassName == null || this.independentInnerClass);
}

执行结果:

null
class com.xiaoxu.test.enclosingClass.Outter
class com.xiaoxu.test.enclosingClass.Outter
false
true
true
file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Apple.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@725bef66
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Apple
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Apple2.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@2aaf7cc2
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Apple2
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Apple3.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@6e3c1e69
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Apple3
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Fal.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@d7b1517
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Fal
false
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Inner.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@16c0663d
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Inner
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Jug.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@23223dd8
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Jug
false
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Real.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@4ec6a292
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Real
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@1b40d5f0
null
com.xiaoxu.test.enclosingClass.Outter
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\TestEnclosingClass.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@ea4a92b
null
com.xiaoxu.test.enclosingClass.TestEnclosingClass
true
----------------------------------------------------------

mybatis的自动配置扫描逻辑,判断@Mapper注解标注的必须是接口,同时isIndependent必须返回true,也就是说只有@Mapper注解标注的接口或者@Mapper注解标注的内部接口,才会被mybatis扫描到。

如果静态内部类被注册为ScannedGenerciBeanDefinition(上面工具类演示了Spring可以读取到内部类或内部接口的.Class文件),那么BeanDefinitionMap的key如下:

package com.xiaoxu.test.enclosingClass;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

/**
 * @author xiaoxu
 * @date 2024-01-18
 * java_demo2:com.xiaoxu.test.enclosingClass.TestEnclosingClass
 */
public class TestEnclosingClass {

    public static void main(String[] args) throws Exception {
        System.out.println(Outter.class.getEnclosingClass());
        System.out.println(Outter.Inner.class.getEnclosingClass());
        System.out.println(Outter.Jug.class.getEnclosingClass());

        System.out.println(Outter.class.isMemberClass());
        System.out.println(Outter.Inner.class.isMemberClass());
        System.out.println(Outter.Jug.class.isMemberClass());

        PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = pathMatchingResourcePatternResolver.getResources("classpath*:com/xiaoxu/test/enclosingClass/*.class");
        for (Resource resource : resources) {
            System.out.println(resource);
            if(resource.isReadable()) {
                System.out.println("哈哈");
                MetadataReader metadataReader = new CachingMetadataReaderFactory().getMetadataReader(resource);
                System.out.println(metadataReader);
                AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                System.out.println(annotationMetadata.getEnclosingClassName());
                System.out.println(annotationMetadata.getClassName());
                System.out.println(annotationMetadata.isIndependent());

                // annotationMetadata.isIndependent() && annotationMetadata.isConcrete() 判断必须是具体实现类,并且是静态内部类或者外部类
                // annotationMetadata.getEnclosingClassName()!=null 排除掉外部类,只保留静态内部类
                if(annotationMetadata.isIndependent() && annotationMetadata.isConcrete() && annotationMetadata.getEnclosingClassName()!=null){
                    System.out.println((char) 46);
                    System.out.println("我是静态内部类");
                    String enclosingClassName = annotationMetadata.getEnclosingClassName();
                    String outterClassName = enclosingClassName.substring(enclosingClassName.lastIndexOf((char) 46) + 1);
                    //从Spring的BeanFactory中的beanDefinitionMap中获取BeanDefinition时,静态内部类的 BeanDefinition名称
                    //是外部类的类名(非全限定类名)  + "." + 静态内部类的类名(非全限定类名)
                    String staticClassName = annotationMetadata.getClassName();
                    // (int) '$'值为 36  (int) '.'值为46
                    String innerClassName = staticClassName.substring(staticClassName.lastIndexOf((char) 36) + 1);

                    System.out.println((int) '$');
                    System.out.println("我是内部类的ScannedGenericBeanDefinition的名称:" + outterClassName + "." + innerClassName);
                }

                System.out.println("----------------------------------------------------------\n");
            }
        }
    }

}

执行结果如下:

null
class com.xiaoxu.test.enclosingClass.Outter
class com.xiaoxu.test.enclosingClass.Outter
false
true
true
file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Apple.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@725bef66
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Apple
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Apple2.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@2aaf7cc2
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Apple2
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Apple3.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@6e3c1e69
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Apple3
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Fal.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@d7b1517
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Fal
false
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Inner.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@16c0663d
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Inner
true
.
我是静态内部类
36
我是内部类的ScannedGenericBeanDefinition的名称:Outter.Inner
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Jug.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@23223dd8
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Jug
false
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter$Real.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@4ec6a292
com.xiaoxu.test.enclosingClass.Outter
com.xiaoxu.test.enclosingClass.Outter$Real
true
.
我是静态内部类
36
我是内部类的ScannedGenericBeanDefinition的名称:Outter.Real
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\Outter.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@1b40d5f0
null
com.xiaoxu.test.enclosingClass.Outter
true
----------------------------------------------------------

file [D:\java_demo2\target\classes\com\xiaoxu\test\enclosingClass\TestEnclosingClass.class]
哈哈
org.springframework.core.type.classreading.SimpleMetadataReader@ea4a92b
null
com.xiaoxu.test.enclosingClass.TestEnclosingClass
true
----------------------------------------------------------

比如下面的XImportAutoSelector类,即便XImportAutoConfiguration没有@Configuration注解,依然会被Spring扫描到(Spring会优先扫描用户定义的Bean,递归processImports再处理@Import注解等逻辑),它的BeanDefinitionMap中的key,即name,也就是上面的Outter类名 + “.” + 静态内部类Inner类名:

package com.xiaoxu.test.impo.autoconfigure;

import com.xiaoxu.test.impo.core.XImportRegistrar;
import com.xiaoxu.test.impo.ifc.EnableSqlMapperProxy;
import com.xiaoxu.test.impo.ifc.RegistrarImport;
import com.xiaoxu.test.impo.ifc.XImport;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Configuration;

/**
 * @author xiaoxu
 * @date 2023-12-26
 * java_demo:com.xiaoxu.test.impo.autoconfigure.XImportAutoConfiguration
 */
@RegistrarImport
public class XImportAutoConfiguration {

    @Configuration
    @XImport
    @EnableSqlMapperProxy
    @ConditionalOnMissingBean(XImportRegistrar.class)
    public static class XImportAutoSelector implements InitializingBean {

        public XImportAutoSelector() {
        }

        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("Not found registrar for registering sqlMapper.");
        }
    }

}

如下代码执行:

@SpringBootApplication
public class MainApplication {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);

        XImportAutoConfiguration.XImportAutoSelector bean = run.getBean("XImportAutoConfiguration.XImportAutoSelector", 
                XImportAutoConfiguration.XImportAutoSelector.class);
        bean.afterPropertiesSet();
    }

}

执行结果如下:

在这里插入图片描述

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值