MimeMessage.saveChanges 真的很慢

MimeMessage.saveChanges is really slow

由于包含 m.saveChanges().

,执行以下测试大约需要 5 秒
import org.junit.Before;
import org.junit.Test;    
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;
import java.io.IOException;
import java.util.Properties;
import static org.junit.Assert.assertEquals;   
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@Test
public void test1() throws MessagingException, IOException {
    Session s = Session.getDefaultInstance(new Properties());
    MimeMessage m = new MimeMessage(s);
    m.setContent("<b>Hello</b>", "text/html; charset=utf-8");
    m.saveChanges();
    assertEquals(m.getContent(), "<b>Hello</b>");
    assertEquals(m.getContentType(), "text/html; charset=utf-8");
}

我也用 mockito 模拟了 Session,但没有用:

Session s = mock(Session.class);
when(s.getProperties()).thenReturn(new Properties());

这里有什么问题?我可以模拟什么来加快速度?

首先修复代码中的 most common mistakes people make when using JavaMail

DNS lookup can hurt performance on some machines. For the JDK you can change the security properties for caching DNS lookup networkaddress.cache.ttl and networkaddress.cache.negative.ttl or set the system properties sun.net.inetaddr.ttl and sun.net.inetaddr.negative.ttl。 JDK 7 及更高版本中的默认行为可以很好地缓存,因此您不必更改这些设置。

最好,您可以使用会话属性来避免这些查找。

  1. mail.from or mail.host (not the protocol versions) as that will prevent the name lookup on InternetAddress.getLocalAddress(Session). Calling MimeMessage.saveChanges(), MimeMessage.updateHeaders(), MimeMessage.updateMessageID(), or MimeMessage.setFrom()设置会话属性将触发获取本地地址的调用。如果未设置上述属性,则此方法将尝试查询主机名。通过设置属性,此方法将从会话中提取主机名字符串,而不是尝试进行昂贵的 DNS 查找。
  2. mail.smtp.localhost or mail.smtps.localhost 设置会话 属性 以防止在 HELO 命令上查找名称。
  3. mail.smtp.from or mail.smtps.from 设置会话 属性 以防止查找 EHLO 命令。
  4. 或者,您可以设置系统 属性 mail.mime.address.usecanonicalhostname to falseif your code is relying on the setFrom() 但如果您应用点 #1,这将得到处理。
  5. 对于 IMAP,您可以尝试将 mail.imap.sasl.usecanonicalhostname or mail.imaps.sasl.usecanonicalhostname 设置为默认值 false

由于您没有传输消息,请将代码更改为应用规则 #1:

@Test
public void test1() throws MessagingException, IOException {
    Properties props = new Properties();
    props.put("mail.host", "localhost"); //Or use IP.
    Session s = Session.getInstance(props);
    MimeMessage m = new MimeMessage(s);
    m.setContent("<b>Hello</b>", "text/html; charset=utf-8");
    m.saveChanges();
    assertEquals(m.getContent(), "<b>Hello</b>");
    assertEquals(m.getContentType(), "text/html; charset=utf-8");
}

如果您要传输消息,请结合规则 #1、#2 和 #3,这将阻止访问主机系统进行名称查找。如果您想在传输过程中阻止所有 DNS 查找,则必须使用 IP 地址。