基于事件驱动发送email、短信
发送邮件就发送邮件呗,为什么说是基于事件驱动呢?因为发送邮件是一个相对耗时的操作,我们基于事件驱动可以异步解耦,
同时通过@Async做一步操作降低接口的耦合性和响应时间。
spring事件驱动组成
spring事件驱动由3个部分组成
1、ApplicationEvent:表示事件本身,自定义事件需要继承该类。用来定义事件
2、ApplicationEventPublisherAware:事件发送器,需要实现该接口。主要用来发布事件.ApplicationContext 也实现了该接口,可以用于发布事件.
Spring4.2之后,ApplicationEventPublisher自动被注入到容器中,采用Autowired即可获取。
3、ApplicationListener:事件监听器接口。监听类实现ApplicationListener 里onApplicationEvent方法即可.
在spring4.2以后我们可以以更加简洁的方式来监听event的发布,监听事件我们不必再实现ApplicationListener接口了,只要在方法上添加注解@EventListener即可.
如果要监听多个事件类型的发布,可以在@EventListener(classes = {FaceEvent.class,ArmEvent.class})指定,spring会多次调用此方法来处理多个事件。但是注意此时,方法参数不能有多个,否则会发生转换异常,可以将使用多个事件的父类作为唯一的方法参数来接收处理事件,但除非必要否则并不推荐监听多个事件的发布。
如果有多个监听器监听同一事件,我们可以在方法上使用spring的@order注解来定义多个监听器的顺序,order越小,优先级越高.
@EventListener还有一个属性,condition()里可以使用SPEL表达式来过滤监听到事件,即只有符合某种条件的才进行接收处理。比如:
| @EventListener(condition = "event.message == 'message'")
|
监听多个事件:
1 2 3 4 5 6 7 8 9 10
| @EventListener({FaceEvent.class,ArmEvent.class}) public void onApplicationEvent3(Object event) {
if(event instanceof FaceEvent){ LOGGER.info("===> B 收到人脸事件: {}",((FaceEvent) event).getEventData()); }else if(event instanceof ArmEvent){ ArmEvent armEvent = (ArmEvent) event; LOGGER.info("===> B 收到臂膀事件: {}",armEvent.getEventData()); } }
|
注意事项
利用@TransactionalEventListener实现监听事件时的事务隔离
很多时候,只有事务提交之后我们才会发布相应的事件处理其他逻辑,比如用户注册之后,发送邮件或者短信。这时候就可以用注解@TransactionalEventListener。
@TransactionalEventListener和@EventListener都可以监听事件,但前者可以对发布事件和监听事件进行一些事务上的隔离。
@TransactionalEventListener是对@EventListener的一个扩展,允许将事件的监听器绑定到事务的某个阶段。可以绑定到以下事务阶段:
- AFTER_COMMIT (默认),事务提交后
- AFTER_ROLLBACK ,事务回滚后
- AFTER_COMPLETION ,事务完成,包括提交后和回滚后
- BEFORE_COMMIT ,事务提交前****
@TransactionalEventListener指不和发布事件的方法在同一个事务内,发布事件的方法事务结束后才会执行本监听方法,监听逻辑内发生异常不会回滚发布事件方法的事务。
@TransactionalEventListener有一个属性为fallbackExecution,默认为false,指发布事件的方法没有事务控制时,监听器不进行监听事件,此为默认情况!fallbackExecution=true,则指发布事件的方法没有事务控制时,监听方法仍可以监听事件进行处理。
整合邮件功能
1 2 3 4 5 6 7 8 9 10
| <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-email</artifactId> <version>1.5</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4</version> </dependency>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import lombok.Data;
@Data public class EmailInfo {
private String title;
private String content;
private String subject;
private String destination;
private String attachmentPath;
private String attachmentDescription;
private String attachmentName; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| import lombok.Data;
import java.io.Serializable; import java.util.Date;
@Data public class EmailServer implements Serializable { private Long id;
private String smtpAddr;
private Integer mailPort;
private String userName;
private String password;
private Integer sslFlag;
private Long sendTime;
private Long connectTime;
private Long receiveTime;
private String senderAccount;
private Integer maxQuantity;
private Date createTime;
private Date modifyTime;
private Integer status;
private String remark;
private static final long serialVersionUID = 1L; }
|
定义Event
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import com.ruoyi.aiot.domain.email.EmailInfo; import com.ruoyi.aiot.domain.email.EmailServer; import org.springframework.context.ApplicationEvent;
public class SendSimpleEmailEvent extends ApplicationEvent {
private EmailServer emailServer;
private EmailInfo emailInfo;
public SendSimpleEmailEvent(Object source) { super(source); }
public SendSimpleEmailEvent(Object source, EmailServer emailServer, EmailInfo emailInfo) { super(source); this.emailServer = emailServer; this.emailInfo = emailInfo; }
public EmailServer getEmailServer() { return emailServer; }
public void setEmailServer(EmailServer emailServer) { this.emailServer = emailServer; }
public EmailInfo getEmailInfo() { return emailInfo; }
public void setEmailInfo(EmailInfo emailInfo) { this.emailInfo = emailInfo; }
}
|
发送Event
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import cn.hutool.json.JSONObject; import com.ruoyi.aiot.domain.email.EmailInfo; import com.ruoyi.aiot.domain.email.EmailServer; import com.ruoyi.aiot.domain.event.SendAttachmentEmailEvent; import com.ruoyi.aiot.domain.event.SendSimpleEmailEvent; import com.ruoyi.common.utils.StringUtils; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component public class EmailHandler implements NotifyHandler{
private final String type = "Email"; private final String provider = "EmailProvider";
@Resource private ApplicationEventPublisher applicationEventPublisher;
@Override public void initClient(JSONObject initMessage) throws Exception { }
@Override public void SendMessage(JSONObject sendMessage) { EmailServer emailServer = sendMessage.get("emailServer", EmailServer.class); EmailInfo emailInfo = sendMessage.get("emailInfo", EmailInfo.class);
if (StringUtils.isEmpty(emailInfo.getAttachmentPath())){ SendSimpleEmailEvent sendSimpleEmailEvent = new SendSimpleEmailEvent(this, emailServer, emailInfo); applicationEventPublisher.publishEvent(sendSimpleEmailEvent); } SendAttachmentEmailEvent sendAttachmentEmailEvent = new SendAttachmentEmailEvent(this, emailServer, emailInfo); applicationEventPublisher.publishEvent(sendAttachmentEmailEvent); } }
|
监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| import com.alibaba.fastjson2.JSON; import com.ruoyi.aiot.domain.email.EmailInfo; import com.ruoyi.aiot.domain.email.EmailServer; import com.ruoyi.aiot.domain.event.SendAttachmentEmailEvent; import com.ruoyi.aiot.domain.event.SendSimpleEmailEvent; import com.ruoyi.common.utils.StringUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.mail.*; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component;
import java.util.Date; import java.util.HashMap; import java.util.Map;
@Slf4j @Component public class MyEmilUtils {
private static final String CHARSET_UTF8 = "UTF-8";
@Async @EventListener public void pushSimpleEmail(SendSimpleEmailEvent sendSimpleEmailEvent) { EmailServer server = sendSimpleEmailEvent.getEmailServer(); EmailInfo emailInfo = sendSimpleEmailEvent.getEmailInfo();
log.info("发送邮件:{},至邮件服务器:{}", JSON.toJSONString(emailInfo), JSON.toJSONString(server)); Email email = new SimpleEmail(); email.setHostName(server.getSmtpAddr()); email.setSmtpPort(server.getMailPort()); email.setAuthentication(server.getUserName(), server.getPassword()); email.setCharset(CHARSET_UTF8); try { email.addTo(emailInfo.getDestination()); email.setFrom(server.getUserName(), server.getSenderAccount()); email.setSubject(emailInfo.getSubject()); email.setMsg(emailInfo.getContent()); email.setSSLOnConnect(server.getSslFlag() == 0); Map<String, String> map = new HashMap<>(); if (!StringUtils.isEmpty(emailInfo.getTitle())) { map.put("主题", emailInfo.getTitle()); } else { map.put("主题", emailInfo.getSubject()); } email.setHeaders(map); email.setSentDate(new Date()); email.send(); log.info("邮件:{},发送成功", JSON.toJSONString(emailInfo)); } catch (EmailException e) { log.info("邮件发送失败message: {}", e.getMessage()); } }
@Async @EventListener public void pushAttachmentEmail(SendAttachmentEmailEvent sendAttachmentEmailEvent) { EmailServer server = sendAttachmentEmailEvent.getEmailServer(); EmailInfo emailInfo = sendAttachmentEmailEvent.getEmailInfo();
log.info("发送带附件的邮件:{},至邮件服务器:{}", JSON.toJSONString(emailInfo), JSON.toJSONString(server)); EmailAttachment attachment = new EmailAttachment(); attachment.setDescription(emailInfo.getAttachmentDescription()); attachment.setDisposition(EmailAttachment.ATTACHMENT); attachment.setName(emailInfo.getAttachmentName()); attachment.setPath(emailInfo.getAttachmentPath());
MultiPartEmail email = new MultiPartEmail(); email.setHostName(server.getSmtpAddr()); email.setSmtpPort(server.getMailPort()); email.setAuthentication(server.getUserName(), server.getPassword()); email.setCharset(CHARSET_UTF8); try { email.addTo(emailInfo.getDestination()); if (server.getSslFlag() == 0) { email.setSSLOnConnect(true); } else { email.setSSLOnConnect(false); } email.attach(attachment); email.setFrom(server.getUserName(), server.getSenderAccount()); email.setSubject(emailInfo.getSubject()); email.setMsg(emailInfo.getContent()); Map<String, String> map = new HashMap<>(); map.put("主题", emailInfo.getTitle()); email.send(); email.setHeaders(map); log.info("带附件的邮件发送成功:{}", JSON.toJSONString(attachment)); } catch (EmailException e) { log.info("邮件发送失败:{},message:{}", e, JSON.toJSONString(attachment)); } } }
|
集成阿里云发送短信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.6.2</version> </dependency>
<dependency> <groupId>com.aliyun</groupId> <artifactId>tea-openapi</artifactId> <version>0.2.6</version> </dependency>
<dependency> <groupId>com.aliyun</groupId> <artifactId>dysmsapi20170525</artifactId> <version>2.0.21</version> </dependency>
|
定义事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import lombok.Data;
import java.util.Map;
@Data public class AliyunSmsInfo {
private String signName;
private String phoneNumbers;
private String templateCode;
}
|
1 2 3 4 5 6 7 8 9 10 11
| import lombok.Data;
@Data public class AliyunSmsServer {
private String accessKeyId;
private String accessKeySecret;
private String orginId; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| import com.ruoyi.aiot.domain.aliyunSms.AliyunSmsInfo; import com.ruoyi.aiot.domain.aliyunSms.AliyunSmsServer; import lombok.Data; import org.springframework.context.ApplicationEvent;
import java.time.Clock; import java.util.Map;
public class SendAliyunSmsEvent extends ApplicationEvent {
private AliyunSmsInfo aliyunSmsInfo;
private AliyunSmsServer aliyunSmsServer;
private Map aliyunSmsParams;
public SendAliyunSmsEvent(Object source, AliyunSmsServer aliyunSmsServer, AliyunSmsInfo aliyunSmsInfo ,Map map) { super(source); this.aliyunSmsInfo = aliyunSmsInfo; this.aliyunSmsServer = aliyunSmsServer; this.aliyunSmsParams = map; }
public Map getAliyunSmsParams() { return aliyunSmsParams; }
public void setAliyunSmsParams(Map aliyunSmsParams) { this.aliyunSmsParams = aliyunSmsParams; }
public SendAliyunSmsEvent(Object source, Clock clock) { super(source, clock); }
public AliyunSmsInfo getAliyunSmsInfo() { return aliyunSmsInfo; }
public void setAliyunSmsInfo(AliyunSmsInfo aliyunSmsInfo) { this.aliyunSmsInfo = aliyunSmsInfo; }
public AliyunSmsServer getAliyunSmsServer() { return aliyunSmsServer; }
public void setAliyunSmsServer(AliyunSmsServer aliyunSmsServer) { this.aliyunSmsServer = aliyunSmsServer; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import cn.hutool.json.JSONObject; import com.ruoyi.aiot.domain.aliyunSms.AliyunSmsInfo; import com.ruoyi.aiot.domain.aliyunSms.AliyunSmsServer; import com.ruoyi.aiot.domain.email.EmailServer; import com.ruoyi.aiot.domain.event.SendAliyunSmsEvent; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.jws.Oneway; import java.util.Map; @Component public class AliyunSmsHandler implements NotifyHandler{ private final String type = "ShortMessage"; private final String provider = "ShortMessageAlibabaServer";
@Resource private ApplicationEventPublisher applicationEventPublisher;
@Override public void initClient(JSONObject initMessage) throws Exception { } @Override public void SendMessage(JSONObject sendMessage) { AliyunSmsServer server = sendMessage.getBean("configuration", AliyunSmsServer.class); AliyunSmsInfo info = sendMessage.getBean("template",AliyunSmsInfo.class); Object templateParam = sendMessage.get("templateParam"); Map<String,String> map = new JSONObject(templateParam).toBean(Map.class); SendAliyunSmsEvent sendAliyunSmsEvent = new SendAliyunSmsEvent(this, server, info, map); applicationEventPublisher.publishEvent(sendAliyunSmsEvent); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import cn.hutool.json.JSONObject; import com.aliyun.dysmsapi20170525.models.SendSmsResponse; import com.aliyun.tea.TeaException; import com.ruoyi.common.exception.ServiceException; import lombok.NonNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import java.util.Map;
public class AliyunSmsUtils {
public static final Logger log = LoggerFactory.getLogger(AliyunSmsUtils.class);
private static volatile com.aliyun.dysmsapi20170525.Client instance;
public static void initClient(@NonNull String accessKeyId, @NonNull String accessKeySecret) throws Exception { if (null == instance){ synchronized (AliyunSmsUtils.class){ if (null == instance){ com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config() .setAccessKeyId(accessKeyId) .setAccessKeySecret(accessKeySecret); config.endpoint = "dysmsapi.aliyuncs.com"; instance = new com.aliyun.dysmsapi20170525.Client(config); } } } }
public static void sendSms(@NonNull String signName, @NonNull String phoneNumbers, @NonNull String templateCode, @NonNull Map templateParam) { com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new com.aliyun.dysmsapi20170525.models.SendSmsRequest(); sendSmsRequest.setPhoneNumbers(phoneNumbers); sendSmsRequest.setTemplateCode(templateCode); sendSmsRequest.setTemplateParam(new JSONObject(templateParam).toString()); sendSmsRequest.setSignName(signName); com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions(); try { SendSmsResponse sendSmsResponse = instance.sendSmsWithOptions(sendSmsRequest, runtime); log.info("消息已发送至:{},响应信息:{}",phoneNumbers,sendSmsResponse.getBody().getMessage()); if (!"OK".equals(sendSmsResponse.getBody().getMessage())){ throw new ServiceException(sendSmsResponse.getBody().getMessage()); } } catch (TeaException error) { com.aliyun.teautil.Common.assertAsString(error.message); } catch (Exception errors) { TeaException error = new TeaException(errors.getMessage(), errors); com.aliyun.teautil.Common.assertAsString(error.message); throw new ServiceException(error.message); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import com.ruoyi.aiot.domain.aliyunSms.AliyunSmsInfo; import com.ruoyi.aiot.domain.aliyunSms.AliyunSmsServer; import com.ruoyi.aiot.domain.event.SendAliyunSmsEvent; import lombok.extern.slf4j.Slf4j; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component;
import java.util.Map; @Slf4j @Component public class MyAliyunSmsUtils { @Async @EventListener public void sendAliyunSms(SendAliyunSmsEvent sendAliyunSmsEvent){ AliyunSmsServer aliyunSmsServer = sendAliyunSmsEvent.getAliyunSmsServer(); AliyunSmsInfo aliyunSmsInfo = sendAliyunSmsEvent.getAliyunSmsInfo(); Map aliyunSmsParams = sendAliyunSmsEvent.getAliyunSmsParams(); try { AliyunSmsUtils.initClient(aliyunSmsServer.getAccessKeyId(),aliyunSmsServer.getAccessKeySecret()); AliyunSmsUtils.sendSms(aliyunSmsInfo.getSignName(), aliyunSmsInfo.getPhoneNumbers(), aliyunSmsInfo.getTemplateCode(), aliyunSmsParams); } catch (Exception e) { e.printStackTrace(); } } }
|