AOP AspectJ 字节码 示例 Hugo MD
Markdown版本笔记 | 我的GitHub首页 | 我的博客 | 我的微信 | 我的邮箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
AOP AspectJ 简介 示例 Hugo MD
目录
AspectJ 简介
沪江封装的库集成配置
简介
配置
官方库集成配置
AspectJ 使用案例
定义拦截点击事件的逻辑
定义拦截自定义注解的逻辑
工具类 ObjToStringUtils
Pointcut 语法格式
常用的 Pointcut 案例
Hugo 简介
简介
使用步骤
实现原理简介
AspectJ 简介
AspectJ 官网
AspectJ 官方详细文档
AspectJ Development Environment Guide
相关词汇
Aspect [ˈæspekt] n.方面; 面貌; 方位,方向; 形势;
oriented ['ɔ:rɪəntɪd] adj.导向的; 定向的; 以…为方向的; 定方向;
OOP:Object Oriented Programming,面向对象编程
AOP:Aspect Oriented Programming,面向切向编程
AspectJ 意思就是 Java 的 Aspect。它就是一个代码编译器
,在Java编译器
的基础上增加了一些它自己的关键字识别和编译方法。
AspectJ编译器在编译期
对所切点所在的目标类进行了重构,在编译层将AspectJ程序与目标程序进行双向关联,生成新的目标字节码
,即将AspectJ的切点和其余辅助的信息类段插入目标方法和目标类中,同时也传回了目标类以及其实例引用,这样便能够在AspectJ程序里对目标程序进行监听甚至操控。
AspectJ 的优点
- 非侵入式监控: 可以在不修监控目标的情况下监控其运行,截获某类方法,甚至可以修改其参数和运行轨迹!
- 学习成本低: 它就是Java,只要会Java就可以用它。
- 功能强大,可拓展性高: 它就是一个编译器+一个库,可以让开发者最大限度的发挥,实现形形色色的AOP程序!
沪江封装的库集成配置
AspectJX
使用 AspectJX 实现的动态权限配置框架
简介
一个基于AspectJ并在此基础上扩展出来可应用于Android开发平台的AOP框架,可作用于java源码
,class文件
及jar包
,同时支持kotlin
的应用。
A Android gradle plugin that effects AspectJ on Android project and can hook methods in
Kotlin
,aar
andjar
file.
开发AspectJX的初衷
- 目前的开源库中还没有发现可应用于Android平台的比较好的AOP框架或者工具,虽然 xposed,dexposed 非常强大,但基于严重的碎片化现状,
兼容问题
永远是一座无法逾越的大山。 - 目前其他的
AspectJ
相关插件和框架都不支持AAR或者JAR切入
的,对于目前在Android圈很火爆的Kotlin
更加无能为力。
配置
1、在项目根目录的build.gradle
里依赖AspectJX
和官方AspectJ
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
classpath 'org.aspectj:aspectjtools:1.8.13'
2、在app
项目的build.gradle
里应用插件
apply plugin: 'android-aspectjx' //或者apply plugin: 'com.hujiang.android-aspectjx'
aspectjx {
enabled true //enabled默认为true,即默认AspectJX生效
exclude 'android.support' //排除所有package路径中包含`android.support`的class文件及jar文件
}
AspectJX默认会处理所有的二进制代码文件和库,为了提升编译效率及规避部分第三方库出现的编译兼容性问题,AspectJX提供
include
、exclude
命令来过滤需要处理的文件及排除某些文件(包括class文件及jar文件)。注意,匹配时时通过
package
路径来匹配class文件及jar文件的,支持*
和**
匹配,例如:
exclude '*'
//忽略所有的class文件及jar文件,相当于AspectJX
不生效
3、如果AspectJ
的代码写在某个library模块中,则必须添加如下配置:
3.1、在使用AspectJ
的模块中添加依赖(不必在意版本)
implementation 'org.aspectj:aspectjrt:1.8.13'
3.2、让app模块依赖此module,这一步是必须要做的,否则运行时立即崩溃
implementation project(path: ':log')
官方库集成配置
AspectJ Weaver 仓库地址
AspectJ Tools 仓库地址
AspectJ 官网下载
已知问题
- 不支持kotlin
- 不能拦截jar包中的类
- 拦截规则不能写在jar包中
- 需要在每一个module都配置脚本
配置
1、在【project】的build.gradle
中配置:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.9'
classpath 'org.aspectj:aspectjweaver:1.8.9'
}
}
2、在【project】的build.gradle
最后面添加脚本:
注意,直接粘贴到build.gradle文件的末尾即可,不要嵌套在别的指令中
//AOP需要执行的脚本,每一个application和library都需要
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
def aop(variants) {
variants.all { variant ->
JavaCompile javaCompile = variant.javaCompile
String[] args;
javaCompile.doFirst {
args = ["-showWeaveInfo",
"-1.8",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", path,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
}
javaCompile.doLast {
new Main().run(args, new MessageHandler(false));
}
}
}
3、在【module】的build.gradle
中添加依赖:
注意,只在基础module中使用api方式依赖即可
api 'org.aspectj:aspectjrt:1.8.9'//只在基础module中使用api方式依赖
4、在【module】的build.gradle
中添加编译脚本:
注意,直接粘贴到build.gradle文件的开始位置或末尾即可,不要嵌套在别的指令中
如果是【application】
rootProject.aop(project.android.applicationVariants)
如果是【library】
rootProject.aop(project.android.libraryVariants)
AspectJ 使用案例
定义拦截点击事件的逻辑
拦截所有View或其子类的【onClick方法】以及通过ButterKnife的注解添加的点击事件
@Aspect
public class OnClickAspect {
@Pointcut("execution(* android.view.View.On*Listener.on*Click(..)) ")//定义匹配范围:执行指定方法时拦截
public void onClick() {//匹配View.OnClickListener中的onClick方法和View.OnLongClickListener中的OnLongClickListener方法
}
@Pointcut("execution(* *.on*ItemClick(..)) ")//如果有多个匹配范围,可以定义多个,多个规则之间通过 || 或 && 控制
public void onItemClick() {//匹配任意名字以on开头以ItemClick结尾的方法
}
@Pointcut("execution(@butterknife.OnClick * *(..))")//匹配通过butterknife的OnClick注解添加的点击事件
public void butterKnifeOnClick() {
}
@Around("onClick() || onItemClick() || butterKnifeOnClick()")//@Around 拦截方法,这个注解可以同时拦截方法的执行前后
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = SystemClock.currentThreadTimeMillis();
printJoinPointInfo(joinPoint);
if (joinPoint.getSignature() instanceof MethodSignature) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//要根据Pointcut匹配的类型强转
printMethodSignatureInfo(signature);
printArgs(joinPoint);
printParameterInfo(joinPoint);
}
Object result = joinPoint.proceed();
Log.i("bqt", "【@Around】返回值=" + ObjToStringUtils.toString(result)// null
+ " 方法执行耗时=" + (SystemClock.currentThreadTimeMillis() - beginTime));//2
return result;
}
//必须是静态方法
private static void printJoinPointInfo(ProceedingJoinPoint joinPoint) {
Log.i("bqt", "【@Around】MethodSignature"
+ "\nKind=" + joinPoint.getKind()//method-execution
+ "\nArgs=" + ObjToStringUtils.toString(joinPoint.getArgs())//[android.widget.TextView{d090a1d V.ED..C.. ...P.... 0,0-1440,210}]
+ "\nSignature=" + ObjToStringUtils.toString(joinPoint.getSignature())//void com.bqt.aop.MainActivity.1.onClick(View)
+ "\nSourceLocation=" + ObjToStringUtils.toString(joinPoint.getSourceLocation())//MainActivity.java:25
+ "\nStaticPart=" + ObjToStringUtils.toString(joinPoint.getStaticPart())//execution(void com.bqt.aop.MainActivity.1.onClick(View))
+ "\nTarget=" + ObjToStringUtils.toString(joinPoint.getTarget())//com.bqt.aop.MainActivity$1@d5c5492
+ "\nThis=" + ObjToStringUtils.toString(joinPoint.getThis()));//com.bqt.aop.MainActivity$1@d5c5492
}
private static void printMethodSignatureInfo(MethodSignature signature) {
//下面通过MethodSignature的方式获取方法的详细信息,也基本都可以通过Method对象获取
Log.i("bqt", "【@Around】MethodSignature"
+ "\n方法=" + ObjToStringUtils.toString(signature.getMethod())// public void com.bqt.aop.MainActivity$1.onClick(android.view.View)
+ "\n方法名=" + signature.getName()// onClick
+ "\n返回值类型=" + ObjToStringUtils.toString(signature.getReturnType())// void
+ "\n声明类型=" + ObjToStringUtils.toString(signature.getDeclaringType())// class com.bqt.aop.MainActivity$1
+ "\n声明类型名=" + signature.getDeclaringTypeName()// com.bqt.aop.MainActivity$1
+ "\n异常类型=" + ObjToStringUtils.toString(signature.getExceptionTypes())// []
+ "\n修饰符=" + signature.getModifiers()// 1,对应为 public static final int PUBLIC = 0x00000001
+ "\n参数名=" + ObjToStringUtils.toString(signature.getParameterNames())// ["v"]
+ "\n参数类型=" + ObjToStringUtils.toString(signature.getParameterTypes()));// [class android.view.View]
}
private static void printArgs(ProceedingJoinPoint joinPoint) {
String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();//获取参数名列表
Object[] parameterValues = joinPoint.getArgs();//获取参数值列表
StringBuilder builder = new StringBuilder("");
for (int i = 0; i < parameterValues.length; i++) {
builder.append("\n")
.append(parameterNames[i])
.append("=")//拼接参数名
.append(ObjToStringUtils.toString(parameterValues[i]));//拼接参数值
}
Log.i("bqt", "【@Around】参数列表" + builder.toString());//v=android.widget.TextView{d090a1d V.ED..C.. ...P.... 0,0-1440,210}
}
private static void printParameterInfo(ProceedingJoinPoint joinPoint) {
Object[] parameterValues = joinPoint.getArgs();//获取参数值列表
for (Object obj : parameterValues) {
if (obj instanceof TextView) {
TextView textView = (TextView) obj;
Log.i("bqt", "【@Around】TextView的信息"
+ " 文字=" + textView.getText()
+ " 所属界面=" + textView.getContext().getClass().getSimpleName()
+ " ID=" + textView.getId()
+ " 父页面名称=" + textView.getParent().getClass().getSimpleName()
);
}
}
}
}
定义拦截自定义注解的逻辑
定义注解
@Target({METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomEvent {
String value();
}
定义拦截规则(注意要更改为正确的包名)
@Aspect
public class CustomEventAspect {
@Pointcut("within(@com.bqt.basic.log.aop.CustomEvent *)")
public void withinAnnotatedClass() {
}
@Pointcut("execution(* *(..)) && withinAnnotatedClass()")
public void methodInsideAnnotatedType() {
}
@Pointcut("execution(*.new(..)) && withinAnnotatedClass()")
public void constructorInsideAnnotatedType() {
}
@Pointcut("execution(@com.bqt.basic.log.aop.CustomEvent * *(..)) || methodInsideAnnotatedType()")
public void method() {
}
@Pointcut("execution(@com.bqt.basic.log.aop.CustomEvent *.new(..)) || constructorInsideAnnotatedType()")
public void constructor() {
}
@Before("method() || constructor()")
public void before(JoinPoint joinPoint) {
SourceLocation location = joinPoint.getSourceLocation();
Log.i("bqt", "【自定义事件" + "(" + location.getFileName() + ":" + location.getLine() + ")");
}
@After("method() || constructor()")
public void after(JoinPoint joinPoint) {
//before、after不能和around同时使用
}
}
工具类 ObjToStringUtils
public class ObjToStringUtils {
public static String toString(Object obj) {
if (obj == null) return "null";
if (obj instanceof CharSequence) return '"' + printableToString(obj.toString()) + '"';
Class<?> cls = obj.getClass();
if (Byte.class == cls) return byteToString((Byte) obj);
if (cls.isArray()) return arrayToString(cls.getComponentType(), obj);
return obj.toString();
}
private static String printableToString(String string) {
int length = string.length();
StringBuilder builder = new StringBuilder(length);
for (int i = 0; i < length; ) {
int codePoint = string.codePointAt(i);
switch (Character.getType(codePoint)) {
case Character.CONTROL:
case Character.FORMAT:
case Character.PRIVATE_USE:
case Character.SURROGATE:
case Character.UNASSIGNED:
switch (codePoint) {
case '\n':
builder.append("\\n");
break;
case '\r':
builder.append("\\r");
break;
case '\t':
builder.append("\\t");
break;
case '\f':
builder.append("\\f");
break;
case '\b':
builder.append("\\b");
break;
default:
builder.append("\\u").append(String.format("%04x", codePoint).toUpperCase(Locale.US));
break;
}
break;
default:
builder.append(Character.toChars(codePoint));
break;
}
i += Character.charCount(codePoint);
}
return builder.toString();
}
private static String arrayToString(Class<?> cls, Object obj) {
if (byte.class == cls) return byteArrayToString((byte[]) obj);
if (short.class == cls) return Arrays.toString((short[]) obj);
if (char.class == cls) return Arrays.toString((char[]) obj);
if (int.class == cls) return Arrays.toString((int[]) obj);
if (long.class == cls) return Arrays.toString((long[]) obj);
if (float.class == cls) return Arrays.toString((float[]) obj);
if (double.class == cls) return Arrays.toString((double[]) obj);
if (boolean.class == cls) return Arrays.toString((boolean[]) obj);
return arrayToString((Object[]) obj);
}
private static String byteArrayToString(byte[] bytes) {
StringBuilder builder = new StringBuilder("[");
for (int i = 0; i < bytes.length; i++) {
if (i > 0) builder.append(", ");
builder.append(byteToString(bytes[i]));
}
return builder.append(']').toString();
}
private static String byteToString(Byte b) {
if (b == null) return "null";
return "0x" + String.format("%02x", b).toUpperCase(Locale.US);
}
private static String arrayToString(Object[] array) {
StringBuilder buf = new StringBuilder();
arrayToString(array, buf, new HashSet<Object[]>());
return buf.toString();
}
private static void arrayToString(Object[] array, StringBuilder builder, Set<Object[]> seen) {
if (array == null) {
builder.append("null");
return;
}
seen.add(array);
builder.append('[');
for (int i = 0; i < array.length; i++) {
if (i > 0) builder.append(", ");
Object element = array[i];
if (element == null) {
builder.append("null");
} else {
Class elementClass = element.getClass();
if (elementClass.isArray() && elementClass.getComponentType() == Object.class) {
Object[] arrayElement = (Object[]) element;
if (seen.contains(arrayElement)) builder.append("[...]");
else arrayToString(arrayElement, builder, seen);
} else {
builder.append(toString(element));
}
}
}
builder.append(']');
seen.remove(array);
}
}
Pointcut 语法格式
Pointcut可以由下列方式来定义或者通过 && || !
的方式进行组合。
args()
@args()
execution()
this()
target()
@target()
within()
@within()
@annotation
其中 execution 是用的最多的,其格式为:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
execution(修饰符? 返回值的类型 所属的类? 方法名(参数列表) 抛出的异常?)
后面带 ?
的表示可以省略,returning type pattern
和 name pattern
和 parameters pattern
是必须的。
- modifiers-pattern:指定方法的修饰符,支持通配符,该部分可以省略
- ret-type-pattern:指定方法的返回值类型,支持通配符,可以使用
*
通配符来匹配所有的返回值类型 - declaring-type-pattern:指定方法所属的类,支持通配符,该部分可以省略
- name-pattern:指定匹配指定的方法名,支持通配符,可以使用
*
通配符来匹配所有方法,也可以用set*
匹配以 set 开头的所有方法 - param-pattern:指定方法声明中的形参列表,支持两个通配符,其中
*
代表一个任意类型的参数,而..
代表零个或多个任意类型的参数。
() 匹配一个不接受任何参数的方法
(..) 匹配一个接受任意数量参数的方法
(*) 匹配了一个接受一个任何类型的参数的方法
(*,String) 匹配了一个接受两个参数的方法,其中第一个参数是任意类型,第二个参数必须是String类型
- throws-pattern:指定方法声明抛出的异常,支持通配符,该部分可以省略
通配符
* 表示任何数量的字符,除了(.)
.. 表示任何数量的字符包括任何数量的(.)
+ 描述指定类型的任何子类或者子接口
操作符,同java一样,提供了一元和二元的条件表达操作符:
一元操作符:!
二元操作符:||和&&
优先权同java
常用的 Pointcut 案例
类和包
- 任意公共方法的执行:
execution(public * *(..))
- 任何一个以 set 开始的方法的执行:
execution(* set*(..))
- 接口或类 com.a.b.Demo 中的任意方法的执行:
execution(* com.a.b.Demo.*(..))
- 定义在 com.a.b 包里的任意方法的执行:
execution(* com.a.b.*.*(..))
- 定义在 com.a.b 包和所有子包里的任意类的任意方法的执行:
execution(* com.a.b..*.*(..))
- 定义在 com.a.b 包和所有子包里的 Demo 类的任意方法的执行:
execution(* com.a.b..Demo.*(..))")
方法和构造器
- 特定的某个方法:
public void Account.debit(float) throws InsufficientBalanceException
- Account中以set开头,并且只有一个参数类型的方法:
public void Account.set*(*)
- Account中所有的没有参数的public void 方法:
public void Account.*()
- Account中所有没有参数的public 方法:
public * Account.*()
- Account中所有的public 方法:
public * Account.*(..)
- Account中所有的方法:
* Account.*(..)
- Account中的所有的方法,包括子类的方法:
* Account+.*(..)
- Account中所有的非public方法:
!public * Account.*(..)
- 所有的read方法:
* java.io.Reader.read(..)
- 所有以
read(char[])
开始的方法,包括read(char[])
和read(char[],int,int)
:* java.io.Reader.read(char[],..)
- 所有以add开始以Listener结尾的方法,参数为EventListener或子类:
* javax..*.add*Listener(EventListener+)
- 抛出RemoteException的所有方法:
* *.*(..) throws RemoteException
构造器,同上面,没有参数的构造器方法:public Account.new()
属性签名模式,同方法一样,属性也查不多
- 所有Account的属性:
* Account.*
- 所有非public static 的属性,在abc的包或者子包中:
!public static * abc..*.*
类型签名样式
Account
类型Account*Account
使用Account名称结束的类型,如SavingsAccount和CheckingAccountjava.*.Date
类型Date在任何直接的java子包中,如java.util.Date和java.sql.Datejava..*
任何在java包或者所有子包中的类型,如java.awt和java.util或者java.awt.event 和java.util.loggingjavax..*Model+
所有javax包或者子包中以Model结尾的类型和其所有子类,如TableModel,TreeModel。! vector
所有除了Vector的类型Vector || Hashtable
Vector或者Hashtable类型java.util.RandomAccess+ && java.util.List+
实现RandomAccess和List的所有子类
Hugo 简介
Hugo是JakeWharton大神开发的一个通过注解触发的Debug日志库
。它是一个非常好的AOP
框架,在Debug模式下,Hugo利用aspectj
库来进行切面编程,插入日志代码,用极小的代价帮我们实现优雅的函数监控
。
简介
Annotation-triggered注解触发 method call logging for your debug builds.
As a programmer, you often add log statements to print method calls, their arguments, their return values, and the time it took to execute.
This is not a question. Every one of you does this. Shouldn't it be easier?
Simply add @DebugLog
to your methods and you will automatically get all of the things listed above logged for free.
@DebugLog
public String getName(String first, String last) {
SystemClock.sleep(15); // Don't ever really do this!
return first + " " + last;
}
V/Example: ⇢ getName(first="Jake", last="Wharton")
V/Example: ⇠ getName [16ms] = "Jake Wharton"
The logging will only happen in debug builds and the annotation itself is never present in the compiled class file注解不存在于编译后的类文件中 for any build type.
This means you can keep the annotation and check it into source control. It has zero effect on non-debug builds.
Add it to your project today!
使用步骤
1、在 project 的 build.gradle 中添加:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1'
}
}
2、在module的 build.gradle 中添加:
apply plugin: 'com.jakewharton.hugo'
3、Disable logging temporarily暂时:
Hugo.setEnabled(true|false)
4、在需要的的类、方法、构造方法上加上注解即可
@DebugLog
实现原理简介
Hugo可以用极小的代价帮我们实现优雅的函数监控。
Hugo给我们提供了一种思路,即在Android中,也可以利用AOP
的思路实现优雅的面向切面编程,例如用来统计打点等。
其实,从源码上来看,除了一个 Hugo 类外,这个库没有其他什么内容了。
一个注解 DebugLog:
@Target({TYPE, METHOD, CONSTRUCTOR}) //注意类是TYPE的一种)和方法,所以可以用在类上
@Retention(CLASS) //保留策略为CLASS,即编译器将把注解记录在类文件中,因为不是RUNTIME,所以运行时注解丢掉了
//注意,如果我们需要在运行时获取注解信息(比如注解中有字段且需要在运行时获取到此字段的值),则Retention必须设为【RUNTIME】
public @interface DebugLog {
}
一个类 Hugo.java
也就一百行代码,其中与我们业务相关的核心代码为 logAndExecute(ProceedingJoinPoint):
@Around("method() || constructor()")
public Object logAndExecute(ProceedingJoinPoint joinPoint) throws Throwable {
enterMethod(joinPoint); //ProceedingJoinPoint有参数信息,输出参数的值
long startNanos = System.nanoTime();//函数执行前记录时间,像我们手动做的一样
Object result = joinPoint.proceed();//这里代表我们监控的函数
long stopNanos = System.nanoTime();//函数执行结束时,打点记录时间,并计算耗时
long lengthMillis = TimeUnit.NANOSECONDS.toMillis(stopNanos - startNanos);
exitMethod(joinPoint, result, lengthMillis);//输出函数的值,执行耗时
return result;
}
2018-4-27
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">
AOP AspectJ 字节码 示例 Hugo MD的更多相关文章
- AOP AspectJ 字节码 语法 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- AutoRegister ASM AOP 字节码 案例 原理 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Javassist 字节码 简介 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 字节码 反编译 APKTool 重新打jar包 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- JVM插庄之一:JVM字节码增强技术介绍及入门示例
字节码增强技术:AOP技术其实就是字节码增强技术,JVM提供的动态代理追根究底也是字节码增强技术. 目的:在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修 ...
- Javassist 字节码 语法 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Javassist字节码增强示例
概述 Javassist是一款字节码编辑工具,可以直接编辑和生成Java生成的字节码,以达到对.class文件进行动态修改的效果.熟练使用这套工具,可以让Java编程更接近与动态语言编程. 下面一个方 ...
- 在Eclipse里查看Java字节码
要理解 Java 字节码,比较推荐的方法是自己尝试编写源码对照字节码学习.其中阅读 Java 字节码的工具必不可少.虽然javap可以以可读的形式展示出.class 文件中字节码,但每次改动源码都需调 ...
- 【转】在Eclipse里查看Java字节码
要理解 Java 字节码,比较推荐的方法是自己尝试编写源码对照字节码学习.其中阅读 Java 字节码的工具必不可少.虽然javap可以以可读的形式展示出.class 文件中字节码,但每次改动源码都需调 ...
随机推荐
- web前端面试经历分享
十天前,我还在纠结这个暑假到底是呆在实验室研究技术好还是找一份实习见识世面好,而现在我已经接到offer准备工作了.这几天真是累得够呛,一方面需要拼命准备期末考试,另一方面,需要往公司里面跑接受面试. ...
- code vs 1094 FBI树 2004年NOIP全国联赛普及组
题目描述 Description 我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串. FBI树是一种二叉树[1],它的结点类型 ...
- UOJ.179.线性规划(单纯形)
题目链接 这写得还不错:http://www.cnblogs.com/zzqsblog/p/5457091.html 引入基变量\(x_{i+n}\),将约束\(\sum_{i=1}^m a_{ij} ...
- hdu 4461 第37届ACM/ICPC杭州赛区I题
题意:给两个人一些棋子,每个棋子有其对应的power,若b没有或者c没有,或者二者都没有,那么他的total power就会减1,total power最少是1,求最后谁能赢 如果b或c出现的话,fl ...
- 【BZOJ-2063】我爸是李刚 数位dp 好题
2063: 我爸是李刚 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 139 Solved: 72[Submit][Status][Discuss] ...
- 12、Redis的事务
写在前面的话:读书破万卷,编码如有神 --------------------------------------------------------------------------------- ...
- UVALive 6911 Double Swords 树状数组
Double Swords 题目连接: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8 ...
- 強大的jQuery Chart组件-Highcharts
Highcharts是一个制作图表的纯Javascript类库,主要特性如下: 兼容性:兼容当今所有的浏览器,包括iPhone.IE和火狐等等: 对个人用户完全免费: 纯JS,无BS: 支持大部分的图 ...
- jtagger Versatile multiprogrammer for FPGAs, MCUs, etc.
jtagger Versatile multiprogrammer for FPGAs, MCUs, etc. Well, it's not really just a jtagger, but I' ...
- Java-JVM-GC
http://www.cnblogs.com/zhguang/p/Java-JVM-GC.html