はじめに
お久しぶりです。Tomita Kentaroです。最近、仕事でアノテーションを使う事が多いので、ちょっとした勉強として、アノテーションを作ってみました。
アノテーションとは?
アノテーションは、英語のAnnotationのことで、「注釈」を意味する言葉です。IT用語では「特定のデータに対して情報タグ(メタデータ)を付加する」という意味で用いられます。
引用元:【NTT西日本】アノテーション(Annotation)|ICT用語集|法人・ 企業向け ICT サービス ・ ソリューション
もっと、簡単に説明するとコメントに近い様なものです。しかし、Springなどのフレームワークでは特殊な効果があったりします。
簡単なアノテーションを作ってみた!
今回は、Kotlinを使って簡単なアノテーションを作りました。この@MyAnnotation
というアノテーションは、Kotlin単体で動かしているため、何の効果もありません。ただのコメントの様なものです。コードは以下のようになります。まずは、MyAnnotation.kt
でannotation class
を使い、アノテーションを定義します。
annotation class MyAnnotation(val value: String)
続いて、作成した@MyAnnotation
をAnnotation.kt
で使用してみます。
@MyAnnotation("This is a custom annotation")
class MyClass {
@MyAnnotation("Annotated function")
fun myFunction() {
println("Hello from myFunction!")
}
}
fun main() {
val myClass = MyClass()
myClass.myFunction()
}
SpringBootで効果付きのアノテーションを作ってみた!
では、ちゃんと効果を発揮させるためには、どうしたら良いか。それは、最初に記述した通り、Springなどのフレームワークで使う事です。今回は、SpringBootを使いました。まずは、簡単なフォルダ構成を以下に示します。
demo
├── HELP.md
├── build
│ └── ...
├── build.gradle
├── gradle
│ └── ...
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
├── main
│ ├── kotlin
│ │ └── com
│ │ └── example
│ │ └── demo
│ │ ├── DemoApplication.kt
│ │ ├── LogExecutionTime.kt
│ │ ├── LoggingAspect.kt
│ │ └── MyService.kt
│ └── resources
│ └── application.properties
└── test
└── ...
続いて、DemoApplication.kt
ファイルのコードを以下に示します。ここが、SpringBootアプリケーションを実行するファイルでmain
関数がある場所になります。
package com.example.demo
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.ApplicationContext
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
val context: ApplicationContext = runApplication<DemoApplication>(*args)
val myService = context.getBean(MyService::class.java)
myService.serve()
}
続いて、LogExecutionTime.kt
のコードを以下に示します。ここが、annotation class
でアノテーションの定義を行なっているファイルになります。
package com.example.demo
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class LogExecutionTime
続いて、LoggingAspect.kt
のコードを以下に示します。ここが、logExecutionTime
のアスペクトを定義しているファイルになります。
package com.example.demo
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.springframework.stereotype.Component
@Aspect
@Component
class LoggingAspect {
@Around("@annotation(com.example.demo.LogExecutionTime)")
fun logExecutionTime(joinPoint: ProceedingJoinPoint): Any? {
val start = System.currentTimeMillis()
val proceed = joinPoint.proceed()
val executionTime = System.currentTimeMillis() - start
println("${joinPoint.signature} executed in $executionTime ms")
return proceed
}
}
続いて、MyService.kt
のコードを以下に示します。ここが、@LogExecutionTime
というアノテーションを使用しているファイルになります。
package com.example.demo
import org.springframework.stereotype.Service
@Service
class MyService {
@LogExecutionTime
fun serve() {
println("Executing serve method...")
Thread.sleep(1000)
}
}
最後に、SpringBootプロジェクトの設定ファイルであるbuild.gradle
を以下に示します。
plugins {
id 'org.springframework.boot' version '3.3.2'
id 'io.spring.dependency-management' version '1.1.6'
id 'org.jetbrains.kotlin.jvm' version '1.9.24'
id 'org.jetbrains.kotlin.plugin.spring' version '1.9.24'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
implementation 'org.springframework.boot:spring-boot-starter-aop'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit5'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions {
freeCompilerArgs = ['-Xjsr305=strict']
jvmTarget = '17'
}
}
tasks.named('test') {
useJUnitPlatform()
}
以下のコマンドで実行します。
$ ./gradlew build
$ ./gradlew bootRun
実行結果は、以下の様になります。
$ ./gradlew bootRun
> Task :bootRun
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.3.2)
2024-08-08T15:47:19.473+09:00 INFO 83615 --- [demo] [ main] com.example.demo.DemoApplicationKt : Starting DemoApplicationKt using Java 17.0.9 with PID 83615 (/Users/tomitakentarou/demo/build/classes/kotlin/main started by tomitakentarou in /Users/tomitakentarou/demo)
2024-08-08T15:47:19.474+09:00 INFO 83615 --- [demo] [ main] com.example.demo.DemoApplicationKt : No active profile set, falling back to 1 default profile: "default"
2024-08-08T15:47:19.790+09:00 INFO 83615 --- [demo] [ main] com.example.demo.DemoApplicationKt : Started DemoApplicationKt in 0.442 seconds (process running for 0.561)
Executing serve method...
void com.example.demo.MyService.serve() executed in 1004 ms
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.8/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD SUCCESSFUL in 2s
5 actionable tasks: 2 executed, 3 up-to-date
void com.example.demo.MyService.serve() executed in 1004 ms
と表示されれば、ちゃんと@LogExecutionTime
というアノテーションが動作している事が確認できます。
最後に
今回は、初めてアノテーションを自作してみました。Kotlinでアノテーションを使っているので、これでアノテーションに対する理解が、また少し深まりました。もし、何か間違いや、アノテーションを使う上で何かアドバイスがあれば、コメントなどに記載していただけると嬉しいです。今回の記事も最後まで、読んでいただき、ありがとうございました。