AspectJ 可以遍历 sun.net.* 包吗?
Can AspectJ weave through sun.net.* packages?
我正在使用 AspectJ 拦截 java.net.Socket 调用。
我创建了非常简单的方面
after(): call(* java.net.Socket.connect(..)) {
System.out.println("Connect intercepted!");
}
和aop.xml
<aspectj>
<aspects>
<aspect name="com.iggroup.lightstreamer.nwtp.SocketExceptionLoggingAspect"/>
</aspects>
<weaver options="-Xlint:ignore -Xset:weaveJavaxPackages=true -Xset:weaveJavaPackages=true">
</weaver>
</aspectj>
当调用栈是这样的时候,我可以看到控制台输出:
java.lang.Exception
at com.iggroup.lightstreamer.nwtp.SocketExceptionLoggingAspect.ajc$after$com_iggroup_lightstreamer_nwtp_SocketExceptionLoggingAspecte16217c(SocketExceptionLoggingAspect.aj:39)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:337)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:134)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:91)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:596)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:475)
at com.iggroup.lightstreamer.nwtp.users.SsoRestClientImpl.lambda[=13=](SsoRestClientImpl.java:68)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
但是当调用堆栈是这样的时候什么也没有注销:
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method) ~[na:1.8.0_65]
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79) ~[na:1.8.0_65]
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[na:1.8.0_65]
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[na:1.8.0_65]
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[na:1.8.0_65]
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) ~[na:1.8.0_65]
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_65]
at java.net.Socket.connect(Socket.java:589) ~[na:1.8.0_65]
at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668) ~[na:1.8.0_65]
at sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:173) ~[na:1.8.0_65]
at sun.net.NetworkClient.doConnect(NetworkClient.java:180) ~[na:1.8.0_65]
at sun.net.www.http.HttpClient.openServer(HttpClient.java:432) ~[na:1.8.0_65]
at sun.net.www.http.HttpClient.openServer(HttpClient.java:527) ~[na:1.8.0_65]
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:781) ~[na:1.8.0_65]
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647) ~[na:1.8.0_65]
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1536) ~[na:1.8.0_65]
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441) ~[na:1.8.0_65]
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254) ~[na:1.8.0_65]
at com.lightstreamer.ls_client.HttpProvider.connectAndGetAnswer(HttpProvider.java:244) ~[lightstreamer-se-client-2.5.2-1110.jar:na]
我想知道是否是因为 sun.net.*
包由于某些安全管理器限制而未加载时编织。
有人知道如何使用 sun.net.*
包吗?
更新 1
我确认我可以拦截 ls_client.HttpProvider.connectAndGetAnswer
调用,但不能拦截其上方的调用 (sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream
)。
sun.*
可以用 AspectJ 编织吗?
我还没有看到 JRE (bootstrap) classes 的加载时编织的成功设置。如果您出于调试目的需要它,我会改用 JRE classes 的构建时间编织。
这个简短的片段将为您编织 JRE jar,并将编织的 classes 放入单个输出 jar 中。它需要 org.aspectj/aspectjtools
作为依赖项。它还会跳过 JRE 的 ext
子文件夹中的 jars,因为这些 jars 将包含一些重复的 classes 并且创建包含重复文件的 jar 文件将导致错误。我也从较新的 JRE 版本中跳过 jfxswt.jar
,因为它会因为缺少 classes.
而失败
String aspectFileName = "src/main/java/pckg/AspectName.aj";
String jreLibPath = "c:/Program Files/Java/jdk1.8.0_40/jre/lib";
String outputJar = "weavedjre.jar";
List<String> jars = new ArrayList<>();
File dir = new File(jreLibPath);
File[] files = dir.listFiles();
for (File file : files) {
if (file.isFile() && file.getName().endsWith(".jar")
&& !file.getName().endsWith("jfxswt.jar")) {
jars.add(file.getAbsolutePath());
}
}
List<String> ajcArgs = new ArrayList<>(Arrays.asList("-showWeaveInfo"));
for (String jar : jars) {
ajcArgs.add("-inpath");
ajcArgs.add(jar);
}
ajcArgs.add(aspectFileName);
ajcArgs.add("-outjar");
ajcArgs.add(outputJar);
org.aspectj.tools.ajc.Main.main(ajcArgs.toArray(new String[] {}));
然后 运行 您的程序使用以下 VM 参数来使用编织的 JRE classes(添加到您的引导 classpath 之前):
-verbose:class -Xbootclasspath/p:path_to/weavedjre.jar
或在 Eclipse 启动配置中:
-verbose:class -Xbootclasspath/p:${resource_loc:/project_name/weavedjre.jar}
我也添加了 class 加载 VM 参数的详细日志记录,因此您可以看到从哪里加载了哪个 class。
Nándor 是对的。我不能在 JRE 库上执行 LTW,因为它们在引导类路径上并且在 AspectJ 可以编织它们之前加载。我不得不进行编译时编织并替换默认库。
这是我所做的:
您需要的文件
workspace
|- Aspect.aj
|- rt.jar
|- aspectjrt-1.8.7.jar
|- aspectjtools-1.8.7.jar
Aspect.aj
package java.net;
import sun.misc.SharedSecrets;
import java.io.FileDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public aspect Aspect {
private static Class dualStackPlainSocketImplClass;
private static Method localPort0;
static {
try {
dualStackPlainSocketImplClass = Class.forName("java.net.DualStackPlainSocketImpl");
localPort0 = dualStackPlainSocketImplClass.getDeclaredMethod("localPort0", Integer.TYPE);
localPort0.setAccessible(true);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
pointcut connect(java.net.DualStackPlainSocketImpl s): target(s) && execution(* java.net.DualStackPlainSocketImpl.socketConnect(..));
after(DualStackPlainSocketImpl s) throwing (Exception e): connect(s) {
try {
Field fdf = dualStackPlainSocketImplClass.getSuperclass().getSuperclass().getDeclaredField("fd");
fdf.setAccessible(true);
FileDescriptor fd = (FileDescriptor) fdf.get(s);
int nativeFd = SharedSecrets.getJavaIOFileDescriptorAccess().get(fd);
int localPort = (int) localPort0.invoke(dualStackPlainSocketImplClass, nativeFd);
System.out.format("[local port=%s][exception=%s]\n", localPort, e.getMessage());
} catch (InvocationTargetException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
} catch (NoSuchFieldException e1) {
e1.printStackTrace();
} catch (NullPointerException e1) {
e1.printStackTrace();
}
}
}
aspect文件很简单,比较棘手的是如何通过反射获取本地端口
请注意 package java.net;
很重要,因为一些 classes/methods 受到保护。
然后 运行 来自工作区
java -cp "aspectjrt-1.8.7.jar;aspectjtools-1.8.7.jar" org.aspectj.tools.ajc.Main -1.8 -inpath rt.jar Aspect.aj -outjar newrt.jar
你会得到一个newrt.jar
。
为了运行你的程序,
java -cp <your_class_path> -Xbootclasspath/p:<path_to_newrt.jar> <main_class>
-Xbootclasspath/p
将 newrt.jar
添加到引导类路径并覆盖 JDK 默认值。
我正在使用 AspectJ 拦截 java.net.Socket 调用。
我创建了非常简单的方面
after(): call(* java.net.Socket.connect(..)) {
System.out.println("Connect intercepted!");
}
和aop.xml
<aspectj>
<aspects>
<aspect name="com.iggroup.lightstreamer.nwtp.SocketExceptionLoggingAspect"/>
</aspects>
<weaver options="-Xlint:ignore -Xset:weaveJavaxPackages=true -Xset:weaveJavaPackages=true">
</weaver>
</aspectj>
当调用栈是这样的时候,我可以看到控制台输出:
java.lang.Exception
at com.iggroup.lightstreamer.nwtp.SocketExceptionLoggingAspect.ajc$after$com_iggroup_lightstreamer_nwtp_SocketExceptionLoggingAspecte16217c(SocketExceptionLoggingAspect.aj:39)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:337)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:134)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:91)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:596)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:475)
at com.iggroup.lightstreamer.nwtp.users.SsoRestClientImpl.lambda[=13=](SsoRestClientImpl.java:68)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
但是当调用堆栈是这样的时候什么也没有注销:
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method) ~[na:1.8.0_65]
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79) ~[na:1.8.0_65]
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[na:1.8.0_65]
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[na:1.8.0_65]
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[na:1.8.0_65]
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) ~[na:1.8.0_65]
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_65]
at java.net.Socket.connect(Socket.java:589) ~[na:1.8.0_65]
at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668) ~[na:1.8.0_65]
at sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:173) ~[na:1.8.0_65]
at sun.net.NetworkClient.doConnect(NetworkClient.java:180) ~[na:1.8.0_65]
at sun.net.www.http.HttpClient.openServer(HttpClient.java:432) ~[na:1.8.0_65]
at sun.net.www.http.HttpClient.openServer(HttpClient.java:527) ~[na:1.8.0_65]
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:781) ~[na:1.8.0_65]
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647) ~[na:1.8.0_65]
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1536) ~[na:1.8.0_65]
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441) ~[na:1.8.0_65]
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254) ~[na:1.8.0_65]
at com.lightstreamer.ls_client.HttpProvider.connectAndGetAnswer(HttpProvider.java:244) ~[lightstreamer-se-client-2.5.2-1110.jar:na]
我想知道是否是因为 sun.net.*
包由于某些安全管理器限制而未加载时编织。
有人知道如何使用 sun.net.*
包吗?
更新 1
我确认我可以拦截 ls_client.HttpProvider.connectAndGetAnswer
调用,但不能拦截其上方的调用 (sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream
)。
sun.*
可以用 AspectJ 编织吗?
我还没有看到 JRE (bootstrap) classes 的加载时编织的成功设置。如果您出于调试目的需要它,我会改用 JRE classes 的构建时间编织。
这个简短的片段将为您编织 JRE jar,并将编织的 classes 放入单个输出 jar 中。它需要 org.aspectj/aspectjtools
作为依赖项。它还会跳过 JRE 的 ext
子文件夹中的 jars,因为这些 jars 将包含一些重复的 classes 并且创建包含重复文件的 jar 文件将导致错误。我也从较新的 JRE 版本中跳过 jfxswt.jar
,因为它会因为缺少 classes.
String aspectFileName = "src/main/java/pckg/AspectName.aj";
String jreLibPath = "c:/Program Files/Java/jdk1.8.0_40/jre/lib";
String outputJar = "weavedjre.jar";
List<String> jars = new ArrayList<>();
File dir = new File(jreLibPath);
File[] files = dir.listFiles();
for (File file : files) {
if (file.isFile() && file.getName().endsWith(".jar")
&& !file.getName().endsWith("jfxswt.jar")) {
jars.add(file.getAbsolutePath());
}
}
List<String> ajcArgs = new ArrayList<>(Arrays.asList("-showWeaveInfo"));
for (String jar : jars) {
ajcArgs.add("-inpath");
ajcArgs.add(jar);
}
ajcArgs.add(aspectFileName);
ajcArgs.add("-outjar");
ajcArgs.add(outputJar);
org.aspectj.tools.ajc.Main.main(ajcArgs.toArray(new String[] {}));
然后 运行 您的程序使用以下 VM 参数来使用编织的 JRE classes(添加到您的引导 classpath 之前):
-verbose:class -Xbootclasspath/p:path_to/weavedjre.jar
或在 Eclipse 启动配置中:
-verbose:class -Xbootclasspath/p:${resource_loc:/project_name/weavedjre.jar}
我也添加了 class 加载 VM 参数的详细日志记录,因此您可以看到从哪里加载了哪个 class。
Nándor 是对的。我不能在 JRE 库上执行 LTW,因为它们在引导类路径上并且在 AspectJ 可以编织它们之前加载。我不得不进行编译时编织并替换默认库。
这是我所做的:
您需要的文件
workspace
|- Aspect.aj
|- rt.jar
|- aspectjrt-1.8.7.jar
|- aspectjtools-1.8.7.jar
Aspect.aj
package java.net;
import sun.misc.SharedSecrets;
import java.io.FileDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public aspect Aspect {
private static Class dualStackPlainSocketImplClass;
private static Method localPort0;
static {
try {
dualStackPlainSocketImplClass = Class.forName("java.net.DualStackPlainSocketImpl");
localPort0 = dualStackPlainSocketImplClass.getDeclaredMethod("localPort0", Integer.TYPE);
localPort0.setAccessible(true);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
pointcut connect(java.net.DualStackPlainSocketImpl s): target(s) && execution(* java.net.DualStackPlainSocketImpl.socketConnect(..));
after(DualStackPlainSocketImpl s) throwing (Exception e): connect(s) {
try {
Field fdf = dualStackPlainSocketImplClass.getSuperclass().getSuperclass().getDeclaredField("fd");
fdf.setAccessible(true);
FileDescriptor fd = (FileDescriptor) fdf.get(s);
int nativeFd = SharedSecrets.getJavaIOFileDescriptorAccess().get(fd);
int localPort = (int) localPort0.invoke(dualStackPlainSocketImplClass, nativeFd);
System.out.format("[local port=%s][exception=%s]\n", localPort, e.getMessage());
} catch (InvocationTargetException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
} catch (NoSuchFieldException e1) {
e1.printStackTrace();
} catch (NullPointerException e1) {
e1.printStackTrace();
}
}
}
aspect文件很简单,比较棘手的是如何通过反射获取本地端口
请注意 package java.net;
很重要,因为一些 classes/methods 受到保护。
然后 运行 来自工作区
java -cp "aspectjrt-1.8.7.jar;aspectjtools-1.8.7.jar" org.aspectj.tools.ajc.Main -1.8 -inpath rt.jar Aspect.aj -outjar newrt.jar
你会得到一个newrt.jar
。
为了运行你的程序,
java -cp <your_class_path> -Xbootclasspath/p:<path_to_newrt.jar> <main_class>
-Xbootclasspath/p
将 newrt.jar
添加到引导类路径并覆盖 JDK 默认值。