是否可以在编译时将 AspectJ 编织到 WAR 打包项目中?
Is it possible to compile-time weave AspectJ into WAR packaged projects?
有很多关于编织成 JAR 文件的文档,例如书中描述的示例 AspectJ in Action: Second Edition。
但是,我几乎看不到任何关于编入 WAR 文件的问题。由于 WAR 文件和 AspectJ 库的流行,我很难相信它不受支持,所以我希望得到一些答案。
假设您有一个 "legacy" Java 项目,该项目正在使用 maven-war-plugin 插件打包为 WAR 文件。请记住,由于所涉及的风险,将其从 WAR 更改为 JAR 包装几乎是不可能的。
现在,假设我想为一些横切功能引入 AOP。由于我们应用程序的性质,我最好的选择是创建一个单独的项目。所以基本上,方面 类 不会与遗留项目在同一个项目中。它将是分开的。
在这部分之后,我卡住了。从阅读本书和其他在线文档来看,编译时编织似乎有两种选择:
- 直接编织资源。这可以通过直接在遗留项目中创建方面 类 来完成,然后使用 aspectj-maven-plugin 插件。这不是一个真正的选择,因为我希望我的 AOP 逻辑影响多个项目,而不仅仅是一个项目。
- 创建一个单独的库并使用 aspectj-maven-plugin 插件编译它。然后创建另一个 maven 项目,它将采用 unwoven 应用程序和方面库,并将它们编织在一起。书中的例子:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<weaveDependencies>
<weaveDependency>
<groupId>ajia.helloworld</groupId>
<artifactId>application-unwoven</artifactId>
</weaveDependency>
</weaveDependencies>
<aspectLibraries>
<aspectLibrary>
<groupId>ajia.helloworld</groupId>
<artifactId>profiling-aspect-library</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
</plugin>
</plugins>
</build>
第二个选项的问题是 <weaveDependency>
要求应用程序是 JAR 文件。至少,据我所知。
我确实在 AspectJ 邮件列表上遇到过类似的问题:http://aspectj.2085585.n4.nabble.com/Weaving-into-a-war-file-td2082924.html。
如果 link 过期,问题的答案是:
you need to take it apart, weave the .jar and put it back together.
You cannot directly weave the jar within the war
但这可能吗?我也没有看到任何相关文档。
SO 上有类似的问题,要么根本没有任何回复,要么 wrong/outdated。
我将不胜感激,因为我不知道我的下一步应该是什么。
我将尝试提出两种截然不同的解决方案,然后您可以pick/choose 或 mix/match 如何实施您的解决方案。
示例 1:您被要求将一些文本转储到 [=18= 中找到的 org.apache.log4j.Logger.info(Object o)
方法的 stdout 前和 post-execution ].
(这是一个非常人为的例子,但我使用它是因为我被要求做一些与过去并不完全不同的事情!)
我假设您使用的是 Multi-Module Maven Project。
为了实现这一点,我们可以使用 log4j
jar(已编译 类),对它们应用一个方面,并吐出一组新的 load-time-woven 已编译 类.我们通过利用您已经在其自己的模块中调用的 weaveDependency 功能来实现这一点。因此,您将不再依赖项目中的 log4j
,而是依赖 ltwjar-enhanced-log4j
,如下所述。
考虑到这一点,创建您的项目层次结构:
ltwjar
/ltwjar-ltwjar-enhanced-log4j
/ltwjar-runtime
将您的父级 pom.xml 设置为如下所示(我们在这里设置了几个属性只是为了 convenience/consistency - 还有其他方法可以控制版本控制,例如 dependencyManagement and imported depdendencies ,但那是针对不同的问题!):
<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>com.example</groupId>
<artifactId>ltwjar</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<aspectj.version>1.8.13</aspectj.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<modules>
<module>ltwjar-enhanced-log4j</module>
<module>ltwjar-runtime</module>
</modules>
</project>
像这样设置您的 ltwjar-enhanced-log4j
pom.xml:
<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>
<parent>
<groupId>com.example</groupId>
<artifactId>ltwjar</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>ltwjar-enhanced-log4j</artifactId>
<dependencies>
<!-- we need this because the post-weave classes may (will) depend on aspectj types -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- ATTENTION! Scope this to provided otherwise it won't work - because of transitive dependencies,
anything that depends on this module will end up getting the _actual_ log4j classes versus the ones we are enriching! -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<!-- instruct aspectj to weave the classes in the jar! -->
<weaveDependencies>
<weaveDependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</weaveDependency>
</weaveDependencies>
<Xlint>warning</Xlint>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
现在让我们设置我们的存根 "runtime" 模块以利用 load-time-wove log4j ltw-enhanced-log4j
通过配置其 pom.xml 进行记录,因此:
<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>
<parent>
<groupId>com.example</groupId>
<artifactId>ltwjar</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>ltwjar-runtime</artifactId>
<dependencies>
<!-- Note that this module doesn't care (itself) about aspectj.
It _does_ care that it depends on the ltwjar-enhanced-log4j module
and not log4j, however. So let's depend on that! -->
<dependency>
<groupId>com.example</groupId>
<artifactId>ltwjar-enhanced-log4j</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
好的,这就是我们的框架设置。现在让我们创建一个毫无意义的方面来证明这一点!
在 ltwjar-enhanced-log4j
中创建类型 com.example.aspect.LoggingAspect
,如下所示:
package com.example.aspect;
import org.apache.log4j.Logger;
public aspect LoggingAspect {
// this pointcut will match "info(Object o)" method executions where the
// target is an instanceof Logger
pointcut logInfoWithObject(Object obj, Logger logger) :
execution(void info(Object)) && args(obj) && target(logger);
// here is our advice - simply sysout something before the execution
// proceeds and after it has finished - regardless of outcome (exception
// or not).
void around(Object obj, Logger logger) : logInfoWithObject(obj, logger) {
System.out.println("Before Logger.info(Object o)");
try {
proceed(obj, logger);
} finally {
System.out.println("After Logger.info(Object o)");
}
}
}
最后在 ltwjar-runtime
中,创建一个显示一切正常的 driver/harness。输入 com.example.Driver
:
package com.example;
import org.apache.log4j.Logger;
public class Driver {
private static final Logger LOG = Logger.getLogger(Driver.class);
public static void main(String[] args) {
LOG.info("In main");
}
}
现在从您的父项目 运行 mvn clean install
然后 mvn -pl ltwjar-runtime exec:java -Dexec.mainClass="com.example.Driver"
.
输出应该是这样的(因为你的类路径上没有 log4j.xml):
Before Logger.info(Object o)
log4j:WARN No appenders could be found for logger (com.example.Driver).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
After Logger.info(Object o)
注意第一行和最后一行。
这里发生了什么:
- 在
ltwjar-enhanced-log4j
中,我们 "enhance" log4j 通过围绕 Logger.info(Object o)
包装一个建议
- 我们将
log4j.jar
重新打包到一个包含编织类型的新罐子 ltwjar-enhanced-log4j.jar
中
- 我们在应用程序代码中依赖
ltwjar-enhanced-log4j.jar
而不是 log4j
...
- ...在这样做的过程中,建议坚持
好的,那么这如何适应最初询问的 war 文件?
简单 - 不要在 war 文件模块中放置任何代码。如果你必须把东西放在那里,让它配置。相反,将所有代码移动到 yourproject-domain
或 yourproject-logic
或 ...(在此处插入模块名称)并让您的 war 文件依赖于 that 模块。这是一个很好的做法,因为它启用了 Separation of Concerns:您的应用程序不应该知道它 运行 在 war 文件保护伞下;它可以像 Spring Boot tomcat uberwar 一样容易 运行ning 等。包装问题 (war) 不同于业务问题 (域)。
今晚我将更新共享方面库的另一个示例,除非有人抢先一步。另外,如果其中一些没有意义,请在评论中大声疾呼!
有很多关于编织成 JAR 文件的文档,例如书中描述的示例 AspectJ in Action: Second Edition。
但是,我几乎看不到任何关于编入 WAR 文件的问题。由于 WAR 文件和 AspectJ 库的流行,我很难相信它不受支持,所以我希望得到一些答案。
假设您有一个 "legacy" Java 项目,该项目正在使用 maven-war-plugin 插件打包为 WAR 文件。请记住,由于所涉及的风险,将其从 WAR 更改为 JAR 包装几乎是不可能的。
现在,假设我想为一些横切功能引入 AOP。由于我们应用程序的性质,我最好的选择是创建一个单独的项目。所以基本上,方面 类 不会与遗留项目在同一个项目中。它将是分开的。
在这部分之后,我卡住了。从阅读本书和其他在线文档来看,编译时编织似乎有两种选择:
- 直接编织资源。这可以通过直接在遗留项目中创建方面 类 来完成,然后使用 aspectj-maven-plugin 插件。这不是一个真正的选择,因为我希望我的 AOP 逻辑影响多个项目,而不仅仅是一个项目。
- 创建一个单独的库并使用 aspectj-maven-plugin 插件编译它。然后创建另一个 maven 项目,它将采用 unwoven 应用程序和方面库,并将它们编织在一起。书中的例子:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<weaveDependencies>
<weaveDependency>
<groupId>ajia.helloworld</groupId>
<artifactId>application-unwoven</artifactId>
</weaveDependency>
</weaveDependencies>
<aspectLibraries>
<aspectLibrary>
<groupId>ajia.helloworld</groupId>
<artifactId>profiling-aspect-library</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
</plugin>
</plugins>
</build>
第二个选项的问题是 <weaveDependency>
要求应用程序是 JAR 文件。至少,据我所知。
我确实在 AspectJ 邮件列表上遇到过类似的问题:http://aspectj.2085585.n4.nabble.com/Weaving-into-a-war-file-td2082924.html。 如果 link 过期,问题的答案是:
you need to take it apart, weave the .jar and put it back together. You cannot directly weave the jar within the war
但这可能吗?我也没有看到任何相关文档。
SO 上有类似的问题,要么根本没有任何回复,要么 wrong/outdated。
我将不胜感激,因为我不知道我的下一步应该是什么。
我将尝试提出两种截然不同的解决方案,然后您可以pick/choose 或 mix/match 如何实施您的解决方案。
示例 1:您被要求将一些文本转储到 [=18= 中找到的 org.apache.log4j.Logger.info(Object o)
方法的 stdout 前和 post-execution ].
(这是一个非常人为的例子,但我使用它是因为我被要求做一些与过去并不完全不同的事情!)
我假设您使用的是 Multi-Module Maven Project。
为了实现这一点,我们可以使用 log4j
jar(已编译 类),对它们应用一个方面,并吐出一组新的 load-time-woven 已编译 类.我们通过利用您已经在其自己的模块中调用的 weaveDependency 功能来实现这一点。因此,您将不再依赖项目中的 log4j
,而是依赖 ltwjar-enhanced-log4j
,如下所述。
考虑到这一点,创建您的项目层次结构:
ltwjar
/ltwjar-ltwjar-enhanced-log4j
/ltwjar-runtime
将您的父级 pom.xml 设置为如下所示(我们在这里设置了几个属性只是为了 convenience/consistency - 还有其他方法可以控制版本控制,例如 dependencyManagement and imported depdendencies ,但那是针对不同的问题!):
<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>com.example</groupId>
<artifactId>ltwjar</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<aspectj.version>1.8.13</aspectj.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<modules>
<module>ltwjar-enhanced-log4j</module>
<module>ltwjar-runtime</module>
</modules>
</project>
像这样设置您的 ltwjar-enhanced-log4j
pom.xml:
<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>
<parent>
<groupId>com.example</groupId>
<artifactId>ltwjar</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>ltwjar-enhanced-log4j</artifactId>
<dependencies>
<!-- we need this because the post-weave classes may (will) depend on aspectj types -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- ATTENTION! Scope this to provided otherwise it won't work - because of transitive dependencies,
anything that depends on this module will end up getting the _actual_ log4j classes versus the ones we are enriching! -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<!-- instruct aspectj to weave the classes in the jar! -->
<weaveDependencies>
<weaveDependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</weaveDependency>
</weaveDependencies>
<Xlint>warning</Xlint>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
现在让我们设置我们的存根 "runtime" 模块以利用 load-time-wove log4j ltw-enhanced-log4j
通过配置其 pom.xml 进行记录,因此:
<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>
<parent>
<groupId>com.example</groupId>
<artifactId>ltwjar</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>ltwjar-runtime</artifactId>
<dependencies>
<!-- Note that this module doesn't care (itself) about aspectj.
It _does_ care that it depends on the ltwjar-enhanced-log4j module
and not log4j, however. So let's depend on that! -->
<dependency>
<groupId>com.example</groupId>
<artifactId>ltwjar-enhanced-log4j</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
好的,这就是我们的框架设置。现在让我们创建一个毫无意义的方面来证明这一点!
在 ltwjar-enhanced-log4j
中创建类型 com.example.aspect.LoggingAspect
,如下所示:
package com.example.aspect;
import org.apache.log4j.Logger;
public aspect LoggingAspect {
// this pointcut will match "info(Object o)" method executions where the
// target is an instanceof Logger
pointcut logInfoWithObject(Object obj, Logger logger) :
execution(void info(Object)) && args(obj) && target(logger);
// here is our advice - simply sysout something before the execution
// proceeds and after it has finished - regardless of outcome (exception
// or not).
void around(Object obj, Logger logger) : logInfoWithObject(obj, logger) {
System.out.println("Before Logger.info(Object o)");
try {
proceed(obj, logger);
} finally {
System.out.println("After Logger.info(Object o)");
}
}
}
最后在 ltwjar-runtime
中,创建一个显示一切正常的 driver/harness。输入 com.example.Driver
:
package com.example;
import org.apache.log4j.Logger;
public class Driver {
private static final Logger LOG = Logger.getLogger(Driver.class);
public static void main(String[] args) {
LOG.info("In main");
}
}
现在从您的父项目 运行 mvn clean install
然后 mvn -pl ltwjar-runtime exec:java -Dexec.mainClass="com.example.Driver"
.
输出应该是这样的(因为你的类路径上没有 log4j.xml):
Before Logger.info(Object o)
log4j:WARN No appenders could be found for logger (com.example.Driver).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
After Logger.info(Object o)
注意第一行和最后一行。
这里发生了什么:
- 在
ltwjar-enhanced-log4j
中,我们 "enhance" log4j 通过围绕Logger.info(Object o)
包装一个建议
- 我们将
log4j.jar
重新打包到一个包含编织类型的新罐子ltwjar-enhanced-log4j.jar
中 - 我们在应用程序代码中依赖
ltwjar-enhanced-log4j.jar
而不是log4j
... - ...在这样做的过程中,建议坚持
好的,那么这如何适应最初询问的 war 文件?
简单 - 不要在 war 文件模块中放置任何代码。如果你必须把东西放在那里,让它配置。相反,将所有代码移动到 yourproject-domain
或 yourproject-logic
或 ...(在此处插入模块名称)并让您的 war 文件依赖于 that 模块。这是一个很好的做法,因为它启用了 Separation of Concerns:您的应用程序不应该知道它 运行 在 war 文件保护伞下;它可以像 Spring Boot tomcat uberwar 一样容易 运行ning 等。包装问题 (war) 不同于业务问题 (域)。
今晚我将更新共享方面库的另一个示例,除非有人抢先一步。另外,如果其中一些没有意义,请在评论中大声疾呼!