从零开始造Spring04---补充之ASM的原理以及在Spring中的应用

简介: ASM 是一个可以操作Java 字节码的框架。可以读取/修改class中的字节码。ASM可以直接产生二进制class文件,也可以在类被加载Java虚拟机之前动态改变类行为,Java class被存储在严格格式定义的.class文件里,这些文件拥有足够的元数据来解析勒种的所有元素:类名称, 方法,属性以及Java字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

什么是ASM


ASM 是一个可以操作Java 字节码的框架。可以读取/修改class中的字节码。ASM可以直接产生二进制class文件,也可以在类被加载Java虚拟机之前动态改变类行为,Java class被存储在严格格式定义的.class文件里,这些文件拥有足够的元数据来解析勒种的所有元素:类名称, 方法,属性以及Java字节码(指令)。ASM从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

与 BCEL 和 SERL 不同,ASM 提供了更为现代的编程模型。对于 ASM 来说,Java class 被描述为一棵树;使用 “Visitor” 模式遍历整个二进制结构;事件驱动的处理方式使得用户只需要关注于对其编程有意义的部分,而不必了解 Java 类文件格式的所有细节:ASM 框架提供了默认的 “response taker”处理这一切。


说明

这是学习刘欣老师《从零开始造Spring》课程的学习笔记


ASM的Visitor模式

c6e4cd7839e9decd753bd3f5dea7e693_70.png


ClassReader : 我现在要解析一个复杂结构的类文件了啊, 每当我解析好一点东西,

我都会通知你来处理。


ClassVisistor : 你怎么通知我?


ClassReader : 当然是回调你的方法啊, 比如说当我开始解析一个方法时,

我就会回调你的visistMethod() , 把相关数据给你发过去, 你就可以处理了


ClassVisitor : 明白!但是当我处理完了,比如修改了字节码, 怎么写回去呢?


ClassReader : 你可以创建一个ClassWriter 对象, 每次处理完以后,再调用同样的方法,

例如visitMethod(), 这样ClassWriter 就知道你的修改了, 它负责写回去。


在Spring中的应用

类图

e9c3d0760bea8b36a44b9c054f2e34d2_70.png


调用层次

首先会调用ClassMetadataReadingVisitor类的visit方法——>

调用AnnotationMetadataReadingVisitor类的visitAnnotation方法

—–>调用AnnotationAttributesReadingVisitor类的visit方法。

—>调用AnnotationAttributesReadingVisitorvisitEnd方法—–> 最后获取注解中的内容


关键代码

测试代码

@Test
    public void  testGetAnnonation() throws IOException {
       ClassPathResource resource = new ClassPathResource("com/jay/spring/service/v4/PetStoreService.class");
       ClassReader reader = new ClassReader(resource.getInputStream());
       AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor();
       reader.accept(visitor, ClassReader.SKIP_DEBUG);
       String annotation = "com.jay.spring.stereotype.Component";
       Assert.assertTrue(visitor.hasAnnotation(annotation));
       AnnotationAttributes attributes = visitor.getAnnotationAttributes(annotation);
       Assert.assertEquals("petStoreService", attributes.get("value"));
   }


ClassMetadataReadingVisitor类

 

public void visit(int version, int access, String name, String signature, String supername, String[] interfaces) {
        this.className = ClassUtils.convertResourcePathToClassName(name);
        this.isInterface = ((access & Opcodes.ACC_INTERFACE) != 0);
        this.isAbstract = ((access & Opcodes.ACC_ABSTRACT) != 0);
        this.isFinal = ((access & Opcodes.ACC_FINAL) != 0);
        if (supername != null) {
            this.superClassName = ClassUtils.convertResourcePathToClassName(supername);
        }
        this.interfaces = new String[interfaces.length];
        for (int i = 0; i < interfaces.length; i++) {
            this.interfaces[i] = ClassUtils.convertResourcePathToClassName(interfaces[i]);
        }
    }


AnnotationMetadataReadingVisitor类

 

@Override
    public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
        String className = Type.getType(desc).getClassName();
        this.annotationSet.add(className);
        return new AnnotationAttributesReadingVisitor(className, this.attributeMap);
    }


4. AnnotationAttributesReadingVisitor

public class AnnotationAttributesReadingVisitor extends AnnotationVisitor {
    private final String annotationType;
    private final Map<String, AnnotationAttributes> attributesMap;
    AnnotationAttributes attributes = new AnnotationAttributes();
    public AnnotationAttributesReadingVisitor(
            String annotationType, Map<String, AnnotationAttributes> attributesMap) {
        super(SpringAsmInfo.ASM_VERSION);
        this.annotationType = annotationType;
        this.attributesMap = attributesMap;
    }
    @Override
    public final void visitEnd() {
        this.attributesMap.put(this.annotationType, this.attributes);
    }
    @Override
    public void visit(String attributeName, Object attributeValue) {
        this.attributes.put(attributeName, attributeValue);
    }


对asm实现的封装

类图

36c0a7f4248d88f3b6767bd290977839_70.png

关键代码

public class SimpleMetadataReader implements MetadataReader {
    private final Resource resource;
    private final ClassMetadata classMetadata;
    private final AnnotationMetadata annotationMetadata;
    public SimpleMetadataReader(Resource resource) throws IOException {
        InputStream inputStream = new BufferedInputStream(resource.getInputStream());
        ClassReader classReader;
        try {
            classReader = new ClassReader(inputStream);
        } finally {
            inputStream.close();
        }
        AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor();
        classReader.accept(visitor, ClassReader.SKIP_DEBUG);
        this.annotationMetadata = visitor;
        this.classMetadata = visitor;
        this.resource = resource;
    }
    @Override
    public Resource getResource() {
        return this.resource;
    }
    @Override
    public ClassMetadata getClassMetadata() {
        return this.classMetadata;
    }
    @Override
    public AnnotationMetadata getAnnotationMetadata() {
        return this.annotationMetadata;
    }


相关文章
|
5月前
|
缓存 Java 开发者
【Spring】原理:Bean的作用域与生命周期
本文将围绕 Spring Bean 的作用域与生命周期展开深度剖析,系统梳理作用域的类型与应用场景、生命周期的关键阶段与扩展点,并结合实际案例揭示其底层实现原理,为开发者提供从理论到实践的完整指导。
673 22
|
5月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
1881 0
|
6月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1060 3
|
5月前
|
SQL Java 数据库连接
Spring Data JPA 技术深度解析与应用指南
本文档全面介绍 Spring Data JPA 的核心概念、技术原理和实际应用。作为 Spring 生态系统中数据访问层的关键组件,Spring Data JPA 极大简化了 Java 持久层开发。本文将深入探讨其架构设计、核心接口、查询派生机制、事务管理以及与 Spring 框架的集成方式,并通过实际示例展示如何高效地使用这一技术。本文档约1500字,适合有一定 Spring 和 JPA 基础的开发者阅读。
566 0
|
4月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
4月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
243 8
|
4月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
544 2
|
6月前
|
Java 应用服务中间件 开发者
Spring Boot 技术详解与应用实践
本文档旨在全面介绍 Spring Boot 这一广泛应用于现代企业级应用开发的框架。内容将涵盖 Spring Boot 的核心概念、核心特性、项目自动生成与结构解析、基础功能实现(如 RESTful API、数据访问)、配置管理以及最终的构建与部署。通过本文档,读者将能够理解 Spring Boot 如何简化 Spring 应用的初始搭建和开发过程,并掌握其基本使用方法。
488 2
|
6月前
|
Java 关系型数据库 数据库
深度剖析【Spring】事务:万字详解,彻底掌握传播机制与事务原理
在Java开发中,Spring框架通过事务管理机制,帮我们轻松实现了这种“承诺”。它不仅封装了底层复杂的事务控制逻辑(比如手动开启、提交、回滚事务),还提供了灵活的配置方式,让开发者能专注于业务逻辑,而不用纠结于事务细节。
|
6月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。