Spring AOP实现原理-动态代理

目录

代理的基础概念

示例1:静态代理(场景:客户通过中介租房东的房子)

示例2:JDK动态代理实现房东、中介出租房屋

示例3:CGLib动态代理实现房东出租房屋

示例4:观察Spring IOC容器中代理对象详情


代理的基础概念

        AOP是基于代理模式实现切点方法的动态扩展。当切点目标类实现了接口,AOP通过JDK自带的动态代理扩展被代理对象方法的功能;当切点目标类未实现接口,Spring 通过CGLib组件实现扩展被代理对象方法功能。

        代理模式的核心是创建一个代理对象,代理对象内部包含封装了被代理对象,最终通过执行被代理对象的方法达到动态扩展方法的功能,代理模式分为静态代理和动态代理。

示例1:静态代理(场景:客户通过中介租房东的房子)

示意图如下:

EstateAgent(中介)和Landord(房东)都实现租房接口,Customer(客户)通过中介实现租房子,代码由以下各类组成:

1、接口(房屋出租接口)

package com.text.pattern;
//房屋出租接口
public interface RentalHouse {void rental();
}

2、房东类-实现租房接口

package com.text.pattern;
//房东类
public class Landlord implements RentalHouse{@Overridepublic void rental() {System.out.println("xxx栋xxx房屋出租");}
}

3、中介类--实现租房接口

package com.text.pattern;
//房产中介
public class EstateAgent implements RentalHouse{private Landlord landlord;//被代理对象public EstateAgent(Landlord landlord) {this.landlord = landlord;}@Overridepublic void rental() {System.out.println("中介收取客户中介费");this.landlord.rental();}
}

4、客户类-测试

package com.text.pattern;
//测试类
public class Customer {public static void main(String[] args) {System.out.println("客户找中介租房子");new EstateAgent(new Landlord()).rental();}
}

5、运行结果:

      

从运行结果中可以看出,房屋出租方法被扩展了中介收取客户手续费的功能。

        静态代理的劣势:如果需要对很多目标类方法进行扩展,就需要额外编写很多的代理类,通过动态代理可以实现一个代理类对一批目标方法进行扩展,也就实现了AOP

示例2:JDK动态代理实现房东、中介出租房屋

1、代理执行hander(ProxyInvocationHandler)

package com.text.pattern;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class ProxyInvocationHandler implements InvocationHandler {private Object target;//被代理对象public ProxyInvocationHandler(Object target) {this.target = target;}/*** @param proxy 代理对象* @param method 被代理对象的方法* @param args 被代理对象方法的参数* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("目标类方法执行之前功能扩展...");Object ret = method.invoke(target, args);System.out.println("目标类方法执行之后功能扩展...");return ret;}
}

2、被代理对象实现的接口

package com.text.pattern;
//房屋出租接口
public interface RentalHouse {void rental();
}

 3、被代理对象,房东和中介都可以被代理

package com.text.pattern;
//房东类
public class Landlord implements RentalHouse{@Overridepublic void rental() {System.out.println("房东出租xxx栋xxx房屋");}
}
package com.text.pattern;
//房产中介
public class EstateAgent implements RentalHouse{@Overridepublic void rental() {System.out.println("中介出租xxx栋xxx房屋,并收取中介费");}
}

4、测试类 生成代理对象,房东的代理,中介的代理

package com.text.pattern;import java.lang.reflect.Proxy;//测试类
public class Customer {public static void main(String[] args) {RentalHouse landlord = new Landlord();//被代理对象RentalHouse proxyObj = (RentalHouse)Proxy.newProxyInstance(landlord.getClass().getClassLoader(),landlord.getClass().getInterfaces(), new ProxyInvocationHandler(landlord));proxyObj.rental();landlord = new EstateAgent();proxyObj = (RentalHouse)Proxy.newProxyInstance(landlord.getClass().getClassLoader(),landlord.getClass().getInterfaces(), new ProxyInvocationHandler(landlord));proxyObj.rental();}
}

5、运行结果:

 从运行结果可以看出,房东和中介都实现了租房的接口,并且都被代理,他们分别在租房的同时都实现了各自方法的扩展,即一个代理类(ProxyInvocationHandler)实现了对多个目标方法的动态扩展。

示例3:CGLib动态代理实现房东出租房屋

如果房东类没有实现接口,Spring 采用CGlib组件实现AOP功能

1、目标类(房东)

package com.text.pattern;
//房东类
public class Landlord2{public void rental() {System.out.println("房东2出租xxx栋xxx房屋");}
}

2、 代理工厂类(CglibProxyFactory)

package com.text.pattern;import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibProxyFactory implements MethodInterceptor {private Object target;//被代理的对象public CglibProxyFactory(Object target) {super();this.target = target;}//创建代理对象public Object getProxyInstance() {Enhancer en = new Enhancer();//父类en.setSuperclass(target.getClass());en.setCallback(this);//创建子类代理对象return en.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)throws Throwable {System.out.println("目标类方法执行之前功能扩展...");Object result = proxy.invokeSuper(obj, args);System.out.println("目标类方法执行之后功能扩展...");return result;}
}

3、测试类:

package com.text.pattern;//测试类
public class Customer {public static void main(String[] args) {Landlord2 landlord2 = new Landlord2();//被代理对象CglibProxyFactory proxyFactory = new CglibProxyFactory(landlord2);//生成代理对象Landlord2 proxyObj = (Landlord2)proxyFactory.getProxyInstance();proxyObj.rental();}
}

4、运行结果:

从运行结果看出,代理对象对房东2出租方法实现了功能扩展。

示例4:观察Spring IOC容器中代理对象详情

代码如下:

1、学生DAO接口、DAO实现类、Service接口、Service实现类、Controller类

package com.text.dao;public interface StudentDao {void getById(String id) throws Exception;
}
package com.text.dao.impl;import com.text.dao.StudentDao;
import org.springframework.stereotype.Repository;@Repository
public class StudentDaoImpl implements StudentDao {@Overridepublic void getById(String id) throws Exception {Thread.sleep(1000);System.out.println("查询学生id=" + id + "的信息");}
}
package com.text.service;import com.text.entity.Student;public interface StudentService {public void save(Student student);public void deleteById(String id);public void updateById(String id) throws Exception;public Student searchById(String id) throws Exception;
}
package com.text.service.impl;import com.text.dao.StudentDao;
import com.text.entity.Course;
import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class StudentServiceImpl implements StudentService {@Resourceprivate StudentDao studentDao;public StudentDao getStudentDao() {return studentDao;}public void setStudentDao(StudentDao studentDao) {this.studentDao = studentDao;}@Overridepublic void save(Student student) {System.out.println(student + "正在被保存...");}@Overridepublic void deleteById(String id) {System.out.println("学生id=" + id + "的记录已被删除...");}@Overridepublic void updateById(String id) throws Exception{System.out.println("学生id=" + id + "的记录正在被修改...");throw new Exception("修改学生信息出异常");}@Overridepublic Student searchById(String id) throws Exception {System.out.println("已查询到学生id=" + id + "的记录...");Student student = new Student("张三",20,new Course("计算机"));return student;}
}
package com.text.controller;import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.stereotype.Controller;import javax.annotation.Resource;@Controller
public class StudentController {@Resourceprivate StudentService studentService;public StudentService getStudentService() {return studentService;}public void setStudentService(StudentService studentService) {this.studentService = studentService;}public Student searchById(String id) throws Exception {return this.studentService.searchById(id);}}

2、切面类 ,实现对com.text包及子包以“DaoImpl”结尾的类的所有方法和com.text包及子包以“Controller”结尾的类的所有方法的环绕通知

package com.text.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.util.Date;/*** 定义方法切面类*/
@EnableAspectJAutoProxy //开启AspectJ的注解方式
@Component
@Aspect //标识为切面类
public class MethodAspect {//配置环绕通知@Around("execution(public * com.text..*DaoImpl.*(..)) || execution(public * com.text..*Controller.*(..)) ")public void countMethodInvokeTime(ProceedingJoinPoint proceedingJoinPoint) {System.out.println("目标方法执行之前记录初始时间...");Date startTime = new Date();try {proceedingJoinPoint.proceed();//执行目标方法 即:StudentDaoImpl.getById方法System.out.println("目标方法执行之后记录结束时间...");String methodName = proceedingJoinPoint.getTarget().getClass().getName() + "." +proceedingJoinPoint.getSignature().getName();Date endTime = new Date();System.out.println(methodName + "方法执行总时长为:" + (endTime.getTime() - startTime.getTime()) + "毫秒");} catch (Throwable throwable) {throwable.printStackTrace();}}
}

3、测试类

package com.text;import com.text.controller.StudentController;
import com.text.dao.StudentDao;
import com.text.service.impl.StudentServiceImpl;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Application {public static void main(String[] args) throws Exception {ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");//jdk动态代理对象StudentDao studentDao = context.getBean("studentDaoImpl", StudentDao.class);//代理对象Object target = ((Advised)studentDao).getTargetSource().getTarget();//方式1:获取被代理的原始对象Object singletonTarget = AopProxyUtils.getSingletonTarget(studentDao);//方式2:获取被代理的原始对象StudentServiceImpl studentService = context.getBean("studentServiceImpl", StudentServiceImpl.class);System.out.println("2种方式获取的被代理对象是否一致:" + (target == singletonTarget));//CGLib代理对象StudentController studentController = context.getBean("studentController", StudentController.class);//controller代理对象studentController.searchById("1");Object controllerTarget = AopProxyUtils.getSingletonTarget(studentController);//获取被代理的原始对象}
}

4、运行结果及分析

5、程序Debug过程中的对象详情

从Debug信息可以看出:

  • excution表达式包含的类,通过ApplicationContext.getBean方法获取的对象都是代理对象(studentDao和studentController对象),其中studentDao 实现了接口,所以是jdk的动态代理对象,studentController没有实现接口,是CGLib组件生成的代理对象。没有被excution表达式包含的类,如studentService对象,ApplicationContext.getBean方法获取的对象就是原始类型的对象
  • 通过Advised.getTargetSource().getTarget()和AopProxyUtils.getSingletonTarget都可以获取被代理的目标对象,从程序看出,被代理的目标对象都是原始类型,并且被代理对象是同一个,内存地址都相同

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

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

相关文章

HDFS_API文件详情查看

代码: private FileSystem fs;Beforepublic void init() throws URISyntaxException, IOException {URI uri new URI("hdfs://master:9000");// 创建一个配置文件Configuration entries new Configuration();// 获取到了客户端对象 // entries.…

科研绘图系列:R语言分组堆积图(stacked barplot)

文章目录 介绍加载R包导入数据数据预处理画图导出数据系统信息介绍 堆积图是一种数据可视化图表,它通过将不同类别的数据以堆叠的形式展现在同一个图表中,来展示各个类别之间的相对大小和它们之间的总和。堆积图可以是柱状图、条形图或面积图的形式,其中每个堆叠的块或区域…

【设计模式-观察者模式】

定义 观察者模式(Observer Pattern)是一种行为型设计模式,用于定义一对多的依赖关系,让多个观察者对象同时监听某一个主题对象(被观察者)的状态变化。当主题状态发生变化时,所有依赖于它的观察…

从更底层的角度理解网站的访问过程

文章目录 1.示例,访问www.baidu.com是如何返回数据的1.输入www.baidu.com回车2.检查本机的C:\Windows\System32\drivers\etc\hosts配置文件夹下有没有这个域名对应的映射: 1.示例,访问www.baidu.com是如何返回数据的 1.输入www.baidu.com回车…

C++之STL—stack栈 queue队列

栈stack,先进后出 * 入栈 --- push * 出栈 --- pop * 返回栈顶 --- top * 判断栈是否为空 --- empty * 返回栈大小 --- size 队列queue,先进先出 - 入队 --- push - 出队 --- pop - 返回队头元素 --- front - 返回队尾元素 --- back -…

EasyCVR全方位安全守护智慧电厂:构建高效视频监控系统优势分析

随着信息技术的飞速发展和数字化时代的到来,电厂作为能源供应的重要枢纽,其安全性和管理效率成为社会各界关注的焦点。为了满足电厂对高效、智能、可靠视频监控系统的需求,基于EasyCVR平台建设的电厂视频监控系统应运而生。 一、系统构成 基…

每日论文1——应用于65nm CMOS锁相环完全电流匹配的电荷泵

《A Charge Pump with Perfect Current Matching Applied to Phase-Locked Loop in 65nm CMOS》2021 IEEE 14th International Conference on ASIC 电荷泵PLL的结构框图如图,其中CP的充放电电流不匹配会引起PLL的频率误差和杂散。 传统的电荷泵结构在输出处的电平…

【关联规则Apriori】【算法】【商务智能方法与应用】课程

探索Apriori算法:数据挖掘中的频繁项集与关联规则 在当今数据驱动的世界中,数据挖掘技术正变得越来越重要。今天,我们将通过一个实际案例,了解并应用Apriori算法,这是一种广泛用于发现频繁项集及其关联规则的算法&…

适合二开的web组态软件

技术文档 官网网站:http://www.hcy-soft.com 体验地址:by组态[web组态插件] 可以广泛应用于化工、石化、制药、冶金、建材、市政、环保、电力等几十个行业。 一、产品简介 BY组态是完全自主研发的集实时数据展示、动态交互等一体的全功能可视化平台。帮…

LeetCode 每日一题 ---- 【2207. 字符串中最多数目的子序列】

LeetCode 每日一题 ---- 【2207. 字符串中最多数目的子序列】 2207.字符串中最多数目的子序列方法:贪心 一次遍历 2207.字符串中最多数目的子序列 方法:贪心 一次遍历 从题意中可以看出来,对于 pattern.charAt(0) 一定是插入到最左侧是最优…

记一次Mac 匪夷所思终端常用网络命令恢复记录

一天莫名奇妙发现ping dig 等基础命令都无法正常使用。还好能浏览器能正常访问&#xff0c;&#xff0c;&#xff0c;&#xff0c; 赶紧拿baidu试试^-^ ; <<>> DiG 9.10.6 <<>> baidu.com ;; global options: cmd ;; connection timed out; no serve…

Mysql——初识Mysql

目录 数据库基础 创建数据库 服务器&#xff0c;数据库&#xff0c;表关系 数据逻辑存储 MySQL架构 SQL分类 存储引擎 mysql服务端是一个网络服务器&#xff0c;采用的是TCP协议在应用层 &#xff0c;mysql有自己的协议。 数据库基础 mysql不是数据库&#xff0c;是mysql的…

信息安全工程师(16)密码学概况

前言 密码学是研究编制密码和破译密码的技术科学&#xff0c;它涵盖了加密技术和解密技术的各个方面&#xff0c;是现代信息安全的核心组成部分。 一、定义与基本概念 定义&#xff1a;密码学是研究如何隐密地传递信息的学科&#xff0c;主要涉及保密通信和数字签名两个方面。它…

【工具】语音朗读PDF的免费工具

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 背景介绍 看累了&#xff0c;不想看&#xff0c;能不能读给我听&#xff01; 工具介绍 Natural Readers Free Text to Speech Online with Realistic…

Ubuntu 16.04安装填坑记录

一. 问题描述&#xff1a; &#xff08;1&#xff09;Ubuntu 16.04使用USB启动盘安装时&#xff0c;出现"try ubuntu without installation"或“install ubuntu”选择&#xff0c;Enter选择安装后&#xff0c;显示器黑屏无任何显示。 原因分析&#xff1a; 显示黑…

Python电能质量扰动信号分类(一)基于LSTM模型的一维信号分类

引言 本文基于Python仿真的电能质量扰动信号&#xff0c;先经过数据预处理进行数据集的制作和加载&#xff0c;然后通过Pytorch实现LSTM模型对扰动信号的分类。Python仿真电能质量扰动信号的详细介绍可以参考下文&#xff08;文末附10分类数据集&#xff09;&#xff1a; Pyth…

8.11Zero Crossing Detection (零交叉检测)

基本概念 零交叉检测是一种基于二阶导数的边缘检测方法&#xff0c;它通过查找二阶导数过零点来定位边缘。 注意: OpenCV没有直接提供这种检测方法&#xff0c;但可以通过结合其他函数来实现。 在OpenCV中&#xff0c;基于C的Zero Crossing Detection&#xff08;零交叉检测&…

项目第一弹:RabbitMQ介绍

RabbitMQ介绍 一、前言1. 回顾生产者消费者模型2.忙闲不均与负载均衡3.改造线程池使其支持负载均衡4.MQ的引入 二、MQ的介绍1.应用/模块解耦&#xff0c;且提高容错性2.异步处理3.流量削峰填谷4.分布式事务1.两阶段提交协议&#xff08;2PC协议&#xff09;2.事务消息&#xff…

《动手学深度学习》笔记2.1——神经网络从基础→进阶 (层和块 - 自定义块)

目录 0. 前言 原书正文&#xff08;第五章&#xff09; 第五章 - 第一节 - 层和块 - 自定义块 1. Sequential() PyTorch高级API 2. MLP() 无传入参数 3. MySequential() 传入任意层(块) 4. FixedHiddenMLP() 无传入参数-固定隐藏层 5. NestMLP() 传入嵌套块-多次嵌套 …

【目标检测】隐翅虫数据集386张VOC+YOLO

隐翅虫数据集&#xff1a;图片来自网页爬虫&#xff0c;删除重复项后整理标注而成 数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;386 标注…