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.rhkb.cn/news/431829.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法】贪心+堆排序实现大根堆及标准库容器类的融合使用

📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 CSDN🙉 📢未来很长&#…

git push错误:Out of memory, malloc failed (tried toallocate 947912704 bytes)

目录 一、错误截图 二、解决办法 一、错误截图 因项目文件过大,http.postBuffer设置的内存不够,所以报错。 二、解决办法 打开cmd窗口,执行如下命令即可 git config --global http.postBuffer 1024000000 如图所示 执行完成以后&#…

NCNN 源码(1)-模型加载-数据预处理-模型推理

参考 ncnn 第一个版本的代码。 0 整体流程 demo:squeezenet ncnn 自带的一个经典 demo:squeezenet 的代码: // 网络加载 ncnn::Net squeezenet; squeezenet.load_param("squeezenet_v1.1.param"); squeezenet.load_model("squeezenet_…

RIFormer:保持你的视觉主干有效但移除令牌混合器

摘要 https://arxiv.org/pdf/2304.05659 本文研究了如何在去除其基本构建块中的标记混合器(token mixers)的同时保持视觉主干的有效性。标记混合器作为视觉变换器(Vision Transformers, ViTs)的自注意力机制,旨在实现…

【排序算法】选择排序、堆排序

文章目录 选择排序选择排序的概念选择排序的基本步骤:选择排序的特点选择排序的代码实现(C语言) 选择排序-优化双向选择排序的步骤 堆堆的基本概念堆排序详细步骤堆排序代码讲解 选择排序 选择排序的概念 选择排序是一种简单直观的排序算法。…

SpringBoot项目编译运行成功,但有些包名类名仍然下划线标红的解决方法 | Idea

目录 问题解决方案:方法一:方法二【我用这个成功的】 问题 如图,成功运行但有些包名类名仍然下划线标红,强迫症抓狂 成功运行: 有些包导入标红: 解决方案: 方法一: 点击fil…

分布式框架 - ZooKeeper

一、什么是微服务架构 1、单体架构 顾名思义一个软件系统只部署在一台服务器上。 ​ 在高并发场景中,比如电商项目,单台服务器往往难以支撑短时间内的大量请求,聪明的架构师想出了一个办法提高并发量:一台服务器不够就加一台&am…

整合SpringSecurity框架经典报错

报错描述Description: Field userDetailsService in com.atguigu.security.config.WebSecurityConfig required a bean of type org.springframe 这是整合SpringSecurity权限认证中经常出现的一个问题,由于SpringSecurity中这个UserDetailsService未找到 解决方案…

【线程】线程的同步---生产消费者模型

本文重点:理解条件变量和生产者消费者模型 同步是在保证数据安全的情况下,让我们的线程访问资源具有一定的顺序性 条件变量cond 当一个线程互斥地访问某个变量时,它可能发现在其它线程改变状态之前,它什么也做不了,…

Java 微服务框架 HP-SOA v1.1.4

HP-SOA 功能完备,简单易用,高度可扩展的Java微服务框架。 项目主页 : https://www.oschina.net/p/hp-soa下载地址 : https://github.com/ldcsaa/hp-soa开发文档 : https://gitee.com/ldcsaa/hp-soa/blob/master/README.mdQQ Group: 44636872, 66390394…

ctf.show---->re2

做题笔记。 下载 查壳 32 ida打开。 WSL先运行一下&#xff1a; &#xff1f; 创建呗。 函数如下&#xff1a; 逻辑很清晰&#xff0c;写脚本咯 &#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h>int main() {char encode[] &qu…

TCP网络编程概述、相关函数、及实现超详解

文章目录 TCP网络编程概述1. TCP协议的特点2. TCP与UDP的差异3. TCP编程流程 TCP网络编程相关函数详解1. socket()&#xff1a;创建套接字参数说明&#xff1a;返回值&#xff1a;示例&#xff1a; 2. connect()&#xff1a;客户端连接服务器参数说明&#xff1a;返回值&#x…

IDEA去除掉虚线,波浪线,和下划线实线的方法

初次安装使用IDEA&#xff0c;总是能看到导入代码后&#xff0c;出现很多的波浪线&#xff0c;下划线和虚线&#xff0c;这是IDEA给我们的一些提示和警告&#xff0c;但是有时候我们并不需要&#xff0c;反而会让人看着很不爽&#xff0c;这里简单记录一下自己的调整方法&#…

Ubunt系统设置NVIDIA显卡性能模式

文章目录 前言一、了解自己的显卡1、输入nvidia-smi指令搞清楚表中的含义 二、通过英伟达官方设置进行修改1、此时的状态3、改完后变为P0 总结 前言 工欲善其事&#xff0c;那性能直接拉满&#xff0c;宁可累坏显卡&#xff0c;也不能影响自己&#xff0c;首先了解自己的显卡以…

OpenAPI鉴权(二)jwt鉴权

一、思路 前端调用后端可以使用jwt鉴权&#xff1b;调用三方接口也可以使用jwt鉴权。对接多个三方则与每个third parth都约定一套token规则&#xff0c;因为如果使用同一套token&#xff0c;token串用可能造成权限越界问题&#xff0c;且payload交叉业务不够清晰。下面的demo包…

uni-app页面调用接口和路由(四)

文章目录 一、路由二、页面调用接口二、路由跳转1.uni.navigateTo(OBJECT)2.uni.redirectTo(OBJECT)3.uni.reLaunch(OBJECT)4.uni.switchTab(OBJECT)5.uni.navigateBack(OBJECT) 总结 一、路由 路由配置 uni-app页面路由为框架统一管理&#xff0c;开发者需要在pages.json里配…

iOS六大设计原则设计模式

六大设计原则&#xff1a; 一、单一职责原则 一个类或者模块只负责完成一个职责或者功能。 类似于&#xff1a;UIView 和 CALayer 二、开放封闭原则 对扩展开放&#xff0c;对修改封闭。 我们要尽量通过扩展软件实体来解决需求变化&#xff0c;而不是通过修改已有的代码来…

ESP32-WROOM-32 [创建AP站点-客户端-TCP透传]

简介 基于ESP32-WROOM-32 开篇(刚买)&#xff0c; 本篇讲的是基于固件 ESP32-WROOM-32-AT-V3.4.0.0&#xff08;内含用户指南, 有AT指令说明&#xff09;的TCP透传设置与使用 设备连接 TTL转USB线, 接ESP32 板 的 GND&#xff0c;RX2&#xff0c; TX2 指令介绍 注意,下面指…

Facebook Marketplace无法使用的原因及解决方案

Facebook Marketplace是一项广受欢迎的买卖平台&#xff0c;然而&#xff0c;有时候用户可能会遇到无法访问或使用该功能的问题。通常&#xff0c;这些问题可以归结为以下几类原因&#xff1a; 地理位置限制&#xff1a; Facebook Marketplace并非在全球每个地区都可用。在某些…

【C++】C++中如何处理多返回值

十四、C中如何处理多返回值 本部分也是碎碎念&#xff0c;因为这些点都是很小的点&#xff0c;构不成一篇文章&#xff0c;所以本篇就是想到哪个点就写哪个点。 1、C中如何处理多个返回值 写过python的同学都知道&#xff0c;当你写一个函数的返回时&#xff0c;那是你想返回…