当我们在开发中使用全局异常处理器处理异常时,针对异常模块找到对应的责任人,然后要将错误通过邮箱或者手机短信的方式发给相关责任人,这节我们一起学习下如何用JMail发送邮件。
要发送邮件,我们要有一个邮箱作为发送的载体,这里我用的是163邮箱。要使用JMail发送邮件,我们需要对163邮箱做一些配置。
第一步:开启SMTP
登录163邮箱,点击"设置",在下拉菜单中点击"POP3/SMTP/IMAP",如下图所示。
我们勾选上两个关于SMTP的复选框,然后点击"保存"按钮。163邮箱会发短信验证码,输入验证码即可完成该操作。
第二步:配置客户端授权密码
点击左侧的"客户端授权密码",如果以前没有开启过,点击开启,163邮箱会向你的手机发送验证码,输入验证码,然后会让输入授权密码(注意:不要与登录密码一样)。然后就配置好了。
经过上面两步,163邮箱的配置便配置好了,下面要进入编程阶段,发送邮件需要用到mail.jar包,我们的taotao-parent工程是统一管理jar包的,因此我们还是在taotao-parent工程统一定义下mail.jar包的版本号,如下图所示。
上图添加的代码如下:
<jmail.version>1.5.6</jmail.version>
<!-- JMail组件 -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>${jmail.version}</version>
</dependency>
在taotao-parent工程统一定义好javax.mail和activation两个jar包的版本号之后,我们在taotao-search-web工程添加对这两个jar包的依赖,如下图所示。这里之所以不写版本号是因为在taotao-parent工程我们已经统一定义好版本了,所以这里就不用写版本号了。
上图添加的依赖代码如下:
<!-- JMail组件 -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
</dependency>
由于发送键功能是一个独立的功能,我们最好把它弄成一个工具类,因此我在taotao-search-web工程添加了一个com.taotao.search.utils包,并在该包下新建了一个工具类"SendMail",如下图所示。
代码如下:
package com.taotao.search.utils;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
public class SendMail {
public static void sendEmail(String subject,String text) throws MessagingException {
Properties properties = new Properties();
properties.setProperty("mail.smtp.auth", "true");//设置访问smtp服务器需要认证
properties.setProperty("mail.transport.protocol", "smtp"); //设置访问服务器的协议
Session session = Session.getDefaultInstance(properties);
session.setDebug(true); //打开debug功能
Message msg = new MimeMessage(session);
//这里填你登录163邮箱所用的用户名
msg.setFrom(new InternetAddress("xxxxxxxx@163.com")); //设置发件人,163邮箱要求发件人与登录用户必须一致(必填),其它邮箱不了解
msg.setSubject(subject); //设置邮件主题
msg.setText(text); //设置邮件内容
Transport trans = session.getTransport();
//下面四个参数,前两个可以认为是固定的,不用变,后两个参数分别是登录163邮箱的用户名以及客户端授权密码(注意,不是登录密码)
trans.connect("smtp.163.com", 25, "xxxxxxxx@163.com", "xxxxxxx"); //连接邮箱smtp服务器,25为默认端口
//要发送到哪个邮箱,这里以qq邮箱为例
trans.sendMessage(msg, new Address[]{new InternetAddress("xxxxxx@qq.com")}); //发送邮件
trans.close(); //关闭连接
}
}
写完了一个工具类,我们还需要一个工具类,就是将抛出的异常堆栈信息转换为字符串,因为邮箱发送的是字符串,而我们无法直接将堆栈信息当成字符串来传,所以就需要转一下,如下图所示。在工具包下新建了一个工具类StackTrace。
上图工具类的代码如下:
package com.taotao.search.utils;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
public class StackTrace {
public static String getStackTrace(Throwable aThrowable) {
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
aThrowable.printStackTrace(printWriter);
return result.toString();
}
}
下面我们在全局异常处理器中使用邮箱发送工具类发送邮件,如下图所示。由于发送邮件时间较长,为不影响主流程的运行,所以使用线程来专门处理发送邮件。
当前全局异常处理类代码如下:
package com.taotao.search.exception;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import com.taotao.search.utils.SendMail;
public class GlobalExceptioResolver implements HandlerExceptionResolver {
//获取logger
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptioResolver.class);
@Override
public ModelAndView resolveException(HttpServletRequest rquest, HttpServletResponse response,
Object handler, Exception e) {
logger.info("进入全局异常处理器。。。");
logger.debug("测试handler的类型:"+handler.getClass());
//控制台打印异常
e.printStackTrace();
//向日志文件中写入异常
logger.error("系统发生异常", e);
//发邮件(采用jmail客户端进行发送)
Runnable myRunnable = new Runnable() {
@Override
public void run() {
try {
SendMail.sendEmail("搜索系统出现异常", StackTrace.getStackTrace(e));
} catch (MessagingException e1) {
e1.printStackTrace();
}
}
};
Thread thread = new Thread(myRunnable);
thread.start();
//发短信
//展示错误页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", "当前网络出现故障,请稍后重试!");
//返回逻辑视图,这样回去访问error目录下的error.jsp
modelAndView.setViewName("error/exception");
return modelAndView;
}
}
我们在上节课的Controller类中人为设置了一个异常,如下图所示。
好了,代码写完了,我们现在要测试一下。先启动要启动的四个服务(图片服务、zookeeper、redis、solr)启动完四个服务之后,我们需要重新打包taotao-parent工程到本地Maven仓库(这是由于我刚才在taotao-parent工程添加东西了,所以需要重新打包)。打包好之后,依次启动taotao-manager、taotao-content、taotao-search三个服务以及taotao-manager-web、taotao-portal-web、taotao-search-web三个系统。
下面我们访问淘淘商城首页,在搜索框中随便输入一个关键词进行搜索都会引发我们人为设置的异常,看能不能发送到邮箱当中。搜索手机会进入到统一异常页面,如下图所示。
稍等一会儿,qq邮箱便会收到一封邮件,如下图所示。
我们点击进去查看异常详细信息,发现确实是我们在Controller层人为设置的异常。这说明我们的邮箱发送功能正常!
上面我们成功完成了单邮件发送给单个人的功能,下面我们再实现一封邮件群发给多个人。在邮件发送工具类中添加一个方法,如下图所示。
新添加的代码及main函数代码如下,为了简单,就在main方法中测试下这个群发功能就可以了。经过实际测试,没问题。
//群发一封邮件
public static void groupSendEmail(String subject,String content) throws MessagingException {
Properties props = new Properties();
props.setProperty("mail.smtp.auth", "true");
props.setProperty("mail.transport.protocol", "smtp");
props.setProperty("mail.host", "smtp.163.com");
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("xxxxxx@163.com", "xxxxxx");//163邮箱用户名和客户端授权密码(注意,不是登录密码)
}
});
session.setDebug(true);
Message msg = new MimeMessage(session);
//邮件发送者
msg.setFrom(new InternetAddress("xxxxxxx@163.com"));
msg.setSubject(subject);
//注意第二个参数要写成"text/html;charset=utf-8",表明这是一封html邮件
msg.setContent(content, "text/html;charset=utf-8");
//要群发给哪些邮箱
msg.setRecipients(RecipientType.TO, InternetAddress.parse("yyyyyyy@163.com,zzzzzz@qq.com"));
Transport.send(msg);
}
public static void main(String[] args){
try {
groupSendEmail("出现了异常,请及时处理!", "内容管理系统出现重大错误,请及时进行处理");
} catch (MessagingException e) {
e.printStackTrace();
}
}