@ScopeType @Retention(RUNTIME) @Target({TYPE, METHOD}) public @interface ClusterScoped {}这样使用
@ClusterScoped public class SecondLevelCache { ... }当然我们要定义一个Context 对象去实现这个Scope.这个就属于一个框架方面的任务.
@Named @ConversationScoped public class StudentBean implements Serializable { private static final long serialVersionUID = 353361676959311121L; @Inject Conversation conversation; //开启会话模式 public void beginConversation() { if ( conversation.isTransient() ) { conversation.begin(); //过期时间20分钟. conversation.setTimeout(1200000); } } //关闭会话模式 @PreDestroy public void endConversation() { if ( !conversation.isTransient() ) { conversation.end(); } } }
如果2个@ConversationScoped. A中@inject了B.那么B的会话周期也是属于A来控制.B不需要开启会话.直接依赖A的周期.但A销毁B也销毁.
下面是JBOSS weld给的一个例子:import javax.enterprise.inject.Produces; import javax.inject.Inject; import javax.persistence.PersistenceContextType.EXTENDED; @ConversationScoped @Stateful public class OrderBuilder { private Order order; private @Inject Conversation conversation; private @PersistenceContext(type = EXTENDED) EntityManager em; @Produces public Order getOrder() { return order; } public Order createOrder() { order = new Order(); conversation.begin(); return order; } public void addLineItem(Product product, int quantity) { order.add(new LineItem(product, quantity)); } public void saveOrder(Order order) { em.persist(order); conversation.end(); } @Remove public void destroy() {} }
如果是POST请求,cid会自动传播.如果是GET请求,需要加cid参数.
CDI规范保留cid这个参数名.cid是一个conversation的唯一标识符.页面EL表达式为:javax.enterprise.context.conversation。
<a href="/addProduct.jsp?cid=#{javax.enterprise.context.conversation.id}">Add Product</a>
或者JSF中更常用的
<h:link outcome="/addProduct.xhtml" value="Add Product"> <f:param name="cid" value="#{javax.enterprise.context.conversation.id}"/> </h:link>
会话管理会有异常。
javax.enterprise.context.NonexistentConversationException(如:页面传递的cid不存在引发)
javax.enterprise.context.BusyConversationException(长时间运行Conversation的并发请求)
这个时候是需要使用 CDI Conversation filter 来进行处理.
public class MyFilter implements Filter { ... @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { chain.doFilter(request, response); } catch (BusyConversationException e) { response.setContentType("text/plain"); response.getWriter().print("BusyConversationException"); } } ...如果要他工作,要在web.xml中定义:
<filter-mapping> <filter-name>My Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>CDI Conversation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
当然上面这个是官方的文档,我真想一口盐汽水喷死他们,我是完全走不通,用他描述的这种方案.后续自己写了个.
得知,conversation的两个异常是
或者是org.jboss.weld.context.NonexistentConversationException或org.jboss.weld.context.BusyConversationException
但他们是继承关系,一样的处理.
1.我可以按照cid是否存在进行处理,或者更深的处理,
2.我可以根据URL进行不同的页面处理.
package org.credo.scope.conversation; import java.io.IOException; import javax.enterprise.context.BusyConversationException; import javax.enterprise.context.NonexistentConversationException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // try { // chain.doFilter(request, response); // } catch (org.jboss.weld.context.NonexistentConversationException e) { // System.out.println("weld NonexistentConversationException"+e.getMessage()); // response.setContentType("text/plain"); // response.getWriter().print("NonexistentConversationException"); // }catch (NonexistentConversationException e) { // System.out.println("NonexistentConversationException"+e.getMessage()); // response.setContentType("text/plain"); // response.getWriter().print("NonexistentConversationException"); // } // catch (Exception e) { // System.out.println("Exception:"+e.getMessage()); // response.getWriter().print("NonexistentConversationException"); // } if (request.getParameter("cid")!=null) { try { System.out.println("1"); chain.doFilter(request, response); throw new RuntimeException("Expected exception not thrown"); } catch (ServletException e) { Throwable cause = e.getCause(); System.out.println("2"); while (cause != null) { System.out.println("e.getCause():"+e.getCause()); if (e.getCause() instanceof NonexistentConversationException) { System.out.println("3"); response.setContentType("text/html"); response.getWriter().print("NonexistentConversationException thrown properly\n"); HttpServletResponse res = (HttpServletResponse) response; res.sendRedirect("/credo-jsf/guest.jsf"); // FIXME WELD-878 // response.getWriter().print("Conversation.isTransient: " + conversation.isTransient()); return; } cause = e.getCause(); } throw new RuntimeException("Unexpected exception thrown"); } } else { chain.doFilter(request, response); } } @Override public void destroy() { } }
<filter> <filter-name>CDI Conversation Filter</filter-name> <filter-class>org.credo.scope.conversation.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>CDI Conversation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
官方给的过滤器示例,我试了好几个小时完全行不通.查遍google表示没看到行得通相关的处理.有高手知道,请教下.
conversation上下文在初始化的时候可以是延迟或者即时.<待测试学习>
延迟
当初始化是延迟加载的时候,conversation context无论是transient或者是长运行(就是conversation.begin或者没begin
),它只是在@ConversationScoped Bean第一次访问的时候初始化conversation上下文.在此,cid 参数是可读以及conversation是恢复的.
如果没有conversation的状态是可访问的,那么conversation上下文可能没去处理所有的请求.
注意.如果一个问题发生在延迟加载的初始化阶段.那么这个conversation会抛出一个BusyConversationException or NonexistentConversationException 异常.
即时
当初始化是即时的,那么这个conversation上下文会在预定义的的时间进行初始化.在任意请求之前,处理listener前,filter or servlet 是唤醒的或者.如果这个CDI Conversation Filter 配置好了,那么会执行这个filter.
配置conversation 上下文初始化的方式使用如下代码进行配置.
<context-param> <param-name>org.jboss.weld.context.conversation.lazy</param-name> <param-value>true</param-value> </context-param>如果这个参数没有去设置,会有下列情况:
除了四个内建的范围,CDI还支持两个伪范围.第一个就是singleton,使用@singleton标注.
注意: 四个内建的范围都是在package javax.enterprise.context下的,而@singleton是在package javax.inject下的.
这里的singleton和其他地方的singleton是不同的,或者说cdi的singleton是有问题的.
你可以看到他是在inject下的,这个scope是伪范围.Bean与@singleton没有代理对象范围.
现在,如果这个singleton Bean是一个简单的,不可变的,可序列化的对象,就如同一个字符串,数字,日期.我们可以不用太在乎它通过序列化进行复制.然后这使得这个singleton Bean并不是一个真正的单例.
但仍然有几种办法确保singleton bean是一个单例如:
public class PaymentCalc { @Inject Calculator calculator; @Inject @New Calculator newCalculator; }