Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
AOP w połączeniu z Kotlin Coroutines | https://vived.io/aop-w-polaczeniu-z-kotlin-coroutines/
package com.example
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.AfterReturning
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.slf4j.LoggerFactory.getLogger
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono
import kotlin.coroutines.Continuation
import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
@SpringBootApplication
class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
@Component
class Runner(
val foo: Foo,
val reactiveFoo: ReactiveFoo,
val coroutineFoo: CoroutineFoo,
) : CommandLineRunner {
override fun run(vararg args: String?) {
foo.add(2, 3)
reactiveFoo.add(2, 3).block()
runBlocking {
coroutineFoo.add(2, 3)
}
}
}
annotation class Logging
annotation class ReactiveLogging
annotation class CoroutineLogging
@Component
class Foo {
@Logging
fun add(a: Int, b: Int): Int = a + b
}
@Component
@Aspect
class LoggingAspect {
private val logger = getLogger(LoggingAspect::class.java)
@AfterReturning(
pointcut = "@annotation(Logging)",
returning = "result"
)
fun logResult(joinPoint: JoinPoint, result: Any?) {
logger.info("`${joinPoint.signature}` returned `${result}`")
}
}
@Component
class ReactiveFoo {
@ReactiveLogging
fun add(a: Int, b: Int): Mono<Int> = Mono.just(a + b)
}
@Component
@Aspect
class ReactiveLoggingAspect {
private val logger = getLogger(ReactiveLoggingAspect::class.java)
@Around(
"""
@annotation(ReactiveLogging) &&
execution(reactor.core.publisher.Mono *(..))
"""
)
fun logResult(joinPoint: ProceedingJoinPoint): Mono<Any> =
(joinPoint.proceed() as Mono<Any>)
.doOnNext { result ->
logger.info("`${joinPoint.signature}` returned `${result}`")
}
}
@Component
class CoroutineFoo {
@CoroutineLogging
suspend fun add(a: Int, b: Int): Int {
delay(100)
return a + b
}
}
val ProceedingJoinPoint.coroutineContinuation: Continuation<Any?>
get() = this.args.last() as Continuation<Any?>
val ProceedingJoinPoint.coroutineArgs: Array<Any?>
get() = this.args.sliceArray(0 until this.args.size - 1)
suspend fun ProceedingJoinPoint.proceedCoroutine(
args: Array<Any?> = this.coroutineArgs
): Any? =
suspendCoroutineUninterceptedOrReturn { continuation ->
this.proceed(args + continuation)
}
fun ProceedingJoinPoint.runCoroutine(
block: suspend () -> Any?
): Any? =
block.startCoroutineUninterceptedOrReturn(this.coroutineContinuation)
@Component
@Aspect
class CoroutineLoggingAspect {
private val logger = getLogger(CoroutineLoggingAspect::class.java)
@Around(
"""
@annotation(CoroutineLogging) &&
args(.., kotlin.coroutines.Continuation)
"""
)
fun logResult(joinPoint: ProceedingJoinPoint): Any? =
joinPoint.runCoroutine {
val result = joinPoint.proceedCoroutine()
logger.info("`${joinPoint.signature}` returned `${result}`")
result
}
}
plugins {
id("org.springframework.boot") version "2.5.3"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
kotlin("jvm") version "1.5.21"
kotlin("plugin.spring") version "1.5.21"
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.springframework.boot:spring-boot-starter-aop")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment