邮件服务器的分类
按照通讯协议分类
- SMTP服务器:将邮件发送给邮件服务器,服务器将邮件发送给客户,此时发送和接受是一对,都是使用的同一种服务器,就好比我们传呼快递员上门取件,淘宝商委派递送员将货物送上门
- POP3/IMAP服务器:打个比方,用户去快递站点取包裹,问有没有我的包裹,站点的人再去帮你找包裹,找到后给到客户,那么站点就相当于POP3/IMAP服务器
那么还是举一个简单的栗子来说明SMTP和POP3的使用过程。
marco想给lisa发一封业务邮件,marco使用的是163邮箱,lisa是sina邮箱,那么marco发送的邮件首先会通过简单邮件传输协议(Simple Mail Transfer Protocol, SMTP) 发送给sina的服务器,sina服务器收到邮件后首先会进行存储,那么当lisa登录邮箱之后他的email会自动请求询问服务器,有没有我的邮件?
服务器则会查找收件人是lisa的邮件,并将邮件发送给她,此时使用的协议则是Post Office Protocol 3*(支持“离线”邮件处理*)Internet Mail Access Protocol(交互式邮件存取协议)简称POP3/IMAP。
Java Mail
JavaMail API 按照功能可以划分为如下三大类:
- 创建和解析邮件的API
- 发送邮件的API
- 接收邮件的API
而这四大类API中有四大核心类帮助我们完成JavaMail程序的构建
JavaMail 的四大核心类
Message 类(javax.mail.Message):
创建和解析邮件的核心 API,这是一个抽象类,通常使用它的子类javax.mail.internet.MimeMessage 类。它的实例对象表示一份电子邮件。客户端程序发送邮件时,首先使用创建邮件的 JavaMail API 创建出封装了邮件数据的 Message 对象,然后把这个对象传递给邮件发送API(Transport 类) 发送。客户端程序接收邮件时,邮件接收API把接收到的邮件数据封装在Message 类的实例中,客户端程序在使用邮件解析API从这个对象中解析收到的邮件数据。
Transport 类(javax.mail.Transport):
发送邮件的核心API 类,它的实例对象代表实现了某个邮件发送协议的邮件发送对象,例如 SMTP 协议,客户端程序创建好 Message 对象后,只需要使用邮件发送API 得到 Transport 对象,然后把 Message 对象传递给 Transport 对象,并调用它的发送方法,就可以把邮件发送给指定的 SMTP 服务器。
Store 类(javax.mail.Store):
接收邮件的核心 API 类,它的实例对象代表实现了某个邮件接收协议的邮件接收对象,例如 POP3 协议,客户端程序接收邮件时,只需要使用邮件接收 API 得到 Store 对象,然后调用 Store 对象的接收方法,就可以从指定的 POP3 服务器获得邮件数据,并把这些邮件数据封装到表示邮件的 Message 对象中。
Session 类(javax.mail.Session):
用于定义整个应用程序所需的环境信息,以及收集客户端与邮件服务器建立网络连接的会话信息,例如邮件服务器的主机名、端口号、采用的邮件发送和接收协议等。Session 对象根据这些信息构建用于邮件收发的 Transport 和 Store 对象,以及为客户端创建 Message 对象时提供信息支持。
那么接下来就演示一个实战的简单发送邮件的项目
public class MailTest {
//发件人账户名
public static String senderAccount = "userName@sohu.com";
//发件人账户密码
public static String senderPassword = "password";
public static void main(String[] args) throws Exception {
//新建一个Properties类用于配置邮件发送所需要的属性和内容
Properties props = new Properties();
//配置mail的auth认证属性,设置为true告诉我们的程序,登录邮箱是要认证的
props.setProperty("mail.smtp.auth", "true");
//发件人的邮箱的 SMTP 服务器地址
props.setProperty("mail.host","smtp.sohu.com");
//传输需要使用的协议,指定传输协议为smtp
props.setProperty("mail.transport.protocol", "smtp");
//通过prop配置对象new一个Session会话,并在Session中配置账户和密码,也可以选择在transport.connect(user, password)中设置
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(senderAccount, senderPassword);
}
});
//设置调试属性为true则会调试
session.setDebug(true);
//调用createMimeMessage()方法新建一封邮件
String Content = "I'm marco~";
MimeMessage message = createMimeMessage(session, "userName@sohu.com", "userName@sina.cn", Content);
//通过会话对象获取传输对象(就好比传呼递送员上门取件)
Transport transport = session.getTransport();
transport.connect();
//调用transport的sendMessage方法发送邮件
//发送邮件,并发送到所有收件人地址,message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
transport.sendMessage(message,message.getAllRecipients());
transport.close();
}
public static MimeMessage createMimeMessage(Session session, String sendMail, String receiveMail,String Content) throws Exception {
//创建一封邮件,Message为抽象类,所以需要new它的子实现类MimeMessage
MimeMessage message = new MimeMessage(session);
//From: 发件人(昵称有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改昵称)
message.setFrom(new InternetAddress(sendMail,"sentFrom","utf-8"));
//To: 收件人(可以增加多个收件人、抄送、密送,CC:抄送人,BCC:密送)
message.setRecipients(MimeMessage.RecipientType.TO, new Address[]{new InternetAddress(receiveMail,"sentTo","UTF-8")});
//Subject: 邮件主题(标题有广告嫌疑,为了避免被邮件服务器误认为是滥发广告以至返回失败,建议修改标题)
message.setSubject("Working Email", "UTF-8");
//Content: 邮件正文(可以使用html标签)(内容有广告嫌疑,为了避免被邮件服务器误认为是滥发广告以至返回失败,建议修改发送内容)
message.setContent(Content, "text/html;charset=utf-8");
//设置发件时间
message.setSentDate(new Date());
//保存设置
message.saveChanges();
return message;
}
}
当然,我们也可以将一些固定的信息放置在properties文件当中
senderAccount=userName@sohu.com
senderPassword=password
mail.smtp.auth=true
mail.host=smtp.sohu.com
mail.transport.protocol=smtp
这样我们在Listener中就可以在服务器启动的时候将配置文件加载进来
新建一个ConfigListener实现ServletContextListener接口
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
try {
prop.load(AutoSendMail.class.getClassLoader().getResourceAsStream("sentMail.properties"));
servletContext.setAttribute("prop", prop);
prop.getProperty("mail.transport.protocol"));
} catch (IOException e) {
e.printStackTrace();
}
}
这样我们在AutoSendMail的Servlet中通过ServletContext的getAttribute()方法获取账号密码就可以啦
以下是完整版的获取多个用户的邮箱信息,进行群发邮件的功能
@WebServlet("/AutoSendMail")
public class AutoSendMail extends HttpServlet {
private static final long serialVersionUID = 1L;
private static Properties prop = new Properties();
Map<String,String[]> rcptMap;//模拟客户注册后自动发送邮件,存放客户注册xxx软件时注册的账号和密码
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
@SuppressWarnings("unchecked")
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
Properties prop = (Properties)request.getServletContext.getAttribute("prop");
String senderAccount = prop.getProperty("senderAccount");
String senderPassword = prop.getProperty("senderPassword");
rcptMap = (Map<String, String[]>) request.getAttribute("rcptMap");
System.out.println("AutoSendEmail-------->" + rcptMap);
if(!rcptMap.isEmpty()) {
//通过prop配置对象new一个Session会话,并在Session中配置账户和密码,也可以选择在transport.connect(user, password)中设置
Session session = Session.getInstance(prop, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(senderAccount, senderPassword);
}
});
//设置调试属性为true则会调试
session.setDebug(true);
//调用createMimeMessage()方法新建一封邮件
MimeMessage message;
Transport transport = null;
try {
message = createMimeMessage(session, senderAccount, rcptMap);
//通过会话对象获取传输对象(就好比传呼递送员上门取件)
transport = session.getTransport();
transport.connect();
//调用transport的sendMessage方法发送邮件
//发送邮件,并发送到所有收件人地址,message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
transport.sendMessage(message,message.getAllRecipients());
/*注册成功之后,跳到主页面,进行登录*/
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<script>");
out.println("window.open ('"+request.getContextPath()+"/index.jsp','_parent')");
out.println("</script>");
out.println("</html>");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
transport.close();
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
}
public static MimeMessage createMimeMessage(Session session, String sendMail, Map<String, String[]> rcptMap) throws Exception {
//创建一封邮件,Message为抽象类,所以需要new它的子实现类MimeMessage
MimeMessage message = new MimeMessage(session);
//From: 发件人(昵称有广告嫌疑,避免被邮件服务器误认为是滥发广告以至返回失败,请修改昵称)
message.setFrom(new InternetAddress(sendMail,"Auto-Send","UTF-8"));
//To: 收件人(可以增加多个收件人、抄送、密送,CC:抄送人,BCC:密送)
List<Address> addressList = new ArrayList<Address>();
if(!rcptMap.isEmpty()) {
Set<String> userNames = rcptMap.keySet();
Iterator<String> iterator = userNames.iterator();
while (iterator.hasNext()) {
String rcptMail = iterator.next();
String userName = rcptMap.get(rcptMail)[0];//虚拟的客户注册我们的软件的账户
String password = rcptMap.get(rcptMail)[1];//虚拟的客户注册我们的软件的密码
InternetAddress internetAdd = new InternetAddress(rcptMail, userName, "UTF-8");
addressList.add(internetAdd);
String Content = "恭喜您注册成功!!请记住您的登录账户和密码<br>账户: " + userName + "<br>密码: " + password;
message.setContent(Content, "text/html;charset=utf-8");
}
Address[] address = (Address[]) addressList.toArray(new Address[addressList.size()]);
message.setRecipients(MimeMessage.RecipientType.TO, address);
}
//Subject: 邮件主题(标题有广告嫌疑,为了避免被邮件服务器误认为是滥发广告以至返回失败,建议修改标题)
message.setSubject("Marco Management System Register");
//Content: 邮件正文(可以使用html标签)(内容有广告嫌疑,为了避免被邮件服务器误认为是滥发广告以至返回失败,建议修改发送内容)
//设置发件时间
message.setSentDate(new Date());
//保存设置
message.saveChanges();
return message;
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}