简介

在企业应用开发中,邮件发送是一个常见但繁琐的功能:用户注册验证、密码找回、订单通知、系统告警……每次都要处理SMTP连接、MIME类型、附件编码等底层细节。原生的JavaMail API虽然功能强大,但使用起来非常复杂,仅发送一封简单的邮件就需要几十行代码。Hutool 的 JakartaMailUtil 对JavaMail进行了深度封装,让邮件发送变得像调用一个方法一样简单。

JakartaMailUtil 的主要应用场景:

  • 用户通知:注册验证、密码重置、订单状态更新
  • 系统告警:异常监控、性能预警、定时任务报告
  • 营销推广:活动通知、优惠券发放、会员服务
  • 业务报表:日报周报、数据统计、财务对账
  • 团队协作:任务提醒、审批通知、会议邀请

核心概念

SMTP协议

SMTP(Simple Mail Transfer Protocol)是发送邮件的标准协议。邮件发送需要配置SMTP服务器地址、端口、认证信息等参数。

常见邮件服务商SMTP配置:

  • QQ邮箱:smtp.qq.com,端口465/587(SSL)
  • 163邮箱:smtp.163.com,端口465/994(SSL)
  • Gmail:smtp.gmail.com,端口465/587(SSL)
  • 企业邮箱:一般由企业IT提供

邮件账户配置

JakartaMailUtil使用MailAccount对象封装邮件账户信息,包括SMTP服务器、端口、用户名、密码、发件人信息等。可以通过代码创建或从配置文件加载。

邮件类型

  • 纯文本邮件:简单的文本内容,不支持格式
  • HTML邮件:支持富文本、图片、样式等,体验更好
  • 带附件邮件:可附带文件、图片等资源

环境准备

Maven依赖

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-extra</artifactId>
    <version>5.8.16</version>
</dependency>

<!-- Jakarta Mail API -->
<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>jakarta.mail</artifactId>
    <version>2.0.1</version>
</dependency>

配置文件(可选)

在resources目录下创建mail.setting文件(可选,也可以纯代码配置):

# SMTP服务器地址
host = smtp.qq.com
# SMTP端口
port = 465
# 发件人邮箱
from = your_email@qq.com
# 用户名(一般与发件人邮箱相同)
user = your_email@qq.com
# 密码(QQ邮箱使用授权码,不是登录密码)
pass = your_authorization_code
# 是否使用SSL
sslEnable = true

注意:QQ邮箱、163邮箱等需要开启SMTP服务并获取授权码,不能直接使用登录密码。


常用API详解

1. 邮件账户配置

1.1 代码方式创建账户 - MailAccount

方法签名:

MailAccount account = new MailAccount();
account.setHost(String host);
account.setPort(Integer port);
account.setFrom(String from);
account.setUser(String user);
account.setPass(String pass);
account.setSslEnable(Boolean sslEnable);

功能:通过代码创建邮件账户配置对象。

示例:

// 创建邮件账户配置(QQ邮箱示例)
MailAccount account = new MailAccount();
account.setHost("smtp.qq.com");
account.setPort(465);
account.setFrom("sender@qq.com");
account.setUser("sender@qq.com");
account.setPass("your_authorization_code");  // QQ邮箱授权码
account.setSslEnable(true);

// 163邮箱配置
MailAccount account163 = new MailAccount();
account163.setHost("smtp.163.com");
account163.setPort(465);
account163.setFrom("sender@163.com");
account163.setUser("sender@163.com");
account163.setPass("your_password");
account163.setSslEnable(true);

// 企业邮箱配置
MailAccount corpAccount = new MailAccount();
corpAccount.setHost("smtp.exmail.qq.com");
corpAccount.setPort(465);
corpAccount.setFrom("admin@company.com");
corpAccount.setUser("admin@company.com");
corpAccount.setPass("your_password");
corpAccount.setSslEnable(true);
1.2 配置文件方式 - getSession()

方法签名:

static Session getSession(MailAccount mailAccount, boolean isSingleton)

功能:根据邮件账户配置获取邮件会话对象。

参数:

  • mailAccount:邮件账户配置
  • isSingleton:是否单例(true表示复用会话,性能更好)

示例:

// 获取邮件会话(单例模式)
MailAccount account = new MailAccount();
account.setHost("smtp.qq.com");
account.setPort(465);
account.setFrom("sender@qq.com");
account.setUser("sender@qq.com");
account.setPass("your_code");
account.setSslEnable(true);

Session session = MailUtil.getSession(account, true);

2. 发送文本邮件

2.1 发送给单个收件人 - sendText()

方法签名:

static String sendText(String to, String subject, String content, File... files)

功能:使用配置文件中的账户发送纯文本邮件。

参数:

  • to:收件人邮箱(多个用逗号或分号分隔)
  • subject:邮件主题
  • content:邮件正文(纯文本)
  • files:附件文件(可选)

返回值:邮件ID

示例:

// 发送简单文本邮件
String messageId = MailUtil.sendText(
    "receiver@qq.com",
    "系统通知",
    "您的账号已激活,欢迎使用我们的服务!"
);
System.out.println("邮件发送成功,ID: " + messageId);

// 发送给多个收件人(逗号分隔)
MailUtil.sendText(
    "user1@qq.com,user2@163.com",
    "会议通知",
    "本周五下午3点召开项目会议,请准时参加。"
);

// 发送给多个收件人(分号分隔)
MailUtil.sendText(
    "user1@qq.com;user2@163.com",
    "假期通知",
    "国庆节放假7天,祝大家节日快乐!"
);

// 带附件的文本邮件
File report = new File("report.pdf");
MailUtil.sendText(
    "manager@company.com",
    "月度报告",
    "请查收本月工作报告",
    report
);
2.2 发送给多人(集合方式) - sendText()

方法签名:

static String sendText(Collection<String> tos, String subject, String content, File... files)

功能:使用集合方式指定多个收件人,发送纯文本邮件。

示例:

// 使用List指定收件人
List<String> recipients = Arrays.asList(
    "user1@qq.com",
    "user2@163.com",
    "user3@gmail.com"
);

MailUtil.sendText(
    recipients,
    "系统维护通知",
    "系统将于今晚22:00-24:00进行维护,期间服务暂停。"
);

// 动态构建收件人列表
List<User> users = userService.findAllActiveUsers();
List<String> emails = users.stream()
    .map(User::getEmail)
    .collect(Collectors.toList());

MailUtil.sendText(
    emails,
    "账户安全提醒",
    "请及时修改初始密码,保护账户安全。"
);

3. 发送HTML邮件

3.1 基础HTML邮件 - sendHtml()

方法签名:

static String sendHtml(String to, String subject, String content, File... files)
static String sendHtml(Collection<String> tos, String subject, String content, File... files)

功能:发送支持HTML格式的邮件,可以包含样式、图片、链接等。

示例:

// 发送简单HTML邮件
String htmlContent = "<html>" +
    "<body>" +
    "<h2 style='color: blue;'>欢迎注册</h2>" +
    "<p>尊敬的用户,您好!</p>" +
    "<p>您的账号已成功注册,请点击下方链接激活:</p>" +
    "<a href='https://example.com/activate?token=123'>点击激活</a>" +
    "</body>" +
    "</html>";

MailUtil.sendHtml(
    "newuser@qq.com",
    "账号激活邮件",
    htmlContent
);

// 发送带样式的HTML邮件
String styledContent = "<html>" +
    "<head>" +
    "<style>" +
    ".container { padding: 20px; background: #f5f5f5; }" +
    ".title { color: #333; font-size: 24px; }" +
    ".content { line-height: 1.6; }" +
    ".button { " +
    "  display: inline-block; " +
    "  padding: 10px 20px; " +
    "  background: #007bff; " +
    "  color: white; " +
    "  text-decoration: none; " +
    "}" +
    "</style>" +
    "</head>" +
    "<body>" +
    "<div class='container'>" +
    "<h1 class='title'>订单确认</h1>" +
    "<div class='content'>" +
    "<p>您的订单已确认,订单号:202401150001</p>" +
    "<p>商品总价:¥299.00</p>" +
    "<a href='https://example.com/order/202401150001' class='button'>查看订单详情</a>" +
    "</div>" +
    "</div>" +
    "</body>" +
    "</html>";

MailUtil.sendHtml(
    "customer@qq.com",
    "订单确认通知",
    styledContent
);
3.2 带内嵌图片的HTML邮件 - sendHtml()

方法签名:

static String sendHtml(String to, String subject, String content, Map<String,InputStream> imageMap, File... files)

功能:发送HTML邮件并内嵌图片,图片直接显示在邮件中而不是作为附件。

参数:

  • imageMap:图片映射,key为图片ID(在HTML中引用),value为图片输入流

示例:

// 准备内嵌图片
Map<String, InputStream> imageMap = new HashMap<>();
imageMap.put("logo", new FileInputStream("logo.png"));
imageMap.put("banner", new FileInputStream("banner.jpg"));

// HTML内容中引用图片(使用cid:图片ID)
String htmlContent = "<html>" +
    "<body>" +
    "<img src='cid:logo' width='200'/>" +
    "<h2>产品推荐</h2>" +
    "<img src='cid:banner' width='600'/>" +
    "<p>我们为您精选了以下优质商品...</p>" +
    "</body>" +
    "</html>";

MailUtil.sendHtml(
    "customer@qq.com",
    "每周精选推荐",
    htmlContent,
    imageMap
);

// 实际应用:营销邮件
Map<String, InputStream> images = new HashMap<>();
images.put("product1", getClass().getResourceAsStream("/images/product1.jpg"));
images.put("product2", getClass().getResourceAsStream("/images/product2.jpg"));

String marketingHtml = "<html>" +
    "<body style='font-family: Arial;'>" +
    "<h1 style='color: #e74c3c;'>限时特惠</h1>" +
    "<div>" +
    "<img src='cid:product1' width='300'/>" +
    "<p>商品A - 原价¥199,现价¥99</p>" +
    "</div>" +
    "<div>" +
    "<img src='cid:product2' width='300'/>" +
    "<p>商品B - 原价¥299,现价¥199</p>" +
    "</div>" +
    "</body>" +
    "</html>";

MailUtil.sendHtml(
    vipEmails,
    "VIP会员专属优惠",
    marketingHtml,
    images
);

4. 通用发送方法

4.1 完整参数发送 - send()

方法签名:

static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files)
static String send(Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, boolean isHtml, File... files)

功能:最完整的邮件发送方法,支持抄送、密送、HTML/文本、附件等所有功能。

参数:

  • to/tos:收件人
  • cc/ccs:抄送人
  • bcc/bccs:密送人
  • subject:主题
  • content:内容
  • isHtml:是否HTML格式
  • files:附件

示例:

// 发送带抄送和密送的邮件
MailUtil.send(
    "recipient@qq.com",           // 收件人
    "cc@qq.com",                  // 抄送
    "bcc@qq.com",                 // 密送
    "项目进度报告",
    "项目已完成80%,预计下周交付。",
    false,                         // 纯文本
    new File("progress.xlsx")      // 附件
);

// 发送给多人,带抄送和密送
List<String> recipients = Arrays.asList("user1@qq.com", "user2@qq.com");
List<String> ccs = Arrays.asList("manager@qq.com");
List<String> bccs = Arrays.asList("admin@qq.com");

MailUtil.send(
    recipients,
    ccs,
    bccs,
    "团队周报",
    "<h3>本周工作总结</h3><p>完成功能开发3个...</p>",
    true,                          // HTML格式
    new File("weekly_report.pdf")
);
4.2 指定账户发送 - send()

方法签名:

static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files)
static String send(MailAccount mailAccount, Collection<String> tos, String subject, String content, boolean isHtml, File... files)

功能:使用指定的邮件账户发送邮件,而不是使用配置文件中的默认账户。

示例:

// 使用特定账户发送
MailAccount notifyAccount = new MailAccount();
notifyAccount.setHost("smtp.163.com");
notifyAccount.setPort(465);
notifyAccount.setFrom("notify@163.com");
notifyAccount.setUser("notify@163.com");
notifyAccount.setPass("password");
notifyAccount.setSslEnable(true);

MailUtil.send(
    notifyAccount,
    "user@qq.com",
    "系统通知",
    "您的订单已发货",
    false
);

// 不同类型邮件使用不同账户
MailAccount marketingAccount = createMarketingAccount();
MailAccount systemAccount = createSystemAccount();

// 营销邮件用营销账户
MailUtil.send(
    marketingAccount,
    vipUsers,
    "新品上市",
    marketingHtml,
    true
);

// 系统通知用系统账户
MailUtil.send(
    systemAccount,
    adminUsers,
    "系统告警",
    alertContent,
    false
);

5. 带内嵌图片的完整发送

方法签名:

static String send(String to, String cc, String bcc, String subject, String content, Map<String,InputStream> imageMap, boolean isHtml, File... files)
static String send(MailAccount mailAccount, Collection<String> tos, Collection<String> ccs, Collection<String> bccs, String subject, String content, Map<String,InputStream> imageMap, boolean isHtml, File... files)

功能:最完整的发送方法,支持所有功能包括内嵌图片。

示例:

// 完整功能邮件发送
Map<String, InputStream> images = new HashMap<>();
images.put("chart", new FileInputStream("sales_chart.png"));
images.put("logo", new FileInputStream("company_logo.png"));

String reportHtml = "<html>" +
    "<body>" +
    "<img src='cid:logo' width='150'/>" +
    "<h2>销售月报</h2>" +
    "<p>本月销售额增长15%</p>" +
    "<img src='cid:chart' width='600'/>" +
    "</body>" +
    "</html>";

MailUtil.send(
    "ceo@company.com",              // 收件人
    "manager@company.com",          // 抄送
    "finance@company.com",          // 密送
    "2024年1月销售报告",
    reportHtml,
    images,                          // 内嵌图片
    true,                            // HTML格式
    new File("detailed_report.xlsx") // 附件
);

实战场景

场景1:用户注册邮件验证

用户注册后发送激活邮件,包含激活链接。

示例代码:

@Service
public class UserRegistrationService {
    
    @Autowired
    private UserRepository userRepository;
    
    public void registerUser(UserRegisterDTO dto) {
        // 1. 创建用户
        User user = new User();
        user.setUsername(dto.getUsername());
        user.setEmail(dto.getEmail());
        user.setPassword(encryptPassword(dto.getPassword()));
        user.setStatus(0);  // 未激活
        
        userRepository.save(user);
        
        // 2. 生成激活token
        String token = generateActivationToken(user.getId());
        
        // 3. 发送激活邮件
        sendActivationEmail(user.getEmail(), user.getUsername(), token);
    }
    
    private void sendActivationEmail(String email, String username, String token) {
        String activationUrl = "https://www.example.com/activate?token=" + token;
        
        String htmlContent = "<html>" +
            "<body style='font-family: Arial, sans-serif;'>" +
            "<div style='max-width: 600px; margin: 0 auto; padding: 20px;'>" +
            "<h2 style='color: #333;'>欢迎注册我们的服务</h2>" +
            "<p>尊敬的 <strong>" + username + "</strong>,您好!</p>" +
            "<p>感谢您注册我们的服务。请点击下方按钮激活您的账户:</p>" +
            "<div style='text-align: center; margin: 30px 0;'>" +
            "<a href='" + activationUrl + "' " +
            "style='display: inline-block; padding: 12px 30px; " +
            "background-color: #007bff; color: white; " +
            "text-decoration: none; border-radius: 5px;'>" +
            "激活账户" +
            "</a>" +
            "</div>" +
            "<p style='color: #666; font-size: 12px;'>" +
            "如果按钮无法点击,请复制以下链接到浏览器:<br/>" +
            activationUrl +
            "</p>" +
            "<p style='color: #666; font-size: 12px;'>" +
            "此链接24小时内有效,请尽快激活。" +
            "</p>" +
            "</div>" +
            "</body>" +
            "</html>";
        
        try {
            MailUtil.sendHtml(
                email,
                "账户激活邮件 - 请验证您的邮箱",
                htmlContent
            );
            log.info("激活邮件已发送至: {}", email);
        } catch (Exception e) {
            log.error("发送激活邮件失败: {}", e.getMessage());
        }
    }
    
    private String generateActivationToken(Long userId) {
        // 生成包含用户ID和过期时间的token
        String data = userId + ":" + System.currentTimeMillis();
        return Base64.getEncoder().encodeToString(data.getBytes());
    }
    
    private String encryptPassword(String password) {
        return SecureUtil.md5(password);
    }
}

场景2:密码重置功能

用户忘记密码时发送重置链接。

示例代码:

@Service
public class PasswordResetService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public void sendResetEmail(String email) {
        // 1. 验证用户是否存在
        User user = userRepository.findByEmail(email);
        if (user == null) {
            throw new BusinessException("邮箱未注册");
        }
        
        // 2. 生成重置token(存入Redis,30分钟过期)
        String token = UUID.randomUUID().toString().replace("-", "");
        String redisKey = "password_reset:" + token;
        redisTemplate.opsForValue().set(redisKey, user.getId().toString(), 30, TimeUnit.MINUTES);
        
        // 3. 发送重置邮件
        sendResetEmailNotification(email, user.getUsername(), token);
    }
    
    private void sendResetEmailNotification(String email, String username, String token) {
        String resetUrl = "https://www.example.com/reset-password?token=" + token;
        
        String htmlContent = "<html>" +
            "<body style='font-family: Arial;'>" +
            "<div style='max-width: 600px; margin: 0 auto; padding: 20px; " +
            "border: 1px solid #ddd; border-radius: 10px;'>" +
            "<h2 style='color: #e74c3c;'>密码重置请求</h2>" +
            "<p>您好,<strong>" + username + "</strong>!</p>" +
            "<p>我们收到了您的密码重置请求。如果这不是您的操作,请忽略此邮件。</p>" +
            "<p>点击下方按钮重置密码:</p>" +
            "<div style='text-align: center; margin: 30px 0;'>" +
            "<a href='" + resetUrl + "' " +
            "style='display: inline-block; padding: 12px 30px; " +
            "background-color: #e74c3c; color: white; " +
            "text-decoration: none; border-radius: 5px;'>" +
            "重置密码" +
            "</a>" +
            "</div>" +
            "<p style='color: #666; font-size: 12px;'>" +
            "此链接30分钟内有效。" +
            "</p>" +
            "<p style='color: #666; font-size: 12px;'>" +
            "如有疑问,请联系客服:support@example.com" +
            "</p>" +
            "</div>" +
            "</body>" +
            "</html>";
        
        MailUtil.sendHtml(
            email,
            "密码重置验证 - 请勿转发此邮件",
            htmlContent
        );
    }
    
    public void resetPassword(String token, String newPassword) {
        // 1. 验证token
        String redisKey = "password_reset:" + token;
        String userId = redisTemplate.opsForValue().get(redisKey);
        
        if (userId == null) {
            throw new BusinessException("重置链接已失效");
        }
        
        // 2. 更新密码
        User user = userRepository.findById(Long.parseLong(userId))
            .orElseThrow(() -> new BusinessException("用户不存在"));
        
        user.setPassword(SecureUtil.md5(newPassword));
        userRepository.save(user);
        
        // 3. 删除token
        redisTemplate.delete(redisKey);
        
        // 4. 发送确认邮件
        sendPasswordChangedNotification(user.getEmail(), user.getUsername());
    }
    
    private void sendPasswordChangedNotification(String email, String username) {
        String textContent = "尊敬的 " + username + ",\n\n" +
            "您的密码已成功修改。如果这不是您的操作,请立即联系客服。\n\n" +
            "客服邮箱:support@example.com\n" +
            "客服电话:400-123-4567";
        
        MailUtil.sendText(
            email,
            "密码修改成功通知",
            textContent
        );
    }
}

场景3:订单通知系统

订单状态变更时自动发送邮件通知。

示例代码:

@Service
public class OrderNotificationService {
    
    public void sendOrderConfirmation(Order order, User user) {
        String htmlContent = buildOrderConfirmationHtml(order, user);
        
        MailUtil.sendHtml(
            user.getEmail(),
            "订单确认 - " + order.getOrderNo(),
            htmlContent
        );
    }
    
    public void sendShippingNotification(Order order, User user, String trackingNo) {
        String htmlContent = "<html>" +
            "<body>" +
            "<h2>您的订单已发货</h2>" +
            "<p>订单号:" + order.getOrderNo() + "</p>" +
            "<p>物流单号:" + trackingNo + "</p>" +
            "<p>预计3-5天送达</p>" +
            "<a href='https://www.example.com/track?no=" + trackingNo + "'>查看物流信息</a>" +
            "</body>" +
            "</html>";
        
        MailUtil.sendHtml(
            user.getEmail(),
            "订单已发货 - " + order.getOrderNo(),
            htmlContent
        );
    }
    
    public void sendDeliveryNotification(Order order, User user) {
        String textContent = "尊敬的客户,\n\n" +
            "您的订单 " + order.getOrderNo() + " 已签收。\n" +
            "感谢您的购买,期待再次为您服务!\n\n" +
            "如有问题,请联系客服。";
        
        MailUtil.sendText(
            user.getEmail(),
            "订单已签收 - " + order.getOrderNo(),
            textContent
        );
    }
    
    private String buildOrderConfirmationHtml(Order order, User user) {
        StringBuilder html = new StringBuilder();
        html.append("<html><body style='font-family: Arial;'>");
        html.append("<div style='max-width: 600px; margin: 0 auto; padding: 20px;'>");
        html.append("<h2 style='color: #27ae60;'>订单确认</h2>");
        html.append("<p>尊敬的 ").append(user.getUsername()).append(",您好!</p>");
        html.append("<p>您的订单已确认,详情如下:</p>");
        
        html.append("<table style='width: 100%; border-collapse: collapse;'>");
        html.append("<tr style='background: #f5f5f5;'>");
        html.append("<th style='padding: 10px; text-align: left;'>商品</th>");
        html.append("<th style='padding: 10px; text-align: right;'>数量</th>");
        html.append("<th style='padding: 10px; text-align: right;'>金额</th>");
        html.append("</tr>");
        
        for (OrderItem item : order.getItems()) {
            html.append("<tr>");
            html.append("<td style='padding: 10px;'>").append(item.getProductName()).append("</td>");
            html.append("<td style='padding: 10px; text-align: right;'>").append(item.getQuantity()).append("</td>");
            html.append("<td style='padding: 10px; text-align: right;'>¥").append(item.getAmount()).append("</td>");
            html.append("</tr>");
        }
        
        html.append("</table>");
        html.append("<p style='text-align: right; font-size: 18px; color: #e74c3c;'>");
        html.append("订单总额:<strong>¥").append(order.getTotalAmount()).append("</strong>");
        html.append("</p>");
        
        html.append("<div style='text-align: center; margin: 30px 0;'>");
        html.append("<a href='https://www.example.com/order/").append(order.getOrderNo()).append("' ");
        html.append("style='padding: 12px 30px; background: #3498db; color: white; text-decoration: none;'>");
        html.append("查看订单详情");
        html.append("</a>");
        html.append("</div>");
        
        html.append("</div></body></html>");
        return html.toString();
    }
}

场景4:系统监控告警

系统异常时自动发送告警邮件给运维人员。

示例代码:

@Component
public class SystemMonitorService {
    
    @Value("${monitor.alert.emails}")
    private List<String> alertEmails;
    
    public void sendErrorAlert(String errorType, String errorMessage, String stackTrace) {
        String subject = "[严重] 系统异常告警 - " + errorType;
        
        String htmlContent = "<html>" +
            "<body style='font-family: monospace;'>" +
            "<div style='background: #ffe6e6; padding: 20px; border-left: 5px solid #e74c3c;'>" +
            "<h2 style='color: #e74c3c;'>⚠ 系统异常告警</h2>" +
            "<p><strong>异常类型:</strong>" + errorType + "</p>" +
            "<p><strong>发生时间:</strong>" + LocalDateTime.now() + "</p>" +
            "<p><strong>错误信息:</strong></p>" +
            "<pre style='background: #f5f5f5; padding: 10px;'>" + errorMessage + "</pre>" +
            "<p><strong>堆栈信息:</strong></p>" +
            "<pre style='background: #f5f5f5; padding: 10px; font-size: 12px;'>" + stackTrace + "</pre>" +
            "</div>" +
            "</body>" +
            "</html>";
        
        MailUtil.sendHtml(
            alertEmails,
            subject,
            htmlContent
        );
    }
    
    public void sendPerformanceAlert(String metric, double value, double threshold) {
        String content = String.format(
            "性能指标告警\n\n" +
            "指标名称:%s\n" +
            "当前值:%.2f\n" +
            "阈值:%.2f\n" +
            "告警时间:%s\n\n" +
            "请及时处理!",
            metric, value, threshold, LocalDateTime.now()
        );
        
        MailUtil.sendText(
            alertEmails,
            "[警告] 性能指标超限 - " + metric,
            content
        );
    }
    
    public void sendDailyReport(SystemStats stats) {
        // 准备图表
        Map<String, InputStream> images = new HashMap<>();
        images.put("cpu_chart", generateCpuChart(stats));
        images.put("memory_chart", generateMemoryChart(stats));
        
        String htmlContent = "<html>" +
            "<body>" +
            "<h2>系统日报 - " + LocalDate.now() + "</h2>" +
            "<h3>CPU使用率</h3>" +
            "<img src='cid:cpu_chart' width='600'/>" +
            "<h3>内存使用率</h3>" +
            "<img src='cid:memory_chart' width='600'/>" +
            "<h3>统计数据</h3>" +
            "<ul>" +
            "<li>请求总数:" + stats.getTotalRequests() + "</li>" +
            "<li>错误数:" + stats.getErrorCount() + "</li>" +
            "<li>平均响应时间:" + stats.getAvgResponseTime() + "ms</li>" +
            "</ul>" +
            "</body>" +
            "</html>";
        
        File logFile = new File("system_" + LocalDate.now() + ".log");
        
        MailUtil.sendHtml(
            alertEmails,
            "系统日报 - " + LocalDate.now(),
            htmlContent,
            images,
            logFile
        );
    }
    
    private InputStream generateCpuChart(SystemStats stats) {
        // 生成图表并返回输入流
        // 实际项目中可使用JFreeChart等图表库
        return null;
    }
    
    private InputStream generateMemoryChart(SystemStats stats) {
        return null;
    }
}

场景5:定时任务报告

每日/每周生成报表并通过邮件发送。

示例代码:

@Component
public class ReportScheduler {
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private UserService userService;
    
    @Value("${report.recipients}")
    private List<String> reportRecipients;
    
    // 每天早上8点发送日报
    @Scheduled(cron = "0 0 8 * * ?")
    public void sendDailyReport() {
        LocalDate yesterday = LocalDate.now().minusDays(1);
        
        // 统计数据
        DailyStats stats = orderService.getDailyStats(yesterday);
        
        // 生成Excel报表
        File excelFile = generateDailyExcel(stats, yesterday);
        
        String htmlContent = buildDailyReportHtml(stats, yesterday);
        
        MailUtil.sendHtml(
            reportRecipients,
            "日报 - " + yesterday,
            htmlContent,
            excelFile
        );
    }
    
    // 每周一早上9点发送周报
    @Scheduled(cron = "0 0 9 ? * MON")
    public void sendWeeklyReport() {
        LocalDate endDate = LocalDate.now().minusDays(1);
        LocalDate startDate = endDate.minusDays(6);
        
        WeeklyStats stats = orderService.getWeeklyStats(startDate, endDate);
        
        // 生成PDF报告
        File pdfFile = generateWeeklyPdf(stats, startDate, endDate);
        
        String htmlContent = buildWeeklyReportHtml(stats, startDate, endDate);
        
        MailUtil.sendHtml(
            reportRecipients,
            String.format("周报 - %s至%s", startDate, endDate),
            htmlContent,
            pdfFile
        );
    }
    
    private String buildDailyReportHtml(DailyStats stats, LocalDate date) {
        return "<html>" +
            "<body style='font-family: Arial;'>" +
            "<h2>日报 - " + date + "</h2>" +
            "<table style='border-collapse: collapse; width: 100%;'>" +
            "<tr><td>订单数:</td><td><strong>" + stats.getOrderCount() + "</strong></td></tr>" +
            "<tr><td>销售额:</td><td><strong>¥" + stats.getTotalSales() + "</strong></td></tr>" +
            "<tr><td>新增用户:</td><td><strong>" + stats.getNewUsers() + "</strong></td></tr>" +
            "<tr><td>活跃用户:</td><td><strong>" + stats.getActiveUsers() + "</strong></td></tr>" +
            "</table>" +
            "<p>详细数据请查看附件。</p>" +
            "</body>" +
            "</html>";
    }
    
    private String buildWeeklyReportHtml(WeeklyStats stats, LocalDate start, LocalDate end) {
        return "<html>" +
            "<body>" +
            "<h2>周报</h2>" +
            "<p>统计周期:" + start + " 至 " + end + "</p>" +
            "<h3>业绩概览</h3>" +
            "<ul>" +
            "<li>总订单数:" + stats.getTotalOrders() + "</li>" +
            "<li>总销售额:¥" + stats.getTotalRevenue() + "</li>" +
            "<li>较上周增长:" + stats.getGrowthRate() + "%</li>" +
            "</ul>" +
            "<h3>热门商品Top5</h3>" +
            "<ol>" +
            stats.getTopProducts().stream()
                .map(p -> "<li>" + p.getName() + " - 销量:" + p.getSales() + "</li>")
                .collect(Collectors.joining()) +
            "</ol>" +
            "</body>" +
            "</html>";
    }
    
    private File generateDailyExcel(DailyStats stats, LocalDate date) {
        // 使用POI生成Excel
        return new File("daily_" + date + ".xlsx");
    }
    
    private File generateWeeklyPdf(WeeklyStats stats, LocalDate start, LocalDate end) {
        // 使用iText生成PDF
        return new File("weekly_" + start + "_" + end + ".pdf");
    }
}

快速使用指南

Maven依赖:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-extra</artifactId>
    <version>5.8.16</version>
</dependency>
<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>jakarta.mail</artifactId>
    <version>2.0.1</version>
</dependency>

最简使用示例:

// 发送简单文本邮件
MailUtil.sendText(
    "receiver@qq.com",
    "测试邮件",
    "这是一封测试邮件"
);

// 发送HTML邮件
MailUtil.sendHtml(
    "receiver@qq.com",
    "欢迎注册",
    "<h2>欢迎</h2><p>注册成功!</p>"
);

// 发送带附件邮件
MailUtil.sendText(
    "receiver@qq.com",
    "报告",
    "请查收附件",
    new File("report.pdf")
);

常见问题

Q:如何获取QQ邮箱的授权码?
A:登录QQ邮箱 -> 设置 -> 账户 -> POP3/SMTP服务 -> 开启服务 -> 生成授权码。注意授权码不是登录密码。

Q:发送邮件失败,提示连接超时?
A:检查SMTP服务器地址和端口是否正确,确认网络能访问SMTP服务器,检查防火墙设置。

Q:发送邮件失败,提示认证失败?
A:确认用户名、密码(授权码)是否正确,部分邮箱需要开启SMTP服务。

Q:HTML邮件样式显示不正常?
A:邮件客户端对CSS支持有限,建议使用内联样式,避免使用复杂的CSS3特性。

Q:如何发送给大量用户?
A:分批发送,每批不超过50个收件人,避免被判定为垃圾邮件。可以使用定时任务分批处理。

Q:内嵌图片和附件有什么区别?
A:内嵌图片直接显示在邮件正文中(使用cid引用),附件需要下载后查看。


总结

JakartaMailUtil提供了简洁高效的邮件发送能力,让复杂的JavaMail操作变得简单。

核心功能:

  • 文本邮件:sendText() 快速发送纯文本
  • HTML邮件:sendHtml() 支持富文本和样式
  • 完整发送:send() 支持抄送、密送、附件
  • 内嵌图片:通过imageMap实现图片内嵌
  • 多账户:支持指定账户发送

典型应用:

  • 用户通知:注册验证、密码重置
  • 订单系统:订单确认、发货通知
  • 系统监控:异常告警、性能报告
  • 定时报表:日报周报自动发送
  • 营销推广:活动通知、优惠信息

使用建议:

  1. 使用授权码而不是登录密码
  2. HTML邮件使用内联样式
  3. 大批量发送要分批处理
  4. 重要邮件添加异常处理和日志
  5. 敏感信息不要明文写在邮件中

JakartaMailUtil是企业应用中邮件功能的首选工具,配合模板引擎和定时任务可以构建完整的邮件通知系统。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐