KotlinとSpring Bootで簡単なRESTfulサービスを作成する

画像



はじめに



Kotlin言語のベータ版のリリースを見越して、その使用の印象を共有したいと思います。



Kotlinは、JVM、Android、およびブラウザー向けのJetBrains(IntelliJ Idea開発者)の優れた新しい言語であり、一見すると改善されたjava(または簡易scala)のように見えます。 しかし、これは一見しただけで、この言語は他の言語から多くの興味深いソリューションを吸収しただけでなく、元のソリューションも示しています。



-swiftのオプション、 kotlinのnullsafe

-scalaのケースクラス、kotlinのデータクラス

-Scalaからの暗黙的なメソッドの置き換え、 拡張機能

- 代表者

- 安全にnull

- スマートキャスト

-さらに、詳細はkotlinlangの公式ウェブサイトで見つけることができます。



javaまたはscalaに精通している人にとっては、 kotlinとjavakotlinとscalaを比較することは興味深いでしょう。



言語の作成者は、2つの目標を達成するよう努めています。

-コンパイル速度をJavaに匹敵させる

-言語は可能な限りシンプルでありながら、十分に表現力豊かであるべきです

したがって、「複雑さ」とコンパイル時間で現在scalaに満足している場合、kotlinは必要ないでしょう。



最初にtankの言語について聞いた人のために、以下は公式ウェブサイトのいくつかの例です:



ハローワールド
package hello fun main(args: Array<String>) { println("Hello World!") }
      
      







引数を読む
 fun main(args: Array<String>) { if (args.size() == 0) { println("Provide a name") return } println("Hello, ${args[0]}!") }
      
      







こんにちは、ワールドカップ
 class Greeter(val name: String) { fun greet() { println("Hello, $name") } } fun main(args: Array<String>) { Greeter(args[0]).greet() }
      
      







kotlinの個人的な経験から、特にこの言語のいくつかの利点に注目したいと思います。



-もちろん、最初はJavaとのやり取りのしやすさです。 javaのすべてのタイプとコレクションは、kotlinの同様のタイプとコレクションに変換され、その逆も同様です。 これは、scalaで発生するすべての「アナーキー」の後に特に喜ばしいことです(はいscala.collection.JavaConversions._とscala.collection.JavaConverters._がありますが、完全に透過的な型変換とは比較できません)。

-Intellij Idea Studioからの優れたサポートも喜ばせざるを得ません。ただし、言語はBeta 4ですが、スタジオのプラグインにより快適に作業できます。

-そして、Scalaからの暗黙的なメソッドのファンのために、kotlinは拡張関数の形で非常に便利なソリューションを提示します

-特に、言語開発者は、 java (hi scala)に匹敵するコンパイル時間を達成することを目標として設定しました。 これは、かなり小さなファイルの1行を小さなJavaプロジェクトと同じ速度でコンパイルする場合、scalaでの長い作業の後に特に喜ばれます。

- インライン機能 -すばらしい革新。 たとえば、言語の現在の機能を拡張したり、状況によっては生産性を高めることができます。

-標準ライブラリの便利な機能

-同じjava 8とは異なり、便利なラムダ。scalaの実装に非常に似ています。



それにもかかわらず、この言語には欠点があります。



-scalaからのパターンマッチングは十分ではありませんが、場合によってはスマートキャストDestructuring Declarationsを節約できますが、他の方法では他の方法で抜け出す必要があります。 通常、パターンマッチングがないことは明らかで、開発者はJavaのコンパイル時間にできるだけ近づけようとしていますが、その存在によって一部のアプリケーションの作成が大幅に簡素化されるため、満足しています。

- リソースを試してみてくださいまだ実装されていません。 しかし、ここで言語の作者は近い将来に状況を修正することを約束します。 それまでの間、既存のソリューションを適用するか、言語拡張機能を使用できます。



try-with-resources
 internal class ResourceHolder : AutoCloseable { val resources = ArrayList<AutoCloseable>() fun <T : AutoCloseable> T.autoClose(): T { resources.add(this) return this } override fun close() { resources.reverse() resources.forEach { try { it.close() } catch (e: Throwable) { e.printStackTrace() } } } } inline internal fun <R> using(block: ResourceHolder.() -> R): R { val holder = ResourceHolder() try { return holder.block() } finally { holder.close() } }
      
      







使用例
 fun copy(from: Path, to: Path) { using { val input = Files.newInputStream(from).autoClose() val output = Files.newOutputStream(to).autoClose() input.copyTo(output) } }
      
      







-非同期とイールドはまだありませんが、著者によると、1.0のリリース後、近い将来に登場するのを待つことができます。



更新コメントの中で最も興味深いのは、scalaとの比較、特にkotlinの利点です。

Scalaに対するKotlinの利点
-Javaとの相互作用、およびその逆。 kotlinでは、通常、javaから何かを呼び出していることを忘れることができます。すべてが非常に透過的で便利です。 反対は真実です。 javaからは、kotlinで作業することも便利です。 ここでは、型の類似性が役立つだけでなく、Javaでの透過的な作業にKotlinがどれだけ重点を置いているかがわかります。 これを示す最も簡単な方法は、例を使用することです。

1.匿名クラスの代わりにラムダを使用します(scalaでは、真実もすぐに現れるかもしれませんが、これまでのところこれは不可能です)。

Javaのコード:

 ExecutorService executor = Executors.newFixedThreadPool(1); executor.execute(System.out::println);
      
      







Scalaでは、これはもうできません。 しかし、kotlinには問題はありません。

 val executor = Executors.newFixedThreadPool(1) executor.execute { println() }
      
      







2.同じリソースの試用版があります(はい、言語の拡張機能を作成できますが、すぐに使用できます。つまり、プロジェクト間でソリューションをドラッグする必要があります)

少なくとも最初に頭に浮かぶのは



-暗黙の欠如。 暗黙のそれ自体は確かに強力なソリューションですが、スタジオがなければ他の人のコードを理解することはほとんど不可能です。 特に著者が「非常に夢中になっている」場合。 また、メソッドを追加するために、kotlinでは拡張機能を作成できます。これは、実際にはscalaの同様のソリューションよりもはるかに便利に見えます。 例:

Scala:

 implicit class RichInt(val x: Int) extends AnyVal { def square: Int = x * x } object App { def print(): Unit = { val two = 2 println(s"The square of 2 is ${two.square}") } }
      
      





コトリン:

 fun Int.richInt(): Int = this * this object App { fun print(): Unit { val two = 2 println("The square of 2 is ${two.richInt()}") } }
      
      







-Optionの代わりにnull入力可能な型。



-標準ライブラリの便利な機能



-そして、もちろんコンパイル速度。 実際には、特にscalaと比較した場合、すべてが実際に非常に迅速にコンパイルされます。



-結果のjarはscalaの場合と同じです。



-そして最も重要なこととして、JetBrainsは完全な後方互換性を約束します (2.10、2.11、2.12などのlibsを使用したscalaが再び登場します)。





gradleを介したアセンブリを使用して、 スプリングブートでの小さなRESTfulアプリケーションを示す例に移りましょう。



スタジオのセットアップ



動作するには、 IntelliJ Idea Communityをインストールする必要があります(ただし、Eclipseを使用できます。プラグインもあります)。インストール後、kotlinプラグインを更新します。 以前にポップアップウィンドウからプラグインを更新することを選択した場合でも、 設定-> pluginを使用して手動で更新する必要があります(少なくとも現時点では、言語はベータ版です)。



また、ローカルgradleを設定し、スタジオの設定に登録することをお勧めします(設定->ビルド、実行、展開-> gradle->ユーザーローカルgradle配布。その後、gradleホームでgradleへのパスを指定)



プロジェクトのセットアップ



gradle kotlinプロジェクトを作成し(新しいプロジェクト-> gradle-> kotlin)、build.gradleの内容を次のように変更します。



Build.gradleコンテンツ
 buildscript { ext.kotlin_version = '1.0.0-beta-4584' repositories { mavenCentral() maven { url "http://repo.spring.io/snapshot" } maven { url "http://repo.spring.io/milestone" } } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.3.0.RELEASE") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") } } apply plugin: 'idea' apply plugin: 'spring-boot' apply plugin: 'kotlin' jar { baseName = 'test-spring-kotlin-project' version = '0.1.0' } repositories { mavenCentral() maven { url "http://repo.spring.io/snapshot" } maven { url "http://repo.spring.io/milestone" } maven { url "http://10.10.10.67:8081/nexus/content/groups/public" } } dependencies { compile("org.springframework.boot:spring-boot-starter-web:1.3.0.RELEASE") compile("org.springframework:spring-jdbc:4.2.3.RELEASE") compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.6.4") compile("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version") }
      
      







src / main / resourcesフォルダーにapplication.propertiesファイルを作成します。ここで、スプリングブートを開始するポートを指定します。



application.properties
 server.port = 8080
      
      







src / main / kotlin / test.kotlin.spring.projectフォルダーにApplication.ktファイルを作成します。 スプリングブートを実行するための基本設定があります。



Application.kt
 package test.kotlin.spring.project import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.registerKotlinModule import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.EnableAutoConfiguration import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration import org.springframework.boot.builder.SpringApplicationBuilder import org.springframework.boot.context.web.SpringBootServletInitializer import org.springframework.context.annotation.Bean import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter @SpringBootApplication @EnableAutoConfiguration(exclude = arrayOf(DataSourceAutoConfiguration::class)) open class Application : SpringBootServletInitializer() { @Bean open fun mapperForKotlinTypes(): MappingJackson2HttpMessageConverter { return MappingJackson2HttpMessageConverter().apply { objectMapper = jacksonMapper } } override fun configure(application: SpringApplicationBuilder): SpringApplicationBuilder = application.sources(Application::class.java) companion object { val jacksonMapper = ObjectMapper().registerKotlinModule() .setSerializationInclusion(JsonInclude.Include.NON_ABSENT) .enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES) @Throws(Exception::class) @JvmStatic fun main(args: Array<String>) { println("starting application...") SpringApplication.run(Application::class.java, *args) } } }
      
      









また、残りのサービスメソッドの設定でファイルを作成する必要があります。 いくつかの方法があります。



-メソッドは、リクエストから入力された姓名データにAckResponseを発行します。

-メソッド、文字列の配列が入力され、そこから最小の文字列が長さで選択され、「_」で割られ、ソートされ、文字「、」で既に文字列に組み立てられます(言語の機能を示します)



src / main / kotlin / test.kotlin.spring.projectフォルダーにServiceController.ktファイルを作成します。



ServiceController.kt
 package test.kotlin.spring.project import org.springframework.http.MediaType import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMethod import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController data class AckResponse(val status: Boolean, val result: String, val message: String? = null) @RestController class ServiceController { @RequestMapping( path = arrayOf("/request"), method = arrayOf(RequestMethod.GET), produces = arrayOf(MediaType.APPLICATION_JSON_UTF8_VALUE)) fun nameRequest( @RequestParam(value = "name") name: String, @RequestParam(value = "surname", required = false) surname: String?): AckResponse { return if (surname == null) AckResponse(status = true, result = "Hi $name", message = "surname is empty") else AckResponse(status = true, result = "Hi $surname,$name") } @RequestMapping( path = arrayOf("/sort_request"), method = arrayOf(RequestMethod.GET), produces = arrayOf(MediaType.APPLICATION_JSON_UTF8_VALUE)) fun findMinimum( @RequestParam(value = "values") values: Array<String>): AckResponse { println("values:") values.forEach { println(it) } val minValue = values.apply { sortBy { it.length } } .firstOrNull() ?.split("_") ?.sorted() ?.joinToString(",") ?: "" return AckResponse(status = true, result = minValue) } }
      
      







操作を実行してテストする



Application.ktからアプリケーションを起動します。 正常に起動した場合、ログは次のようになります。



アプリケーションログ
 アプリケーションを開始しています...

   。  ____ _ __ _ _
  / \\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
 (()\ ___ | '_ |' _ | | '_ \ / _` | \ \ \ \
  \\ / ___)|  | _)|  |  |  |  |  ||  (_ | |))))))
   '| ____ |  .__ | _ |  | _ | _ |  | _ \ __、|  / / / /
  ========= | _ | =============== | ___ / = / _ / _ / _ /
  ::スプリングブート::(v1.3.0.RELEASE)

 2016-01-12 12:47:48.242 INFO 88 --- [main] tksproject.Application $ Companion:Starting Application.Companion on Lenovo-PC with PID 88(D:\ IDA_Projects \ test \ build \ classes \ main started by Dの管理者:\ IDA_Projects \ test)
 2016-01-12 12:47:48.247 INFO 88 --- [main] tksproject.Application $コンパニオン:アクティブなプロファイルはありません
 2016-01-12 12:47:48.413 INFO 88 --- [main] ationConfigEmbeddedWebApplicationContext:Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@dbf57b3:startup date [Tue Jan 12 12:47:48 MSK 2016]; コンテキスト階層のルート
 2016-01-12 12:47:50.522 INFO 88 --- [main] osbfsDefaultListableBeanFactory:Bean 'beanNameViewResolver'のBean定義を別の定義で上書き:[ルートBean:クラス[null]; スコープ=;  abstract = false;  lazyInit = false;  autowireMode = 3;  dependencyCheck = 0;  autowireCandidate = true;  primary = false;  factoryBeanName = org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration $ WhitelabelErrorViewConfiguration;  factoryMethodName = beanNameViewResolver;  initMethodName = null;  destroyMethodName =(推測);  [ルートBean:クラス[null]を使用して、クラスパスリソース[org / springframework / boot / autoconfigure / web / ErrorMvcAutoConfiguration $ WhitelabelErrorViewConfiguration.class]]で定義されています。 スコープ=;  abstract = false;  lazyInit = false;  autowireMode = 3;  dependencyCheck = 0;  autowireCandidate = true;  primary = false;  factoryBeanName = org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration $ WebMvcAutoConfigurationAdapter;  factoryMethodName = beanNameViewResolver;  initMethodName = null;  destroyMethodName =(推測); クラスパスリソースで定義されています[org / springframework / boot / autoconfigure / web / WebMvcAutoConfiguration $ WebMvcAutoConfigurationAdapter.class]]
 2016-01-12 12:47:51.066 INFO 88 --- [main] trationDelegate $ BeanPostProcessorChecker:タイプ[クラスorg.springframework.transaction.annotation.ProxyTransactionMan $ oncerCan $ hancerganのBean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' $$ ede1977c]は、すべてのBeanPostProcessorsによって処理される資格がありません(例:自動プロキシの資格がありません)
 2016-01-12 12:47:51.902 INFO 88 --- [main] sbcetTomcatEmbeddedServletContainer:Tomcatがポート(s)で初期化されました:8080(http)
 2016-01-12 12:47:51.930 INFO 88 --- [main] o.apache.catalina.core.StandardService:サービスTomcatの開始
 2016-01-12 12:47:51.937 INFO 88 --- [main] org.apache.catalina.core.StandardEngine:サーブレットエンジンの起動:Apache Tomcat / 8.0.28
 2016-01-12 12:47:52.095 INFO 88 --- [ost-startStop-1] oaccC [Tomcat]。[Localhost]。[/]:Spring埋め込みWebApplicationContextの初期化
 2016-01-12 12:47:52.095 INFO 88 --- [ost-startStop-1] osweb.context.ContextLoader:ルートWebApplicationContext:初期化は3688ミリ秒で完了しました
 2016-01-12 12:47:52.546 INFO 88 --- [ost-startStop-1] osbceServletRegistrationBean:マッピングサーブレット:「dispatcherServlet」を[/]に
 2016-01-12 12:47:52.556 INFO 88 --- [ost-startStop-1] osbcembedded.FilterRegistrationBean:マッピングフィルター: 'characterEncodingFilter'から:[/ *]
 2016-01-12 12:47:52.557 INFO 88 --- [ost-startStop-1] osbcembedded.FilterRegistrationBean:Mapping filter: 'hiddenHttpMethodFilter' to:[/ *]
 2016-01-12 12:47:52.559 INFO 88 --- [ost-startStop-1] osbcembedded.FilterRegistrationBean:マッピングフィルター: 'httpPutFormContentFilter'から:[/ *]
 2016-01-12 12:47:52.559 INFO 88 --- [ost-startStop-1] osbcembedded.FilterRegistrationBean:マッピングフィルター: 'requestContextFilter'から:[/ *]
 2016-01-12 12:47:52.985 INFO 88 --- [main] swsmmaRequestMappingHandlerAdapter:@ControllerAdviceを探しています:org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@dbf57b3:起動日[火1月12日12:47:48 MSK 2016]; コンテキスト階層のルート
 2016-01-12 12:47:53.089 INFO 88 --- [main] swsmmaRequestMappingHandlerMapping:Mapped "{[/ request]、methods = [GET]、produces = [application / json; charset = UTF-8]}" public final test.kotlin.spring.project.AckResponse test.kotlin.spring.project.ServiceController.pullUpdate(java.lang.String、java.lang.String)
 2016-01-12 12:47:53.094 INFO 88 --- [main] swsmmaRequestMappingHandlerMapping: "{[/ error]}"をpublic org.springframework.http.ResponseEntity <java.util.Map <java.lang.Stringにマッピングしました、java.lang.Object >> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
 2016-01-12 12:47:53.094 INFO 88 --- [main] swsmmaRequestMappingHandlerMapping:「{[/ error]、produces = [text / html]}」をパブリックorg.springframework.web.servlet.ModelAndView組織にマッピングしました。 springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
 2016-01-12 12:47:53.138 INFO 88 --- [main] oswshandler.SimpleUrlHandlerMapping:タイプ[class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]のハンドラーにマッピングされたURLパス[/ webjars / **]
 2016-01-12 12:47:53.139 INFO 88 --- [main] oswshandler.SimpleUrlHandlerMapping:タイプ[class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]のハンドラーへのURLパス[/ **]のマッピング
 2016-01-12 12:47:53.195 INFO 88 --- [main] oswshandler.SimpleUrlHandlerMapping:タイプ[class org.springframework.web.servlet.resource。]のハンドラーにマッピングされたURLパス[/**/favicon.ico]。 ResourceHttpRequestHandler]
 2016-01-12 12:47:53.512 INFO 88 --- [main] osjeaAnnotationMBeanExporter:起動時のJMX公開用のBeanの登録
 2016-01-12 12:47:53.612 INFO 88 --- [main] sbcetTomcatEmbeddedServletContainer:Tomcatはポート(s)で開始されました:8080(http)
 2016-01-12 12:47:53.620 INFO 88 --- [main] tksproject.Application $ Companion:Started Application.Companion in 6.076 seconds(JVM running for 7.177)
 2016-01-12 12:47:57.874 INFO 88 --- [nio-8080-exec-1] oaccC [Tomcat]。[Localhost]。[/]:Spring FrameworkServlet 'dispatcherServlet'の初期化
 2016-01-12 12:47:57.874 INFO 88 --- [nio-8080-exec-1] osweb.servlet.DispatcherServlet:FrameworkServlet 'dispatcherServlet':初期化が開始されました
 2016-01-12 12:47:57.897 INFO 88 --- [nio-8080-exec-1] osweb.servlet.DispatcherServlet:FrameworkServlet 'dispatcherServlet':初期化は23ミリ秒で完了しました 




正常に起動したら、 名前を指定しリクエストページを開いてみてください。 答えは次のようになります。

 { status: true, result: "Hi Kris", message: "surname is empty" }
      
      





そして、 nameとsurnameのリクエストは、答えは少し異なります:



 { status: true, result: "Hi Eagle, Kris" }
      
      





データの並べ替えを確認するための呼び出し: 並べ替え 。 結果は次のようになります。



 { status: true, result: "1,3,value,virst" }
      
      





同じ呼び出しですが、空の配列を使用します: Call

 { status: true, result: "" }
      
      





必要に応じて、コマンドgradle buildを使用して、プロジェクト全体を1つの実行可能なjarにアセンブルできます。 その結果、プロジェクトは解凍せずにすべての依存関係を含む1つのアーカイブに収集されます。 このアプローチでは、すべての依存関係をアンパックしてプロジェクトを1つのアーカイブにアセンブルすると、同じアセンブルに比べてプロジェクトのビルド時間が大幅に増加します。



おわりに



結論として、kotlinはjavaを代替として使用するプロジェクトで作業するための非常に便利な言語であることが判明したことに注意してください。 この言語のエコシステムはまだ同じscalaほど広範囲ではありませんが、Java APIがある同じビッグデータで使用できるようになりました。 さらに、kotlinからjavaとやり取りするのは非常に簡単なので、javaにあるすべてのものをkotlinでも使用できます。 さらに、スタジオからは、Javaファイルをkotlinの類似ファイルに簡単に変換できる可能性があります(ただし、変換後に小さな手でファイルを修正する必要があります)。 JetBrainsは、javaとscalaを置き換える完璧な言語を作成するという素晴らしい仕事をしました。 そして、将来、kotlinの使用傾向が成長することを願っています。



ソースはgithubで入手できます。



ご清聴ありがとうございました。



All Articles