记录实例的任何方法调用
Log any method call of an instance
是否可以记录 class 实例的任何方法调用?我有以下实例:
InputStream serverInputStream = this.serverProcess.getInputStream();
我想在运行时的给定时间查看在 serverInputStream
中调用了哪些方法。我相信解决方案是反射,特别是代理。我已经尝试让 this example 工作,但无法做到 运行。
我想到了类似这样的代码:
MyInterceptor myInterceptor = new MyInterceptor(this.serverProcess.getInputStream());
InputStream serverInputStream = myInterceptor.getInterceptedInstance();
serverInputStream.methodOne();
serverInputStream.methodTwo();
serverInputStream.methodThree();
myInterceptor.printIntercepts();
得到类似这样的结果:
1. InputStream.InputStream();
2. InputStream.methodOne();
3. InputStream.methodTwo();
4. InputStream.methodThree();
这可能吗?
这里有两个选择:
- 使用 JDK 中包含的动态代理功能,或
- 使用字节码操作库,例如 ASM,
java.lang.reflect.Proxy只能生成一组接口的实现,而字节码操作库也可以修补现有的类。
你有什么理由不能只写一个吗?然后你可以用其中之一包裹你想看的 InputStream
。
class LoggingInputStream extends InputStream {
private final InputStream original;
public LoggingInputStream(InputStream original) {
super();
this.original = original;
}
@Override
public int read(byte[] b) throws IOException {
// Logging here - and below.
return super.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return super.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return super.skip(n);
}
@Override
public int available() throws IOException {
return super.available();
}
@Override
public void close() throws IOException {
super.close();
}
@Override
public synchronized void mark(int readlimit) {
super.mark(readlimit);
}
@Override
public synchronized void reset() throws IOException {
super.reset();
}
@Override
public boolean markSupported() {
return super.markSupported();
}
@Override
public int read() throws IOException {
return original.read();
}
}
这是由 IntelliJ 自动生成的。
我会尝试Java方面。通过定义一些切入点,您可以监视或劫持其他 class 方法的行为,拦截参数,计算方法被调用的次数...
举个例子:
package org.bpa.fd;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class ListAspect {
@Pointcut("call(public * java.io.InputStream.*(..))")
public void inputStreamMethod() {
}
@Before("inputStreamMethod()")
public void anyMethod(JoinPoint jp) {
System.out.println(jp.toLongString());
}
}
使用此切入点,您将看到对 class InputStream 的 public 方法的任何调用。 JointPoint 还存储方法参数以获得更完整的概述。
您可以使用 gradle 导入此库:
buildscript {
repositories {
maven {
url "https://maven.eveoh.nl/content/repositories/releases"
}
}
dependencies {
classpath "nl.eveoh:gradle-aspectj:2.0"
}
}
project.ext {
aspectjVersion = '1.8.12'
}
apply plugin: 'aspectj'
如果您想执行类似 Java 动态代理的操作,proxy 可能是一种替代解决方案。它非常轻便且易于使用。
Maven 配置:
<dependency>
<groupId>com.ericsson.commonlibrary</groupId>
<artifactId>proxy</artifactId>
<version>1.2.0</version>
</dependency>
满足您要求的代码示例:
public class ProxyExample {
public static void main(String[] args) {
SomeImpl proxy = Proxy.intercept(new SomeImpl(), new MyInterceptor());
proxy.log("hello world");
//Output:
//SomeImpl.log
//hello world
}
public static class SomeImpl {
public void log(String log) {
System.out.println(log);
}
}
public static class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println(invocation.getThis().getClass().getSuperclass().getSimpleName() + "." + invocation.getMethodName());
Object returnObject = invocation.invoke(); //original method invocation.
return returnObject;
}
}
}
要扩展 Ning 关于使用代理的建议,
你可以简单地做这个 oneliner:
InputStream serverInputStream = Proxy.addTimerToMethods(this.serverProcess.getInputStream());
serverInputStream.anymethodreally();
// any usage will now be printed to console with parameters+ performance data
使用新的 Java 8+ API:
更清晰地重写了 Ning 的代理建议
InputStream serverInputStream = with(this.serverProcess.getInputStream())
.interceptAll(i -> {
Object result = i.invoke();
System.out.println("after method: " + i.getMethodName() + " param: " + i.getParameter0());
return result;
}).get();
serverInputStream.anymethodreally();
// any usage will now be printed to console(or whatever you decide to do!)
使用 Proxy 而不是 AspectJ 的绝对主要好处是不需要学习一些自定义编译或语言。
Proxy 基本上是一种更轻量级且易于使用的解决方案(当然也有限制)
使用代理与普通 decorator/interceptor/proxy class 相比,您当然不必创建一个新的 class 来实现每个方法。而且您也不必为每个要以这种方式拦截的额外 class 实施新的 decorator/interceptor/proxy class。
Proxy 基本上允许您创建一个可以应用于任何 class.
的通用方法拦截器
是否可以记录 class 实例的任何方法调用?我有以下实例:
InputStream serverInputStream = this.serverProcess.getInputStream();
我想在运行时的给定时间查看在 serverInputStream
中调用了哪些方法。我相信解决方案是反射,特别是代理。我已经尝试让 this example 工作,但无法做到 运行。
我想到了类似这样的代码:
MyInterceptor myInterceptor = new MyInterceptor(this.serverProcess.getInputStream());
InputStream serverInputStream = myInterceptor.getInterceptedInstance();
serverInputStream.methodOne();
serverInputStream.methodTwo();
serverInputStream.methodThree();
myInterceptor.printIntercepts();
得到类似这样的结果:
1. InputStream.InputStream();
2. InputStream.methodOne();
3. InputStream.methodTwo();
4. InputStream.methodThree();
这可能吗?
这里有两个选择:
- 使用 JDK 中包含的动态代理功能,或
- 使用字节码操作库,例如 ASM,
java.lang.reflect.Proxy只能生成一组接口的实现,而字节码操作库也可以修补现有的类。
你有什么理由不能只写一个吗?然后你可以用其中之一包裹你想看的 InputStream
。
class LoggingInputStream extends InputStream {
private final InputStream original;
public LoggingInputStream(InputStream original) {
super();
this.original = original;
}
@Override
public int read(byte[] b) throws IOException {
// Logging here - and below.
return super.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return super.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return super.skip(n);
}
@Override
public int available() throws IOException {
return super.available();
}
@Override
public void close() throws IOException {
super.close();
}
@Override
public synchronized void mark(int readlimit) {
super.mark(readlimit);
}
@Override
public synchronized void reset() throws IOException {
super.reset();
}
@Override
public boolean markSupported() {
return super.markSupported();
}
@Override
public int read() throws IOException {
return original.read();
}
}
这是由 IntelliJ 自动生成的。
我会尝试Java方面。通过定义一些切入点,您可以监视或劫持其他 class 方法的行为,拦截参数,计算方法被调用的次数...
举个例子:
package org.bpa.fd;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class ListAspect {
@Pointcut("call(public * java.io.InputStream.*(..))")
public void inputStreamMethod() {
}
@Before("inputStreamMethod()")
public void anyMethod(JoinPoint jp) {
System.out.println(jp.toLongString());
}
}
使用此切入点,您将看到对 class InputStream 的 public 方法的任何调用。 JointPoint 还存储方法参数以获得更完整的概述。
您可以使用 gradle 导入此库:
buildscript {
repositories {
maven {
url "https://maven.eveoh.nl/content/repositories/releases"
}
}
dependencies {
classpath "nl.eveoh:gradle-aspectj:2.0"
}
}
project.ext {
aspectjVersion = '1.8.12'
}
apply plugin: 'aspectj'
proxy 可能是一种替代解决方案。它非常轻便且易于使用。
Maven 配置:
<dependency>
<groupId>com.ericsson.commonlibrary</groupId>
<artifactId>proxy</artifactId>
<version>1.2.0</version>
</dependency>
满足您要求的代码示例:
public class ProxyExample { public static void main(String[] args) { SomeImpl proxy = Proxy.intercept(new SomeImpl(), new MyInterceptor()); proxy.log("hello world"); //Output: //SomeImpl.log //hello world } public static class SomeImpl { public void log(String log) { System.out.println(log); } } public static class MyInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println(invocation.getThis().getClass().getSuperclass().getSimpleName() + "." + invocation.getMethodName()); Object returnObject = invocation.invoke(); //original method invocation. return returnObject; } } }
要扩展 Ning 关于使用代理的建议, 你可以简单地做这个 oneliner:
InputStream serverInputStream = Proxy.addTimerToMethods(this.serverProcess.getInputStream());
serverInputStream.anymethodreally();
// any usage will now be printed to console with parameters+ performance data
使用新的 Java 8+ API:
更清晰地重写了 Ning 的代理建议 InputStream serverInputStream = with(this.serverProcess.getInputStream())
.interceptAll(i -> {
Object result = i.invoke();
System.out.println("after method: " + i.getMethodName() + " param: " + i.getParameter0());
return result;
}).get();
serverInputStream.anymethodreally();
// any usage will now be printed to console(or whatever you decide to do!)
使用 Proxy 而不是 AspectJ 的绝对主要好处是不需要学习一些自定义编译或语言。 Proxy 基本上是一种更轻量级且易于使用的解决方案(当然也有限制)
使用代理与普通 decorator/interceptor/proxy class 相比,您当然不必创建一个新的 class 来实现每个方法。而且您也不必为每个要以这种方式拦截的额外 class 实施新的 decorator/interceptor/proxy class。
Proxy 基本上允许您创建一个可以应用于任何 class.
的通用方法拦截器