不修改源代码,动态注入Java代码的方法(转)
转自:https://blog.csdn.net/hiphoon_sun/article/details/38707927
有时,我们需要在不修改源代码的前提下往一个第三方的JAVA程序里注入自己的代码逻辑。一种情况是拿不到它的源代码,另一种情况是即使有源代码也不想修改,想让注入的代码与第三方程序代码保持相对独立。
public void run() {
System.out.println("A is running.");
}
}
public static void main(String... args) {
A a = new A();
a.run();
}
}
public void run() {
System.out.println("B is running.");
}
}
Java Instrumentation
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
class InjectCodeClassWriter extends ClassWriter {
private static final String oldClass = "A";
private static final String newClass = "B";
InjectCodeClassWriter(int flags) {
super(flags);
}
@Override
public int newUTF8(final String value) {
// 将App.class中常量池(constant pool)中类A的名字的字符串改为类B的名字
if (value.equals(oldClass)) {
return super.newUTF8(newClass);
}
return super.newUTF8(value);
}
}
class InjectCodeTransformer implements ClassFileTransformer {
private static final String appClass = "App";
public byte[] transform(ClassLoader loader, String className,
Class classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
if (className.equals(appClass)) {
ClassWriter classWriter=new InjectCodeClassWriter(0);
ClassReader classReader=new ClassReader(classfileBuffer);
classReader.accept(classWriter, 0);
return classWriter.toByteArray();
} else {
return null;
}
}
}
public class InjectCodeAgent {
public static void premain(String args, Instrumentation inst) {
inst.addTransformer(new InjectCodeTransformer());
}
}
jar -cfm InjectCode.jar MANIFEST.MF InjectCodeAgent.class B.class
Class Loader
import java.io.InputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.ConcurrentHashMap;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
public class InjectCodeClassLoader extends URLClassLoader {
private static final String appClass = "App";
private static final String oldClass = "A";
private static final String newClass = "B";
private final ConcurrentHashMap<String, Object> locksMap = new ConcurrentHashMap<String, Object>();
public InjectCodeClassLoader(ClassLoader parent) {
super(((URLClassLoader) parent).getURLs(), parent);
}
private static class InjectCodeClassWriter extends ClassWriter {
InjectCodeClassWriter(int flags) {
super(flags);
}
@Override
public int newUTF8(final String value) {
if (value.equals(oldClass)) {
return super.newUTF8(newClass);
}
return super.newUTF8(value);
}
}
private Class defineClassFromClassFile(String className, byte[] classFile)
throws ClassFormatError {
return defineClass(className, classFile, 0, classFile.length);
}
private Class<?> replaceClass(String name)
throws ClassNotFoundException {
InputStream is = getResourceAsStream(name.replace('.', '/') + ".class");
if (is == null) {
throw new ClassNotFoundException();
}
ClassWriter classWriter=new InjectCodeClassWriter(0);
try {
ClassReader classReader=new ClassReader(is);
classReader.accept(classWriter, 0);
} catch (IOException e) {
throw new ClassNotFoundException();
}
Class c = defineClassFromClassFile(name, classWriter.toByteArray());
return c;
}
private Object getLock (String name) {
Object lock = new Object();
Object oldLock = locksMap.putIfAbsent(name, lock);
if (oldLock == null) {
oldLock = lock;
}
return oldLock;
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Object lock = getLock(name);
synchronized(lock) {
Class c = findLoadedClass(name);
try {
if (c == null) {
if (name.equals(appClass)) {
c = replaceClass(name);
} else {
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
} catch (ClassNotFoundException e) {
}
}
return super.loadClass(name, resolve);
}
}
不修改源代码,动态注入Java代码的方法(转)的更多相关文章
- 【我的Android进阶之旅】Android 源代码中的Java代码中//$NON-NLS-1$ 注释是什么意思?
1.背景 最近在负责公司基础业务和移动基础设施的开发工作,正在负责Lint代码静态检查工作.因此编写了自定义的Lint规则,在编写自定义的Lint规则前,当然是需要去把Google的关于Lint检测的 ...
- [改善Java代码]asList方法产生的List对象不可更改
上一个建议之处了asList方法在转换基本类型数组时候存在的问题,在看下asList方法返回的列表有何特殊的地方.看代码: import java.util.Arrays; import java.u ...
- [改善Java代码]注意方法中传递的参数要求(replaceAll和replace的区别)
有这样一个简单的需求:写一个方法,实现从原始字符串中删除与之匹配的所有子字符串,比如"蓝蓝的天,白云飘"中,删除"白云飘",输出"蓝蓝的天," ...
- java代码---indexOf()方法
总结:indexOf(String str,int index)方法.从参数指定位置开始,如果index值超过了字符串长度,则返回-1 package com.a.b; import java.io. ...
- java代码equals方法
package com.bc; public class Test_6 { // 我们知道java中的每个类都继承自Object类,equals是Object方法之一 String name; int ...
- java代码-----indexOf()方法--从字符串的某个字符的第一次出现的位子开始
总结:方法是indedOf()方法.this is my sister //indexOf()方法是indexOf('m')==7 .那么就是字符m第一次出现的位置是顺数第7个,就会正常显示‘t ...
- java代码----substring()方法是按索引截取字符串。。。下标0开始
总结:按照索引substring(2,5);意思是从字符串的索引为2开始(包括)到第6个字符(不包括)的位置的截取部分 package com.s.x; //substring public clas ...
- (转载)JAVA动态编译--字节代码的操纵
在一般的Java应用开发过程中,开发人员使用Java的方式比较简单.打开惯用的IDE,编写Java源代码,再利用IDE提供的功能直接运行Java 程序就可以了.这种开发模式背后的过程是:开发人员编写的 ...
- 如何向AcmeAir注入问题代码
为什么要注入问题代码? AcmeAir的常规代码是为了压测测试准备的,所以绝大部分的操作都是可以在几十毫秒中就可以正常返回的.为了向用户展示我们APM工具可以在源代码级别发现系统潜在问题,我们需要在A ...
随机推荐
- oracle-SQL语句执行原理和完整过程详解
SQL语句执行过程详解 一条sql,plsql的执行到底是怎样执行的呢? 一.SQL语句执行原理 第一步:客户端吧语句发个服务端执行 当我们在客户端执行select语句时,客户端会把这条SQL语句发送 ...
- postcss-px-to-viewport 的 exclude 配置无效
原来是由于版本太低的缘故: postcss-px-to-viewport 0.0.3版本不支持 exclude属性. 更新到 1.1.0 配置: // 路径中包含 PC ,则忽略 px转换为vw,vh ...
- vue+element-ui 实现分页(根据el-table内容变换的分页)
官方例子 官方提示: 设置layout,表示需要显示的内容,用逗号分隔,布局元素会依次显示.prev表示上一页,next为下一页,pager表示页码列表,除此以外还提供了jumper和total,si ...
- 《嵌入式软件设计基础——基于ARM Cortex—M3》读书笔记
此书有点深,记录点自己能够看懂的. 1.内存管理一章:讲到变量的类型.生存周期.内存分配. auto static register 局部变量,全局变量 malloc free 内存碎片,消除内存池的 ...
- [转]Win10企业版无法访问共享文件夹
Win10系统电脑在更新后,当我们访问共享文件夹时可能会出现如下图所示窗口,导致我们无法访问.那么这个问题如何解决呢?具体如下:1. 首先我们按“Windows+R键”打开运行窗口.2. 在该窗口文本 ...
- SQL各种JOIN
JOIN(= INNER JOIN):返回匹配的结果,没有匹配则没结果: LEFT JOIN(= LEFT OUTER JOIN):返回匹配的与左表的所有数据: RIGHT JOIN(= RIGHT ...
- cython安装、使用
cython安装.使用 原创 2012年09月27日 17:25:11 8436 0 0 一.cython 在linux(ubuntu)下安装 sudo apt-get install cython ...
- arcpy 常用操作
目录: 通用操作 条件函数 前提: import arcpy from arcpy.sa import * 1.通用操作 设置工作路径:arcpy.env.workspace("path_o ...
- selenium2-java 浏览器不同窗口切换
1,获取浏览器的单个窗口: String parentWindowid = driver.getWindowHandle(); System.out.println( ...
- Linux用户空间与内核地址空间
Linux 操作系统和驱动程序运行在内核空间,应用程序运行在用户空间,两者不能简单地使用指针传递数据,因为Linux使用的虚拟内存机制,用户空间的数据可能被换出,当内核空间使用用户空间指针时,对应的数 ...