`
java-mans
  • 浏览: 11422119 次
文章分类
社区版块
存档分类
最新评论

动态非侵入拦截

 
阅读更多

动态非侵入拦截
什么叫无侵入拦截?
在JAVA中要拦截一个方法调用,有多种方式,最容易也是最流行的就是动态代理。
动态代理方式实现起来简单,你只要提供一个接口和拦截处理的handler并在invoke中提供要拉截的方法调用时的附件操作,
然后所有对需要拦截的方法所在的对象都由代理来生成就可以在运行时动态地实现对方法调进行拦截。

事实上动态代理模式从描述上也看出了它的无奈。
1.所有需要拦截的方法所在的类必须要实现一个接口供代理来"制造"这个类的实例。
2.必须改变原有实现的调用方式。即原来instance.m();的调用必须全部改成proxy.m();
3.当需要对原来没有实现接口的类增加进行拦截的时候必须先强制实现接口再重新使用代理的方式生成对象。
4.对方法调用时的内部环境无法感知。
这个动态有些免强,其实代码一旦生成根本无法再动态。而要实现这个拦截方式,原有的类设计方式被强制修改(必须提供接口),
类使用方式也被强制修改(必须从代理类生成),这是一种侵入式的实现。简单说要实现这个功能你必须要在你的代码中嵌入你的拦
截实现。

如果我们采用字节码生成器来进行拦截实现,我们就可以以非侵入方式来拦截。这种方式的实现对应用透明。程序员根本
不必考虑在业务逻辑实现时如何提供方法调用的拦截。一切都由JVM在loadClass的时候偷偷地将你的class文件替换成可以
进过包装的class来进行拦截。这种方式的好处是不影响类的设计和实现,并且拦截的功能非常强大,可以获取方法调用时的
本地变量,异常栈等内部信息。
虽然字节码生成器的实现方式也是在运行时进行动态方法拦截,但我这里要说的动态非侵入拦截并不是指运行时拦截这种动态。

如果我们仅仅是实现一个经过对原有class文件的替换过的class,在JVM启动时使用ClassLoader进行redefine来实现拦截,
这同样要进行侵入,要么要修改System.ClassLoader来自动redefine一个class,要么就象代理模式一样来控制每个类的调用方式。
而且,如果我们对某一个类中的方法进行拦截,一旦JVM启动,就要在整个过程中进行都进行拦截。

我要说的动态是指在JVM启动后正常的时候JVM执行的是原始的class,在我需要的时候JVM能动态执行进过字节码生成器包装过的class.
然后在我进行调试,诊断等操作后JVM又能即时执行原有的class,就象没有发生任何拦截一样。我这里的用词不是很准,
JVM执行class是说JVM在运行时链接的class对象,然后JIT编译器根据这个class生成本地码来执行。

上面说清楚我们要达到的目的,下面就来谈具体的实现。
首先是字节码生成器,在没有字节码生器以前,我们要动态生成一个内存中的class,我们只能进行动态编译。
(http://blog.csdn.net/axman/archive/2004/11/04/167002.aspx)
但字节码生成器提供了在内存中动态构造class的方式。目前主流的字节码生成器有ASM,BCEL,SERP。功能基本相同,
但ASM实现非常短小精悍,性能最强。是本人最喜欢的一款字节码生成器,如果你喜欢其它的字节码生成器,不影响本文的说明。

本文不是介绍ASM的文档,所以不会详细介绍ASM的相关内容。但基于要说明的问题,提供一个很小的例子:

Coder实现了一个业务逻辑类:


这是一个非常普通的业务逻辑,对,我们就要它普通,对于Coder来说,他的实现要以一切正常的方式来运行。
当这个类作为一个项目的实现之一被正常运行后,在运行时我想要看到test或test1被调用时的情况,我们就要实现
它的拦截手段:


这个方法是产生经过包装的class。其中的MyClassAdapter:

非常简单,就是在生成新的方法时如果是在指定的methods中就调用MyAdviceAdapter来包装,否则返回原来的方法.
MyAdviceAdapter也是一个回调接口,是在生成某方法时把onMethodEnter和onMethodExit方法中的指令插入到原来的方法前后再生成
包装后的字节码.注意这是注入到生成的class文件中:

利用字节码生成器提供的功能我们还可以获取方法栈中的本地变量,异常栈等,这是代理方式不能做到的。详细的功能请看ASM文档。特别是在方法抛出异常时,为了帮助分析,我们最需要的能恢复现场,所以在拦截器中导出方法运行时的参数,方法内的本地变量等“当时信息”具有非常的意义。

当我们获取到经过包装的class的byte[]后,我们如何让JVM动态执行新的class?
JAVA5以后JVM提供了一个javaagent接口,就是在执行Mail方法前会预执行premain方法。这个方法签名是:
public static void premain(String agentArgs, Instrumentation inst);
其中的Instrumentation的实例inst就可以redefine一个原来的Class
当我们的项目中的MyBusiness在被main方法调用前,inst可以将原来的class替换成包装后的class:

在将应用打包的时候在MANIFEST.MF文件中加上:

Premain-Class: 包含primain方法的类,最好是和main放在一起。
Can-Redefine-Classes: true
Boot-Class-Path: 打包后的jar文件如agent.jar

这样对于开发人员而言这个拦截过程是完全透明的。我们只需要启动时加上
java -javaagent:agent.jar选项就可以在应用完全不感知的情况下拦截应用中的方法

但是,这仍然不能做到动态,因为JVM启动后,所有原来对MyBusiness的business调用会一直被替换为包装后的代码。
所以我们不能直接在premain中redefine,而是将inst传给一个线程:


OK,在JVM正常启动后,你只要在那个用来通讯的文件中加上className和methods就可以在你需要的时候redefineClasses,在你不需要的时候恢复原始的class。比如一开如先在a.txt中写入: XXX:YYY:ZZZ

这样的命令那么守护线程什么也不做,而主线程会打印

"I'm TestClass.test() ."

"I'm TestClass.test1() ."

然后将a.txt内容改成: DEBUG:org.axman.test.TestClass:test

就会在"I'm TestClass.test() ."前后打印出before和after的注入信息。这时没有redefine test1方法。

再将a.txt的内容改成:DEBUG:org.axman.test.TestClass:test,test1就会看到

"I'm TestClass.test() ."和"I'm TestClass.test1() ."的前后都打印了注入的信息。然后再修改成

RESET:org.axman.test.TestClass:test,test1,又恢复了默认的打印信息。完全按我们的控制来进行方法调用的

拦截。

这才是真正的“动态无侵入拦截”。当然要记得一个真正的实现不要用文件来通讯。

详细的拦截实现下一篇再说。

分享到:
评论

相关推荐

    权限管理(过滤器粗粒度无侵入拦截方式)

    权限管理(过滤器粗粒度无侵入拦截方式)

    spring aop切面拦截指定类和方法实现流程日志跟踪

    spring aop切面拦截指定类和方法实现流程日志跟踪 一般情况下,在不侵入业务代码的情况下,需要做流程日志跟踪是比较合理的 采用springaop切面思想

    swift-一个无侵入的iOScrash防护框架

    一个无侵入的 iOS crash 防护框架

    mybatis分页拦截器(自动封装版)

    近期由于工作需要,想要开发一版能够满足多数人员使用的mybatis分页拦截器,在网上查找了很多资料并结合自己的一些理解,开发出了以下一版简单的代码。 起初想要遵循江湖惯例,通过各个层级间传递分页参数来实现目标...

    struts2.1.8学习

    2、Struts2 非侵入式设计、Struts1 属于侵入式设计 3、Struts1 与ServletAPI、strutsAPI 紧密耦合;Struts1 则不 4、Struts2 利用拦截器进行AOP 编程,实现如权限拦截功能 5、Struts2 提供了类型转换器 6、Struts2 ...

    System Mechanic ProUltimate Defense 22.3一款便捷的多功能电脑系统保护与病毒拦截工具

    System Mechanic Ultimate Defense是一款便捷的多功能电脑系统保护与病毒拦截工具,它在界面中为用户提供了一套全面的安全,隐私和性能功能;它在单个System Mechanic仪表板中结合了7种功能强大的产品,通过一个便捷...

    一个基于接口和注解的Java HTTP客户端.docx

    特性 注解简单: 遵循大家的命名习惯,@Body、@Query、@Var等注解见名之意。 无侵入: 接口不需要继承。...拦截器可满足大部分业务需求,如:计算请求耗时,动态添加公共请求头,返回错误统一处理等等。

    【JavaScript源代码】vue响应式原理与双向数据的深入解析.docx

    vue响应式原理与双向数据的深入解析 ...对象整体代理 (vue3.x) Proxy 提示:以下是本篇文章正文内容,下面案例可供参考 Vue 最独特的特性之一,是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript

    自DirectX 3D游戏应用程序的几何模型导出重建‘英文版’

    该文章不仅显示了DirectX 9图形管道中拦截流的基本机制如何导致图形应用程序的非侵入性扩展机制,而且还显示如何适当地操纵流水线和状态来重建几何信息和导出模型 不同风格 在描述我们的系统如何有效地重建图形库...

    ResponseDetective, 网络层的福尔摩斯.zip

    ResponseDetective, 网络层的福尔摩斯 对于拦截应用程序和服务器之间的任何传出请求和传入响应,ResponseDetective 是一个非侵入性框架,用于在你的应用程序之间进行调试。要求ResponseDetective用英镑 Swift,支持

    AdvertFilter-crx插件

    移除非侵入式广告列表 Version 4.3: 2.修正两个扩展错误问题 历史记录: Version 4.2: 1.修正大量界面错误问题 Version 4.2: 2.修正部分接口失效问题 Version 4.2: 3.修正面板界面过长问题 支持语言:English (United ...

    spring mvc+mybatis分页

    此插件使用mybatis拦截器原理,对代码没有侵入性

    Spring2.5的新特性

    简介 从诞生之初,Spring框架就坚守它的宗旨:简化企业级应用开发,同时给复杂问题提供强大的、非侵入性解决方案。一年前发布的Spring2.0就把这些主题推到了一个新的高度。XML Schema的支持和自定义命名空间的...

    ResponseDetective:网络层的Sherlock Holmes

    ResponseDetective是一个非侵入性框架,用于拦截应用程序和服务器之间的所有传出请求和传入响应,以进行调试。要求ResponseDetective用Swift 5.3编写,并支持iOS 9.0 + , macOS 10.10+和tvOS 9.0+ 。用法将...

    谷歌浏览器屏蔽广告插件

    用户可以选择启用或者禁用广告的过滤清单,当然这个清单是由Adblock Plus插件社区发布的,主要启用了其中的一个清单列表,在网站上显示的该类型广告都会被拦截,另外,用户也可以选择允许一些非侵入式广告来支持网站...

    Cat 客户端埋点插件用户手册1

    1. cat-client-agent: 考虑到Cat埋点最好实现无侵入性,所需cat-client相关jar包与业务应用jar包不放在一起,因此实现了自定义类

    10-Second Focus-crx插件

    分心威慑力! 通过站点拦截器进行的简单分散干扰威慑力足以长期坚持使用。 功能:阻止分散注意力的站点,然后仅允许...功能:轻巧的设计,非侵入式权限,可自定义的阻止列表/威慑计时器/解锁时间段。 支持语言:English

    js2D物理引擎PoorPhy.zip

    有时会出现物体重叠(侵入)现象 无法应对体积小 速度快(如 子弹)的物体 稳定性略有欠缺。例如:一个水平的0弹力正方形 落到另一个比较大的水平的0弹力正方形正上方时, 本应该稳稳的停在上面。 但是PoorPhy ...

    本科毕业设计+基于SpringBoot+Vue构建的中小企业进销存管理系统

    优势 严格遵循阿里编码规约开发,便于阅读及二次开发 支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQL...使用Mybatis拦截器实现数据权限,对代码侵入小 完成Activiti6集成,可实现

    tiangang-canal-demo

    最开始听说canal是从mysql与redis双写一致性解决方案,当时并没有太在意,最近由于需要实时同步数据,如果在代码对insert/update/delete做拦截也可以实现,但`对代码侵入性太大了`,并且后期更改时容易有遗漏,风险...

Global site tag (gtag.js) - Google Analytics