了解Spring AOP拦截器的开销

时间:2019-06-04 11:12:25

标签: java spring-boot kotlin aspectj spring-aop

好的,因此我需要将一些CDI拦截器迁移到Spring Boot,并且我编写了一个简单的“概念验证”测试用例:

package ch.cypherk.myapp.util.aop

import org.aspectj.lang.JoinPoint
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.EnableAspectJAutoProxy
import org.springframework.stereotype.Component
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import javax.inject.Inject

@RunWith(SpringJUnit4ClassRunner::class)
@SpringBootTest
class SimpleInterceptorIT{
    companion object {
        val expectedArg = "Hakuna Matata"
        lateinit var interceptorOutput:String
    }

    @Inject
    private lateinit var client:ClientClass


    @Test
    fun `interceptor works`(){
        client.foo()


        assertThat(interceptorOutput).isEqualTo("ch.cypherk.myapp.util.aop.TargetClass.foo(\"$expectedArg\")")
    }
}
@Component
class TargetClass{
    fun foo(arg:String){
        println("I did something.")
    }
}

@Component
class ClientClass(val target:TargetClass){
    fun foo(){
        target.foo("Hakuna Matata")
    }
}

@Aspect
@Configuration
@EnableAspectJAutoProxy
class TestInterceptorConfiguration{

    @Before("execution(* ch.cypherk.myapp.util.aop.TargetClass.*(..))")
    fun intercept(joinPoint:JoinPoint){
        val signature = joinPoint.signature
        println(signature)
        SimpleInterceptorIT.interceptorOutput =
            "${signature.declaringTypeName}.${signature.name}(${
            joinPoint.args
                .map { when(it){
                    is String -> "\"$it\""
                    else -> it
                }}
                .joinToString(",")
            })"
    }
}

这些是该软件包中的 ONLY 类。

输出:

void ch.cypherk.myapp.util.aop.TargetClass.foo(String)
I did something.

测试是绿色的。

现在让我们扩大搜索范围...

@Aspect
@Configuration
@EnableAspectJAutoProxy
class TestInterceptorConfiguration{

    @Before("execution(* ch.cypherk.myapp.util.aop.*.*(..))")
    fun intercept(joinPoint:JoinPoint){
        val signature = joinPoint.signature
        println(signature)
        SimpleInterceptorIT.interceptorOutput =
            "${signature.declaringTypeName}.${signature.name}(${
            joinPoint.args
                .map { when(it){
                    is String -> "\"$it\""
                    else -> it
                }}
                .joinToString(",")
            })"
    }
}

这需要 ETERNITY 来开始。

是的,输出为

void ch.cypherk.myapp.util.aop.ClientClass.foo()
void ch.cypherk.myapp.util.aop.TargetClass.foo(String)
I did something.

是的,测试是绿色的。

是的,测试运行的时间仅为117ms

但是Spring需要花很长时间才能启动。

为什么?更重要的是,我该怎么办?因为这不是很好。

我看过spring-boot-starter-aop并定义了

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.1.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.1.6.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.2</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

所有这些-正如您将注意到的-编译限定范围。

那么,我的期望是,所有这些都将在编译时被编织。 似乎并非如此。否则,它不会阻止Spring对程序包做任何奇怪的事情。

在这里,我希望能帮助您了解正在发生的事情。

2 个答案:

答案 0 :(得分:1)

我做的和你做的一样,而且运行很快。

但是我有不同的依赖性。

你可以尝试

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <scope>compile</scope>
    </dependency>

编辑:

我已经运行了您提供的Kotlin代码,输出为:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.5.RELEASE)

2019-06-04 17:05:19.411  INFO 19840 --- [           main] c.c.myapp.util.aop.SimpleInterceptorIT   : Starting SimpleInterceptorIT on LAPTOP-FQPHQ5E5 with PID 19840 (started by simon in C:\Users\simon\Workspace\stackoverflow\demo-aop-kotlin)
2019-06-04 17:05:19.411  INFO 19840 --- [           main] c.c.myapp.util.aop.SimpleInterceptorIT   : No active profile set, falling back to default profiles: default
2019-06-04 17:05:21.353  INFO 19840 --- [           main] c.c.myapp.util.aop.SimpleInterceptorIT   : Started SimpleInterceptorIT in 2.358 seconds (JVM running for 4.008)
void ch.cypherk.myapp.util.aop.ClientClass.foo()
void ch.cypherk.myapp.util.aop.TargetClass.foo(String)
I did something.

两次执行之间没有区别。

答案 1 :(得分:1)

我不确定,但是我如何理解Aspectjweaver,在加载时向Java类介绍建议

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.2</version>
</dependency>

编译时编织是最简单的方法

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7</version>
    <configuration>
        <complianceLevel>1.8</complianceLevel>
        <source>1.8</source>
        <target>1.8</target>
        <showWeaveInfo>true</showWeaveInfo>
        <verbose>true</verbose>
        <Xlint>ignore</Xlint>
        <encoding>UTF-8 </encoding>
    </configuration>
    <executions>
        <execution>
            <goals>
                <!-- use this goal to weave all your main classes -->
                <goal>compile</goal>
                <!-- use this goal to weave all your test classes -->
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Havent尝试了一下,但希望对您有用。