はじめに
Java(Kotlin)からメールを送るといえばjavamailを使用している方が多いかと思いますが、springにもメール機能があるので、こちらの機能について調べました。
ソースのパッケージにdatabasetestとありますが、別の奴の使いまわしをしているだけなので気にしないでください。
環境
Kotlin
Spring-boot 2.0.1.RELEASE
SMTPサーバーとしてsmtp4devを使っています。
FakeSMTPの方がシンプルなのですが、本格的な使い方として少し足りない部分があり断念しました。
シンプルで好きなんですけどね。
Dependency
今回はMavenでやるので、pom.xmlに以下を追加します。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
まずはシンプルなメールを送ってみる
とりあえずシンプルなテキストメールを送ってみます。
spring.mail.host=localhost
spring.mail.port=25
package com.tasogarei.databasetest.contorller
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.mail.MailException
import org.springframework.mail.MailSender
import org.springframework.mail.SimpleMailMessage
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ResponseBody
@Controller
class MailController {
@Autowired
lateinit var mailSender : MailSender
@ResponseBody
@GetMapping("/mail")
fun sendmail() : ResponseEntity<String> {
var message = SimpleMailMessage().apply {
setFrom( "test@test.jp")
setTo("tasogarei34@gmail.com")
setSubject("test subject")
setText("test body")
}
try {
mailSender.send(message)
} catch (e : MailException) {
// do not something
}
return ResponseEntity.ok().build()
}
}
たったこれだけで問題ありません。
SmtpAuthにも対応しているため、いくつかのプロパティを追加するだけでそれらを実現することが可能となっています。
MTAをGmailに変更して飛ばしたいみたいなことも簡単に可能です。
ただし、こちらをしておかないとアプリケーションから飛ばせないのでご注意ください。
簡単に外部に飛ばせたり出来るようになってしまっているので、あんまりこの設定を使ってメール飛ばす確認はしない方が良いとは思います。
まぁ、自前のMTAは設定するのが面倒くさいのでしょうがない面もありますが、smtp4devならSmtpAuthも出来るので、そちらを使用してテストしましょう。
一応設定だけ載せておきます。
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username={your mail address}
spring.mail.password={your password}
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
Beanにテンプレートを登録して送信
SimpleMailMessageはBean登録可能なので、テンプレートとして登録して可変箇所だけ他で設定して送信することも可能となっています。
分かりづらいので、使わない方が良い気はします。
spring.mail.host=localhost
spring.mail.port=25
package com.tasogarei.databasetest.config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.mail.SimpleMailMessage
import org.springframework.stereotype.Component
@Component
@Configuration
class MailConfig {
@Bean("templateMessage")
fun template() : SimpleMailMessage {
var template = SimpleMailMessage()
template.setFrom("tasogarei34@gmail.com")
template.setTo("test@test.jp")
template.setSubject("test subject")
template.setText("test body")
return template
}
}
package com.tasogarei.databasetest.contorller
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.mail.MailException
import org.springframework.mail.MailSender
import org.springframework.mail.SimpleMailMessage
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ResponseBody
@Controller
class MailController {
@Autowired
lateinit var mailSender : MailSender
@Autowired
lateinit var templateMessage : SimpleMailMessage
@ResponseBody
@GetMapping("/mail")
fun sendmail() : ResponseEntity<String> {
try {
mailSender.send(templateMessage)
} catch (e : MailException) {
println(e.message)
}
return ResponseEntity.ok().build()
}
}
テンプレート登録についてはBeanを複数個登録すればその分作成可能となっています。
package com.tasogarei.databasetest.config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import org.springframework.mail.SimpleMailMessage
import org.springframework.stereotype.Component
@Component
@Configuration
class MailConfig {
@Bean("templateMessage")
fun template() : SimpleMailMessage {
var template = SimpleMailMessage()
template.setFrom("tasogarei34@gmail.com")
template.setTo("test@test.jp")
template.setSubject("test subject")
template.setText("test body")
return template
}
@Bean("template2Message")
fun template2() : SimpleMailMessage {
var template = SimpleMailMessage()
template.setFrom("tasogarei34@gmail.com")
template.setTo("test@test.jp")
template.setSubject("test subject2")
template.setText("test body2")
return template
}
}
package com.tasogarei.databasetest.contorller
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.mail.MailException
import org.springframework.mail.MailSender
import org.springframework.mail.SimpleMailMessage
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ResponseBody
@Controller
class MailController {
@Autowired
lateinit var mailSender : MailSender
@Autowired
lateinit var templateMessage : SimpleMailMessage
@Autowired
lateinit var template2Message : SimpleMailMessage
@ResponseBody
@GetMapping("/mail")
fun sendmail() : ResponseEntity<String> {
try {
mailSender.send(templateMessage)
} catch (e : MailException) {
println(e.message)
}
return ResponseEntity.ok().build()
}
@ResponseBody
@GetMapping("/mail2")
fun sendmail2() : ResponseEntity<String> {
try {
mailSender.send(template2Message)
} catch (e : MailException) {
println(e.message)
}
return ResponseEntity.ok().build()
}
}
HTMLメールを送る
今のままではリッチなコンテンツを送る事が出来ないので、HTMLを送ってみようと思います。
送るのは凄く簡単ですが、Textのみとは少し違いがあります。
変更点としては
・MailSender
ではなくJavaMailSender
を使って送信する
・SimpleMailMessage
ではなくMimeMessagePreparator
を実装してメッセージを作成する
という2点があります。
MimeMessagePreparator
はMimeMessageHelper
を使うことでとてつもなく簡単に値が設定できるHelperクラスがあるので、これを使うと良いです。
Kotlinの実装は特に楽を感じられてすっきりした実装が可能となっていて良い感じです。
application.propertiesに書く内容は変更ないので、ここでは割愛します。
package com.tasogarei.databasetest.contorller
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.mail.MailException
import org.springframework.mail.javamail.JavaMailSender
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ResponseBody
import javax.mail.internet.InternetAddress
import org.springframework.mail.javamail.MimeMessageHelper
@Controller
class MailController {
@Autowired
lateinit var mailSender : JavaMailSender
@ResponseBody
@GetMapping("/mail")
fun sendmail() : ResponseEntity<String> {
try {
mailSender.send({ mimeMessage ->
MimeMessageHelper(mimeMessage, true, "UTF-8").apply {
setTo("tasogarei@gmail.com")
setFrom(InternetAddress("test@test.jp"))
setSubject("test subject")
setText("text part", "<html><body>html</body></html>")
}
})
} catch (e : MailException) {
println(e.message)
}
return ResponseEntity.ok().build()
}
}
色んな面倒を飛び越えてもう本当に楽です。
ちなみに、添付ファイルも入れたい場合はMimeMessageHelper
にあるaddAttachment()
に指定すると添付ファイルとして扱ってくれます。
引数は色々と渡せるのですが、とりあえず/src/main/resource
配下にあるファイルを添付する例をのせておきます。
addAttachment("test.txt",DefaultResourceLoader().getResource("classpath:test.txt").file )
envelop fromの付与
業務上、envelope fromとfromが違う場合がありますが、それを行う方法です。
簡単に設定するクラスとしてSMTPMessage
があるので、これを使用します。
MimeMessage
から変換可能になっているため、Helper
を使ってMimeMessage
を作成して変換、設定を経て送信という流れで実現します。
ちなみにHeader
もHelper
では設定出来ないようなので、カスタムヘッダーを行いたい場合もMimeMessage
に変換して設定してあげる必要があるようです。
なぜさせてくれないのかがさっぱり分からないです・・・
Meesage-IDについてはupdateMessageID()
のOverrideが必要となるため、やるならMimeMessage
の継承して実装するというのは変わってません。
package com.tasogarei.databasetest.contorller
import com.sun.mail.smtp.SMTPMessage
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.ResponseEntity
import org.springframework.mail.MailException
import org.springframework.mail.javamail.JavaMailSender
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ResponseBody
import javax.mail.internet.InternetAddress
import org.springframework.mail.javamail.MimeMessageHelper
@Controller
class MailController {
@Autowired
lateinit var mailSender : JavaMailSender
@ResponseBody
@GetMapping("/mail")
fun sendmail() : ResponseEntity<String> {
var messageHelper = MimeMessageHelper(mailSender.createMimeMessage(), true, "UTF-8").apply {
setTo("tasogarei@gmail.com")
setFrom(InternetAddress("test@test.jp"))
setSubject("test subject")
setText("text part", "<html><body>html</body></html>")
}
var message = SMTPMessage(messageHelper.mimeMessage).apply {
envelopeFrom = "envelop@test.jp"
}
try {
mailSender.send(message)
} catch (e : MailException) {
println(e.message)
}
return ResponseEntity.ok().build()
}
}
最後に
未だにJavaMailも開発が行われており積極的に変更する理由はありませんが、Springを使っているのであれば、楽にMultiPartを表現できるSpringMailを使用するという選択肢は大いにありという感想です。
見た限りではJavMailと動作がそんなに変わらなさそうにも見えるので、乗り換えも楽そうではあります。