JavaEE 6 中的异步方法
Asynchronous method in JavaEE 6
我尝试在 JavaEE 6 中使用 异步 方法。我创建了 Following 类。但是输出log的结果是synchronous。不正确。
无国籍Class
@javax.ejb.Stateless
public class MailManager implements Serializable{
@Asynchronous
public void send(List<String> toList, Mail mail, final SystemSetting systemSetting) throws MailException, UnsupportedEncodingException{
boolean isError = false;
for(InternetAddress addr :addressList){
MimeMessage msg = getMimeMessage(addr, mail, session);
Transport.send(msg);
log.info("Sent email to ["+addr+"].");
}
if(isError){
throw new MailException("Mail sending error occurred. Check error log.");
}
log.debug("Async finish.");
}
}
ManagedBean
@Named
@SessionScoped
public class MailController extends AbstractController{
@Inject @new(MailCreater.class)
private MailFactory mailCreater;
@Inject
private MailManager mailManager;
public void send(){
Mail mail = null;
mail = mailCreater.createFromTemplate(this, false,
mimeInfo, "template/testTemplate.txt","template/FOOTER1.body", map);
mailManager.send(list, mail, systemSetting);
log.debug("send() finish.");
}
}
日志
INFO[Log4jLogger] - Sent email to [test1@test.co.jp].
INFO [Log4jLogger] - Sent email to [test2@test.co.jp].
.
.
.
DEBUG [Log4jLogger] - Async finish.
DEBUG [Log4jLogger] - send() finish.
更新
将@Inject 更改为@EJB 时的错误代码
warning : javax.ejb.EJBException: javax.ejb.EJBException:
javax.ejb.CreateException: Could not create stateless EJB
at
com.sun.ejb.containers.StatelessSessionContainer._getContext(StatelessSessionContainer.java:454)
at com.sun.ejb.containers.BaseContainer.getContext(BaseContainer.java:2547)
at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1899)
at com.sun.ejb.containers.EjbAsyncTask.call(EjbAsyncTask.java:99)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: javax.ejb.EJBException: javax.ejb.CreateException: Could not
create stateless EJB
at
com.sun.ejb.containers.StatelessSessionContainer$SessionContextFactory.create(StatelessSessionContainer.java:726)
at
com.sun.ejb.containers.util.pool.NonBlockingPool.getObject(NonBlockingPool.java:247)
at
com.sun.ejb.containers.StatelessSessionContainer._getContext(StatelessSessionContainer.java:449)
... 7 more
Caused by: javax.ejb.CreateException: Could not create stateless EJB
at
com.sun.ejb.containers.StatelessSessionContainer.createStatelessEJB(StatelessSessionContainer.java:534)
at
com.sun.ejb.containers.StatelessSessionContainer.access[=13=]0(StatelessSessionContainer.java:95)
at
com.sun.ejb.containers.StatelessSessionContainer$SessionContextFactory.create(StatelessSessionContainer.java:724)
... 9 more
Caused by: java.lang.NullPointerException
at java.util.concurrent.ConcurrentHashMap.hash(ConcurrentHashMap.java:333)
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:988)
at
org.jboss.weld.manager.BeanManagerImpl.getBean(BeanManagerImpl.java:1076)
at org.jboss.weld.manager.BeanManagerImpl.getBean(BeanManagerImpl.java:148)
at
org.glassfish.weld.services.JCDIServiceImpl._createJCDIInjectionContext(JCDIServiceImpl.java:169)
at
org.glassfish.weld.services.JCDIServiceImpl.createJCDIInjectionContext(JCDIServiceImpl.java:146)
at
com.sun.ejb.containers.BaseContainer.createEjbInstanceAndContext(BaseContainer.java:1639)
at
com.sun.ejb.containers.StatelessSessionContainer.createStatelessEJB(StatelessSessionContainer.java:475)
... 11 more
您的代码 MailController.send() 示例缺少您当前获取 MailManager 引用的方式。我假设 mailManager 是参考,但示例没有显示该参考是如何制作的,所以我在这里有点盲目地告诉你到底出了什么问题。
但是,一般来说,您需要做的是将 MailManager EJB 注入到 MailController 中,使用类似 @EJB MailManager mailManager;
的东西
另外,您似乎依赖日志来告诉您事情是否真的发生了异步。由于时间的原因,这可能会出现问题。此外,您包含的日志片段不包含 "Sent email to" 消息,如果一切顺利,它看起来 MailManager.send()
应该发出。
--更新--
@KensukeSato 提出了一个很好的问题,"Should I use @Inject
or @EJB
as both 'work' in his case?"
基本上可以归结为 JavaEE6 CDI bean 和 EJB bean 提供不同的功能,EJB 会话 bean 是 CDI bean 但 CDI bean 不是 EJB 会话 bean。因此,如果您只需要 EJB 服务,您可以使用 @EJB
将会话 bean 注入到 CDI bean 中。但是,如果除了 @EJB
服务之外,您还希望 bean 利用 CDI 服务,那么您应该使用 @Inject
.
CDI 为您提供了哪些 EJB 本身无法提供的功能?
- 基于注解的编程模型;刻板印象
- 类型安全注入、拦截器、装饰器
- 上下文管理、范围、对话
- 事件和观察者
- 生产者和处置者
- 通过自定义范围的可扩展性;以编程方式定义的 bean;等等
因此,如果除了 EJB 功能之外您还需要其中任何一个,那么您可以使用 @Inject
,否则您可以简单地使用 @EJB
。
我尝试在 JavaEE 6 中使用 异步 方法。我创建了 Following 类。但是输出log的结果是synchronous。不正确。
无国籍Class
@javax.ejb.Stateless
public class MailManager implements Serializable{
@Asynchronous
public void send(List<String> toList, Mail mail, final SystemSetting systemSetting) throws MailException, UnsupportedEncodingException{
boolean isError = false;
for(InternetAddress addr :addressList){
MimeMessage msg = getMimeMessage(addr, mail, session);
Transport.send(msg);
log.info("Sent email to ["+addr+"].");
}
if(isError){
throw new MailException("Mail sending error occurred. Check error log.");
}
log.debug("Async finish.");
}
}
ManagedBean
@Named
@SessionScoped
public class MailController extends AbstractController{
@Inject @new(MailCreater.class)
private MailFactory mailCreater;
@Inject
private MailManager mailManager;
public void send(){
Mail mail = null;
mail = mailCreater.createFromTemplate(this, false,
mimeInfo, "template/testTemplate.txt","template/FOOTER1.body", map);
mailManager.send(list, mail, systemSetting);
log.debug("send() finish.");
}
}
日志
INFO[Log4jLogger] - Sent email to [test1@test.co.jp].
INFO [Log4jLogger] - Sent email to [test2@test.co.jp].
.
.
.
DEBUG [Log4jLogger] - Async finish.
DEBUG [Log4jLogger] - send() finish.
更新
将@Inject 更改为@EJB 时的错误代码
warning : javax.ejb.EJBException: javax.ejb.EJBException:
javax.ejb.CreateException: Could not create stateless EJB
at
com.sun.ejb.containers.StatelessSessionContainer._getContext(StatelessSessionContainer.java:454)
at com.sun.ejb.containers.BaseContainer.getContext(BaseContainer.java:2547)
at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1899)
at com.sun.ejb.containers.EjbAsyncTask.call(EjbAsyncTask.java:99)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: javax.ejb.EJBException: javax.ejb.CreateException: Could not
create stateless EJB
at
com.sun.ejb.containers.StatelessSessionContainer$SessionContextFactory.create(StatelessSessionContainer.java:726)
at
com.sun.ejb.containers.util.pool.NonBlockingPool.getObject(NonBlockingPool.java:247)
at
com.sun.ejb.containers.StatelessSessionContainer._getContext(StatelessSessionContainer.java:449)
... 7 more
Caused by: javax.ejb.CreateException: Could not create stateless EJB
at
com.sun.ejb.containers.StatelessSessionContainer.createStatelessEJB(StatelessSessionContainer.java:534)
at
com.sun.ejb.containers.StatelessSessionContainer.access[=13=]0(StatelessSessionContainer.java:95)
at
com.sun.ejb.containers.StatelessSessionContainer$SessionContextFactory.create(StatelessSessionContainer.java:724)
... 9 more
Caused by: java.lang.NullPointerException
at java.util.concurrent.ConcurrentHashMap.hash(ConcurrentHashMap.java:333)
at java.util.concurrent.ConcurrentHashMap.get(ConcurrentHashMap.java:988)
at
org.jboss.weld.manager.BeanManagerImpl.getBean(BeanManagerImpl.java:1076)
at org.jboss.weld.manager.BeanManagerImpl.getBean(BeanManagerImpl.java:148)
at
org.glassfish.weld.services.JCDIServiceImpl._createJCDIInjectionContext(JCDIServiceImpl.java:169)
at
org.glassfish.weld.services.JCDIServiceImpl.createJCDIInjectionContext(JCDIServiceImpl.java:146)
at
com.sun.ejb.containers.BaseContainer.createEjbInstanceAndContext(BaseContainer.java:1639)
at
com.sun.ejb.containers.StatelessSessionContainer.createStatelessEJB(StatelessSessionContainer.java:475)
... 11 more
您的代码 MailController.send() 示例缺少您当前获取 MailManager 引用的方式。我假设 mailManager 是参考,但示例没有显示该参考是如何制作的,所以我在这里有点盲目地告诉你到底出了什么问题。
但是,一般来说,您需要做的是将 MailManager EJB 注入到 MailController 中,使用类似 @EJB MailManager mailManager;
另外,您似乎依赖日志来告诉您事情是否真的发生了异步。由于时间的原因,这可能会出现问题。此外,您包含的日志片段不包含 "Sent email to" 消息,如果一切顺利,它看起来 MailManager.send()
应该发出。
--更新--
@KensukeSato 提出了一个很好的问题,"Should I use @Inject
or @EJB
as both 'work' in his case?"
基本上可以归结为 JavaEE6 CDI bean 和 EJB bean 提供不同的功能,EJB 会话 bean 是 CDI bean 但 CDI bean 不是 EJB 会话 bean。因此,如果您只需要 EJB 服务,您可以使用 @EJB
将会话 bean 注入到 CDI bean 中。但是,如果除了 @EJB
服务之外,您还希望 bean 利用 CDI 服务,那么您应该使用 @Inject
.
CDI 为您提供了哪些 EJB 本身无法提供的功能?
- 基于注解的编程模型;刻板印象
- 类型安全注入、拦截器、装饰器
- 上下文管理、范围、对话
- 事件和观察者
- 生产者和处置者
- 通过自定义范围的可扩展性;以编程方式定义的 bean;等等
因此,如果除了 EJB 功能之外您还需要其中任何一个,那么您可以使用 @Inject
,否则您可以简单地使用 @EJB
。