使用Spring Mail和FreeMaker发送HTML邮件
引言
最近在写自己的博客项目,有收到新评论后发送邮件通知的功能,使用MQ通知服务,使用了没接触过的FreeMaker和JavaMail,记录一下实现过程,代码仓库:https://github.com/mashirot/MashiroBlog/tree/master/mail-service
实现
项目结构:
代码语言:javascript复制└─src
└─main
├─kotlin
│ └─ski
│ └─mashiro
│ │ MailServiceApplication.kt
│ │
│ ├─annotation
│ │ Slf4j.kt
│ │
│ ├─config
│ │ JacksonConfig.kt
│ │
│ ├─constant
│ │ RabbitMQConsts.kt
│ │
│ ├─dto
│ │ CommentMailDTO.kt
│ │
│ ├─listener
│ │ MailQueueListener.kt
│ │
│ └─service
│ │ MailService.kt
│ │
│ └─impl
│ MailServiceImpl.kt
│
└─resources
│ application.yml
│
└─templates
CommentAdvice.ftlh
环境
Springboot 3.1.2
,JDK17
,Kotlin 1.8.22
依赖
代码语言:javascript复制dependencies {
implementation("org.springframework.boot:spring-boot-starter-amqp")
implementation("org.springframework.boot:spring-boot-starter-mail")
implementation("org.springframework.boot:spring-boot-starter-freemarker")
implementation("org.jetbrains.kotlin:kotlin-reflect")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.amqp:spring-rabbit-test")
implementation("com.fasterxml.jackson.core:jackson-databind:2.15.2")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2")
}
代码
Jackson配置
代码语言:javascript复制/**
* @author MashiroT
*/
@Configuration
class JacksonConfig {
@Bean
fun objectMapper(): ObjectMapper {
return ObjectMapper().registerModule(JavaTimeModule())
}
}
@Slf4j注解
代码语言:javascript复制/**
* @author MashiroT
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Slf4j {
companion object {
val <reified T> T.log: Logger
inline get() = LoggerFactory.getLogger(T::class.java)
}
}
MQ监听
代码语言:javascript复制/**
* @author MashiroT
*/
@Component
class MailQueueListener(
val mailService: MailService
) {
@RabbitListener(queues = [RabbitMQConsts.MAIL_QUEUE])
fun listenMailQueue(msg: String) {
mailService.sendNewCommentAdvice2Owner(msg)
}
}
MailService
代码语言:javascript复制/**
* @author MashiroT
*/
@Service
@Slf4j
class MailServiceImpl(
val objectMapper: ObjectMapper,
val mailSender: JavaMailSender,
val freeMakerConfiguration: Configuration,
) : MailService {
@Value("${blog.from}")
private var from: String? = null
@Value("${blog.to}")
private var to: String? = null
override fun sendNewCommentAdvice2Owner(msg: String) {
try {
val commentMailDTO = objectMapper.readValue(msg, CommentMailDTO::class.java)
// 使用MimeMessage发送复杂邮件,如无需求可使用MailSender配合SimpleMailMessage使用
val mimeMessage = mailSender.createMimeMessage()
val mimeMessageHelper = MimeMessageHelper(mimeMessage, "utf-8")
// 发送人邮箱
mimeMessageHelper.setFrom(from!!)
// 收件人邮箱
mimeMessageHelper.setTo(to!!)
val title = "你的帖子「${commentMailDTO.articleTitle}」有新回复"
// 邮件主题
mimeMessageHelper.setSubject(title)
val map = mapOf(
"title" to title,
"senderNickname" to commentMailDTO.senderNickname!!,
"content" to if (commentMailDTO.content!!.length > 50) commentMailDTO.content.substring(
0,
50
) else commentMailDTO.content
)
// 获取Template模板,默认路径为classpath:/templates/
val template = freeMakerConfiguration.getTemplate("CommentAdvice.ftlh")
// 传入参数并转为string
val text = FreeMarkerTemplateUtils.processTemplateIntoString(template, map)
// 邮件正文
mimeMessageHelper.setText(text, true)
// 发送日期(不填默认当前)
mimeMessageHelper.setSentDate(Date())
mailSender.send(mimeMessage)
log.info("文章:${commentMailDTO.articleTitle} 收到新评论,已发送邮件通知")
} catch (e: Exception) {
e.printStackTrace()
}
}
}
配置
代码语言:javascript复制mail:
protocol: smtp
host: smtp.office365.com
port: 587
username: shiinamashiro@outlook.com
password: sakurasou
properties:
mail:
# debug: true
smtp:
starttls:
enable: true
# socks:
# host: 127.0.0.1
# port: 7890
遇到的问题
SMTPSendFailedException: 501 5.1.7 Invalid address
message的from
属性设置错误,应为发送者邮箱
451 5.7.3 STARTTLS is required to send mail
Outlook的smtp使用starttls协议,在mail的配置中增加如下:
代码语言:javascript复制properties:
mail:
# debug: true
smtp:
starttls:
enable: true
参考文章
- Java 发送邮件实现(JavaMail 和 Spring 实现)
- Spring Boot整合JavaMail实现邮件发送
- Spring Boot Freemarker 中的弯弯绕!
- FreeMarker简单入门,这篇就够了