是否可以从罐子中编织一些 类 而排除其余的 类?
Is it possible to weave some classes from a jar while excluding the rest of the classes?
我正在尝试使用新功能扩展第三个库代码。
并且由于我只需要围绕一个方法从一个 class 注入一些代码,我想我可以:
- 分叉项目并应用我的更改 (
seems sloppy, and will definitely require lots of work whenever I try to upgrade to a newer version of the lib, not to mention licensing nightmare
)
- 使用 [AOP] 从一个巨大的 jar (
seems cleaner
) 中的那个 class 拦截那个方法并注入我的额外测试
万一你们想知道,那个 class 不是 spring bean 并且在代码深处使用,所以我不能简单地扩展它和 override/wrap 这种方法很容易,它至少需要额外的几层 extend&override/wrap。所以 AspectJ 和 AOP 似乎是更好的选择。
我设法用一些插件设置我的项目来调用 ajc
并在 -inpath
参数中用我想要的 jar 编织代码。唯一的问题是 ajc
似乎编织了一切(或者至少复制了它);
所以我基本上需要的是让 AJC
简单地挥动那个罐子里的那个 class,而不是整个罐子!
如您所见,AspectJ 编译器总是输出在 weave 依赖项 (in-JARs) 中找到的所有文件,无论它们是否更改。无法通过命令行 AFAIK 更改此行为。所以你需要自己打包 JAR。
这是一个示例项目,包括。 Maven POM 向您展示了如何做到这一点。我选择了一个涉及 Apache Commons Codec 的相当愚蠢的例子:
示例应用程序:
应用程序对文本进行 base64 编码,再次解码并将两个文本打印到控制台。
package de.scrum_master.app;
import org.apache.commons.codec.binary.Base64;
public class Application {
public static void main(String[] args) throws Exception {
String originalText = "Hello world!";
System.out.println(originalText);
byte[] encodedBytes = Base64.encodeBase64(originalText.getBytes());
String decodedText = new String(Base64.decodeBase64(encodedBytes));
System.out.println(decodedText);
}
}
通常输出如下所示:
Hello world!
Hello world!
这里没有惊喜。但是现在我们定义了一个方面来操纵从第三方库返回的结果,用'0'(零)替换每个字符'o'(哦):
package de.scrum_master.aspect;
import org.apache.commons.codec.binary.Base64;
public aspect Base64Manipulator {
byte[] around() : execution(byte[] Base64.decodeBase64(byte[])) {
System.out.println(thisJoinPoint);
byte[] result = proceed();
for (int i = 0; i < result.length; i++) {
if (result[i] == 'o')
result[i] = '0';
}
return result;
}
}
顺便说一句,如果您只是在这里使用 call()
而不是 execution()
,则无需实际编入第三方代码。但无论如何,你要求它,所以我向你展示如何去做。
Maven POM:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.Whosebug</groupId>
<artifactId>aspectj-weave-single-3rd-party-class</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source-target.version>1.8</java.source-target.version>
<aspectj.version>1.8.10</aspectj.version>
<main-class>de.scrum_master.app.Application</main-class>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<!-- IMPORTANT -->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.9</version>
<configuration>
<!--<showWeaveInfo>true</showWeaveInfo>-->
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<Xlint>ignore</Xlint>
<complianceLevel>${java.source-target.version}</complianceLevel>
<encoding>${project.build.sourceEncoding}</encoding>
<!--<verbose>true</verbose>-->
<!--<warn>constructorName,packageDefaultMethod,deprecation,maskedCatchBlocks,unusedLocals,unusedArguments,unusedImport</warn>-->
<weaveDependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
</weaveDependencies>
</configuration>
<executions>
<execution>
<!-- IMPORTANT -->
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.5.0</version>
<configuration>
<mainClass>${main-class}</mainClass>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>remove-unwoven</id>
<!-- Phase 'process-classes' is in between 'compile' and 'package' -->
<phase>process-classes</phase>
<goals>
<goal>clean</goal>
</goals>
<configuration>
<!-- No full clean, only what is specified in 'filesets' -->
<excludeDefaultDirectories>true</excludeDefaultDirectories>
<filesets>
<fileset>
<directory>${project.build.outputDirectory}</directory>
<includes>
<include>org/apache/commons/codec/**</include>
<include>META-INF/**</include>
</includes>
<excludes>
<exclude>**/Base64.class</exclude>
</excludes>
</fileset>
</filesets>
<!-- Set to true if you want to see what exactly gets deleted -->
<verbose>false</verbose>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
</dependencies>
<organization>
<name>Scrum-Master.de - Agile Project Management</name>
<url>http://scrum-master.de</url>
</organization>
</project>
如您所见,我在 AspectJ Maven 插件中使用了 <weaveDependencies>
(对于 AspectJ 编译器来说转换为 -inpath
)并结合了 Maven Clean 插件的特殊执行,该插件删除了所有不需要的 classes 和原始 JAR 中的 META-INF 目录。
如果你 运行 mvn clean package exec:java
你看到:
[INFO] ------------------------------------------------------------------------
[INFO] Building aspectj-weave-single-3rd-party-class 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
(...)
[INFO] --- aspectj-maven-plugin:1.9:compile (default) @ aspectj-weave-single-3rd-party-class ---
[INFO] Showing AJC message detail for messages of types: [error, warning, fail]
(...)
[INFO] --- maven-clean-plugin:2.5:clean (remove-unwoven) @ aspectj-weave-single-3rd-party-class ---
[INFO] Deleting C:\Users\Alexander\Documents\java-src\SO_AJ_MavenWeaveSingle3rdPartyClass\target\classes (includes = [org/apache/commons/codec/**, META-INF/**], excludes = [**/Base64.class])
(...)
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ aspectj-weave-single-3rd-party-class ---
[INFO] Building jar: C:\Users\Alexander\Documents\java-src\SO_AJ_MavenWeaveSingle3rdPartyClass\target\aspectj-weave-single-3rd-party-class-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- exec-maven-plugin:1.5.0:java (default-cli) @ aspectj-weave-single-3rd-party-class ---
Hello world!
execution(byte[] org.apache.commons.codec.binary.Base64.decodeBase64(byte[]))
Hell0 w0rld!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
这就是我的 target/classes
目录在构建后的样子:
如您所见,创建的 JAR 中只剩下一个 Apache Commons class 文件。
我正在尝试使用新功能扩展第三个库代码。
并且由于我只需要围绕一个方法从一个 class 注入一些代码,我想我可以:
- 分叉项目并应用我的更改 (
seems sloppy, and will definitely require lots of work whenever I try to upgrade to a newer version of the lib, not to mention licensing nightmare
) - 使用 [AOP] 从一个巨大的 jar (
seems cleaner
) 中的那个 class 拦截那个方法并注入我的额外测试
万一你们想知道,那个 class 不是 spring bean 并且在代码深处使用,所以我不能简单地扩展它和 override/wrap 这种方法很容易,它至少需要额外的几层 extend&override/wrap。所以 AspectJ 和 AOP 似乎是更好的选择。
我设法用一些插件设置我的项目来调用 ajc
并在 -inpath
参数中用我想要的 jar 编织代码。唯一的问题是 ajc
似乎编织了一切(或者至少复制了它);
所以我基本上需要的是让 AJC
简单地挥动那个罐子里的那个 class,而不是整个罐子!
如您所见,AspectJ 编译器总是输出在 weave 依赖项 (in-JARs) 中找到的所有文件,无论它们是否更改。无法通过命令行 AFAIK 更改此行为。所以你需要自己打包 JAR。
这是一个示例项目,包括。 Maven POM 向您展示了如何做到这一点。我选择了一个涉及 Apache Commons Codec 的相当愚蠢的例子:
示例应用程序:
应用程序对文本进行 base64 编码,再次解码并将两个文本打印到控制台。
package de.scrum_master.app;
import org.apache.commons.codec.binary.Base64;
public class Application {
public static void main(String[] args) throws Exception {
String originalText = "Hello world!";
System.out.println(originalText);
byte[] encodedBytes = Base64.encodeBase64(originalText.getBytes());
String decodedText = new String(Base64.decodeBase64(encodedBytes));
System.out.println(decodedText);
}
}
通常输出如下所示:
Hello world!
Hello world!
这里没有惊喜。但是现在我们定义了一个方面来操纵从第三方库返回的结果,用'0'(零)替换每个字符'o'(哦):
package de.scrum_master.aspect;
import org.apache.commons.codec.binary.Base64;
public aspect Base64Manipulator {
byte[] around() : execution(byte[] Base64.decodeBase64(byte[])) {
System.out.println(thisJoinPoint);
byte[] result = proceed();
for (int i = 0; i < result.length; i++) {
if (result[i] == 'o')
result[i] = '0';
}
return result;
}
}
顺便说一句,如果您只是在这里使用 call()
而不是 execution()
,则无需实际编入第三方代码。但无论如何,你要求它,所以我向你展示如何去做。
Maven POM:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.Whosebug</groupId>
<artifactId>aspectj-weave-single-3rd-party-class</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source-target.version>1.8</java.source-target.version>
<aspectj.version>1.8.10</aspectj.version>
<main-class>de.scrum_master.app.Application</main-class>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<!-- IMPORTANT -->
<useIncrementalCompilation>false</useIncrementalCompilation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.9</version>
<configuration>
<!--<showWeaveInfo>true</showWeaveInfo>-->
<source>${java.source-target.version}</source>
<target>${java.source-target.version}</target>
<Xlint>ignore</Xlint>
<complianceLevel>${java.source-target.version}</complianceLevel>
<encoding>${project.build.sourceEncoding}</encoding>
<!--<verbose>true</verbose>-->
<!--<warn>constructorName,packageDefaultMethod,deprecation,maskedCatchBlocks,unusedLocals,unusedArguments,unusedImport</warn>-->
<weaveDependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
</weaveDependencies>
</configuration>
<executions>
<execution>
<!-- IMPORTANT -->
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.5.0</version>
<configuration>
<mainClass>${main-class}</mainClass>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<id>remove-unwoven</id>
<!-- Phase 'process-classes' is in between 'compile' and 'package' -->
<phase>process-classes</phase>
<goals>
<goal>clean</goal>
</goals>
<configuration>
<!-- No full clean, only what is specified in 'filesets' -->
<excludeDefaultDirectories>true</excludeDefaultDirectories>
<filesets>
<fileset>
<directory>${project.build.outputDirectory}</directory>
<includes>
<include>org/apache/commons/codec/**</include>
<include>META-INF/**</include>
</includes>
<excludes>
<exclude>**/Base64.class</exclude>
</excludes>
</fileset>
</filesets>
<!-- Set to true if you want to see what exactly gets deleted -->
<verbose>false</verbose>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
</dependencies>
<organization>
<name>Scrum-Master.de - Agile Project Management</name>
<url>http://scrum-master.de</url>
</organization>
</project>
如您所见,我在 AspectJ Maven 插件中使用了 <weaveDependencies>
(对于 AspectJ 编译器来说转换为 -inpath
)并结合了 Maven Clean 插件的特殊执行,该插件删除了所有不需要的 classes 和原始 JAR 中的 META-INF 目录。
如果你 运行 mvn clean package exec:java
你看到:
[INFO] ------------------------------------------------------------------------
[INFO] Building aspectj-weave-single-3rd-party-class 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
(...)
[INFO] --- aspectj-maven-plugin:1.9:compile (default) @ aspectj-weave-single-3rd-party-class ---
[INFO] Showing AJC message detail for messages of types: [error, warning, fail]
(...)
[INFO] --- maven-clean-plugin:2.5:clean (remove-unwoven) @ aspectj-weave-single-3rd-party-class ---
[INFO] Deleting C:\Users\Alexander\Documents\java-src\SO_AJ_MavenWeaveSingle3rdPartyClass\target\classes (includes = [org/apache/commons/codec/**, META-INF/**], excludes = [**/Base64.class])
(...)
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ aspectj-weave-single-3rd-party-class ---
[INFO] Building jar: C:\Users\Alexander\Documents\java-src\SO_AJ_MavenWeaveSingle3rdPartyClass\target\aspectj-weave-single-3rd-party-class-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- exec-maven-plugin:1.5.0:java (default-cli) @ aspectj-weave-single-3rd-party-class ---
Hello world!
execution(byte[] org.apache.commons.codec.binary.Base64.decodeBase64(byte[]))
Hell0 w0rld!
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
这就是我的 target/classes
目录在构建后的样子:
如您所见,创建的 JAR 中只剩下一个 Apache Commons class 文件。