浅谈Spring的AOP实现-代理机制

  说起Spring的AOP(Aspect-Oriented Programming)面向切面编程大家都很熟悉(Spring不是这次博文的重点),但是我先提出几个问题,看看同学们是否了解,如果了解的话可以不用继续往下读:

  1. Spring的AOP的实现方式有哪些?

  2. 为什么使用代理机制?

  3. 它们是怎么实现的?

  4. 它们的区别是什么?

  下面进入正题,Spring采用代理的方式实现AOP,具体采用了JDK的动态代理和CGLib实现。使用动态代理和CGLib的目的是在现有类的基础上增加一些功能。简单地将就是有一个Proxy类,实现了原始类的方法,并且在原始类的基础上增加了新的功能。那么这么做可以实现很多功能:

  1. 在方法前后进行日志处理。

  2. 进行额外的校验,比如参数的验证功能等。

  3. 实现一些懒加载,也就是实例化的时候如果不去调用真正的方法的时候,这个类的属性就不会存在(Hibernate有这样类似的功能)。

  下面咱们用简单的代码实现它是如何进行代理的,首先采用的是JDK的动态代理实现:

  定义一个接口:

package com.hqs.proxy;/*** 操作系统光接口* @author hqs**/
public interface OpSystem {public void work();
}

  定义一个实现类:

package com.hqs.proxy;/*** Mac 的实现* @author hqs**/
public class Mac implements OpSystem {public void work() {System.out.println("Mac is running");    }}

  关键位置来了,我们通过实现JDK自带的反射机制的包的InvocationHandler来进行反射处理,实现它之后需要实现里边的invoke方法,这个invoke方法里边的参数分别为:代理类实例,用于调用method的;method参数是实际执行的方法;args所传输的参数数组。

package com.hqs.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class OpHandler implements InvocationHandler {private final OpSystem ops; public OpHandler(OpSystem ops) {this.ops = ops;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before system running");method.invoke(ops, args);System.out.println("After system running");return null;}public static void main(String[] args) {Mac mac = new Mac();OpHandler oph = new OpHandler(mac);OpSystem os = (OpSystem)Proxy.newProxyInstance(oph.getClass().getClassLoader(),mac.getClass().getInterfaces(), oph);os.work();System.out.println(os.getClass());}}输出:
Before system running
Mac is running
After system running
class com.sun.proxy.$Proxy0

   然后看到里边的main方法中,代理类实例化对象的方法Proxy.newProxyInstance,这个是JDK的反射方法去实例化代理类,其中有三个参数分别是,去实例化代理类的class loader;所代理的类的所有接口Class数组;hander处理类,用于做拦截使用的类。最后我输出了一下os.getClass(),大家可以看到的是代理类的实例,而不是真正代理类的实例,这么做的好处就是很方便的复用这个代理类,比如你可以重复调用它而不用去重新实例化新类,再一点就是你可以针对不同的方法进行拦截,比如你可以method.getName()去判断调用的方法名字是什么从而更细粒度的拦截方法。咱们继续看用CGLib的实现:  

package com.hqs.proxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;/*** CGLib Interceptor用于方法拦截* @author hqs**/
public class CGLibInterceptor implements MethodInterceptor {private final Mac mac;public CGLibInterceptor(Mac mac) {this.mac = mac;}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("Before system running");method.invoke(mac, args);System.out.println("After system running");return null;}public static void main(String[] args) {Mac mac = new Mac(); //实例而非接口MethodInterceptor handler = new CGLibInterceptor(mac);Mac m = (Mac)Enhancer.create(mac.getClass(), handler);m.work();System.out.println(m.getClass());}}输出:
Before system running
Mac is running
After system running
class com.hqs.proxy.Mac$$EnhancerByCGLIB$$1f2c9d4a

  首先需要引入cglib包,然后才能使用他的MethodInterptor,它也采用method.invoke实现对代理类的调用。它的代理类创建采用Enhancer的create方法,其中传入了需要创建的类的class,以及Callback对象,因为MethodInterceptor继承了Callback对象。用于指向方法前后进行调用的类。  

public interface MethodInterceptor
extends Callback

  这是这两个类的基本实现,那么它们的区别是什么呢?

  1. JDK的动态代理只能针对接口和其实现类,如果没有实现类只有接口也是可以代理的,这里就不在举例了。为什么JDK的动态代理只针对接口代理,因为这个是JDK的定义。
  2. 如果不针对接口实现动态代理那就用到了CGLib了,也就是可以针对具体的类进行代理,大家可以参考我的代码。

  这些是它们的根本区别,但是Spring推荐使用JDK的动态代理,面向接口去编程。使用CGLib去做代理的时候需要注意,它生成的代理类存放在JVM的Perm space里边,那么是不是生成的代理对象就不进行回收了?其实不是的,不经常回收但是还是回收的,当类被加载,加载类的classLoader什么时候变得对垃圾回收可用的时候才进行回收。也就是你自己创建所有类移除classLoader之后,那么这个classLoader就会被回收,一般非常精通CGLib的话可以进行这块内容深入开发,因为它可以做出amzing的事情如果你熟悉的话。

  如果有不对的地方欢迎拍砖~

  

  

转载于:https://www.cnblogs.com/huangqingshi/p/7651376.html

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

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

相关文章

java读图片显示到前端_java从前端获取参数添加到图片后返回整张图片

java从前端获取参数添加到图片后返回整张图片以后为整个servlet代码,请自行加入到j2ee工程。import java.awt.Color;import java.awt.Font;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.RenderingHints;import java.awt.image.BufferedIma…

java远程调试挂起线程_java进程的远程调试

一、jdb调试mqbroker(jvm参数配置)2.利用jdb/idea/eclipse 远程调试,同本地调试一样二、jvisualvm jvm 监视使用jstatd连接nohup $JAVA_HOME/bin/jstatd -p 1099 -J-Djava.security.policy使用jmx连接(需配置jvm启动参数据)由于jvisualvm采器需要使用jmx&#xff0c…

简单图形打印(需求分解)

打印任意高度的实心菱形 菱形的打印是一个相对复杂的需求,我们需要把它分解成为多个简单的需求来完成。 将它看作是两个部分,上面一个三角形,下面一个倒三角形。 再依次实现打印。 代码如下: //打印任意高度的菱形 public class G…

机器人研究员的崛起 |《自然》论文

来源:Nature自然科研根据本周《自然》发表的一篇论文A mobile robotic chemist,汽车装配线上常见的机器人经过改造后,可以在化学实验室内和人类一起工作。该机器人和机器学习算法相连,能够在给定需要检验什么假设的情况下&#xf…

python 求最大值实例_Python 求数组局部最大值的实例

求数组局部最大值给定一个无重复元素的数组A[0…N-1],求找到一个该数组的局部最大值。规定:在数组边界外的值无穷小。即:A[0]>A[-1],A[N-1] >A[N]。显然,遍历一遍可以找到全局最大值&#xff0c…

Python+selenium第一个自动化脚本

第一个自动化脚本(用Python写的) from selenium import webdriver #从selenium导入webdriber driverwebdriber.Firefox() #打开Firefox软件 driver.get("http://baidu.com") #在Firefox的URL里输入百度,打开百度页面 driver.f…

《EE Times》评出2020年全球最值得关注的18家传感器公司

来源:仪商网每年,美国著名电子技术类杂志《EE Times》都会评选出全球值得关注的新创半导体公司排行榜。今年已轮到了第20届,相较以往,最大的变化是这一届的榜单由60家增至100家。在今年的榜单中,保留了上一届中的49家公…

webstorm配置Monokai-Sublime.jar主题

https://github.com/OtaK/jetbrains-monokai-sublime 导入下载的Monokai-Sublime.jar jar包即可使用。 转载于:https://www.cnblogs.com/sirwang/p/7657975.html

用洪小文,人工智能予力永续未来

来源: 微软研究院AI头条 二十一世纪第三个十年的开局时刻有些艰难。当许多人还在为澳大利亚森林大火和席卷东非、西亚的蝗虫灾害而担忧时,一场突如其来的疫情又在全球各国次第爆发,迄今仍未有止息迹象。事实上,被疫情困扰的不仅是…

python 通信模块_python 多进程通信模块

多进程通信方法好多,不一而数。刚才试python封装好嘅多进程通信模块 multiprocessing.connection。简单测试一下,效率还可以,应该系对socket封装,效率可以达到4krps,可以满足好多方面嘅需求啦。附代码如下:client#!/us…

关于h5中背景音乐的自动播放

音乐的自动播放属性&#xff0c;这里也介绍一下&#xff1a; <audio controls"controls" autoplay"autoplay"><source src"song.ogg" type"audio/ogg" /><source src"song.mp3" type"audio/mpeg" …

腾讯发布2020人工智能白皮书:泛在智能

来源&#xff1a;腾讯研究院7月10日&#xff0c;在世界人工智能大会腾讯论坛上&#xff0c;腾讯集团副总裁、腾讯研究院院长司晓正式发布了《腾讯人工智能白皮书&#xff1a;泛在智能》&#xff08;以下简称白皮书&#xff09;。作为腾讯第一份全面介绍AI、阐述腾讯人工智能布局…

相学java_从这两套题,重新认识JS的this、作用域、闭包、对象

日常开发中&#xff0c;我们经常用到this。例如用Jquery绑定事件时&#xff0c;this指向触发事件的DOM元素&#xff1b;编写Vue、React组件时&#xff0c;this指向组件本身。对于新手来说&#xff0c;常会用一种意会的感觉去判断this的指向。以至于当遇到复杂的函数调用时&…

洛谷1373 小a和uim之大逃离

https://www.luogu.org/problem/show?pid1373 题目背景 小a和uim来到雨林中探险。突然一阵北风吹来&#xff0c;一片乌云从北部天边急涌过来&#xff0c;还伴着一道道闪电&#xff0c;一阵阵雷声。刹那间&#xff0c;狂风大作&#xff0c;乌云布满了天空&#xff0c;紧接着豆大…

java 日期处理工具类_Java日期处理工具类DateUtils详解

本文实例为大家分享了Java日期处理工具类DateUtils的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下import java.sql.Timestamp;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Date;/*** */public…

Ipython知识点总结

初识Python&#xff1a; Python是一种面向对象、直译式计算机程序语言。也是一种功能强大而完善的通用型语言&#xff0c;已经具有十多年的发展历史&#xff0c;成熟且稳定。Python 具有脚本语言中最丰富和强大的类库&#xff0c;足以支持绝大多数日常应用。 Python语法简捷而清…

光刻技术的“鬼斧”之变

来源:脑极体 在我们今天看来&#xff0c;晶体管发明以后&#xff0c;集成电路的出现一直到今天超大规模集成电路的出现&#xff0c;似乎是一件水到渠成的事情。但是如果回到半导体产业初兴的历史现场&#xff0c;我们就会发现没有任何一项关键技术的突破是“必然产生”的。&…

php设置html全局路径_PHPCMS V9 URL去掉或修改/html路径的方法

PHPCMS V9 URL去掉或修改/html路径的方法&#xff0c;在后台找了好长时间没有找到在哪儿可以设置栏目生成的前缀路径/html,不过最终还是找到了。修改配置文件。找到\caches\configs\system.php 找到“html_root”这一项&#xff0c;然后把/html这个字符串删除即可。更新缓存&am…

[PA 2014]Kuglarz

Description 魔术师的桌子上有n个杯子排成一行&#xff0c;编号为1,2,…,n&#xff0c;其中某些杯子底下藏有一个小球&#xff0c;如果你准确地猜出是哪些杯子&#xff0c;你就可以获得奖品。花费c_ij元&#xff0c;魔术师就会告诉你杯子i,i1,…,j底下藏有球的总数的奇偶性。采…