Spring基础之AOP和代理模式

news2024/9/21 12:07:23

文章目录

    • 理解AOP
      • AOP的实现原理
    • AOP代理模式
      • 静态代理
      • 动态代理
        • 1-JDK动态代理
        • 2-CGLIB动态代理
    • 总结

理解AOP

OOP - - Object Oriented Programming 面向对象编程
AOP - - Aspect Oriented Programming 面向切面编程
AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。从而避免了在业务逻辑的代码中混入了很多的系统性相关的逻辑–比如:事务管理、日志记录、权限管理等,从而达到了将不同的关注点分离出来松耦合的效果。

AOP的实现原理

AOP的实现原理就是代理模式----其关键点是AOP框架自动创建的AOP代理
AOP代理分为静态AOP代理和动态AOP代理:

  1. 静态AOP代理 –– 是指AspectJ实现的AOP代理,它是将切面代码直接编译到Java字节码文件中,发生在编译时。
  2. 动态AOP代理 –– 是指将切面代码进行动态织入实现的AOP代理,发生在运行时。Spring的AOP即为动态AOP实现技术为:JDK提供的动态代理技术和CGLIB(Code Generation Library动态字节码增强技术)。尽管实现技术不一样,但都是基于代理模式,都是生成一个代理对象。

AOP代理模式

代理模式的核心作用就是通过代理,控制对被代理的目标对象的访问。它的设计思路是:定义一个抽象角色{接口},让代理角色和真实角色分别去实现它。

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。它只关注真正的业务逻辑。

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并在前后可以附加自己的操作。
代理模式实现歌星演出场景

静态代理

要求被代理类和代理类同时实现相应的一套接口,通过代理类调用重写接口的方法,实际上调用的是原始对象的同样的方法。如下图

在这里插入图片描述

/**
 * 明星接口
 */
public interface Star {
    void sing();
}
/**
 * 真实明星类
 */
public class RealStar implements Star{
    @Override
    public void sing() {
        System.out.println("明星本人开始唱歌...");
    }
}

/**
 * 明星静态代理类
 */
public class ProxyStar implements Star{
    private Star star; //接收明星对象

    /**
     * 通过构造方法传进来真实的明星对象
     * @param star
     */
    public ProxyStar(Star star) {
        this.star = star;
    }

    @Override
    public void sing() {
        System.out.println("明星代理先进行商务谈判...");
        //真实明星唱歌
        star.sing();
        System.out.println("明星演出完,代理收款...");
    }
}

这样的话,逻辑就非常清晰了。在代理类中,可以看到,维护了一个Star对象,通过构造方法传进来一个真实的Star对象给其赋值,
然后在唱歌这个方法里,使用真实对象来唱歌。所以说面谈、收钱都是由代理对象来实现的,唱歌是代理对象让真实对象来做。

/**
 * 测试客户端
 */
public class TestClient {
    /**
     * 测试静态代理结果
     * @param args
     */
    public static void main(String[] args) {
        Star realStar = new RealStar(); //创建真实对象
        Star proxy = new ProxyStar(realStar); //创建代理对象
        proxy.sing();

    }
}

静态代理的优缺点:
优点:
  1.被代理的业务类只需要做好自己的业务,实现了责任分离,保证了业务类的重用性
  2.将业务类隐藏起来,起到了保护作用
缺点:
  1.代理对象的某个接口只服务于某一个业务对象,每个真实对象都得创建一个代理对象
  2.如果接口增加一个方法,除了所有实现类需要实现这个方法外,代理类也需要实现,增加了代码的复杂度和成本

动态代理

在程序运行时由JVM通过反射机制动态的创建出代理对象的字节码。代理对象和真实对象的关系是在程序运行时才确定

1-JDK动态代理

JDK动态代理是使用java.lang.reflect包中的Proxy类与InvocationHandler接口来完成的,要使用JDK动态代理,必须要定义接口

API分析:
(1) java.lang.reflect.Proxy 类
  |-Java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
   public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler hanlder)
方法职责:为指定类加载器、一组接口及调用处理器  生成动态代理类实例 
参数:
   loader        :类加载器
   interfaces        :目标(真实)对象实现的接口
   hanlder        :代理执行处理器
返回:动态生成的代理对象
(2) java.lang.reflect.InvocationHandler接口:
public Object invoke(Object proxy, Method method, Object[] args)
方法职责:负责集中处理动态代理类上的所有方法调用
参数: 
    proxy :  生成的代理对象
    method: 当前调用的真实方法对象
    args:    当前调用方法的实参
返回: 真实方法的返回结果
jdk动态代理操作步骤 
 实现InvocationHandler接口,创建自己增强代码的处理器。
② 给Proxy类提供ClassLoader对象和代理接口类型数组,创建动态代理对象。
③ 在处理器中实现增强操作。

示例:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理处理类
 */
public class JdkProxyHandler implements InvocationHandler {
    /**
     * 用来接收真实明星对象
     */
    private Object realStar;

    /**
     * 传入真实明星对象,动态创建一个代理
     *
     * @param realStar
     * @return
     */
    public Object bind(Object realStar) {
        this.realStar = realStar;
        return Proxy.newProxyInstance(realStar.getClass().getClassLoader(),
                realStar.getClass().getInterfaces(), this);
    }


    /**
     * 当调用代理对象的方法时,会调用此方法
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("明星代理先进行谈判...");
        Object result = method.invoke(realStar, args); //真实对象明星唱歌
        System.out.println("演出完,代理收款...");
        return result;
    }
}
/**
 * 测试Jdk动态代理客户端
 */
public class TestJdkProxyClient {
    public static void main(String[] args) {
        Star realStar = new RealStar(); //真实对象
        //创建一个代理对象实例
        Star proxy = (Star)new JdkProxyHandler().bind(realStar);
        //代理对象调用方法
        proxy.sing();
    }
}

相对于静态代理,JDK 动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度

2-CGLIB动态代理

由上面的分析可知,JDK 实现动态代理需要实现类通过接口定义业务方法,那对于没有接口的类,如何实现动态代理呢,这就需要 CGLIB 了。

  • CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类.
  • CGLIB动态代理是针对没有接口的类进行的代理,和JDK动态代理的区别是获取代理对象的方法不一样
/**
 * 真实明星类
 */
public class RealStar{
    public void sing() {
        System.out.println("明星本人开始唱歌...");
    }
}

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * Cglib代理处理类
 */
public class CglibProxyHandler implements MethodInterceptor {
    /**
     * 维护的目标对象
     */
    private Object realStar;
    /**
     *  Enhancer类是CGLIB中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展
     */
    private Enhancer enhancer = new Enhancer();

    /**
     * 根据被代理对象的类型 ,动态创建一个代理对象
     * @param realStar
     * @return
     */
    public Object bind(Object realStar){
this.realStar = realStar;
        enhancer.setSuperclass(realStar.getClass()); //使用增强器,设置被代理对象为父类
        enhancer.setCallback(this);  //设置回调方法,拦截器
        //动态创建一个代理类对象,并返回
        return enhancer.create();
    }

    /**
     *  当调用代理对象的方法时,会调用此方法进行拦截
     * @param proxy 代理对象
     * @param method 需要增强的方法
     * @param objects 需要增强方法的参数
     * @param methodProxy  需要增强的方法的代理
     * @return 方法执行后的返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理明星先进行演出谈判...");
        //invokeSuper方法调用的对象已经是增强了
        Object result = methodProxy.invokeSuper(object,args); //明星唱歌
        System.out.println("演出完成,代理去收款...");
        return result;
    }
}

/**
 * 测试Cglib动态代理客户端
 */
public class TestCglibProxyClient {
    public static void main(String[] args) {
        RealStar realStar = new RealStar(); //真实对象
        //创建一个代理对象实例
        RealStar proxy = (RealStar) new CglibProxyHandler().bind(realStar);
        //代理对象调用方法
        proxy.sing();
    }
}

关于CGLIB 动态代理总结:

  • CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,但是CGLIB 创建代理对象时所花费的时间却比JDK多得多,所以对于单例singleton的对象,因为无需频繁创建对象,用 CGLIB 合适,反之使用JDK方式要更为合适一些。
  • CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,诸如private的方法也是不可以作为切面的。

小结
Spring AOP 中的代理使用逻辑:如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP;如果目标对象没有实现了接口,则采用 CGLIB 库,Spring 会自动在 JDK 动态代理和 CGLIB 动态代理之间转换

总结

  • AOP面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • 使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。
  • Spring的AOP为动态AOP,实现的技术为: JDK提供的动态代理技术 和 CGLIB(动态字节码增强技术)
  • 代理模式--官方的定义为“为其他对象提供一种代理以控制对这个对象的访问”
  • java中有静态代理、JDK动态代理、CGLib动态代理的方式。静态代理指的是代理类是在编译期就存在的,相反动态代理则是在程序运行期动态生成的

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

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

相关文章

SpringCloud(16)之SpringCloud OpenFeign和Ribbon

一、Spring Cloud OpenFeign介绍 Feign [feɪn] 译文 伪装。Feign是一个轻量级的Http封装工具对象,大大简化了Http请求,它的使用方法 是定义一个接口&#xff0c;然后在上面添加注解。不需要拼接URL、参数等操作。项目主页&#xff1a;GitHub - OpenFeign/feign: Feign makes w…

Android 如何添加自定义字体

Android 如何添加自定义字体 比如我要添加 jetbrains 相关字体 在 res 文件夹中添加 font 文件夹。里面放入你的字体文件 .ttf .otf&#xff0c;字体文件名需要是小写&#xff0c;只能是字母和下划线。 在 xml 布局文件中直接通过 android:fontFamily"font/jetbrainsmo…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--大模型、扩散模型

专属领域论文订阅 VX 关注{晓理紫}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 如果你感觉对你有所帮助&#xff0c;请关注我&#xff0c;每日准时为你推送最新论文。 分类: 大语言模型LLM视觉模型VLM扩散模型视觉语言…

ChatGPT/GPT4科研应用与AI绘图及论文写作

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

LeetCode 448.找到所有数组中消失的数字

目录 1.题目 2.代码及思路 3.进阶 3.1题目 3.2代码及思路 1.题目 给你一个含 n 个整数的数组 nums &#xff0c;其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字&#xff0c;并以数组的形式返回结果。 示例 1&#xff1a; 输入&am…

代码随想录算法训练营第二十七天|93.复原IP地址、78.子集、90.子集II

93.复原IP地址 刷题https://leetcode.cn/problems/restore-ip-addresses/description/文章讲解https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html视频讲解https://www.bilibili.com/video/BV1XP4y1U73i/?vd_sourceaf4853e80f89e28094a5fe1e220d9…

【Java程序设计】【C00282】基于Springboot的校园台球厅人员与设备管理系统(有论文)

基于Springboot的校园台球厅人员与设备管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的校园台球厅人员与设备管理系统 本系统分为系统功能模块、管理员功能模块以及用户功能模块。 系统功能模块&#xf…

认识IP地址:互联网通信的关键

IP地址 IPv4和IPv6 在Internet上为每台计算机指定的唯一的地址称为IP地址&#xff08;Internet Protocol Address&#xff09;&#xff0c;也称网际地址 在IPv4中&#xff0c;IP地址由32位二进制数组成&#xff0c;分为4段&#xff0c;其中每8位构成一段。为了便于识别和表达…

SpringBoot和SpringCloud的区别,使用微服务的好处和缺点

SpringBoot是一个用于快速开发单个Spring应用程序的框架&#xff0c;通过提供默认配置和约定大于配置的方式&#xff0c;快速搭建基于Spring的应用。让程序员更专注于业务逻辑的编写&#xff0c;不需要过多关注配置细节。可以看成是一种快速搭建房子的工具包&#xff0c;不用从…

Chrome插件(二)—Hello World!

本小节将指导你从头到尾创建一个基本的Chrome插件&#xff0c;你可以认为是chrome插件开发的“hello world”&#xff01; 以下详细描述了各个步骤&#xff1a; 第一步&#xff1a;设置开发环境 确保你拥有以下工具&#xff1a; 文本编辑器&#xff1a;如Visual Studio Cod…

国际贸易报关需要向海关提交哪些资料 | 全球数字贸易发展联盟 | 箱讯科技

1、进出口货物报关单。一般进口货物应填写一式二份;需要由海关核销的货物&#xff0c;如加工贸易货物和保税货物等&#xff0c;应填写专用报关单一式三份;货物出口后需国内退税的&#xff0c;应另填一份退税专用报关单。 2、货物发票。要求份数比报关单少一份&#xff0c;对货…

broom系列包: 整理模型输出结果

broom包 说明 tidy、augment和glance函数的输出总是一个小tibble。 输出从来没有行名。这确保了您可以将它与其他整洁的输出组合在一起&#xff0c;而不用担心丢失信息(因为R中的行名不能包含重复)。 有些列名保持一致&#xff0c;这样它们就可以跨不同的模型进行组合。 tidy(…

技术社区项目—利用SSE协议实现技术社区公众号扫码登录

前言 在技术社区项目中&#xff0c;用户登录是一个至关重要的环节&#xff0c;而传统的用户名密码登录方式已经无法满足用户对于便捷、安全的需求。为了提升用户体验&#xff0c;我们开发小组采用了一种基于SSE&#xff08;Server-Sent Events&#xff0c;服务器发送事件&…

常用显示屏学习——LCD12864(含高级驱动程序)

LCD12864液晶显示屏 屏幕介绍 ① 可显示四行字符&#xff0c;每行可显示8个汉字或者16个数字和字母&#xff1b; ②可串行通信和并行通信&#xff1b; ③ 串口接口管脚信号 通信方法 &#xff08;一&#xff09;八位并行通信方法 &#xff08;二&#xff09;串行通信方法 用…

Spring Session:入门案例

Spring Session provides an API and implementations for managing a user’s session information. Spring Session提供了一种用于管理用户session信息管理的API。 Spring Session特点 传统的Servlet应用中&#xff0c;Session是存储在服务端的&#xff0c;即&#xff1a;Ses…

RapidJson开源库使用

1.下载 GitHub - Tencent/rapidjson: A fast JSON parser/generator for C with both SAX/DOM style APIA fast JSON parser/generator for C with both SAX/DOM style API - Tencent/rapidjsonhttps://github.com/Tencent/rapidjson 官方使用手册&#xff1a;RapidJSON: 首页…

码上飞:免费制作自己的ChatBot

今天为大家介绍一款可以免费制作ChatBot的软件开发平台&#xff0c;码上飞CodeFlying&#xff0c;只需几步&#xff0c;即可完成ChatBot的开发&#xff0c;并且支持自定义添加自己的知识库&#xff01; 第一步&#xff1a;访问官网&#xff1a;码上飞 CodeFlying | AI 智能软件…

SpringMVC 学习(一)之 SpringMVC 介绍

目录 1 MVC 介绍 2 SpringMVC 介绍 2.1 SpringMVC 特点 2.2 SpringMVC 的核心组件 2.3 SpringMVC 执行流程 3 参考文档 1 MVC 介绍 MVC (Model View Controller) 是一种设计思想&#xff0c;它将应用程序分为三大组件&#xff1a;模型 (Model)、视图 (View)、控制器 (Con…

群晖NAS DSM7.2.1安装宝塔之后无法登陆账号密码问题解决

宝塔的安装就不在这赘述了&#xff0c;只说下&#xff0c;启动之后默认账号密码无法登陆的问题。 按照上面给出的账号密码&#xff0c;无法登陆 然后点忘记密码&#xff0c;由于是docker安装的&#xff0c;根目录下没有/www/server/panel 。 也没有bt命令 要怎么修改呢。 既然…

go使用trpc案例

1.go下载trpc go install trpc.group/trpc-go/trpc-cmdline/trpclatest 有报错的话尝试配置一些代理&#xff08;选一个&#xff09; go env -w GOPROXYhttps://goproxy.cn,direct go env -w GOPROXYhttps://goproxy.io,direct go env -w GOPROXYhttps://goproxy.baidu.com/…