后端:Spring AOP原理--动态代理

server/2024/11/17 21:18:23/

文章目录

    • 1. Spring AOP底层原理
    • 2. 代理模式
    • 3. 静态代理
    • 4. 动态代理
      • 4.1 jdk 实现动态代理
      • 4.2 cglib 实现动态代理
      • 4.3 jdk、cglib动态代理两者的区别

1. Spring AOP底层原理

  • 创建容器 new applicationContext();
  • Spring把所有的Bean进行创建,进行依赖注入;
  • ioc.getBean(UserService.class)此时获取得到Bean并不是原本的UserService类,而是通过动态代理生成一个代理类,这个代理类继承自UserService类。

在这里插入图片描述

2. 代理模式

代理模式是一种比较好的设计模式;

  • 使用代理对象来增强目标对象(target object),这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能;
  • 将核心业务代码和非核心的公共代码分离解耦,提高代码的可维护性,让被代理类专注业务降低代码复杂度。
    • 被代理类专注业务;
    • 代理类非核心的公共代码;

通常使用代理实现比如拦截器、事务控制,还有测试框架 mock、用户鉴权、日志、全局异常处理等。

3. 静态代理

代理类:

java">package com.lize.demo.aop.static1;public class CeoProxy extends Ceo{@Overridepublic void meeting(String name) {if(name.equals("张三")){super.meeting(name);}else{System.out.println("登记:"+name);}}
}

被代理类:

java">package com.lize.demo.aop.static1;public class Ceo {public void meeting(String name){System.out.println("接见客户:"+name);}
}

测试:

java">package com.lize.demo.aop.static1;import org.junit.jupiter.api.Test;
public class TestProxy {@Testpublic void test(){Ceo ceo = new CeoProxy();ceo.meeting("张三");ceo.meeting("李四");}
}

运行结果:
在这里插入图片描述

使用静态代理的弊端:一个代理类只能代理一个类型。

4. 动态代理

4.1 jdk 实现动态代理

jdk自带的,不需要额外导入其他依赖;
Proxy类中使用频率最高的方法是:newProxyInstance(),Proxy这个方法主要用来生成一个代理对象。

java">public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h
)

三个参数分别表示的意思是:

  • loader:类加载器,用于加载代理对象,传入被代理类的类加载器;
  • interfaces:被代理类实现的一些接口;被代理的类需要实现接口;
  • h:实现了InvocationHandler接口的对象;
java">package com.lize.demo.aop.autoProxy;public interface IUserService {void add();
}
java">package com.lize.demo.aop.autoProxy;// 被代理的类
public class UserService implements IUserService{@Overridepublic void add() {System.out.println("增加。。。");}
}
java">package com.lize.demo.aop.autoProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class MyHandler implements InvocationHandler {private Object target;public MyHandler(Object target){this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("前置通知");// args 传递过来的参数Object res = method.invoke(target, args);System.out.println("后置通知");return res;}}
java">package com.lize.demo.aop.autoProxy;import org.junit.jupiter.api.Test;import java.lang.reflect.Proxy;public class TestProxy {@Testpublic void test(){IUserService userService = (IUserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),UserService.class.getInterfaces(),new MyHandler(new UserService()));userService.add();System.out.println(userService.getClass());}
}

运行结果:
在这里插入图片描述

4.2 cglib 实现动态代理

java">package com.lize.demo.aop.autoProxy2;
// 被代理的类
public class UserService {public void add() {System.out.println("增加。。。");}
}
java">package com.lize.demo.aop.autoProxy2;import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;// 做增强处理
public class MyCallback implements MethodInterceptor {private Object target;public MyCallback(Object target) {this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("前置通知");Object res = proxy.invoke(target, args);System.out.println("后置通知");return res;}
}
java">package com.lize.demo.aop.autoProxy2;import org.junit.jupiter.api.Test;
import org.springframework.cglib.proxy.Enhancer;public class TestProxy {@Testpublic void test(){Enhancer enhancer = new Enhancer();// 设置被代理的类enhancer.setSuperclass(UserService.class);enhancer.setCallback(new MyCallback(new UserService()));UserService userService = (UserService) enhancer.create();// 代理的类userService.add();System.out.println(userService.getClass());}
}

在这里插入图片描述

4.3 jdk、cglib动态代理两者的区别

  • jdk动态代理只能代理实现了接口的类;而cglib可以代理未实现任何接口的类;
  • cglib动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为finnal类型的类和方法;
  • 在Spring中默认使用jdk动态代理,判断目标类是否实现了接口,如果实现了接口使用jdk动态代理,否则使用cglib;在Spring Boot中默认使用的是cglib动态代理

在Spring中,如果想修改动态代理方式,可以通过注解的方式进行修改。

java">@EnableAspectJAutoProxy(proxyTargetClass = true) // cglib
@EnableAspectJAutoProxy(proxyTargetClass = false) // jdk

在Spring Boot中,如果想修改动态代理方式,可以通过在配置文件application.properties中添加配置即可。

spring.aop.proxy-target-class=false

http://www.ppmy.cn/server/142452.html

相关文章

Invar-RAG:基于不变性对齐的LLM检索方法提升生成质量

Invar-RAG&#xff1a;基于不变性对齐的LLM检索方法提升生成质量 论文链接:https://arxiv.org/html/2411.07021v1 论文概述 在检索增强型生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;系统中直接应用大型语言模型&#xff08;Large Language Models, LLM…

Python习题 250:删除空文件夹

(编码题)编写一段 Python 代码,删除指定目录的空文件夹。 参考答案: 使用 pathlib 库可以更简洁地处理文件路径。下面是一个使用 pathlib 库递归删除空文件夹的 Python 代码:from pathlib import Pathdef remove_empty_dirs(directory):# 遍历目录及其子目录for path in…

Linux 进程信号初识

目录 0.前言 1.什么是信号 1.1生活中的信号 1.2 OS中的信号 2.认识信号 2.1信号概念 2.2查看信号 2.3 signal函数 2.4代码示例 3. 信号处理方式 3.1 忽略信号 3.2 默认处理 3.3 自定义处理 4.小结 &#xff08;图像由AI生成&#xff09; 0.前言 在之前的学习中&#xff0c;我…

Redis 字符串(String)

Redis 字符串数据类型的相关命令用于管理 redis 字符串值&#xff0c;基本语法如下&#xff1a; 语法 redis 127.0.0.1:6379> COMMAND KEY_NAME实例 redis 127.0.0.1:6379> SET w3ckey redis OK redis 127.0.0.1:6379> GET w3ckey "redis"在以上实例中我…

【回溯法】——分割回文串

131. 分割回文串 一、题目难度 中等 二、相关标签与相关企业 [相关标签] [相关企业] 三、题目描述 给你一个字符串 s s s&#xff0c;请你将 s s s 分割成一些子串&#xff0c;使每个子串都是回文串。返回 s s s 所有可能的分割方案。 四、示例 示例1 输入&#xf…

Bugku CTF_Web——点login咋没反应

Bugku CTF_Web——点login咋没反应 进入靶场 随便输个试试 看来确实点login没反应 抓包看看 也没有什么信息 看了下源码 给了点提示 一个admin.css try ?12713传参试试 拿到一个php代码 <?php error_reporting(0); $KEYctf.bugku.com; include_once("flag.php&q…

Kafka-Controller选举

一、上下文 《Kafka-broker粗粒度启动流程》博客中我们分析了broker的大致启动流程&#xff0c;这个时候每个broker都不是controller角色&#xff0c;下面我们就来看下它是如何选举出来的吧 二、设置ZooKeeper ‌ZooKeeper是一个开源的分布式协调服务&#xff0c;主要用于分…

机器学习-4:机器学习的建模流程

机器学习的建模流程 流程为&#xff1a; 原始数据 --> 数据预处理 --> 特征工程 --> 建模 --> 验证。 原始数据收集 所有AI或机器学习的基础就是数据&#xff0c;没有数据就什么都做不了&#xff0c;在搭建一个系统之前首要考虑的就是有没有足够多的数据可以支撑这…