Spring Boot集成AWS SES与JavaMailSender遇AbstractMethodError求助
Spring Boot集成AWS SES时AbstractMethodError问题解决建议
问题场景
在Spring Boot应用中集成AWS SES,已添加相关依赖、编写邮件上下文抽象类及实现类,通过JavaMailSender创建MimeMessage发送邮件时,触发AbstractMethodError错误。
依赖配置
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-aws</artifactId> <version>2.1.0.RELEASE</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk-ses</artifactId> <version>1.11.505</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>javax.mail-api</artifactId> <version>1.6.2</version> </dependency> <dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.6.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency>
邮件上下文代码
public abstract class AbstractEmailContext { private String from; private String to; private String subject; private String email; private String attachment; private String fromDisplayName; private String emailLanguage; private String displayName; private String templateLocation; private Map<String, Object> context; // getter and setter methods public AbstractEmailContext() { this.context = new HashMap<>(); } public abstract <T> void init(T context); public abstract <T> void rinit(T context); public Object put(String key, Object value) { return key == null ? null : this.context.put(key.intern(), value); } public String getTemplateLocation() { return templateLocation; } public void setTemplateLocation(String templateLocation) { this.templateLocation = templateLocation; } public void setContext(Map<String, Object> context) { this.context = context; } } public class AccountVerificationEmailContext extends AbstractEmailContext{ private String token; @Override public <T> void init(T context){ CCUser ccUser = (CCUser) context; put("name", ccUser.getName()); setTemplateLocation("/confirm"); setSubject("Complete your registration!"); setFrom("no_reply@crosscheck.tech"); setTo(ccUser.getEmail()); } @Override public <T> void rinit(T context){ CCUser ccUser = (CCUser) context; put("email", ccUser.getEmail()); setTemplateLocation("/creset"); setSubject("Change your Password!"); setFrom("no_reply@crosscheck.tech"); setTo(ccUser.getEmail()); } public void setToken(String token){ this.token = token; put("token", token); } public void buildVerificationUrl(final String baseUrl, final String token){ final String url = UriComponentsBuilder.fromHttpUrl(baseUrl).path("/registration/verify").queryParam("token",token).toUriString(); put("verificationURL",url); } public void buildResetUrl(final String baseUrl, final String token){ final String url = UriComponentsBuilder.fromHttpUrl(baseUrl).path("/forgotpass/resetpass").queryParam("token",token).toUriString(); put("verificationURL",url); } }
邮件发送实现代码
@Autowired private JavaMailSender emailSender; @Autowired private SpringTemplateEngine templateEngine; @Override public void sendMail(AbstractEmailContext email) throws MessagingException{ MimeMessage message = emailSender.createMimeMessage(); MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message, MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED, StandardCharsets.UTF_8.name()); Context context = new Context(); context.setVariables(email.getContext()); String emailContent = templateEngine.process(email.getTemplateLocation(),context); mimeMessageHelper.setTo(email.getTo()); mimeMessageHelper.setSubject(email.getSubject()); mimeMessageHelper.setFrom(email.getFrom()); mimeMessageHelper.setText(emailContent,true); emailSender.send(message); }
报错栈
java.lang.AbstractMethodError: Receiver class org.springframework.cloud.aws.mail.simplemail.SimpleEmailServiceJavaMailSender does not define or inherit an implementation of the resolved method 'abstract jakarta.mail.internet.MimeMessage createMimeMessage()' of interface org.springframework.mail.javamail.JavaMailSender. at com.xr.crosscheck.service.CCEmailServiceImpl.sendMail(CCEmailServiceImpl.java:25) ~[classes/:na] at com.xr.crosscheck.service.UserServiceImpl.sendRegistrationConfirmationEmail(UserServiceImpl.java:104) ~[classes/:na] at com.xr.crosscheck.service.UserServiceImpl.register(UserServiceImpl.java:68) ~[classes/:na] at com.xr.crosscheck.controller.RegistrationController.userRegistration(RegistrationController.java:52) ~[classes/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:262) ~[spring-web-6.1.2.jar:6.1.2] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:190) ~[spring-web-6.1.2.jar:6.1.2] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) ~[spring-webmvc-6.1.2.jar:6.1.2] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:917) ~[spring-webmvc-6.1.2.jar:6.1.2] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:829) ~[spring-webmvc-6.1.2.jar:6.1.2] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-6.1.2.jar:6.1.2] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.2.jar:6.1.2] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.2.jar:6.1.2] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.2.jar:6.1.2] at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) ~[spring-webmvc-6.1.2.jar:6.1.2]
报错原因
核心是Jakarta EE与javax规范的依赖冲突:
- 从报错栈中的
spring-web-6.1.2可知,使用的是Spring Boot 3.x版本,该版本已全面切换到Jakarta EE规范,Spring Mail的JavaMailSender接口定义的createMimeMessage()方法返回的是jakarta.mail.internet.MimeMessage。 - 引入的
spring-cloud-starter-aws 2.1.0.RELEASE是基于旧版javax.mail的依赖,它提供的SimpleEmailServiceJavaMailSender实现的是javax版本的createMimeMessage()方法,与当前Spring Mail的接口签名不匹配,导致抽象方法未实现的错误。 - 手动引入的
javax.mail-api和com.sun.mail.javax.mail进一步加剧了依赖冲突,Spring Boot 3.x的spring-boot-starter-mail已经默认使用Jakarta Mail的依赖。
解决步骤
替换旧的AWS SES依赖
移除spring-cloud-starter-aws和aws-java-sdk-ses,改用Spring Boot官方适配Jakarta EE的AWS邮件 starter:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aws-mail</artifactId> <!-- 版本由Spring Boot parent自动管理,无需手动指定 --> </dependency>或者直接使用AWS SDK v2的SES依赖(更推荐,支持最新AWS特性):
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>ses</artifactId> </dependency>清理javax.mail相关依赖
删除手动引入的javax.mail-api和com.sun.mail.javax.mail,因为spring-boot-starter-mail已经包含了Jakarta Mail的依赖:<!-- 移除这两个依赖 --> <dependency> <groupId>javax.mail</groupId> <artifactId>javax.mail-api</artifactId> <version>1.6.2</version> </dependency> <dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.6.2</version> </dependency>配置AWS SES参数
在application.properties或application.yml中配置AWS SES的SMTP信息:spring.mail.host=email-smtp.<你的AWS区域>.amazonaws.com spring.mail.username=你的SES SMTP用户名 spring.mail.password=你的SES SMTP密码 spring.mail.port=587 spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true验证依赖树
执行Maven命令检查是否还有javax.mail的残留依赖,如有则排除:mvn dependency:tree | grep javax.mail
注意事项
- 如果选择使用AWS SDK v2直接调用SES API,需要调整邮件发送实现代码,改用SES客户端发送,而非
JavaMailSender。 - 确保你的AWS账号已正确配置SES权限,并且发送邮箱已通过验证。
内容的提问来源于stack exchange,提问作者SERKAN AYDOGDU
相关产品推荐
相关产品推荐

