2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kotlinでアノテーションを作ってみた!

Posted at

はじめに

 お久しぶりです。Tomita Kentaroです。最近、仕事でアノテーションを使う事が多いので、ちょっとした勉強として、アノテーションを作ってみました。

アノテーションとは?

 アノテーションは、英語のAnnotationのことで、「注釈」を意味する言葉です。IT用語では「特定のデータに対して情報タグ(メタデータ)を付加する」という意味で用いられます。

 引用元:【NTT西日本】アノテーション(Annotation)|ICT用語集|法人・ 企業向け ICT サービス ・ ソリューション

 もっと、簡単に説明するとコメントに近い様なものです。しかし、Springなどのフレームワークでは特殊な効果があったりします。

簡単なアノテーションを作ってみた!

 今回は、Kotlinを使って簡単なアノテーションを作りました。この@MyAnnotationというアノテーションは、Kotlin単体で動かしているため、何の効果もありません。ただのコメントの様なものです。コードは以下のようになります。まずは、MyAnnotation.ktannotation classを使い、アノテーションを定義します。

MyAnnotation.kt
annotation class MyAnnotation(val value: String)

 続いて、作成した@MyAnnotationAnnotation.ktで使用してみます。

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関数がある場所になります。

DemoApplication.kt
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でアノテーションの定義を行なっているファイルになります。

LogExecutionTime.kt
package com.example.demo

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class LogExecutionTime

 続いて、LoggingAspect.ktのコードを以下に示します。ここが、logExecutionTimeのアスペクトを定義しているファイルになります。

LoggingAspect.kt
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というアノテーションを使用しているファイルになります。

MyService.kt
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を以下に示します。

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でアノテーションを使っているので、これでアノテーションに対する理解が、また少し深まりました。もし、何か間違いや、アノテーションを使う上で何かアドバイスがあれば、コメントなどに記載していただけると嬉しいです。今回の記事も最後まで、読んでいただき、ありがとうございました。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?