文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Springjcl及springcore源码深度解析

2022-11-13 19:11

关注

这两个内容源码虽然不算少,但是内容不太重要,在其他的 Module 里应用到了再做具体的学习。

1.spring-jcl

1.1.日志加载

spring-jcl 对外提供统一的接口,对日志的操作委托给具体的日志框架,5.0.2.RELEASE 版本中支持的日志如下:

private enum LogApi {LOG4J, SLF4J_LAL, SLF4J, JUL}

其中 JUL 为 java.util.logging ,JDK提供的基础日志功能,默认为 JUL,其他日志功能需要引入对应依赖。

静态块在类进行加载的时候就会尝试加载上述日志框架。当调用 LogFactory 工厂的 getLog 静态方法时,根据对应日志框架名称,创建对应日志类,部分源码如下:

public abstract class LogFactory {
    private static LogApi logApi = LogApi.JUL;
    static {
        ClassLoader cl = LogFactory.class.getClassLoader();
        try {
            // Try Log4j 2.x API
            cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger");
            logApi = LogApi.LOG4J;
        }
        catch (ClassNotFoundException ex1) {
            try {
                // Try SLF4J 1.7 SPI
                cl.loadClass("org.slf4j.spi.LocationAwareLogger");
                logApi = LogApi.SLF4J_LAL;
            }
            catch (ClassNotFoundException ex2) {
                try {
                    // Try SLF4J 1.7 API
                    cl.loadClass("org.slf4j.Logger");
                    logApi = LogApi.SLF4J;
                }
                catch (ClassNotFoundException ex3) {
                    // Keep java.util.logging as default
                }
            }
        }
    }
    public static Log getLog(String name) {
        switch (logApi) {
            case LOG4J:
                return Log4jDelegate.createLog(name);
            case SLF4J_LAL:
                return Slf4jDelegate.createLocationAwareLog(name);
            case SLF4J:
                return Slf4jDelegate.createLog(name);
            default:
                // Defensively use lazy-initializing delegate class here as well since the
                // java.logging module is not present by default on JDK 9. We are requiring
                // its presence if neither Log4j nor SLF4J is available; however, in the
                // case of Log4j or SLF4J, we are trying to prevent early initialization
                // of the JavaUtilLog adapter - e.g. by a JVM in debug mode - when eagerly
                // trying to parse the bytecode for all the cases of this switch clause.
                return JavaUtilDelegate.createLog(name);
        }
    }
}

该源码比较与 apache 的 jcl 简化了很多,核心类只有 LogFactory。

2.spring-core

spring核心包,主要包含 Spring 框架基本的核心工具类, Spring 的其他纽件都要用到这个包里的类, Core 模块是其他纽件的基本核心。

编译 spring-core 报错:找不到 DefaultNamingPolicy,Objenesis 类:为了避免第三方 class 的冲突,Spring 把最新的 cglib 和 objenesis 重新打包,并没有再源码提供这部分的代码,在官方文档里注释:这种重新打包技术避免了与应用程序级或第三方库和框架中不同 Objensis 版本之间的依赖关系的任何潜在冲突。

解决办法:在IDEA中打开右侧边栏的gradle,找到 Tasks --- other 模块,分别双击 cglibRepackJar 和 objenesisRepackJar 将两部分重新打包即可。或者也可以手动在 spring-core.gradle 配置文件中的 dependcies 配置项末尾添加:

dependencies {
    cglib("cglib:cglib:${cglibVersion}@jar")
    objenesis("org.objenesis:objenesis:${objenesisVersion}@jar")
    jarjar("com.googlecode.jarjar:jarjar:1.3")
    compile(files(cglibRepackJar))
    compile(files(objenesisRepackJar))
    compile(project(":spring-jcl"))
    optional("net.sf.jopt-simple:jopt-simple:5.0.4")
    optional("org.aspectj:aspectjweaver:${aspectjVersion}")
    optional("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
    optional("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
    optional("io.projectreactor:reactor-core")
    optional("io.reactivex:rxjava:${rxjavaVersion}")
    optional("io.reactivex:rxjava-reactive-streams:${rxjavaAdapterVersion}")
    optional("io.reactivex.rxjava2:rxjava:${rxjava2Version}")
    optional("io.netty:netty-buffer")
    testCompile("io.projectreactor:reactor-test")
    testCompile("javax.xml.bind:jaxb-api:2.3.0")
    testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
    testCompile("com.fasterxml.woodstox:woodstox-core:5.0.3") {
        exclude group: "stax", module: "stax-api"
    }
    compile fileTree(dir: 'libs', include : '*.jar') //添加该行
}

重新导入即可。

2.1.目录结构

asm:一个 Java 字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类

cglib:一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为 jdk 的动态代理提供了很好的补充。通常可以使用 Java 的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,cglib 是一个好的选择

core:核心包

lang:注解定义

objenesis:用于实例化一个特定 class 的对象,在类库中经常会有类必须拥有一个默认构造器的限制。Objenesis 通过绕开对象实例构造器来克服这个限制。常见使用场景有:

序列化,远程调用和持久化对象需要实例化并存储为到一个特殊的状态,而没有调用代码

代理,AOP 库和 Mock 对象,类可以被子类继承而子类不用担心父类的构造器

容器框架,对象可以以非标准的方式被动态实例化

util:工具类,这个包的工具类可以独立于 Spring 框架而存在;而 core 工具类主要还是为 Spring 框架所用,与 Spring 结合比较紧密。

2.2.源码说明

2.2.1.asm类解读

*Visitor 抽象类:包含 AnnotationVisitor、ClassVisitor、FieldVisitor、MethodVisitor、ModuleVisitor 五个抽象类:

*Writer:包含 AnnotationWriter、ClassWriter、FieldWriter、MethodWriter、ModuleWriter 五个类,分别继承上述对应的 *Visitor 抽象类,用于生成上述对应类型的二进制字节码。

2.2.2.core

annotation:注解、元注解、合并的注解等,注解相关的类和操作都在该包下,主要包含两大部分,一部分是关于 @AliasFor 注解的定义及其相关的使用说明,如AnnotationUtils,AnnotationAttributes 等类都是为了 @AliasFor 注解的使用做出的相关配套设施,另一部分是 @Order 类及其相关配套:

第一部分:@AliasFor 及其部分相关配套

@AliasFor:该注解相关信息必须通过 AnnotationUtils 加载,通常有以下几种用法:

别名,在注解定义中的属性上使用,如 @RequestMapping 注解的 path 和 value 属性,指定其中一个即指定了另一个:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    String name() default "";
    @AliasFor("path")
    String[] value() default {};
    @AliasFor("value")
    String[] path() default {};
    RequestMethod[] method() default {};
    String[] params() default {};
    String[] headers() default {};
    String[] consumes() default {};
    String[] produces() default {};
}

继承父注解的属性,不重写属性名,子注解的属性值的读写,其实是对父注解的属性值的读写,如@Service、@Controller 等价于 @Component:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component  //注意要声明父注解
public @interface Service {
    
    @AliasFor(annotation = Component.class)
    String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component  //注意要声明父注解
public @interface Controller {
    
    @AliasFor(annotation = Component.class)
    String value() default "";
}

继承父注解的属性,并重写属性名,需指定父注解的类型以及具体的属性,子注解的属性值的读写,其实是对父注解的属性值的读写,若两个都指明属性值,要求值必须相同,否则会报错。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
@Inherited
public @interface MyAnnotation {
    @AliasFor(attribute = "location")
    String value() default "";
    @AliasFor(attribute = "value")
    String location() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
@Inherited
@MyAnnotation  //注意要声明父注解
public @interface SubMyAnnotation {
    @AliasFor(attribute = "value", annotation = MyAnnotation.class)
    String subValue() default "";
    @AliasFor(attribute = "location", annotation = MyAnnotation.class)
    String subLocation() default "";
//    subLocation属性写成下边这两种结果是一样的
//    @AliasFor(attribute = "value", annotation = MyAnnotation.class)
//    String subLocation() default "";
//    @AliasFor(value = "location", annotation = MyAnnotation.class)
//    String subLocation() default "";
//
}

注解的叠加复用,如@SpringBootApplication,在注解定义上配置需要复用的注解,并在指定属性上声明复用的注解属性 (本质上还是相当于实现了注解的继承功能):

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    //复用@EnableAutoConfiguration注解的exclude属性
    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};
    //复用@EnableAutoConfiguration注解的excludeName属性
    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};
    //复用@ComponentScan的basePackages属性
    @AliasFor(annotation = ComponentScan.class,attribute = "basePackages")
    String[] scanBasePackages() default {};
    //复用@ComponentScan的basePackageClasses属性
    @AliasFor(annotation = ComponentScan.class,attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};
}

@AliasFor 注解需要通过 Spring 中提供的工具类 AnnotationUtils 或 AnnotatedElementUtils 来解析才能生效。AnnotatedElementUtils 内部还是调用的 AnnotationUtils。互为别名的属性值,使用的时候如果均赋值,同时又通过 AnnotationUtils 的 findAnnotation 方法获取属性值,那么会抛出异常。Spring 其实是自己实现了 jdk 动态的拦截器来实现别名功能。

@RequestMapping("/test")
public class App{}
@Test
public void test(){
    RequestMapping springAnnotationProxy = AnnotationUtils.findAnnotation(App.class, RequestMapping.class);
    System.out.println("springAnnotationProxy path:" + springAnnotationProxy.path());
    System.out.println("springAnnotationProxy value:" + springAnnotationProxy.value());
    RequestMapping jdkAnnotation = App.class.getAnnotation(RequestMapping.class);
    System.out.println("jdkAnnotation path:" + jdkAnnotation.path());
    System.out.println("jdkAnnotation value:" + jdkAnnotation.value());
}

debug 功能可以看出,springAnnotationProxy 的底层类型是 SynthesizedAnnotationInvocationHandler,其 value 和 path 属性值均为 test,而 jdkAnnotation 的底层类型是 AnnotationInvocationHandler,其只有 value 属性值为 test,path 属性为空。

AnnotationAttributes:本质上是一个 Map<String, Object> 集合,继承 LinkedHashMap,用于保存某个注解实例的全部属性 (属性名和对应的属性值) 。

AnnotationAwareOrderComparator:对 OrderComparator 的增强 (继承) ,可以对 Ordered 对象、使用 @Order 注解的对象进行比较。与OrderComparator 一样提供了如上两个静态的排序方法。

AnnotationUtils 中包含了很多解析注解的方法:

public static <A extends Annotation> A getAnnotation(Annotation ann, Class<A> annotationType)
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType)
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType)
public static Annotation[] getAnnotations(AnnotatedElement annotatedElement)
public static Annotation[] getAnnotations(Method method)
// 获取函数上或注解上的注解
        -   public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType)
            public static <A extends Annotation> A findAnnotation(Method method, @Nullable Class<A> annotationType)
            public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType)
            递归查找父注解
        -   public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz)
            public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz)
            public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType, @Nullable Class<? extends Annotation> metaAnnotationType)
            是否声明了/继承了某个注解
        -   public static Map<String, Object> getAnnotationAttributes(Annotation annotation)
            public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString)
            public static AnnotationAttributes getAnnotationAttributes(@Nullable AnnotatedElement annotatedElement, Annotation annotation)
            获取注解的所有属性,返回 AnnotationAttributes
        -   public static Object getValue(Annotation annotation)
            public static Object getValue(@Nullable Annotation annotation, @Nullable String attributeName)
            public static Object getDefaultValue(Annotation annotation)
            public static Object getDefaultValue(@Nullable Annotation annotation, @Nullable String attributeName)
            public static Object getDefaultValue(Class<? extends Annotation> annotationType)
            public static Object getDefaultValue(@Nullable Class<? extends Annotation> annotationType, @Nullable String attributeName)
    获取注解的值以及默认值

第二部分:@Order 及其部分相关配套

-   @Order:@Order 注解定义了类、方法和字段的优先级(排序情况),value 是可选的,默认为 Ordered.LOWEST_PRECEDENCE,即最低优先级。表示 Ordered 接口中的 order 属性。

-   OrderUtils:主要用于获取 @Order 注解的相关属性。

-   OrderComparator(根目录下):Ordered 对象的比较器,对外提供了两个静态的排序方法

public static void sort(List<?> list)、public static void sort(Object[] array)。

codec:编解码工具,不常用,略

convert:主要用于类型转换

TypeDescriptor:对java中所有数据类型的描述,包括 Collection、Map、自定义 Object、数组、基本数据类型及其包装类型。它被成对地用于GenericConversionService 中,表示从 src 类型到 target 类型的转换,会有一个相应的 converter 实现与之对应。为了提高性能,对基本数据类型及其包装类型做了缓存。

DefaultConversionService:对外提供的 API 的主要服务类

env:提供一个代表系统环境的 StandardEnvironment,包括对 System.getProperties()、System.getenv() 的访问。在 Spring Web 中StandardServletEnvironment 继承了 StandardEnvironment,又增加对 Servlet 容器系统参数的访问。

io:供外部调用的入口API主要是 FileSystemResourceLoader、ClassRelativeResourceLoader。spring web、spring context 等模块也会自定义覆盖/实现ResourceLoader的子类和子接口。

serializer: java 对象的字节流序列化和反序列化

style:略

task:实现了两个适配器,分别是

并提供了一个简单的异步 TaskExecutor 的实现 SimpleAsyncTaskExecutor。它继承 CustomizableThreadCreator,用来创建线程并给线程命名(有意义的名字),需要注意的是它并没有使用线程池。ConcurrencyThrottleSupport 在 SimpleAsyncTaskExecutor 中用于控制并发,应用了生产者-消费者模式。

type:对外暴露的、可直接使用的API接口为 CachingMetadataReaderFactory。基于 ASM,使用了访问者设计模式,同时使用 cache 来避免了对 class 的重复加载和解析。

根目录的部分其他类:

2.2.3.util

2.2.4.cglib&langobjenesis

以上就是Spring jcl及spring core源码深度解析的详细内容,更多关于Spring jcl spring core解析的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯