【问题标题】:Why is Spring initialising my Aspect twice?为什么 Spring 两次初始化我的 Aspect?
【发布时间】: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 被初始化两次的内容。

http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/aop.html#aop-proxying

但是它说的是以下内容(我使用的是 Spring 4.3.2):

从 Spring 4.0 开始,代理对象的构造函数将不再是 由于将创建 CGLIB 代理实例,因此不再调用两次 通过 Objenesis。仅当您的 JVM 不允许构造函数时 绕过,您可能会看到双重调用和相应的调试 来自 Spring 的 AOP 支持的日志条目。

还写了以下内容,由于我使用的是@EnableAspectJAutoProxy注解,这也应该适用,所以我知道我使用的是CGLIB代理:

要明确:使用proxy-target-class="true" on &lt;tx:annotation-driven/&gt;&lt;aop:aspectj-autoproxy/&gt;&lt;aop:config/&gt; 元素将强制对所有三个元素使用 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


【解决方案1】:

我在 Spring 文档中找到了将 AspectJ 与 Spring 应用程序一起使用的说明。正如它所说:“这确保 Spring 通过向 AspectJ 请求它而不是尝试自己创建实例来获取切面实例。”,您可以从中找到示例:

<bean id="profiler" class="com.xyz.profiler.Profiler" factory-method="aspectOf">
    <property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>

希望对你有帮助。

【讨论】:

    【解决方案2】:

    尝试使用其他编译器,如果您使用的是 Ajc,请从您的 IDE 设置中将其更改为其他内容;它对我有用。

    【讨论】:

      猜你喜欢
      • 2013-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-13
      • 2017-09-23
      • 2020-03-12
      • 1970-01-01
      • 2014-11-22
      相关资源
      最近更新 更多