Liferay 7自定义Portlet调用MailService.sendMail仅服务器端失效问题
看起来你已经做了不少扎实的排查工作——本地代码跑起来正常、服务器上用telnet能连SMTP发邮件、甚至Liferay自带的密码找回邮件也能正常发,那问题肯定出在自定义Portlet和Liferay服务交互的细节上。我来给你梳理几个最可能的排查方向:
1. 检查MailService的调用方式是否符合OSGi规范
Liferay 7改成了OSGi模块化架构,要是你的自定义Portlet是模块开发的,千万别再用老一套的MailServiceUtil.sendMail()静态调用了!这种方式在模块化环境里很容易因为类加载器隔离或者服务实例获取不到的问题出岔子。
正确的姿势是用**声明式服务(Declarative Services)**注入MailService,比如:
import com.liferay.mail.kernel.service.MailService; import org.osgi.service.component.annotations.Reference; public class YourCustomPortlet extends MVCPortlet { @Reference private MailService _mailService; public void triggerEmail(ActionRequest request, ActionResponse response) { // 先构建好MailMessage对象 MailMessage mailMessage = new MailMessage(...); // 用注入的服务实例调用发送方法 _mailService.sendMail(mailMessage); } }
如果你的Portlet还是传统WAR包(非OSGi模块),得确保依赖的Liferay API包版本和服务器完全一致,不然很容易出现类加载冲突。
2. 排查Portlet是否私自覆盖了邮件配置
有些自定义Portlet会在自己的配置文件(比如portlet.xml、liferay-portlet.xml)里硬加邮件相关的参数,或者在代码里手动创建JavaMail Session,这会直接覆盖Liferay的全局邮件配置,导致和系统自带功能用的不是同一个会话。
- 先检查Portlet的配置文件,有没有
mail.session.mail.smtp.host这类参数,有的话直接删掉,让Portlet用全局的邮件配置。 - 再看代码里有没有手动创建Session的逻辑,比如
Session.getInstance(props, auth)这种,赶紧换成依赖MailService的方式,让它用Liferay已经配置好的会话。
3. 开DEBUG日志抓具体错误
光猜没用,得看服务器上的具体错误日志。你可以给Liferay的邮件相关模块开DEBUG级别的日志,这样能看到发送过程中的每一步细节:
找到Liferay的日志配置文件(一般是portal-log4j.xml或者log4j2.xml),添加这两段配置:
<Logger name="com.liferay.mail" level="DEBUG" additivity="false"> <AppenderRef ref="CONSOLE" /> <AppenderRef ref="FILE" /> </Logger> <Logger name="javax.mail" level="DEBUG" additivity="false"> <AppenderRef ref="CONSOLE" /> <AppenderRef ref="FILE" /> </Logger>
重启Liferay后,触发Portlet的邮件发送操作,然后去日志里找具体的报错信息——比如是不是认证失败、会话获取不到,还是有什么权限限制,一看日志就清楚了。
4. 检查Portlet的运行上下文与权限
有时候Portlet是在特定站点(Site)或者普通用户上下文下运行的,可能会影响MailService的调用。你可以试试切换到系统用户的上下文来发送邮件,排除权限或者上下文的问题:
// 获取系统默认用户 User systemUser = UserLocalServiceUtil.getDefaultUser(companyId); try { // 临时切换到系统用户权限 PermissionCheckerUtil.setPermissionChecker( PermissionCheckerFactoryUtil.create(systemUser)); // 发送邮件 _mailService.sendMail(mailMessage); } finally { // 记得还原权限上下文 PermissionCheckerUtil.setPermissionChecker(null); }
5. 验证MailMessage的构建细节
虽然本地跑正常,但服务器上可能有一些细微差异,比如字符编码、附件路径、收件人格式:
- 确保收件人地址没有多余空格或者特殊字符,格式完全正确;
- 给MailMessage明确设置字符编码,比如
mailMessage.setCharset("UTF-8"),避免服务器上出现乱码导致发送失败; - 如果邮件带附件,检查服务器上的附件路径是否存在——本地存在的文件,服务器上可能没同步过去。
内容的提问来源于stack exchange,提问作者Alessandra

