范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

让我们来揭开AOP和动态代理技术的神秘面纱

  AOP 是什么东西
  首先来说 AOP 并不是 Spring 框架的核心技术之一,AOP 全称 Aspect Orient Programming,即面向切面的编程。其要解决的问题就是在不改变源代码的情况下,实现对逻辑功能的修改。常用的场景包括记录日志、异常处理、性能监控、安全控制(例如拦截器)等,总结起来就是,凡是想对当前功能做变更,但是又不想修改源代码的情况下,都可以考虑是否可以用 AOP 实现。
  为什么要面向切面呢,我直接改源代码不是很好吗?当然没有问题,如果情况允许。但是考虑到下面这些情况,我本来写好了1000个方法,有一天,我想加入一些控制,我想在执行方法逻辑之前,检查一些系统参数,参数检查没问题再执行逻辑,否则不执行。这种情况怎么办呢,难道要修改这1000个方法吗,那简直就是灾难。还有,有些线上逻辑执行缓慢,但我又不想重新部署环境,因为那样会影响线上业务,这种情况下,也可以考虑 AOP 方式,Btrace 就是这样一个线上性能排查的神器。Spring AOP 的用法
  面向切面编程,名字好像很炫酷,但是使用方式已经被 Spring 封装的非常简单,只需要简单的配置即可实现。使用方式不是本文介绍的重点,下面仅演示最简单最基础的使用,实现对调用的方法进行耗时计算,并打印出来。
  环境说明: JDK 1.8 ,Spring mvc 版本 4.3.2.RELEASE
  1、首先引用 Spring mvc 相关的 maven 包,太多了,就不列了,只列出 Spring-aop 相关的     org.springframework     spring-aop      4.3.2.RELEASE        org.aspectj     aspectjrt     1.8.9       org.aspectj     aspectjweaver     1.8.9 
  2、在 Spring mvc 配置文件中增加关于 AOP 的配置,内容如下:<?xml version="1.0" encoding="UTF-8"?>                     
  3、创建切面类,并在 kite.lab.spring.service 包下的方法设置切面,使用 @Around 注解监控,实现执行时间的计算并输出,内容如下:import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import org.springframework.util.StopWatch;  @Component @Aspect public class PerformanceMonitor {          //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点     @Pointcut("execution(* kite.lab.spring.service..*(..))")     public void aspect(){ }      @Around("aspect()")     public Object methodTime(ProceedingJoinPoint pjp) throws Throwable {         StopWatch stopWatch = new StopWatch();         stopWatch.start();         // 开始         Object retVal = pjp.proceed();         stopWatch.stop();         // 结束         System.out.println(String.format("方法 %s 耗时 %s ms!",pjp.getSignature().toShortString(), stopWatch.getTotalTimeMillis()));         return retVal;     } }
  4、被切面监控的类定义如下:package kite.lab.spring.service;  public class Worker {     public String dowork(){         System.out.println("生活向来不易,我正在工作!");         return "";     } }
  5、加载 Spring mvc 配置文件,并调用 Worker 类的方法public static void main(String[] args) {         String filePath = "spring-servlet.xml";          ApplicationContext ac = new FileSystemXmlApplicationContext(filePath);         Worker worker = (Worker) ac.getBean("worker");         worker.dowork();     }
  6、显示结果如下:
  用法介绍完了,让我们脱下 AOP 华丽的衣衫,不,是揭下她神秘的面罩,来看一看她的芳容,了解一下她的内心。Spring AOP 原理
  AOP 的实现原理就是动态的生成代理类,代理类的执行过程为:执行我们增加的代码(例如方法日志记录)—-> 回调原方法 ——> 增加的代码逻辑。看图比较好理解:
  Spring AOP 动态代理可能采用 JDK 动态代理或 CGlib 动态生成代理类两种方式中的一种, 决定用哪一种方式的判断标准就是被切面的类是否有其实现的接口,如果有对应的接口,则采用 JDK 动态代理,否则采用 CGlib 字节码生成机制动态代理方式。
  代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。代理类和委托类实现相同的接口,所以调用者调用代理类和调用委托类几乎感觉不到差别。
  是不是看完了定义,感觉正好可以解决切面编程方式要解决的问题。下图是基本的静态代理模式图:
  而动态代理的意思是运行时动态生成代理实现类,由于 JVM 的机制,需要直接操作字节码,生成新的字节码文件,也就是 .class 文件。JDK 动态代理
  JDK 动态代理模式采用 sun 的 ProxyGenerator 的字节码框架。要说明的是,只有实现了接口的类才能使用 JDK 动态代理技术,实现起来也比较简单。
  1、只要实现 InvocationHandler 接口,并覆写 invoke方法即可。具体实现代码如下:package kite.lab.spring.aop.jdkaop;  import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;  /**  * JdkProxy  *  * @author fengzheng  */ public class JdkProxy implements InvocationHandler {      private Object target;      /**      * 绑定委托对象并返回一个代理类      *      * @param target      * @return      */     public Object bind(Object target) {         this.target = target;         //取得代理对象         return Proxy.newProxyInstance(target.getClass().getClassLoader(),                 target.getClass().getInterfaces(), this);     }       /**      * 调用方法      */     @Override     public Object invoke(Object proxy, Method method, Object[] args)             throws Throwable {         Object result = null;         System.out.println("事物开始");         //执行方法         result = method.invoke(target, args);         System.out.println("事物结束");         return result;     } }
  Proxy.newProxyInstance 方法用于动态生成实际生成的代理类,三个参数依次为被代理类的类加载器、被代理类所实现的接口和当前代理拦截器。
  覆写的 invoke 中可以加入我们增加的业务逻辑,然后回调原方法。
  2、被代理的类仍然用的前面 spring aop 介绍的那个worker 类,只不过我们需要让这个类实现自接口,接口定义如下:package kite.lab.spring.service;  /**  * IWorker  * **/ public interface IWorker {     String dowork(); }
  3、实际调用如下: public static void main(String[] args) {         JdkProxy jdkProxy = new JdkProxy();         IWorker worker = (IWorker) jdkProxy.bind(new Worker());         worker.dowork(); }
  原理说明: jdkProxy.bind 会生成一个实际的代理类,这个生成过程是利用的字节码生成技术,生成的代理类实现了IWorker 接口,我们调用这个代理类的 dowork 方法的时候,实际在代理类中是调用了 JdkProxy (也就是我们实现的这个代理拦截器)的 invoke 方法,接着执行我们实现的 invoke 方法,也就执行了我们加入的逻辑,从而实现了切面编程的需求。
  我们把动态生成的代理类字节码文件反编译一下,也就明白了。由于代码较长,只摘出相关部分。
  首先看到类的接口和继承关系:public final class $Proxy0 extends Proxy implements IWorker
  代理类被命名为 $Proxy0 ,继承了 Proxy ,并且实现了IWorker ,这是关键点。
  找到 dowork 方法,代码如下:public final String dowork() throws  {         try {             return (String)super.h.invoke(this, m3, (Object[])null);         } catch (RuntimeException | Error var2) {             throw var2;         } catch (Throwable var3) {             throw new UndeclaredThrowableException(var3);         }     }
  super.h 就是我们实现的JdkProxy 这个类,可以看到调用了这个类的 invoke 方法,并且传入了参数 m3 ,再来看 m3 是什么,m3 = Class.forName("kite.lab.spring.service.IWorker")                .getMethod("dowork", new Class0);
  看到了吧,m3 就是 dowork 方法,是不是流程就明确了。
  但是,并不是所有的被代理的类(要被切面的类)都实现了某个接口,没有实现接口的情况下,JDK 动态代理就不行了,这时候就要用到 CGlib 字节码框架了。CGLIB 动态代理
  CGlib库使用了ASM这一个轻量但高性能的字节码操作框架来转化字节码,它可以在运行时基于一个类动态生成它的子类。厉害了吧,不管有没有接口,凡是类都可以被继承,拥有这样的特点,原则上来说,它可以对任何类进行代码拦截,从而达到切面编程的目的。
  CGlib 不需要我们非常了解字节码文件(.class 文件)的格式,通过简单的 API 即可实现字节码操作。
  基于这样的特点,CGlib 被广泛用于如 Spring AOP 等基于 代理模式的AOP框架中。
  下面就基于 CGlib 实现一个简单的动态代理模式。
  1、创建拦截类实现 MethodInterceptor接口,并覆intercept 方法,在此方法中加入我们增加的逻辑,代码如下:public class MyAopWithCGlib implements MethodInterceptor {     @Override     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {         System.out.println("嘿,你在干嘛?");         methodProxy.invokeSuper(o, objects);         System.out.println("是的,你说的没错。");         return null;     }
  2、被代理的类依然是上面的 Worker 类,并且不需要接口。
  3、客户端调用代理方法的代码如下: public static void main(String[] args) {         System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib");         MyAopWithCGlib aop = new MyAopWithCGlib();         Enhancer enhancer = new Enhancer();         enhancer.setSuperclass(Worker.class);         enhancer.setCallback(aop);         Worker worker = (Worker) enhancer.create();         worker.dowork();     }
  代码第一行是要将动态生成的字节码文件持久化到磁盘,方便反编译观察。
  利用 CGlib 的 Enhancer 对象,设置它的继承父类,设置回调类,即上面实现的 拦截类,然后用create 方法创造一个 Worker 类,实际这个类是 Worker 类的子类,然后调用dowork方法。执行结果如下:
  可以看到我们织入的代码起作用了。
  4、上面功能比较简单,它会横向切入被代理类的所有方法中,下来我们稍微做的复杂一点。控制一下,让有些方法被织入代码,有些不被织入代码,模仿 Spring aop ,我们新增一个注解,用于注解哪些方法要被横向切入。注解如下:package kite.lab.spring.aop.AopWithCGlib;  import java.lang.annotation.*;  /**  * CGLIB  *  * @author fengzheng  */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface CGLIB {     String value() default ""; }
  5、然后再 Worker 中增加一个方法,并应用上面的注解package kite.lab.spring.service;  import kite.lab.spring.aop.AopWithCGlib.CGLIB;  /**  * Worker  *  * @author fengzheng  */ public class Worker  {     public String dowork(){         System.out.println("生活向来不易,我正在工作!");         return "";     }      @CGLIB(value = "cglib")     public void dowork2(){         System.out.println("生活如此艰难,我在奔命!");     } }
  我们在 dowrok2 上应用了上面的注解
  6、在拦截方法中加入注解判断逻辑,如果加了上面的注解,就加入织入的代码逻辑,否则不加入,代码如下:@Override     public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {         Annotation[] annotations = method.getDeclaredAnnotations();         boolean isCglib = false;         for(Annotation annotation: annotations){             if (annotation.annotationType().getName().equals("kite.lab.spring.aop.AopWithCGlib.CGLIB")){                 isCglib = true;             }         }         if(isCglib) {             System.out.println("嘿,你在干嘛?");             methodProxy.invokeSuper(o, objects);             System.out.println("是的,你说的没错。");         }         return null;     }
  7、调用方法如下:public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib");         MyAopWithCGlib aop = new MyAopWithCGlib();         Enhancer enhancer = new Enhancer();         enhancer.setSuperclass(Worker.class);         enhancer.setCallback(aop);         Worker worker = (Worker) enhancer.create();         worker.dowork();          worker.dowork2();     }
  执行结果应该为 dowork 不执行被织入的逻辑,dowork2 执行被织入的代码逻辑,执行结果如下:
  另外说一下,CGlib 不支持 final 类, CGlib 的执行速度比较快,但是创建速度比较慢,所以如果两种动态代理都适用的场景下,有大量动态代理类创建的场景下,用 JDK 动态代理模式,否则可以用 CGlib 。
  标准的 Spring MVC 框架,一般都是一个服务接口类对应一个实现类,所以根据Spring AOP 的判断逻辑,应该大部分情况下都是使用的 JDK 动态代理模式。当然也可以手动改成 CGlib 模式。最后
  掌握 AOP 的实现原理,你就掌握了又一个核心科技。回过头一看,害,也挺简单。
  作者:古时的风筝
  链接:https://juejin.im/post/5ed723606fb9a047a07f2ec2

苹果ios15。4beta新增音乐TV同播共享按钮等今日苹果推送了iOS15。4Beta系统,为iPhone带来了多项新功能,正式支持戴口罩进行FaceID面部解锁。网友发现,新版中文版系统为AppleMusicTV应用带来了同播共视频号出走张小龙文新熵,作者嘉荣,编辑伊页1997年,BAT还可能只是软盘里的一行代码。那一年,中国人口数量为12。36亿,而能上网的计算机仅29。9万台,合下来每四千多人才拥有一台。也是在那一年新能源汽车养护小知识随着越来越多的人选择电动汽车,关于电动汽车的保养问题也越来越多的受到大家的关注,下面就为大家科普一下有关电动汽车的保养知识。一电池养护1正确把握电池的充电时间和频次。新能源汽车怎么微博拟上线一键隔离网络暴力模式金融界1月28日消息据微博管理员,微博拟上线一键隔离网络暴力模式,加强人身攻击不友善言论识别处置,优化新闻当事人保护,确保用户安全。如大家在平台上发现类似违规行为,欢迎通过前台投诉三大运营商降无可降,提速降费是不是要到头了?提起移动联通电信这三大运营商,相信很多人对于它们都没有什么好印象,毕竟很多人每个月的花费和网费都是非常高的,不过大家有没有考虑过一个问题,那就是我国的通行费用真的高吗?2022年,短视频神曲持续霸榜,华语乐坛回到彩铃时代?作者黄启哲岁末年初,各类音乐榜单陆续推出。打开热歌榜,上面的歌手歌名大多令人一头雾水。可点进去一听,网友感慨竟然都有印象,甚至还能哼唱出个别歌曲的副歌旋律。这些音乐基本走红于短视频提问标题北汽新能源电车召回后被限制的充电速度严重影响使用,315居然管不了提问标题北汽新能源电车召回后被限制的充电速度严重影响使用,315居然管不了提问时间20220127144951提问内容2018年购入北汽新能源EX360车型此车正常的充电情况是夏天比亚迪半导体过会今年IPO过关第26家中金公司过2单来源中国经济网中国经济网北京1月28日讯创业板上市委员会2022年第5次审议会议于昨日召开,审议结果显示,比亚迪半导体股份有限公司(简称比亚迪半导体)首发符合发行条件上市条件和信息换电的终局作者李大鹏出品电动公会2007年,一家名为BetterPlace的电动汽车换电公司在以色列成立。一年后,BetterPlace斥巨资打造的首座换电站落地,并与雷诺公司签订了提供10小米首发2亿像素镜头?联想再次截胡三星在去年的时候就发布了2亿像素的ISOCELLHP1摄像头传感器,但目前为止市场上仍然没有一款手机采用,早先的传闻是该镜头将会由小米的旗舰新机首发搭载,不过据最新的消息显示,小米传跨境支付PingPong正考虑港股上市,估值约50亿美元据彭博社报道,中国金融科技初创公司乒乓智能技术有限公司(PingPong)正在考虑在香港IPO,筹资多达10亿美元。报导引述消息人士称,PingPong正与顾问商讨上市事宜,最快可
百元喷墨打印机怎么选,多款喷墨家用打印机教你选择高性价比机型前言大家好,我是小猴。喷墨打印机以其价格便宜,耗材成本低的优势,在目前家用打印机市场中占有重要地位。目前市面上大多数家用喷墨打印机都在百元区间,那么后期的耗材使用成本如何呢?下面就国潮经典品牌,西安人带你了解你所不知道的冰峰前言大家好,我是小猴。随着炎炎夏日来临。想必大家每天都会喝上一瓶冰镇凉爽的饮料。作为西安人,夏日冰峰则是必不可少的日常饮料。那么作为一款老牌儿国民饮料,你又对它了解多少呢?冰峰的来高效办公,宜商宜家,高性价比激光打印机推荐前言大家好,我是小猴。作为商用办公的必备神器,激光打印机不仅在打印速度上要远高于普通喷墨打印机,同时其打印精度也要高于大部分家用机打印机。因此对于商用办公来说,选择一台高性价比的激拯救我们的颈椎,教你如何选择颈部按摩仪四款热门颈部按摩仪前言大家好,我是小猴。现代生活工作节奏紧张,每天的生活都在拼命奔跑,只为达成自己的目标。而高负荷的工作随之带来了身体的各种疾病,典型的就是各种脊椎问题,特别是颈椎和腰椎问题都是现代威士忌?伏特加?带你揭秘洋酒背后的资本秘密之保乐力加集团前言大家好,我是小猴。经常喝酒的你经常会见到琳琅满目的各种酒水品牌,不管是葡萄酒,啤酒,威士忌还是朗姆酒,伏特加等,这些品牌不仅仅代表了不同酒水的品牌口碑,同时其背后资本也同样值得智能静音我全有,OcleanXPro旗舰版智能声波电动牙刷前言大家好,我是小猴。牙齿健康是我们身体健康的基本保证,有了好的牙口才能让我们吃嘛嘛香,身体倍棒。刷牙作为每天重要的必备环节,是保持牙齿健康的重要环节,近些年的电动牙刷为我们彻底清融资330亿元,李泽楷何超琼联袂入股,威马汽车能否迎头赶上在造车新势力的赛道上,威马汽车与蔚小理相比,名声显然要小很多,但其融资的总额却毫不逊色。10月5日,威马汽车正式宣布获得超过3亿美元D1轮融资,领投方为电讯盈科和信德集团。电讯盈科小米发布首款洗衣机众筹价1999元,你能抢到吗?如果喜欢我的回答,可以给我点赞并关注我,随时获取最新的数码科技咨询和使用技巧。12月28日消息,小米发布首款洗衣机米家互联网洗烘一体机10kg版,售价为2499元,初期众筹价为19小米Play不是游戏机是移动热点2018年即将结束,小米召开了最后的一次发布会,选在平安夜这天新推了Play这条新的产品线。他们刚刚发表的小米Play,是一款定位落在中低端的产品。它的外观还是小米今年已套用了无数小米米家投影仪青春版正式发布如果喜欢我的回答,可以给我点赞并关注我,随时获取最新的数码科技咨询和使用技巧。今天,小米商城2019年第一期众筹上线,它就是米家投影仪青春版。小米似乎对此款产品非常的有信心,一进入小米4K激光投影仪小米在1。14发布了小米激光投影仪4K版本。尝鲜价9999元。1。18抢购。同时原来的1080P版本,降价1000,到8999元。主要参数使用的是0。47DMD,目前支持4K的DM