spring原理(第八天)

news2024/9/29 7:18:28

aop的实现原理

AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能

除此以外,aspectj 提供了两种另外的 AOP 底层实现:

  • 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中

  • 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能

  • 作为对比,之前学习的代理是运行时生成新的字节码

简单比较的话:

  • aspectj 在编译和加载时,修改目标字节码,性能较高

  • aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强

  • 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行

ajc 编译器实现增强

编写类,并在其中准备要增强的方法

@Service
public class MyService {

    private static final Logger log = LoggerFactory.getLogger(MyService.class);

    public static void foo() {
        log.debug("foo()");
    }
}

编写切面类

@Aspect // ⬅️注意此切面并未被 Spring 管理
public class MyAspect {

    private static final Logger log = LoggerFactory.getLogger(MyAspect.class);

    @Before("execution(* com.sky.service.MyService.foo())")
    public void before() {
        log.debug("before()");
    }
}

在pom文件中添加插件

<plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.14.0</version>
                <configuration>
                    <complianceLevel>1.8</complianceLevel>
                    <source>8</source>
                    <target>8</target>
                    <showWeaveInfo>true</showWeaveInfo>
                    <verbose>true</verbose>
                    <Xlint>ignore</Xlint>
                    <encoding>UTF-8</encoding>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <!-- use this goal to weave all your main classes -->
                            <goal>compile</goal>
                            <!-- use this goal to weave all your test classes -->
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

此时通过这个插件能在编译时能修改 class 文件实现增强,并没有使用代理对象

编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强

注意

  • 目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16

  • 一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器

学到了什么
            1. aop 的原理并非代理一种, 编译器也能玩出花样 

agent 类加载实现增强

修改要被增强的类(基于代理实现的话并不会增强目标方法调用的方法)

@Service
public class MyService {

    private static final Logger log = LoggerFactory.getLogger(MyService.class);

    final public void foo() {
        log.debug("foo()");
        this.bar();
    }

    public void bar() {
        log.debug("bar()");
    }
}

  注意几点
    1. 目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
    2. 运行时需要在 VM options 里加入 -javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
        把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址

编写启动类

@SpringBootApplication
public class Spring09Application {

    private static final Logger log = LoggerFactory.getLogger(A10.class);

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Spring09Application.class, args);
        MyService service = context.getBean(MyService.class);

        // ⬇️MyService 并非代理, 但 foo 方法也被增强了, 做增强的 java agent, 在加载类时, 修改了 class 字节码
        log.debug("service class: {}", service.getClass());
        service.foo();

        context.close();


    }
}

此时agent类加载器会在类加载期间修改class的字节码,增强了方法

 学到了什么
            1. aop 的原理并非代理一种, agent 也能, 只要字节码变了, 行为就变了
 

AOP 实现之 proxy

 jdk 动态代理

jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系

编写代码

public class JdkProxyDemo {

    interface Foo {
        void foo();
    }

    static class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }
    }

    public static void main(String[] param) {
                // 目标对象
        Target target = new Target();

        ClassLoader loader = JdkProxyDemo.class.getClassLoader(); // 用来加载在运行期间动态生成的字节码
        Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, (p, method, args) -> {
            System.out.println("before...");
            // 目标.方法(参数)
            // 方法.invoke(目标, 参数);
            Object result = method.invoke(target, args);
            System.out.println("after....");
                    return result;
                });
        // 调用代理
        proxy.foo();
    }
}

在运行期间修改字节码文件,可以发现基于jdk实现代理增强功能实现了 

cglib 代理

  • cglib 不要求目标实现接口,它生成的代理类是目标的子类,因此代理与目标之间是子父关系

  • 限制⛔:根据上述分析 final 类无法被 cglib 增强

  此时需要引入aop起步依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

编写代码

public class CglibProxyDemo {

    static class Target {
        public void foo() {
            System.out.println("target foo");
        }
    }

    public static void main(String[] param) {
        // 目标对象
        Target target = new Target();
        // 代理对象
        Target proxy = (Target) Enhancer.create(Target.class,
                (MethodInterceptor) (p, method, args, methodProxy) -> {
            System.out.println("before...");
//            Object result = method.invoke(target, args); // 用方法反射调用目标
            // methodProxy 它可以避免反射调用
//            Object result = methodProxy.invoke(target, args); // 内部没有用反射, 需要目标 (spring)
            Object result = methodProxy.invokeSuper(p, args); // 内部没有用反射, 需要代理
            System.out.println("after...");
                    return result;
                });
        // 调用代理
        proxy.foo();
    }
}

运行结果和jdk代理一样

注: methodProxy.invoke和methodProxy.invokeSuper不是基于反射原理实现的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1980458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux之文件系统

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言进阶 数据结构初阶 Linux C初阶 算法 C进阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂 目录 一.磁盘 二.对磁盘进行管理 三.通过inode找到文件…

Netty的几种IO模式的实现与切换

写在文章开头 今天我们就基于Netty来简单聊聊开发中几种常见的IO模式以及Netty对于这几种IO模式的实现&#xff0c;希望对你有帮助。 Hi&#xff0c;我是 sharkChili &#xff0c;是个不断在硬核技术上作死的 java coder &#xff0c;是 CSDN的博客专家 &#xff0c;也是开源项…

如何在RabbitMQ中防止消息丢失

如何在RabbitMQ中防止消息丢失 在分布式系统中&#xff0c;消息的可靠传递是至关重要的。RabbitMQ作为一个强大的消息队列系统&#xff0c;提供了多种机制来确保消息不会丢失。本文将介绍在RabbitMQ中防止消息丢失的几种方法。 消息确认机制 消息发布确认 在RabbitMQ中&…

pdf转换器哪个好?不要错过这4款转换工具

pdf转换器哪个好&#xff1f;选择一款高效的PDF转换器&#xff0c;无疑能极大地便利我们的日常工作与学习。它不仅能够轻松实现PDF文件与Word、Excel、图片等多种格式之间的互转&#xff0c;还支持批量处理&#xff0c;显著提高工作效率。无论是编辑修改、格式调整还是分享传阅…

深入浅出消息队列----【RocketMQ 和 Kafka 消息存储差异对比】

深入浅出消息队列----【RocketMQ 和 Kafka 消息存储差异对比】 RocketMQ 的消息存储Kafka 的消息存储对比 RocketMQ 与 Kafka 本文仅是文章笔记&#xff0c;整理了原文章中重要的知识点、记录了个人的看法 文章来源&#xff1a;编程导航-鱼皮【yes哥深入浅出消息队列专栏】 Roc…

指南!网上卖药品需要什么资质?

随着互联网技术的飞速发展&#xff0c;医药电商已经成为药品和医疗器械销售的重要渠道。处方药的网络销售政策逐步放宽&#xff0c;医药电商行业迎来了快速发展的春天。在这一领域&#xff0c;主要的参与者包括药品销售公司和电商平台。 为了吸引流量和满足处方药审方的需求&a…

第18课 Scratch入门篇:时钟-当前时间

时钟 故事背景&#xff1a; 在一个遥远的科技星球上&#xff0c;时间对于居民们来说无比珍贵。这个星球上的居民们都是技术高手&#xff0c;他们使用先进的编程技术来管理自己的生活。然而&#xff0c;星球上的时间系统最近出现了故障&#xff0c;导致时间的流逝变得不稳定。为…

【终极指南】大模型二次开发:从零基础到高手之路

随着人工智能技术的发展&#xff0c;预训练的大模型&#xff08;例如GPT系列、BERT等&#xff09;已成为自然语言处理领域的关键技术之一。对于开发者来说&#xff0c;掌握如何基于这些大模型进行二次开发&#xff0c;不仅可以提升自身的技术实力&#xff0c;还能为企业带来更多…

Flink 如何处理背压

文章目录 目录 前言 一、什么是背压&#xff1f; 二、处理背压的步骤 1.模拟背压机制 2.为什么要关心背压问题&#xff1f; 总结 前言 初次接触Flink的同学会对背压有很多的疑问。本文就是我学习的一些心得和体会&#xff0c;以及借鉴一些文章的感想。 Flink 如何处理背压效应…

使用snap的安装docker配置阿里云镜像加速

使用snap安装docker非常的简单&#xff0c;一条命令即可 snap install docker 但是通过这个命令安装的docker, 配置阿里云镜像跟常规安装的配置起来不太一样, 下面讲一下配置流程 修改docker配置文件/var/snap/docker/current/config/daemon.json 这个文件应该是已经创建好…

重磅!LangChain 官方发布 Agent IDE!!

1 LangChain 开发现状 LangChain 从应用开发框架出发&#xff0c;提供了一套代码级工具集&#xff0c;旨在降低 LLM 的开发难度&#xff0c;在过去一年中吸引了众多开发者&#xff0c;助力他们迅速打造 AI 大模型应用。然而&#xff0c;还有一群用户&#xff0c;他们希望门槛…

NC 最长无重复子数组

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 给定一个长度…

idea连接oracle

配置 注意&#xff1a; SID指的是实例名称

C语言宠物系统3

在前面的基础上&#xff0c;加上了修改功能和排序功能&#xff0c;可以选择姓名排序&#xff0c;年龄排序&#xff0c;价格排序。 test.c源文件 #include "Pet.h"void menu() {printf("------------------------\n");printf("- 欢迎来到宠物商店 …

实践出真知:Agents 领域“一年打怪升级”的经验分享

编者按&#xff1a;在你构建 AI Agents 时&#xff0c;是否曾遇到这些困扰&#xff1a;总是在简单任务上出错&#xff0c;从而让你有时会怀疑自己的技术水平&#xff1f;面对客户的需求&#xff0c;AI Agent 表现得像个“笨蛋”&#xff0c;无法准确理解和执行指令&#xff1f;…

不同网络上的计算机怎么通信

从 一个网络上计算机的通信 &#xff0c;我们知道&#xff0c;在一个网络里&#xff0c;多台主机通过交换机连接起来&#xff0c;每台主机的网卡有全球唯一的 MAC 地址&#xff0c;一个网络上的主机通过 MAC 地址通信。 那么&#xff0c;多个网络之间如何互联和通信&#xff1…

【轨物方案】智慧供热物联网整体解决方案

目前城市供暖系统当中&#xff0c;供暖设备一直得不到更新和升级&#xff0c;没有合理的监控设备&#xff0c;导致对供暖的合理调控不理想&#xff0c;供暖严重失调而浑然不知&#xff0c;进而出现冷热不均的问题&#xff0c;极易造成资源严重浪费。缺乏成熟的管理系统&#xf…

上门按摩小程序项目开发功能介绍

上门按摩小程序通常设计为连接按摩服务提供者和客户的平台&#xff0c;提供便捷的预约和服务管理功能。以下是这类小程序可能包含的功能&#xff1a; 用户注册和登录&#xff1a; 用户可以注册个人账户并登录&#xff0c;以便管理个人信息和预约记录。 按摩师信息浏览&#xf…

JAVA中实现线程安全的三种方式

JAVA中实现线程安全的三种方式 1. 同步代码块2. 同步方法3. ReentrantLock4. 总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1. 同步代码块 使用synchronized关键字加在需要同步的代码块上&#xff0c;并指定一个锁对象。这种方式可以…

Java作用域

目录 1.作用域 基本使用 2.作用域的注意事项和细节使用 1.作用域 基本使用 局部变量一般是成员方法里的变量 。全局变量有默认值&#xff0c;局部变量没有默认值。 在类内但是方法外定义的变量是局部变量&#xff0c;有初始值0可以不赋初值&#xff0c;在方法内的是局部变量…