【问题标题】:Is it possible to weave some classes from a jar while excluding the rest of the classes?是否可以从 jar 中编织一些类而排除其余类?
【发布时间】:2017-01-12 12:58:34
【问题描述】:

我正在尝试使用新功能扩展第三个库代码。

而且由于我只需要围绕一个类的一种方法注入一些代码,我想我可以:

  1. fork 项目并应用我的更改 (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)
  2. 使用 [AOP] 从一个巨大的 jar (seems cleaner) 中的一个类中截取一个方法并注入我的额外测试

如果你们中的任何人想知道,一个类不是 Spring bean,而是在代码中使用,所以我不能简单地扩展它并轻松覆盖/包装该方法,它至少需要几个额外的扩展和覆盖/包装层。所以 AspectJ 和 AOP 似乎是更好的选择。

我设法使用一些插件设置我的项目以调用ajc 并在-inpath 参数中使用我想要的jar 编织代码。唯一的问题是ajc 似乎编织了一切(或至少复制了它);

所以我需要的基本上是让AJC 简单地从那个罐子里挥动那个类,而不是整个罐子!

【问题讨论】:

  • 在我看来,这就像您构建中的一个简单的预处理/后处理步骤。编织您的 jar 依赖项,结果将是某处的一堆二进制类。取你需要的修改类,把原jar中的其他类不修改,打包成jar。
  • @NándorElődFekete 我的项目是一个 OSGI 包,当ajc 创建编织类时,它们会自动打包在包中。但是一些 3d 方类确实依赖于其他 3rd 方库(可选库),在我的情况下,这些库被从未到达的代码使用,但导致 OSJI 容器安装缺少依赖项的包
  • 在这种情况下,我没有看到 OSGI 有任何改变。当然,除非您不使用无头构建环境,而是依赖 Eclipse IDE 来构建您的项目。
  • 这就像使用一个精确的切入点一样简单,它实际上只是修改了您想要增强的一个类或方法。如果您可以描述您想以哪种方式更改哪个类/方法,我可以提供更精确的解决方案。查看您的方面代码也会有所帮助。为什么您仍然担心重复但未更改的类?
  • @kriegaex 我很担心它们,因为我在我的 OSGI 包上启用了动态导入,所以如果要在我的包 jar 中包含一个类,所有引用的包都需要在容器,以便能够安装/激活捆绑包,即使是那些由无法访问的代码引用的捆绑包。并且作为记录,我的切入点通过名称、限定符和参数列表 && 对象调用该方法引用了确切的方法,它不会因任何不需要的调用而触发;-)

标签: java aop aspectj bytecode aspectj-maven-plugin


【解决方案1】:

正如您所注意到的,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'(哦)替换为 '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 插件中使用了&lt;weaveDependencies&gt;(对于 AspectJ 编译器,它转换为 -inpath),并结合了删除所有不需要的类和 META- 的 Maven Clean 插件的特殊执行。来自原始 JAR 的 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 类文件。

【讨论】:

  • 首先感谢您的付出!您的回答表明您确实投入了一些努力。但我最终放弃了这种方法并重写了几层以实现所需的行为。即使我使用 gradle 来管理我的依赖项和构建,我还是想出了一个类似于你的解决方案,只是后来才发现我的包中存在该包的一个类会阻止 OSGI 容器导入其余的来自原始库的那个包中的类...
  • 由于我的包是 ECM 解决方案的插件,并且应该可以插入到该解决方案的多个版本中,我认为为每个版本的主机软件...并为此维护一个单独的项目。
  • 但是正如我所说,你的回答显示了很多努力,如果我没有整个 OSGi 复杂性,肯定会起作用,所以在这个问题上投票和标记为正确答案是当之无愧的案例!
  • 我没有使用 OSGi 的经验,所以我很抱歉错过了那部分。但显然你在尝试同样的事情时也注意到了。无论如何,感谢您接受答案。你最终做了什么?我想为每个版本创建编织依赖 JAR?
  • 感谢院长,这帮助我解决了一些高级别的 BS
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-02
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多