我正在尝试使用新功能扩展第3个lib代码。
因为我只需要从一个类中为一个方法注入一些代码,我想我可以:
分叉项目并应用我的更改( 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 )并注入我的额外测试如果您想知道任何一个类,那么一个类不是一个Spring bean并且在代码中深入使用,所以我不能简单地扩展它并轻松覆盖/包装该方法,它至少需要几个额外的层延伸覆盖&/包装。 所以AspectJ和AOP似乎是更好的方式。
我设法用一些插件设置我的项目来调用ajc并在-inpath param中用我想要的jar编写代码。 唯一的问题是ajc似乎编织了所有东西(或者至少重复它);
所以我最需要的就是让AJC简单地从那个jar中挥动那个类,而不是整个jar!
I am trying to to extend a 3rd lib code with new capabilities.
And since I only need to inject some code around one method from one class, I figured I can either:
Fork the project and apply my changes (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) Use [AOP] to intercept that one method from that one class inside a huge jar (seems cleaner) and inject my extra testsIn case any of you wants to know, that one class is not a spring bean and is used deep in the code, so I can not simply extend it and override/wrap that method easily, it would require at least a couple extra layers of extend&override/wrap. So AspectJ & AOP seems like the better way to go.
I managed to set my project up with some plugin to invoke ajc and weave the code with my desired jar in the -inpath param. And the only problem is that ajc seems to weave everything (or at least duplicate it);
So what I need basically is to ask AJC to simply wave that class from that jar, and not the whole jar !
最满意答案
正如您所注意到的,AspectJ编译器始终输出在weave依赖项(in-JAR)中找到的所有文件,无论它们是否被更改。 无法通过命令行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!这里没有惊喜。 但是现在我们定义一个方面来操纵从第三方库返回的结果,将每个字符'o'(oh)替换为'0'(零):
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.stackoverflow</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插件的特殊执行,删除所有不需要的类和META-INF目录。原始JAR。
如果你运行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目录在构建之后的样子:
如您所见,只剩下一个Apache Commons类文件进入创建的JAR。
As you have noticed, the AspectJ compiler always outputs all files found in weave dependencies (in-JARs), no matter if they are changed or not. This behaviour cannot be changed via command line, AFAIK. So you need to take care of packaging your JARs by yourself.
Here is a sample project incl. Maven POM showing you how to do that. I have chosen a rather stupid example involving Apache Commons Codec:
Sample application:
The application base64-encodes a text, decodes it again and prints both texts to console.
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); } }Normally the output looks like this:
Hello world! Hello world!No surprises here. But now we define an aspect which manipulates the results returned from the third party library, replacing each character 'o' (oh) by '0' (zero):
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; } }BTW, if you would just use call() instead of execution() here, there would be no need to actually weave into third party code. But anyway, you asked for it, so I am showing you how to do it.
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.stackoverflow</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>As you can see I am using <weaveDependencies> in the AspectJ Maven plugin (which translates to -inpath for the AspectJ compiler) in combination with a special execution of the Maven Clean plugin that deletes all unneeded classes and the META-INF directory from the original JAR.
If the you run mvn clean package exec:java you see:
[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] ------------------------------------------------------------------------And this is what my target/classes directory looks like after the build:
As you can see, there is only one Apache Commons class file left which goes into the created JAR.
更多推荐
发布评论