Java 邮件发送实战:基于 Spring 的微软 EWS 功能封装与复用

1. 核心依赖配置

pom.xml 中添加 EWS Java API 依赖:

<dependency>
    <groupId>com.microsoft.ews-java-api</groupId>
    <artifactId>ews-java-api</artifactId>
    <version>2.0</version>
</dependency>

2. EWS 连接配置类

封装 Exchange 服务连接参数:

@Configuration
public class EwsConfig {
    @Value("${ews.service.url}") private String serviceUrl;
    @Value("${ews.username}") private String username;
    @Value("${ews.password}") private String password;

    @Bean
    public ExchangeService exchangeService() throws Exception {
        ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
        service.setUrl(new URI(serviceUrl));
        service.setCredentials(new WebCredentials(username, password));
        return service;
    }
}

application.properties 配置:

ews.service.url=https://outlook.office365.com/EWS/Exchange.asmx
ews.username=your-email@domain.com
ews.password=your-password

3. 邮件服务封装层

实现可复用的邮件发送组件:

@Service
public class EwsMailService {
    private final ExchangeService exchangeService;

    public EwsMailService(ExchangeService exchangeService) {
        this.exchangeService = exchangeService;
    }

    /**
     * 发送带附件的邮件
     * @param to 收件人
     * @param subject 主题
     * @param body 正文 (支持HTML)
     * @param attachments 附件路径列表
     */
    public void sendEmailWithAttachments(String to, String subject, String body, List<String> attachments) 
            throws Exception {
        
        EmailMessage message = new EmailMessage(exchangeService);
        message.setSubject(subject);
        message.setBody(MessageBody.getMessageBodyFromHtml(body));
        message.getToRecipients().add(to);

        // 添加附件
        for (String filePath : attachments) {
            File file = new File(filePath);
            message.getAttachments().addFileAttachment(file.getName(), file);
        }

        message.send();
    }

    /**
     * 发送会议邀请
     * @param attendees 参会人邮箱
     * @param subject 会议主题
     * @param startTime 开始时间
     * @param endTime 结束时间
     */
    public void sendMeetingInvite(List<String> attendees, String subject, 
                                  Date startTime, Date endTime) throws Exception {
        
        Appointment meeting = new Appointment(exchangeService);
        meeting.setSubject(subject);
        meeting.setStart(startTime);
        meeting.setEnd(endTime);

        for (String email : attendees) {
            meeting.getRequiredAttendees().add(email);
        }

        meeting.save();
    }
}

4. 业务层调用示例

在 Controller 或 Service 中复用封装功能:

@RestController
@RequestMapping("/mail")
public class MailController {
    private final EwsMailService mailService;

    public MailController(EwsMailService mailService) {
        this.mailService = mailService;
    }

    @PostMapping("/send")
    public ResponseEntity<String> sendMail(@RequestBody MailRequest request) {
        try {
            mailService.sendEmailWithAttachments(
                request.getTo(),
                request.getSubject(),
                request.getBody(),
                request.getAttachments()
            );
            return ResponseEntity.ok("邮件发送成功");
        } catch (Exception e) {
            return ResponseEntity.status(500).body("发送失败: " + e.getMessage());
        }
    }
}

5. 高级功能扩展
// 1. 邮件跟踪(已读回执)
message.setIsReadReceiptRequested(true);

// 2. 延迟发送(定时邮件)
message.setExtendedProperty(
    new ExtendedPropertyDefinition(
        DefaultExtendedPropertySet.Common, 
        0x0E07, 
        MapiPropertyType.Boolean
    ), 
    true
);
message.setDelayDeliveryMinutes(120); // 延迟2小时

// 3. 邮件分类
message.setCategories(Collections.singletonList("重要通知"));

6. 最佳实践建议
  1. 连接复用:避免频繁创建 ExchangeService 实例,使用连接池管理
  2. 异常处理:统一捕获 EWSException 并转换为业务异常
  3. 异步发送:使用 @Async 注解实现非阻塞发送
    @Async
    public void asyncSendEmail(...) { ... }
    

  4. 模板引擎:集成 Thymeleaf 实现动态邮件模板
    Context ctx = new Context();
    ctx.setVariable("name", "张三");
    String htmlBody = templateEngine.process("welcome.html", ctx);
    

7. 性能优化方案
// 批量发送时启用分页控制
ItemView view = new ItemView(50); // 每页50条
FindItemsResults<Item> results = service.findItems(folderId, view);

// 使用 NIO 处理大附件
try (FileChannel channel = FileChannel.open(Paths.get(filePath))) {
    message.getAttachments().addFileAttachment(
        fileName, 
        channel, 
        channel.size()
    );
}

关键提示:微软已逐步将 EWS 迁移至 Microsoft Graph API,建议新项目直接使用 Microsoft Graph SDK 实现更现代化的集成方案。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐