という勉強会をやることになった。そういうことをやってみたい、教えてほしいという若者が多いのである。
そういう若者を相手にすると、勉強になるのは私である。若者は私がちょちょいでそんなことが出来ると思っているようだが、そんなことやる機会は普通に仕事していたらないのである。だいたい困っている現場に呼ばれるだけだからな。だいたいの知識はあるつもりだけども断片的だし、最初からやるとしたらどうするのがベストとか、よく知らないのである。というわけで、一番ためになっているのは私であり、若者をダシに知識の整理をして楽しもう。
とはいえ、プログラミングの学習も、環境を作ってHello Worldするまでが一番難しい。一度、ビルド出来てしまえばそこからは買ってきた本の通りになるけど、ビルド出来るようになるまでに起きることは自分の環境に依存した出来事だからだ。
というわけで、なーんもよくわかってない若者前提で、手順を考える。今日はWindows環境前提でやる。ぶっちゃけ私もなんもわかっていない。
まずは、Javaをインストールする。これは記事に書いた。好きなものをwingetしてくれればよい。GitもWingetすればいいはず。ここまでは私のPCにも入ってた。
次はGradleかな。これはwingetできない。というかダウンロードしてフォルダに展開するだけ。公式はC:\Gradleを作ってそこに入れろとしているが、趣味でホームディレクトリの下に入れる事にする。~/bin/gradleの下に公式からダウンロードしたzipを解凍したものをそのまま入れる。
そして、これを環境変数のPATHへセットする。どうやっても難しいが、PowerShellでやる方法をここでは書いておこう。
PS C:\Users\Tambourine> [System.Environment]::SetEnvironmentVariable('PATH', $Env:PATH + 'C:\Users\Tambourine\bin\gradle\gradle-8.6\bin;', 'User')
PowerShellはホント、しっくりこないよ・・・
これで、ターミナルを再起動すればPATHが設定されているはず。設定したターミナルでは$Env:PATH
の値は変化しないので注意。
PS C:\Users\Tambourine> gradle -v Welcome to Gradle 8.6! Here are the highlights of this release: - Configurable encryption key for configuration cache - Build init improvements - Build authoring improvements For more details see https://docs.gradle.org/8.6/release-notes.html ------------------------------------------------------------ Gradle 8.6 ------------------------------------------------------------ Build time: 2024-02-02 16:47:16 UTC Revision: d55c486870a0dc6f6278f53d21381396d0741c6e Kotlin: 1.9.20 Groovy: 3.0.17 Ant: Apache Ant(TM) version 1.10.13 compiled on January 4 2023 JVM: 20.0.2 (Eclipse Adoptium 20.0.2+9) OS: Windows 11 10.0 amd64
JDKとGradleが入ったらいよいよSpringの出番だ。Getting Started的なドキュメントを読むと、とりあえずSpring Initializrでひな形を作れと書いてある。Springはただでさえ黒魔術が多いのに、Spring Bootすると何がどういう仕組みで動いているのかが黒魔術ラッパーで覆われてしまうので気が重いが、初心者は公式ドキュメントの通りにやるのが基本だ。
というわけで、 https://spring.pleiades.io/guides/gs/spring-boot を見ながら進もう。
まずはSpring Initializrでポチポチする。依存ライブラリは3つだけ入れた
この選択があっているのかどうかはよくわからない。生成されたコードをダウンロードして、vscodeで開いてみる。開くとJavaとかGradleのプラグインをいれたらどうかねと言われるので、入れる。言われるがままである。
では、コントローラーを作ってみよう。
といっても、ドキュメントをまんまパクる
package one.tmbrms.readingsns; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloWorldController { @GetMapping("/") public String index(){ return "Hello World!"; } }
とりあえず、これだけ作れば、GradleでbootRunタスクを実行すると、localhost:8080 でHello Worldは表示される。素晴らしい。
ただし、ドキュメントはアプリケーションクラスをもっとちゃんとせいと書いてある。で、CommandLineRunnerメソッドを追加して、beanの一覧を取得している。
@Bean public CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { System.out.println("Let's inspect the beans provided by Spring Boot:"); String[] beanNames = ctx.getBeanDefinitionNames(); Arrays.sort(beanNames); for (String beanName : beanNames) { System.out.println(beanName); } }; }
これをアプリケーションクラスに追加する。すると、起動時に以下が出力される。
Let's inspect the beans provided by Spring Boot: applicationAvailability applicationTaskExecutor basicErrorController beanNameHandlerMapping beanNameViewResolver characterEncodingFilter classPathFileSystemWatcher classPathRestartStrategy commandLineRunner conditionEvaluationDeltaLoggingListener conventionErrorViewResolver defaultServletHandlerMapping defaultTemplateResolver defaultViewResolver dispatcherServlet dispatcherServletRegistration error errorAttributes errorPageCustomizer errorPageRegistrarBeanPostProcessor fileSystemWatcherFactory fileWatcher flashMapManager forceAutoProxyCreatorToUseClassProxying formContentFilter handlerExceptionResolver handlerFunctionAdapter helloWorldController httpMessageConvertersRestClientCustomizer httpRequestHandlerAdapter jacksonObjectMapper jacksonObjectMapperBuilder jsonComponentModule jsonMixinModule jsonMixinModuleEntries lifecycleProcessor liveReloadServer liveReloadServerEventListener localeCharsetMappingsCustomizer localeResolver mappingJackson2HttpMessageConverter messageConverters multipartConfigElement multipartResolver mvcContentNegotiationManager mvcConversionService mvcHandlerMappingIntrospector mvcPathMatcher mvcPatternParser mvcResourceUrlProvider mvcUriComponentsContributor mvcUrlPathHelper mvcValidator mvcViewResolver optionalLiveReloadServer org.springframework.aop.config.internalAutoProxyCreator org.springframework.boot.autoconfigure.AutoConfigurationPackages org.springframework.boot.autoconfigure.aop.AopAutoConfiguration org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$ClassProxyingConfiguration org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonMixinConfiguration org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$SimpleAsyncTaskExecutorBuilderConfiguration org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$TaskExecutorBuilderConfiguration org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$TaskExecutorConfiguration org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$ThreadPoolTaskExecutorBuilderConfiguration org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$SimpleAsyncTaskSchedulerBuilderConfiguration org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$TaskSchedulerBuilderConfiguration org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$ThreadPoolTaskSchedulerBuilderConfiguration org.springframework.boot.autoconfigure.thymeleaf.TemplateEngineConfigurations$DefaultTemplateEngineConfiguration org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$DefaultTemplateResolverConfiguration org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafWebMvcConfiguration org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafWebMvcConfiguration$ThymeleafViewResolverConfiguration org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration org.springframework.boot.context.internalConfigurationPropertiesBinder org.springframework.boot.context.properties.BoundConfigurationProperties org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.methodValidationExcludeFilter org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$RestartConfiguration org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.event.internalEventListenerFactory org.springframework.context.event.internalEventListenerProcessor parameterNamesModule preserveErrorControllerTargetClassPostProcessor propertySourcesPlaceholderConfigurer readingsnsApplication requestContextFilter requestMappingHandlerAdapter requestMappingHandlerMapping resourceHandlerMapping restClientBuilder restClientBuilderConfigurer restClientSsl restTemplateBuilder restTemplateBuilderConfigurer restartingClassPathChangedEventListener routerFunctionMapping server-org.springframework.boot.autoconfigure.web.ServerProperties servletWebServerFactoryCustomizer simpleAsyncTaskExecutorBuilder simpleAsyncTaskSchedulerBuilder simpleControllerHandlerAdapter spring.devtools-org.springframework.boot.devtools.autoconfigure.DevToolsProperties spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties spring.lifecycle-org.springframework.boot.autoconfigure.context.LifecycleProperties spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties spring.sql.init-org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties spring.ssl-org.springframework.boot.autoconfigure.ssl.SslProperties spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties spring.thymeleaf-org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties spring.web-org.springframework.boot.autoconfigure.web.WebProperties sslBundleRegistry sslPropertiesSslBundleRegistrar standardJacksonObjectMapperBuilderCustomizer stringHttpMessageConverter taskExecutorBuilder taskSchedulerBuilder templateEngine themeResolver threadPoolTaskExecutorBuilder threadPoolTaskSchedulerBuilder thymeleafViewResolver tomcatServletWebServerFactory tomcatServletWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer viewControllerHandlerMapping viewNameTranslator viewResolver webServerFactoryCustomizerBeanPostProcessor websocketServletWebServerCustomizer welcomePageHandlerMapping welcomePageNotAcceptableHandlerMapping
この長いリストを見ると、だいぶ気分が悪くなる。が、まあ、仕方がないね。ちなみに、この@Bean
を付けたメソッドは、HelloWorldControllerに最初間違えて付けちゃった。でも、動く。キモいねー
次が単体テストなんだが・・・エンドポイントのテストをしようとしている。キモい。個人的なことを言えば、RESTのエンドポイントのテストは単体テストだとは思わない。これはE2Eとユニットテストの中間に位置するもので、Postmanとかでテストしたい。まあ、Postman使わずにテストできたら便利じゃないかと言われればそうだけども、若者にこれをユニットテストだと思ってほしくないので、ここはとりあえず飛ばす。こういうものがあるんだということは覚えておこう。
というか、build.gradleの依存のところに
dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' developmentOnly 'org.springframework.boot:spring-boot-devtools' testImplementation 'org.springframework.boot:spring-boot-starter-test' }
勝手に入っているspring-boot-starter-testを消したいなあ・・・
さて、その次には監視とかのアクチュエーターを追加するところ。 あー、こういうのは便利だね。でも、とりあえず飛ばしても良いだろう。
starterと仲良くなるには、だいぶ時間がかかりそうだなー
というわけで、いったんここまでで終わりにしようか。