博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CDI(Weld)基础<4>Scopes and contexts
阅读量:6307 次
发布时间:2019-06-22

本文共 9265 字,大约阅读时间需要 30 分钟。

hot3.png

1. Scope types

CDI的特点之一是高可扩展性.比如你可以自己定义一个Scope.如下:
@ScopeType@Retention(RUNTIME)@Target({TYPE, METHOD})public @interface ClusterScoped {}
这样使用
@ClusterScopedpublic class SecondLevelCache { ... }
当然我们要定义一个Context 对象去实现这个Scope.这个就属于一个框架方面的任务.
但更多的时候我们是使用 cdi built-in scopes.

2.Built-in scopes

@RequestScoped
@SessionScoped
@ApplicationScoped
@ConversationScoped
Managed beans的 @SessionScoped 和 @ConversationScoped 必须是可序列化的.就是必须要 implements Serializable.因为他们要为为容器钝化HTTP会话(passivates the HTTP session).(其实我是全部都implements Serializable的...= =!)
@RequestScoped,@SessionScoped,@ApplicationScoped这3个就不再说了.主要说@ConversationScoped.

3.@ConversationScoped

ConversationScope其实功能上和传统的SessionScope是有点像的,但不一样的是SessionBean是不能手动控制的.也就是说会话结束,你的SessionBean才会销毁,所以用的时候很谨慎.但Conversation则可以手动进行开启,删除.如果@ConversationScoped不开启,相当于@RequestScoped.如果开启了,相当于一个可以让开发者能手动删除的@SessionScoped.
一:Conversation的基本使用,Conversation timeout
如下代码.@ConversationScoped生命周期的控制,就在这2个方法中.
timeout需要对应场景来设置.不要忽略!
isTransient是用来判断是否开启了Conversation.
这2个方法都是必须使用的.
@Named@ConversationScopedpublic class StudentBean implements Serializable {private static final long serialVersionUID = 353361676959311121L;@InjectConversation conversation;//开启会话模式public void beginConversation() {if ( conversation.isTransient() ){conversation.begin();//过期时间20分钟.conversation.setTimeout(1200000);}}//关闭会话模式@PreDestroypublic 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 @Statefulpublic 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() {}}

4.Conversation传播---cid

如果是POST请求,cid会自动传播.如果是GET请求,需要加cid参数.

CDI规范保留cid这个参数名.cid是一个conversation的唯一标识符.页面EL表达式为:javax.enterprise.context.conversation。

Add Product

或者JSF中更常用的

5.CDI Conversation filter

会话管理会有异常。

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中定义:
My Filter
/*
CDI Conversation Filter
/*

当然上面这个是官方的文档,我真想一口盐汽水喷死他们,我是完全走不通,用他描述的这种方案.后续自己写了个.

得知,conversation的两个异常是

  • import javax.enterprise.context.BusyConversationException;
  • import javax.enterprise.context.NonexistentConversationException;

或者是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() {	}}
CDI Conversation Filter
org.credo.scope.conversation.MyFilter
CDI Conversation Filter
/*

官方给的过滤器示例,我试了好几个小时完全行不通.查遍google表示没看到行得通相关的处理.有高手知道,请教下.

6.Lazy and eager conversation context initialization

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 上下文初始化的方式使用如下代码进行配置.

org.jboss.weld.context.conversation.lazy
true
如果这个参数没有去设置,会有下列情况:
  1. CDI Conversation Filter配置好,那么conversation context会在这个过滤器中进行即时加载.
  2. 如果在全局有cdi event事件的一个观察者observer来观察@Initialized(ConversationScoped.class) or @Destroyed(ConversationScoped.class) 事件,那么conversation context会即时加载.
  3. 此外,就都是会延迟加载.

7.The singleton pseudo-scope

除了四个内建的范围,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是一个单例如:

  1. 有singleton bean实现 writeResolve()和readReplace()---是java serialization规范定义的.
  2. 确保客户端仅仅保留一个瞬态singleton bean的引用.
  3. 在使用的时候用 @Inject Instance<X> instance;的方式来注入.
  4. 最好的解决方案是用@ApplicationScoped.
  5. 其实这个伪范围更多的用来避免使用默认伪范围@Dependent.

8.The dependent pseudo-scope

如果一个Bean未使用任何范围注解.(4内置+1 singleton pseudo-scope).
那么scope type就是@Dependent.如下代码
public class Calculator { ... }
他的scope就是@Dependent.
@Dependent的Bean严重依赖注入他的Bean.注入他的Bean是@RequestScoped,那么他就是@RequestScoped.注入他的Bean是@SessionScoped,那么他就是@SessionScoped.
注意:在JSF不要使用@Dependent范围的Bean的相关EL表达式. If you need to access a bean that really has to have the scope @Dependent from a JSF page, inject it into a different bean, and expose it to EL via a getter method.
@Dependent Bean 并不需要一个代理对象。客户端直接引用它的实例。
CDI可以很容易地得到一个@Dependent bean,即使该bean已经被声明为其他一些范围类型的Bean。

9.The @New qualifier

警告:
@New qualifier在CDI 1.1已被弃用.CDI建议使用@Dependent scoped.
@New可以使得我们能获取一个指定类的依赖对象.
@Inject @New Calculator calculator;
这个类必须是一个有效的Bean或者EJB Session Bean,但是不需要启用.
即使这个Bean已经申明了不同的范围类型.例如:
@ConversationScoped
public class Calculator { ... }
下面则是注入得到Calculator 的不同实例.
public class PaymentCalc {   @Inject Calculator calculator;   @Inject @New Calculator newCalculator;}

calculator;注入一个@ConversationScoped的实例。该newCalculator注入了新的实例,与被绑定到PaymentCalc,和PaymentCalc一个生命周期。

转载于:https://my.oschina.net/zhaoqian/blog/261100

你可能感兴趣的文章
MySQL MVCC && 事务隔离级别 && 锁
查看>>
[转载] 七龙珠第一部——第014话 竞争者来访
查看>>
手把手系列:(三)安装Oracle 12c数据库- linux
查看>>
WebView控件中的onConsoleMessage方法不被调用
查看>>
MyBatis学习总结(9)——使用MyBatis Generator自动创建代码
查看>>
MyBatis学习总结(八)——Mybatis3.x与Spring4.x整合
查看>>
了解SQL中 inner join、 left join 、right join、 outer join之间的区别
查看>>
app刚开始启动时(即:appdelegate文件中)可以写的几个功能
查看>>
MyBatis学习总结(1)——MyBatis快速入门
查看>>
Spring常用注解
查看>>
squid缓存代理服务基本配置
查看>>
Spring学习详解(1)——Spring入门详解
查看>>
自动安装linux系统(kickstart,pxe,tftp,dhcp,ftp)一
查看>>
ios系统相册中视频的获取和另存
查看>>
最近几天玩lxc的经历
查看>>
细谈软件插件式开发
查看>>
dedecms的ask缓存
查看>>
XSLFormatter应对大部头出版物的排版软件
查看>>
使用OpenSessionInViewFilter 的影响
查看>>
JSON解析【Fast Json】
查看>>