【发布时间】:2016-09-25 17:04:55
【问题描述】:
我有一个简单的方面是围绕一种方法做一些逻辑。我正在使用带有 AspjectJ 的 Spring Boot。由于某种原因,方面的构造函数被调用了两次。
我的外观是这样的:
@Aspect
@Component
public class HandlerLoggingAspect {
private static final Logger log = LoggerFactory.getLogger(HandlerLoggingAspect.class);
public HandlerLoggingAspect() {
log.info("Initialising HandlerLoggingAspect");
}
@Around("execution (* io.netty.handler.codec.ByteToMessageDecoder+.decode(*,*,*)) && args(ctx,byteBuf,outList)")
public void interceptByteDecoding(ProceedingJoinPoint joinPoint, ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> outList) throws Throwable {
setupMdcAroundJoinPoint(joinPoint, ctx);
}
//... rest of the code ...
}
方面在我期望的方法周围运行良好,但出于某种原因,Spring Boot 两次初始化我的方面。 Intialising HandlerLoggingAspect 消息在开始时出现两次。
2016-09-25 18:36:26.041 [main] DEBUG Running with Spring Boot v1.4.0.RELEASE, Spring v4.3.2.RELEASE
2016-09-25 18:36:26.041 [main] INFO No active profile set, falling back to default profiles: default
2016-09-25 18:36:29.891 [main] INFO Initialising HandlerLoggingAspect
2016-09-25 18:36:29.892 [main] INFO Initialising HandlerLoggingAspect
如果我从 Aspect 中删除 @Component,它根本不会被初始化。
我的主课是这样的:
@ComponentScan
@Configuration
@EnableAutoConfiguration
@EnableAspectJAutoProxy
@SpringBootApplication
public class Launcher implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(Launcher.class);
public static void main(String[] args) {
SpringApplication.run(Launcher.class, args);
}
@Override
public void run(String... strings) throws Exception {
//... logic performed by the class ...
}
}
如果有什么不同,这是我在 pom.xml 中的插件配置,用于 AspectJ 编译时编织。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven-compiler-plugin.version>3.5.1</maven-compiler-plugin.version>
<aspectj-maven-plugin.version>1.8</aspectj-maven-plugin.version>
<org.aspectj.version>1.8.9</org.aspectj.version>
<org.springframework.boot.version>1.4.0.RELEASE</org.springframework.boot.version>
</properties>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<proc>none</proc>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>${aspectj-maven-plugin.version}</version>
<configuration>
<complianceLevel>${java.version}</complianceLevel>
<source>${java.version}</source>
<target>${java.version}</target>
<showWeaveInfo/>
<forceAjcCompile>true</forceAjcCompile>
<sources/>
<weaveDirectories>
<weaveDirectory>${project.build.directory}/classes</weaveDirectory>
</weaveDirectories>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal> <!-- use this goal to weave all your main classes -->
<goal>test-compile</goal> <!-- use this goal to weave all your test classes -->
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${org.aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
当我分析其余日志时,似乎只有一个实例实际上拦截了切入点。所以至少那是好的。
这种双重初始化的原因可能是什么?
** 更多信息 **
似乎在 Spring 文档中,提到了关于 Aspects 被初始化两次的内容。
但是它说的是以下内容(我使用的是 Spring 4.3.2):
从 Spring 4.0 开始,代理对象的构造函数将不再是 由于将创建 CGLIB 代理实例,因此不再调用两次 通过 Objenesis。仅当您的 JVM 不允许构造函数时 绕过,您可能会看到双重调用和相应的调试 来自 Spring 的 AOP 支持的日志条目。
还写了以下内容,由于我使用的是@EnableAspectJAutoProxy注解,这也应该适用,所以我知道我使用的是CGLIB代理:
要明确:使用
proxy-target-class="true"on<tx:annotation-driven/>、<aop:aspectj-autoproxy/>或<aop:config/>元素将强制对所有三个元素使用 CGLIB 代理。
我使用的是 Java 1.8。我正在使用的组件组合之间是否存在任何已知的不兼容性,这会阻止上述构造函数绕过?
【问题讨论】:
-
对于初学者,请尝试从您的应用程序中删除所有注释,但
@SpringBootApplication。其他所有内容都已隐含,默认情况下启用@EnableAspectJAutoProxy(或者您可以添加spring-boot-starter-aop以获得正确的依赖项。另外,您为什么使用aspectj插件,因为您使用的是代理而不是不需要编织。 -
@M.Deinum 如果我删除了
maven-aspectj-plugin,我的@Aspect的这两个切入点不会被触发。我拦截的类不是 Spring Managed bean。如果我删除其他注释并只留下@SpringBootApplication,效果是一样的。 -
那为什么还要打扰
@Component和@EnableAspectJAutoProxy。使用编织时,您不需要它,因为这仅适用于基于代理的 AOP。通过编织,您可以消除弹簧,因为方面已经成为您编译代码的一部分。 -
@M.Deinum 所以,在没有
@Component的情况下尝试了它,它解决了我遇到的小问题。方面不是在启动时初始化(两次),而是按需初始化,并且只出现一次。不知道如果我也需要对方面进行弹簧管理,我将如何处理它,同时保留建议非弹簧管理类的可能性。 -
我从未使用过 Spring,但也许您想在
@Bean方法中使用Aspects.aspectOf(..)以返回实例。另请参阅here 了解更多详情。
标签: java spring-boot java-8 aspectj spring-aop