php cdi

再次问好! :)

这次,我选择了一项常见任务,我认为大多数情况下都以错误的方式完成:发送电子邮件。 并非所有人都不知道电子邮件API的工作方式,例如JavaMail或Apache的commons-email 。 我通常看到的一个问题是,它们低估了使发送邮件例程异步的需求,并且该例程也应该仅在基础事务成功提交(大多数情况下)时运行。

想一想用户在线购物的常见用例。 完成后,他可能希望接收订单确认电子邮件。 下订单的过程有点复杂:我们通常会在许多不同的表中插入记录,也可能会删除记录以从库存中删除物品等。 当然,所有这些都必须在单个原子事务中完成:

//A sample EJB method
//(using CMT for transaction management)
public void saveOrder() {//saving some productsentityManager.persist(product1);entityManager.persist(product2);//removing them from stockentityManager.remove(product1);//and at last, we have to send that emailsendOrderConfirmationMail(); //the transaction has not yet been commited by this point
}

就像上面的伪代码一样,我们通常会努力将事务逻辑排除在代码之外。 也就是说,我们使用CMT(容器管理的事务)来使容器为我们做所有事情,并使代码更整洁。 我们的方法调用完成这样的权利后 ,EJB容器提交我们的事务。 这是问题编号1:当调用sendOrderConfirmationMail()方法时,我们无法知道事务是否成功。 用户可能会收到不存在的订单的确认。

如果您尚未意识到这一点,则只需使用您的任何代码进行测试。 在对我们的封闭方法调用结束之前,对EntityManager.persist()的那些调用不会触发任何数据库命令。 只需设置一个断点,然后自己看看即可。 我已经多次看到这样的困惑。

因此,在发生回滚的情况下,我们无需发送任何电子邮件。 发生问题的原因有很多:系统故障,某些业务规则可能会拒绝购买,信用卡验证等。

因此,我们已经知道,使用CMT时,我们很难知道交易何时成功。 下一个问题是使邮件例程异步,完全独立于我们的订购例程。 想象一下,如果订购过程一切正常,但是尝试发送电子邮件时发生一些异常怎么办? 我们是否应该仅因为无法发送确认邮件而回滚所有内容? 我们是否应该仅仅因为我们的邮件服务器表现不佳而真的阻止用户在我们的商店购买商品?

我知道这样的业务需求可以任意选择,但是请记住,通常希望使发送邮件的固有延迟不干扰订单处理。 大多数时候,处理订单是我们的主要目标。 诸如发送电子邮件之类的低优先级任务甚至可以推迟到服务器负载较低的时候。

开始了

为了解决这个问题,我选择了一种纯Java EE方法。 无需使用第三方API。 我们的环境包括:

  • JDK 7或更高版本。
  • Java EE 7(JBoss Wildfly 8.1.0)
  • CDI 1.1
  • EJB 3.2
  • JavaMail 1.5

我已经建立了一个小型网络项目,因此您可以看到所有工作, 如果需要 , 可以在此处下载 。

在深入研究代码之前,请简要观察一下:下面显示的解决方案主要包括CDI事件和EJB异步调用。 这是因为CDI 1.1规范不提供异步事件处理。 似乎仍在为CDI 2.0规范进行讨论。 因此,纯CDI方法可能会比较棘手。 我并不是说这是不可能的,我什至没有尝试过。

该代码示例仅是一个“注册客户”用例的信条。 我们将在其中发送电子邮件以确认用户注册的位置。 总体架构如下所示:

该代码示例还提供了一个“失败测试用例”,因此您实际上可以看到,在进行回滚时没有发送电子邮件。 我只是在这里向您展示“幸福的道路”,从托管Bean调用我们的CustomerService EJB开始。 没什么有趣的,只是样板:

在我们的CustomerService EJB内部,事情开始变得有趣。 通过使用CDI API,我们可以在saveSuccess()方法的末尾触发MailEvent事件:

@Stateless
public class CustomerService {@Injectprivate EntityManager em;@Injectprivate Event<MailEvent> eventProducer;public void saveSuccess() {Customer c1 = new Customer();c1.setId(1L);c1.setName("John Doe");em.persist(c1);sendEmail();}private void sendEmail() {MailEvent event = new MailEvent();event.setTo("some.email@foo.com");event.setSubject("Async email testing");event.setMessage("Testing email");eventProducer.fire(event); //firing event!}
}

MailEvent类只是代表我们事件的常规POJO。 它封装了有关电子邮件的信息:收件人,主题,文本消息等:

public class MailEvent {private String to; //recipient addressprivate String message;private String subject;//getters and setters
}

如果您是CDI的新手,并且对此事件仍然有些困惑, 请阅读docs 。 它应该给您一个想法。

接下来是时候使用事件观察器MailService EJB了。 这是一个简单的EJB,带有一些JavaMail魔术和一些应注意的注释

@Singleton
public class MailService {@Injectprivate Session mailSession; //more on this later@Asynchronous@Lock(LockType.READ)public void sendMail(@Observes(during = TransactionPhase.AFTER_SUCCESS) MailEvent event) {try {MimeMessage m = new MimeMessage(mailSession);Address[] to = new InternetAddress[] {new InternetAddress(event.getTo())};m.setRecipients(Message.RecipientType.TO, to);m.setSubject(event.getSubject());m.setSentDate(new java.util.Date());m.setContent(event.getMessage(),"text/plain");Transport.send(m);} catch (MessagingException e) {throw new RuntimeException(e);}}
}

就像我说的那样,这只是一个常规的EJB。 使此类成为事件观察者,更确切地说是sendMail()方法的原因是第9行中的@Observes批注。仅此批注将使此方法在事件触发后运行。

但是,我们只需要在提交事务 !时才触发此事件。 回滚不应触发电子邮件。 这就是“ during”属性的来源。通过指定值TransactionPhase.AFTER_SUCCESS,我们确保仅在事务成功提交后才触发事件。

最后但并非最不重要的一点是,我们还需要使此逻辑与主逻辑在单独的线程中运行。 它必须异步运行。 为此,我们仅使用了两个EJB批注@Asynchronous@Lock(LockType.READ) 。 后者@Lock(LockType.READ)不是必需的,但强烈建议使用。 它保证不使用锁,并且多个线程可以同时使用该方法。

在JBoss Wildfly 8.1.0中配置邮件会话

作为奖励,我将展示如何在JBoss WildFly中正确配置邮件“源”。 邮件源与数据源非常相似,除了它们用于发送电子邮件而不是用于数据库:)。 这是一种使代码与如何建立与邮件服务器的连接脱钩的方法。 我使用了与我的Gmail帐户的连接,但是您无需切换MailService类中的任何代码即可切换到所需的任何内容。

可以使用@Resource批注以其JNDI名称检索javax.mail.Session对象:

@Resource(mappedName = "java:jboss/mail/Gmail")
private Session mailSession;

您可能已经注意到,在我以前的代码片段中,我没有使用@Resource批注,而仅使用了CDI的@Inject 。 好吧,如果您好奇我是怎么做到的,只需下载源代码并看一下即可。 ( 提示:我使用了生产者帮助器类 。)

继续,只需打开standalone.xml (如果处于域模式,则打开domain.xml),然后首先查找“邮件子系统”。 它看起来应该像这样:

<subsystem xmlns="urn:jboss:domain:mail:2.0"><mail-session name="default" jndi-name="java:jboss/mail/Default"><smtp-server outbound-socket-binding-ref="mail-smtp"/></mail-session>
</subsystem>

默认情况下,已经在本地主机上运行了一个已提供的邮件会话。 由于您的开发机器上可能没有运行任何邮件服务器,因此我们将添加一个指向gmail的新邮件服务器:

<subsystem xmlns="urn:jboss:domain:mail:2.0"><mail-session name="default" jndi-name="java:jboss/mail/Default"><smtp-server outbound-socket-binding-ref="mail-smtp"/></mail-session><mail-session name="gmail" jndi-name="java:jboss/mail/Gmail" from="your.account@gmail.com"><smtp-server outbound-socket-binding-ref="mail-gmail" ssl="true" username="your.account@gmail.com" password="your-password"/></mail-session>
</subsystem>

查看第5、6和7行如何突出显示。 那是我们的新邮件会话。 但这还不是全部。 我们仍然需要创建一个套接字绑定到我们的新邮件会话。 因此,在standalone.xml内查找一个名为socket-binding-group的元素:

<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}"><!-- a bunch of stuff here --><outbound-socket-binding name="mail-smtp"><remote-destination host="localhost" port="25"/></outbound-socket-binding></socket-binding-group>

现在,通过创建新的outbound-socket-binding元素,将gmail端口添加到现有端口:

<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}"><!-- a bunch of stuff here --><outbound-socket-binding name="mail-smtp"><remote-destination host="localhost" port="25"/></outbound-socket-binding><!-- "mail-gmail" is the same name we used in the mail-session config --><outbound-socket-binding name="mail-gmail"><remote-destination host="smtp.gmail.com" port="465"/></outbound-socket-binding></socket-binding-group>

就是这个。 如果您有任何问题,请发表评论:)。 后来!

翻译自: https://www.javacodegeeks.com/2015/03/cdi-ejb-sending-asynchronous-mail-on-transaction-success.html

php cdi

php cdi_CDI和EJB:在事务成功时发送异步邮件相关推荐

  1. CDI和EJB:在事务成功时发送异步邮件

    再一次问好! :) 这次,我选择了一项常见任务,我认为大多数情况下都以错误的方式完成:发送电子邮件. 并非所有人都不知道电子邮件API的工作方式,例如JavaMail或Apache的commons-e ...

  2. shell脚本编写监控本机内存和硬盘剩余空间,剩余内存小于 500M、根分区剩余空间小于 1000M时,发送报警邮件给 root 管理员

    监控本机内存和硬盘剩余空间,剩余内存小于 500M.根分区剩余空间小于 1000M时,发送报警邮件给 root 管理员 # 创建shell脚本文件 vim free.sh #!/bin/bash di ...

  3. Java实现注册时发送激活邮件验证

    在很多网站注册的时候,为了验证用户信息的真实合法,往往需要验证用户所填邮件的准确性.形式为:用户注册时填写邮箱,注册完成后,网站会向用户所填邮箱发送一封激活邮件,用户点击激活邮件中的链接后,方可完成注 ...

  4. python生日祝福短信_python-定时发送生日邮件祝福

    前言:按照人事的想法,能不能帮助他们定时发送员工生日祝福邮件. 正好学到python ,打算练下手 直接附上代码 个人想法:第一步数据库建立一张员工信息表:用于记录员工的名字,生日,邮箱.对于表没有专 ...

  5. 下载最新版本Maven 3.3.9 ,检测安装是否成功时发现Java版本JDK却低于1.7时报错

    下线最新版本Maven 3.3.9 ,检测安装是否成功时发现Java版本JDK却低于1.7时报错 cmd: mvn -v Exception in thread "main" ja ...

  6. 备份事务日志时遇到 log corruption

    故障描述: 备份事务日志时遇到以下错误 Backup detected log corruption in database FakeDBName. Context is FirstSector. L ...

  7. Spark修炼之道(高级篇)——Spark源码阅读:第九节 Task执行成功时的结果处理...

    Task执行成功时的结果处理 在上一节中,给出了Task在Executor上的运行代码演示,我们知道代码的最终运行通过的是TaskRunner方法 class TaskRunner(execBacke ...

  8. 等概率情况下查找成功时的平均查找长度

    4.设散列表的地址范围为0-17,散列函数为:H(K)=K MOD 13,K为关键字.用线性探测法处理冲突,输入关键字序列:(10,24,32,17,31,30,46,47,40,63,49),完成以 ...

  9. 有事务冲突时节点怎么加入MGR集群

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 文章目录 1. 问题场景描述 2. 如何修复 2.1 找出事务差异点 2.2 决定如何处理 3. 小结 个别节点可能存在事 ...

最新文章

  1. SpringBoot2.x 不反回空值属性
  2. 将Excel文件数据库导入SQL Server
  3. 零售业如何用Hadoop开启大数据之门?
  4. 西电oj1066 费马小定理
  5. 如何成为国内敏捷BI领跑者?这家企业的经验值得借鉴
  6. 看奥运之四:“鸟巢”旁边的洋人求票者
  7. python3.4学习笔记(二十一) python实现指定字符串补全空格、前面填充0的方法
  8. c#.net利用RNGCryptoServiceProvider产生任意范围强随机数的办法
  9. 01.php面向对象
  10. ios虚拟机安装(二)
  11. Charles 最新版(Charles 4.1.4 ) 破解注册
  12. CIM+规划:自带CIM平台的数字规划咨询服务,提升城市空间价值和产业活力
  13. Python3开启自带http服务
  14. Unity Canvas Scaler 组件的使用
  15. 谁也没想到,苹果为了利润竟然如此敷衍用户!
  16. 5月已更新PS2021m1直装版!Photoshop2021 Mac真正完美适配M1芯片!完美解决2019黑屏闪退卡启动界面等所有问题!
  17. 《关于大学生熬夜状况的调查》
  18. 微信小程序转盘demo
  19. 计算机视觉实习生面试经验(微软/腾讯(AI Lab优图)/阿里巴巴)
  20. Unity3D里实现可以朝向另一目标广告牌(billboard)效果

热门文章

  1. YbtOJ#20240-[冲刺NOIP2020模拟赛Day10]弱者对决【笛卡尔树,区间dp】
  2. [CF995F] Cowmpany Cowmpensation(树形dp,拉格朗日插值)
  3. Codeforces1142D
  4. codeforces E. Game with String 概率
  5. 从零开始用好 Maven : 从 Hello World 到日常使用
  6. 再有人问你volatile是什么,把这篇文章也发给他(深入分析)
  7. Java 多线程 —— 深入理解 volatile 的原理以及应用
  8. Java并发包:ConcurrentMap
  9. Java命令学习系列(三)——Jmap
  10. 漫画:Bitmap算法 整合版