struts2iocstruts获取sessionn怎么设置bean

struts2中怎么获取session,jsp页面参数信息的值
时间: 14:02:04
struts2中action如何获取Session,jsp页面参数等等信息的值
1. ActionContext
在Struts2开发中,除了将请求参数自动设置到Action的字段中,我们往往也需要在Action里直接获取请求(Request)或会话(Session)的一些信息,甚至需要直接对JavaServlet Http的请求(HttpServletRequest),响应(HttpServletResponse)操作. 我们需要在Action中取得request请求参数&username&的值:
ActionContext context = ActionContext.getContext();
Map params = context.getParameters();
String username = (String) params.get(&username&);
on执行时的上下文,上下文可以看作是一个容器(其实我们这里的容器就是一个Map而已),它存放的是Action在执行时需要用到的对象. 一般情况, 我们的ActionContext都是通过: ActionContext context = (ActionContext) actionContext.get();来获取的.我们再来看看这里的actionContext对象的创建:
static ThreadLocal actionContext = new ActionContextThreadLocal();
ActionContextThreadLocal是实现ThreadLocal的一个内部类.ThreadLocal可以命名为&线程局部变量&,它为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突.这样,我们ActionContext里的属性只会在对应的当前请求线程中可见,从而保证它是线程安全的.
通过ActionContext取得HttpSession: Map session = ActionContext.getContext().getSession();
2. ServletActionContext
ServletActionContext(com.opensymphony.webwork. ServletActionContext),这个类直接继承了我们上面介绍的ActionContext,它提供了直接与Servlet相关对象访问的功能,它可以取得的对象有:
(1)javax.servlet.http.HttpServletRequest : HTTPservlet请求对象
(2)javax.servlet.http.HttpServletResponse : HTTPservlet相应对象
(3)javax.servlet.ServletContext : Servlet上下文信息
(4)javax.servlet.ServletConfig : Servlet配置对象
(5)javax.servlet.jsp.PageContext : Http页面上下文
如何从ServletActionContext里取得Servlet的相关对象:
&1&取得HttpServletRequest对象: HttpServletRequest request = ServletActionContext. getRequest();
&2&取得HttpSession对象: HttpSession session = ServletActionContext. getRequest().getSession();
3. ServletActionContext和ActionContext联系
ServletActionContext和ActionContext有着一些重复的功能,在我们的Action中,该如何去抉择呢?我们遵循的原则是:如果ActionContext能够实现我们的功能,那最好就不要使用ServletActionContext,让我们的Action尽量不要直接去访问Servlet的相关对象.
注意:在使用ActionContext时有一点要注意: 不要在Action的构造函数里使用ActionContext.getContext(),因为这个时候ActionContext里的一些值也许没有设置,这时通过ActionContext取得的值也许是null;同样,HttpServletRequest req = ServletActionContext.getRequest()也不要放在构造函数中,也不要直接将req作为类变量给其赋值。至于原因,我想是因为前面讲到的static ThreadLocal
actionContext = new ActionContextThreadLocal(),从这里我们可以看出ActionContext是线程安全的,而ServletActionContext继承自ActionContext,所以ServletActionContext也线程安全,线程安全要求每个线程都独立进行,所以req的创建也要求独立进行,所以ServletActionContext.getRequest()这句话不要放在构造函数中,也不要直接放在类中,而应该放在每个具体的方法体中(eg:login()、queryAll()、insert()等),这样才能保证每次产生对象时独立的建立了一个req。
4. struts2中获得request、response和session
(1)非IoC方式
方法一:使用org.apache.struts2.ActionContext类,通过它的静态方法getContext()获取当前Action的上下文对象。
ActionContext ctx = ActionContext.getContext();
ctx.put(&liuwei&, &andy&); //request.setAttribute(&liuwei&, &andy&);
Map session = ctx.getSession(); //session
HttpServletRequest request = ctx.get(org.apache.struts2.StrutsStatics.HTTP_REQUEST);
HttpServletResponse response = ctx.get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);
细心的朋友可以发现这里的session是个Map对象, 在Struts2中底层的session都被封装成了Map类型. 我们可以直接操作这个Map对象进行对session的写入和读取操作, 而不用去直接操作HttpSession对象.
方法二:使用org.apache.struts2.ServletActionContext类
public class UserAction extends ActionSupport {
&&& //其他代码片段
&&& private HttpServletR
// private HttpServletRequest req = ServletActionContext.getRequest(); 这条语句放在这个位置是错误的,同样把这条语句放在构造方法中也是错误的。
&&& public String login() {
&&&&&&& req = ServletActionContext.getRequest(); //req的获得必须在具体的方法中实现
&&&&&&& user = new User();
&&&&&&& user.setUid(uid);
&&&&&&& user.setPassword(password);
&&&&&&& if (userDAO.isLogin(user)) {
&&&&&&&&&&& req.getSession().setAttribute(&user&, user);
&&&&&&&&&&& return SUCCESS;
&&&&&&& return LOGIN;
&&& public String queryAll() {
&&&&&&& req = ServletActionContext.getRequest(); //req的获得必须在具体的方法中实现
&&&&&&& uList = userDAO.queryAll();
&&&&&&& req.getSession().setAttribute(&uList&, uList);
&&&&&&& return SUCCESS;
&&& //其他代码片段
(2)IoC方式(即使用Struts2 Aware拦截器)
要使用IoC方式,我们首先要告诉IoC容器(Container)想取得某个对象的意愿,通过实现相应的接口做到这点。
public class UserAction extends ActionSupport implements SessionAware, ServletRequestAware, ServletResponseAware {
&&& private HttpServletR
&&& private HttpServletR
&&& public void setServletRequest(HttpServletRequest request) {
&&&&&&& this.request =
&&& public void setServletResponse(HttpServletResponse response) {
&&&&&&& this.response =
&&& public String execute() {
&&&&&&& HttpSession session = request.getSession();
&&&&&&& return SUCCESS;
$T.total > 0 && $T.page <= $T.pageNum}
{#foreach $T.data as r}
{$T.r.formt_tm}{#if $T.r.nickname}{#else}匿名{#/if}
{$T.r.content}
{#if $T.page > 1 && $T.pageNum > 1)
$T.s_num > 2}
{#for index = $T.s_num to $T.e_num}
$T.pageNum > $T.pageNavSize+ 2 && $T.s_num != $T.pageNum - $T.pageNavSize}
{#if $T.pageNum > 1}
{#if $T.pageNum != $T.page && $T.pageNum > 1}
<a href="javascript:void(0);" page="{$T.page 下一页
您的回应...
也许你感兴趣
(C)2012 本站提供的内容来源于广大网络用户,我们不保证内容的正确性。如果转载了您的内容,希望删除的请联系我们!
SQLSTATE[HY000]: General error: 1114 The table 'tbl_session' is full二次元同好交流新大陆
扫码下载App
汇聚2000万达人的兴趣社区下载即送20张免费照片冲印
扫码下载App
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
在LOFTER的更多文章
loftPermalink:'',
id:'fks_',
blogTitle:'[转]助你全面攻破struts2笔试面试(一)',
blogAbstract:'摘要:&本文共22道题,包含struts2的工作流程、struts2工作原理、struts2的设计模型、拦截过滤器区别、struts2配置文件如何加载、struts2对action的管理、struts2如何完成文件的上出传、struts2的优缺点等内容,让你全面了解struts2,并帮助你顺利通过企业笔试面试。',
blogTag:'struts2,java',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:0,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}16196人阅读
singleton:SpringIoc容器只会创建该Bean的唯一实例,所有的请求和引用都只使用这个实例
Property: 每次请求都创建一个实例
request:&&& 在一次Http请求中,容器会返回该Bean的同一个实例,而对于不同的用户请求,会返回不同的实例。需要注意的是,该作用域仅在基于Web的Spring ApplicationContext情形下有效,以下的session和global Session也是如此
session:同上,唯一的区别是请求的作用域变为了session
global session:全局的HttpSession中,容器会返回该bean的同一个实例,典型为在是使用portlet context的时候有效(这个概念本人也不懂)
注意:如果要用到request,session,global session时需要配置
servlet2.4及以上:
在web.xml中添加:
&listener&
&&& &listener-class&org.springframework.web.context.scope.RequestContextListener /&
&/listener&
servlet2.4以下:
需要配置一个过滤器
&&& &filter-name&XXXX&/filter-name&
&&& &filter-class&org.springframework.web.filter.RequestContextFilter&/filter-class&
&filter-mapping&
&&& &filter-name&XXXX&/filter-name&
&&& &url-pattern&/*&/url-pattern&
&/filter-mapping&
另外,从2.0开始,可以自己定义作用域,但需要实现scope,并重写get和remove方法
特别要引起注意的是:
&& 一般情况下前面两种作用域是够用的,但如果有这样一种情况:singleton类型的bean引用一个prototype的bean时会出现问题,因为singleton只初始化一次,但prototype每请求一次都会有一个新的对象,但prototype类型的bean是singleton类型bean的一个属性,理所当然不可能有新prototpye的bean产生,与我们的要求不符
解决方法:
1.放弃Ioc,这与设计初衷不符,并代码间会有耦合
2,Lookup方法注入,推荐
但在用Lookup方法注入时也需要注意一点:需要在引用的Bean中定一个一个抽象地返回被引用对象的方法
package com.huyong.
import java.util.C
* @author &
public class CurrentTime {
private Calendar now = Calendar.getInstance();
public void printCurrentTime() {
System.out.println(&Current Time:& &#43; now.getTime());
package com.huyong.
* @author HuYong&
public abstract class LookupBean {
private CurrentTime currentT
public CurrentTime getCurrentTime() {
return currentT
public void setCurrentTime(CurrentTime currentTime) {
this.currentTime = currentT
public abstract CurrentTime createCurrentTime();
&?xml version=&1.0& encoding=&UTF-8&?&
&beans xmlns=&http://www.springframework.org/schema/beans&
xmlns:xsi=&http://www.w3.org/2001/XMLSchema-instance&
xsi:schemaLocation=&http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd&&
&bean id=&currentTime& class=&com.huyong.lookup.CurrentTime&
scope=&prototype&&
&bean id=&lookupBean& class=&com.huyong.lookup.LookupBean&
scope=&singleton&&
&lookup-method name=&createCurrentTime& bean=&currentTime& /&
&property name=&currentTime& ref=&currentTime&&&/property&
Main Test:
package com.huyong.
import org.springframework.beans.factory.BeanF
import org.springframework.beans.factory.xml.XmlBeanF
import org.springframework.core.io.ClassPathR
* @author HuYong Email:
public class LookupMain {
* @param args
* @throws Exception
public static void main(String[] args) throws Exception {
ClassPathResource resource = new ClassPathResource(
&applicationContext.xml&);
BeanFactory factory = new XmlBeanFactory(resource);
LookupBean lookupBean = (LookupBean) factory.getBean(&lookupBean&);
System.out.println(&----------first time---------&);
System.out.println(&getCurrentTime:&);
lookupBean.getCurrentTime().printCurrentTime();
System.out.println(&createCurrentTime:&);
lookupBean.createCurrentTime().printCurrentTime();
Thread.sleep(12345);
System.out.println(&---------second time---------&);
System.out.println(&getCurrentTime:&);
LookupBean lookupBean02 = (LookupBean) factory.getBean(&lookupBean&);
lookupBean02.getCurrentTime().printCurrentTime();
System.out.println(&createCurrentTime:&);
lookupBean02.createCurrentTime().printCurrentTime();
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:273560次
积分:1155
积分:1155
排名:千里之外
原创:20篇
评论:28条
(1)(1)(1)(1)(2)(6)(8)(2)(2)Spring MVC高速上手教程_struts2 详细配备_zookeeper学习记要三(session,watcher,persit机制)__脚本百事通
稍等,加载中……
^_^请注意,有可能下面的2篇文章才是您想要的内容:
Spring MVC高速上手教程
struts2 详细配备
zookeeper学习记要三(session,watcher,persit机制)
Spring MVC高速上手教程
Spring MVC快速上手教程
Spring Framework可以被使用在很多场合之中,考虑到目前大多数Java EE的项目是B/S结构的,所以这里的快速上手教程会以Spring MVC为切入点,用最简单的代码一步一步来实现一个图书列表的页面。在正式动手之前需要做一些准备工作,先安装并设置好JDK 1.5和Tomcat 5,关于数据库及其访问方式可以根据个人习惯进行选择,教程中使用MySQL数据库和Hibernate(映射由Hibernate Annotation实现)。请将实际使用到的jar文件复制到WEB-INF/lib目录中,整个项目的结构见图1,教程中用到的jar文件见图2。结构及用到的Jar文件项目中的Bean定义分散在多个XML文件中,每完成一部分代码就给出相应的配置,最后再进行整合和部署。配置中使用default-autowire="byName"实现了Bean的自动织入,节省了很多个工作量,只需注意Bean及属性的命名即可。Step 1.Business Objects & DAO教程中的例子涉及到两个实体对象,代表文章的Article类和代表作者的Author类,分别对应了数据库中的article表和author表,一篇文章有一个作者,而一个作者可以有多篇文章。类的代码如下(省略getter和setter):代码:Article.javapackage demo.import javax.persistence.*;@Entitypublic class Article ...{
@GeneratedValue
@ManyToOne
}代码:Author.javapackage demo.import java.util.Limport javax.persistence.*;@Entitypublic class Author ...{
@GeneratedValue
@OneToMany
private List&Article&
}在MySQL中创建数据表的SQL语句如下,数据请自行添加(如果使用Hibernate,表可以根据映射自动生成,具体做法请参考Hibernate文档):代码:数据库创建SQLCREATE DATABASE `articles` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_USECREATE TABLE `article` (
`id` bigint(20) NOT NULL auto_increment,
`title` varchar(100) NOT NULL default '',
`author_id` bigint(20) NOT NULL default '0',
PRIMARY KEY
(`id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8;CREATE TABLE `author` (
`id` bigint(20) NOT NULL auto_increment,
`name` varchar(100) NOT NULL default '',
PRIMARY KEY
(`id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8;考虑到可能会有多种DAO的实现,所以在DAO层先定义一个IArticleDao接口,随后可以自由选择具体的实现方式,此处结合Spring的HibernateDaoSupport使用Hibernate来进行实现:代码:IArticleDao.javapackage demo.import java.util.Limport demo.model.Apublic interface IArticleDao ...{
public List&Article& loadAllArticles();}代码:ArticleDao.javapackage demo.import java.util.Limport org.springframework.orm.hibernate3.support.HibernateDaoSimport demo.model.Apublic class ArticleDao extends HibernateDaoSupport implements IArticleDao ...{
@SuppressWarnings("unchecked")
public List&Article& loadAllArticles() ...{
return (List&Article&)getHibernateTemplate().loadAll(Article.class);
}}接下来对Hibernate进行相应的配置,如果使用了JDO或者iBatis,请参考Spring文档。applicationContext-dao.xml内容如下:代码:applicationContext-dao.xml&?xml version="1.0" encoding="UTF-8"?&&beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
default-autowire="byName"&
&!-- DAO配置于此 --&
&bean id="articleDao" class="demo.dao.ArticleDao"/&
&!-- 数据源 --&
&!-- JNDI数据源 --&
&bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"&
&property name="jndiName" value="${datasource.jndi.name}"/&
&!-- JDBC数据源 --&
&bean id="dataSource" class="mons.dbcp.BasicDataSource" destroy-method="close"&
&property name="driverClassName" value="${datasource.jdbc.driverClassName}" /&
&property name="url" value="${datasource.jdbc.url}" /&
&property name="username" value="${datasource.jdbc.username}" /&
&property name="password" value="${datasource.jdbc.password}" /&
&!-- 使用Annotation映射的sessionFactory --&
&bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"&
&property name="dataSource" ref="dataSource"/&
&property name="hibernateProperties"&
&prop key="hibernate.dialect"&${hibernate.dialect}&/prop&
&prop key="hibernate.show_sql"&${hibernate.show_sql}&/prop&
&prop key="hibernate.cache.use_query_cache"&${hibernate.cache.use_query_cache}&/prop&
&prop key="hibernate.cache.provider_class"&${hibernate.cache.provider_class}&/prop&
&/property&
&property name="annotatedClasses"&
&value&demo.model.Article&/value&
&value&demo.model.Author&/value&
&/property&
&!-- 事务管理器,此处为Hibernate的事务管理器 --&
&bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" /&
&/beans&此处如果使用JNDI提供数据源,请根据注释进行调整。Spring的事务管理需要声明事务管理器,由于Hibernate、JDO、JDBC的事务管理器都不一样,所以将其与其他事务的配置分开存放。此外,配置中的一些参数使用了占位符(形如${}),这些内容将在Step 4中进行加载。Step 2.ServiceService层只是调用DAO中的方法为控制器提供图书列表,Service最好能先给出接口,随后进行实现,但此处的功能比较简单,就直接进行实现了:代码:ArticleService.javapackage demo.import java.util.Limport demo.dao.IArticleDimport demo.model.Apublic class ArticleService ...{
private IArticleDao articleD
public List&Article& loadAllArticles() ...{
return articleDao.loadAllArticles();
public void setArticleDao(IArticleDao articleDao) ...{
this.articleDao = articleD
}}Spring通过setArticleDao方法为ArticleService注入DAO,也可以选择通过构造方法注入,2.5中还能用@Autowired进行注入。applicationContext-services.xml内容如下:代码:applicationContext-services.xml&?xml version="1.0" encoding="UTF-8"?&&beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
default-autowire="byName"&
&!-- Service配置于此 --&
&bean id="articleService" class="demo.service.ArticleService" /&&/beans&Step 3.Controller & ViewSpring MVC提供了多种实现控制器的方式,此处直接实现Controller接口,开发一个单一动作的简单控制器,从Service中取得图书列表,提供给视图进行呈现,ListArticleController内容如下:代码:ListArticleController.javapackage demo.import java.util.Limport javax.servlet.http.HttpServletRimport javax.servlet.http.HttpServletRimport org.springframework.web.servlet.ModelAndVimport org.springframework.web.servlet.mvc.Cimport demo.model.Aimport demo.service.ArticleSpublic class ListArticleController implements Controller ...{
private ArticleService articleS
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception ...{
List&Article& articles = articleService.loadAllArticles();
ModelAndView mav = new ModelAndView();
mav.addObject(articles);
public void setArticleService(ArticleService articleService) ...{
this.articleService = articleS
}}ModelAndView中保存了要传递给视图的对象和具体要使用的视图文件,自2.0起, Spring MVC提供了Convention over Configuration的机制,大大简化了代码与配置。简单地说,名字以Controller结尾的控制器类都会被映射为相应的地址,ListArticleController对应/listarticle*,如果是MultiActionController则会被映射为一个目录;向ModelAndView添加对象时可以不用指定键(key),单一对象的键取决于类名,比如x.y.User的键是user,而某一类对象的 Set、List或数组则稍有些复杂,取第一个对象的类名加上“List”作为它的键,比如这里的articles是一个存放Article对象的 List,它的键就是articleList;具体的视图会根据请求自动在指定目录中寻找对应的视图文件,本例中就会寻找listarticle(后缀由配置文件决定)。关于Convention over Configuration还有些别的细节,请参考Spring文档的相关章节。此处的视图比较简陋,只是一张表格,显示了图书的编号、书名和作者,使用JSTL的&c:forEach&标签来遍历列表,具体代码如下:代码:listarticle.jsp&%...@ page pageEncoding="UTF-8"%&&%...@ taglib prefix="c" uri="/jsp/jstl/core" %&&html&
&title&Article List&/title&
&table width="80%" cellspacing="0" cellpadding="0" border="1"&
&tr align="center"&
&td width="20%"&编号&/td&&td width="50%"&书名&/td&&td width="30%"&作者&/td&
&c:forEach items="${articleList}" var="article"&
&td align="center"&${article.id}&/td&
&td&${article.title}&/td&
&td&${article.author.name}&/td&
&/c:forEach&
&/body&&/html&为了使用Spring MVC,需要在web.xml中配置一个分派器,将一些特定格式的请求交给Spring MVC来处理(其实就是一个Servlet,这和Struts有些类似),如果它的名字是dispatcher,那么Spring默认会去寻找名为 dispatcher-servlet.xml的配置文件,该文件内容如下:代码:dispatcher-servlet.xml&?xml version="1.0" encoding="UTF-8"?&&beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"
default-autowire="byName"&
&!-- SpringMVC相关Bean配置 --&
&!-- View Resolver --&
&bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"&
&property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /&
&property name="prefix" value="/WEB-INF/view/" /&
&property name="suffix" value=".jsp" /&
&bean id="viewNameTranslator" class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/&
&bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/&
&!-- 以下为Controller --&
&bean id="listArticleController" class="demo.controller.ListArticleController" /&
&/beans&配置中的DefaultRequestToViewNameTranslator和 ControllerClassNameHandlerMapping就是用来实现Convention over Configuration的,而名为viewResolver的Bean则指定了一些视图的信息。Step 4.Configuration & Deployment至此,大部分的工作已经完成了,接下来就是加载properties文件和配置事务属性,这些都放在applicationContext.xml中:代码:applicationContext.xml&?xml version="1.0" encoding="UTF-8"?&&beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"&
&bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&
&property name="locations"&
&value&classpath:config.properties&/value&
&/property&
&!-- 事务 --&
&tx:advice id="txAdvice"&
&tx:attributes&
&tx:method name="get*" read-only="true" /&
&tx:method name="find*" read-only="true" /&
&tx:method name="load*" read-only="true" /&
&tx:method name="*" /&
&/tx:attributes&
&/tx:advice&
&aop:config proxy-target-class="true"&
&aop:advisor advice-ref="txAdvice" pointcut="execution(* demo.service..*.*(..))" /&
&/aop:config&&/beans&pointcut属性确定了AOP拦截的方法,用的是AspectJ pointcut expression,此处对demo.service中每一个类的所有方法都进行了拦截,也就是它们都在事务中执行。config.properties中保存了一些与数据库和Hibernate相关的配置信息,它们会代替XML中对应的占位符:代码:config.properties# DataSource# JNDI datasource Eg. java:comp/env/jdbc/mydsdatasource.jndi.name=# JDBC datasourcedatasource.jdbc.driverClassName=com.mysql.jdbc.Driverdatasource.jdbc.url=jdbc:mysql://localhost/articles?useUnicode=true&characterEncoding=utf8datasource.jdbc.username=rootdatasource.jdbc.password=# Hibernatehibernate.dialect=org.hibernate.dialect.MySQLDialecthibernate.show_sql=falsehibernate.cache.use_query_cache=truehibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider最后要看到的就是web.xml,每个Java EE的Web项目都会有这个配置文件,具体内容如下:代码:web.xml&?xml version="1.0" encoding="UTF-8"?&&web-app version="2.4"
xmlns="/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="/xml/ns/j2ee
/xml/ns/j2ee/web-app_2_4.xsd"&
&!-- Spring ApplicationContext配置文件的路径可使用通配符,多个路径用,号分隔,此参数用于后面的Spring-Context loader --&
&context-param&
&param-name&contextConfigLocation&/param-name&
&param-value&/WEB-INF/modules/applicationContext*.xml&/param-value&
&/context-param&
&!-- SpringMVC 分派器及相关映射 --&
&servlet-name&dispatcher&/servlet-name&
&servlet-class&org.springframework.web.servlet.DispatcherServlet&/servlet-class&
&load-on-startup&1&/load-on-startup&
&/servlet&
&servlet-mapping&
&servlet-name&dispatcher&/servlet-name&
&url-pattern&*.html&/url-pattern&
&/servlet-mapping&
&servlet-mapping&
&servlet-name&dispatcher&/servlet-name&
&url-pattern&*.do&/url-pattern&
&/servlet-mapping&
&!--Spring ApplicationContext 载入 --&
&listener&
&listener-class&org.springframework.web.context.ContextLoaderListener&/listener-class&
&/listener&
&!-- Spring 刷新Introspector防止内存泄露 --&
&listener&
&listener-class&org.springframework.web.util.IntrospectorCleanupListener&/listener-class&
&/listener&
&!-- 支持session scope的Spring bean --&
&listener&
&listener-class&org.springframework.web.context.request.RequestContextListener&/listener-class&
&/listener&
&!-- Character Encoding filter --&
&filter-name&setCharacterEncoding&/filter-name&
&filter-class&org.springframework.web.filter.CharacterEncodingFilter&/filter-class&
&init-param&
&param-name&encoding&/param-name&
&param-value&UTF-8&/param-value&
&/init-param&
&filter-mapping&
&filter-name&setCharacterEncoding&/filter-name&
&url-pattern&/*&/url-pattern&
&/filter-mapping&
&!--Hibernate Open Session in View Filter--&
&filter-name&hibernateFilter&/filter-name&
&filter-class&org.springframework.orm.hibernate3.support.OpenSessionInViewFilter&/filter-class&
&filter-mapping&
&filter-name&hibernateFilter&/filter-name&
&url-pattern&/*&/url-pattern&
&/filter-mapping&&/web-app&此处加载了Spring的配置文件,并对字符编码进行了处理,*.do和*.html的请求都转交给了Spring MVC的分派器。OpenSessionInViewFilter是用来解决Hibernate的OpenSessionInView问题的,如果没有使用Hibernate则无需配置此过滤器。项目的部署和一般的Web项目没有任何区别,将项目打成War包或者直接将目录放到Tomcat的webapps中即可。假设目录的名字是SpringDemo,启动Tomcat后访问http://localhost:8080/SpringDemo/listarticle.html就能看到页面的效果了。
struts2 详细配备
struts2 详细配置
Struts2的配置文件是以XML的形式出现的。不过它的XML的语义比较简单,下面是我抽取了位于struts2-core-2.0.14.jar内部的struts-default.xml的片段:   Xml代码   &struts&
&bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" /&
&bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" /&
&bean type="com.opensymphony.xwork2.ActionProxyFactory" name="xwork" class="com.opensymphony.xwork2.DefaultActionProxyFactory"/&
&bean type="com.opensymphony.xwork2.ActionProxyFactory" name="struts" class="org.apache.struts2.impl.StrutsActionProxyFactory"/&
&!-- 省略了其他的bean节点的定义 --&
Only have static injections --&
&bean class="com.opensymphony.xwork2.ObjectFactory" static="true" /&
&bean class="com.opensymphony.xwork2.util.XWorkConverter" static="true" /&
&!-- 省略了其他的静态注入的定义 --&
&package name="struts-default" abstract="true"&
&result-types&
&result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/&
&result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/&
&result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/&
&!-- 省略了其他的ResultType的定义 --&
&/result-types&
&interceptors&
&interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/&
&interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/&
&!-- 省略了其他的Interceptor的定义 --&
&!-- Basic stack --&
&interceptor-stack name="basicStack"&
&interceptor-ref name="exception"/&
&interceptor-ref name="servletConfig"/&
&interceptor-ref name="prepare"/&
&interceptor-ref name="checkbox"/&
&interceptor-ref name="params"/&
&interceptor-ref name="conversionError"/&
&/interceptor-stack&
&!-- A complete stack with all the common interceptors in place.
Generally, this stack should be the one you use, though it
may do more than you need. Also, the ordering can be
switched around (ex: if you wish to have your servlet-related
objects applied before prepare() is called, you'd need to move
servlet-config interceptor up.
This stack also excludes from the normal validation and workflow
the method names input, back, and cancel. These typically are
associated with requests that should not be validated.
&interceptor-stack name="defaultStack"&
&interceptor-ref name="exception"/&
&interceptor-ref name="alias"/&
&interceptor-ref name="servletConfig"/&
&interceptor-ref name="prepare"/&
&interceptor-ref name="i18n"/&
&interceptor-ref name="chain"/&
&interceptor-ref name="debugging"/&
&interceptor-ref name="profiling"/&
&interceptor-ref name="scopedModelDriven"/&
&interceptor-ref name="modelDriven"/&
&interceptor-ref name="fileUpload"/&
&interceptor-ref name="checkbox"/&
&interceptor-ref name="staticParams"/&
&interceptor-ref name="params"&
&param name="excludeParams"&dojo..*&/param&
&/interceptor-ref&
&interceptor-ref name="conversionError"/&
&interceptor-ref name="validation"&
&param name="excludeMethods"&input,back,cancel,browse&/param&
&/interceptor-ref&
&interceptor-ref name="workflow"&
&param name="excludeMethods"&input,back,cancel,browse&/param&
&/interceptor-ref&
&/interceptor-stack&
&!-- 省略了其他的interceptor-stack节点的定义 --&
&/interceptors&
&default-interceptor-ref name="defaultStack"/&
&/package&&/struts&在这个配置文件中,我们可以看到,Struts2的XML自身所支持的节点和子节点并不是很多,大致来说,这些节点可以分成基本配置定义和Runtime配置定义。   基本配置定义   基本配置定义,主要是针对在Struts2内部所使用的各种元素的声明。这些声明往往规定了Struts2内部的一些行为特征。   例如,配置文件中的&bean&节点,被用于定义Struts2中所使用的接口和实现类,通过Struts2内部实现的IoC,你就可以在不同的实现类之间进行切换。   再例如,配置文件中的&result-type&节点和&interceptor&节点。他们用于定义Struts2中所支持的所有的Result类型和拦截器,这些定义和声明,将在Runtime的配置定义中被引用。   我之所以把配置文件中的这些节点单独列出来,作为一个种类,是因为这些节点是不可省略的,也是无法简化的。所以,如果我们试图在Struts2中简化配置,我们就需要在Runtime配置定义中下功夫,而这些基本配置定义,我们可以认为是Runtime配置定义的基础。   Runtime配置定义   Runtime配置定义,主要指的的是对Struts2运行过程中,具体的某个Action的行为的指定。这些指定主要通过&package&节点中的&action&节点来完成。   仔细翻阅&action&节点,我们可以发现,它是URL与Action之间沟通的桥梁,也就是说,它定义了URL与Action之间的对应关系。同时,它还指定了Action在执行过程中的具体行为,包括Action执行的时候使用什么样的拦截器、Action执行完毕后,转向到什么样的Result等等。   Runtime配置定义是可以简化的,Struts2中提供了很多种简化配置的方式,这个在之后的文章中会详细提到。   模块化管理配置文件一旦项目变得很大,项目中同时也并不采取什么简化配置的措施,那么在默认情况下,配置文件就会变得很大而不易于维护。这个时候,对于配置文件的模块化管理的需求就显现出来。Struts2提供了两种方式对配置文件进行模块化管理。   plugin机制   Struts2有plugin的机制,有关plugin的具体的知识,请参考我的另外一篇专栏文章:《深入plugin》 ——  /wiki/struts2/1333-deep-into-plugin。在这里,我也就不详细介绍了。   在每个plugin中,都会有一个叫做struts-plugin.xml的配置文件,这个配置文件的格式与struts-default.xml的格式是相同的。可以在其中做出任何的Struts2的定义和配置。我们知道,Struts2的配置文件的加载顺序,是按照以下的顺序来:   Struts2 Referece 写道1. struts-default.xml (bundled in the Core JAR) 2. struts-plugin.xml (as many as can be found in other JARs) 3. struts.xml (provided by your application)  所以,struts-plugin.xml中的配置的效果实际上与struts-default.xml的效果是相同的。这样,通过各种各样不同的plugin,就等于将Struts2的配置,按照plugin的功能不同而分开了。从而起到了配置文件模块化管理的效果。   使用include节点   plugin中的配置文件,实际上是位于classpath的JAR包中的,那么我们在项目中,如何对一个庞大的配置文件进行拆分呢?在Struts2中,可以使用include节点对所有的Struts2配置文件进行拆分和模块化管理。例如:   Xml代码  &struts&
&include file="struts-default.xml"/&
&include file="web/struts-config.xml"/&
&include file="web/struts-action.xml"/&&/struts&其中,file所指定的文件是相对于classpath的相对目录中的文件。而每个配置文件的格式与struts-default.xml的格式也是相同的。   通过include节点,我们就可以对一个比较大的配置文件按照功能和模块进行拆分,这在一个大型的团队开发中,是相当有意义的。  简单的IoC  在基本配置定义中,有两个很常用的节点:&bean&和&constant&。在系统启动的时候,Struts2会根据配置文件中这些&bean&和&constant&节点的定义进行加载,并初始化成为Struts2的默认行为。这种初始化的行为,非常类似于Spring中的依赖注入(IoC),从而使得你不再需要担心这些对象在运行时的创建和销毁,所有的工作都由Struts2内部的机制实现。接下来我们就来看看Struts2是如何实现IoC的。   Struts2 Reference 写道  Internally, the framework uses its own dependency injection container that is very similar to Google Guice (both were originally developed by Bob Lee)  这是来自于Struts2的Reference对它自身的IoC的描述。如果熟悉Guice的朋友一定知道,Guice的实现使用了Annotation的方式进行,而整个依赖注入的实现,是通过一个内部的容器类进行的。Struts2的依赖注入,与Guice的机制完全一致。根据注入的内容的不同,Struts2的IoC可以对容器中的对象的依赖关系进行管理,也可以注入一些静态变量。   bean注入   对于bean的注入,对应于XML中的bean的节点声明。我把其中的机制分成了3个部分:   1. 容器中对象的声明   Xml代码 &bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" /& &bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" /&这点没什么好说的,在struts.xml中,你可以为某个接口声明它所对应的实现类。   name属性   你可以声明多个实现类,使用name属性进行区分。在注入的时候,将使用这个属性的值作为接口实现类的选择。   required属性   你还可以通过required属性,来指定是否在运行时必不可少的注入。如果reqired被设置成false,那么当不存在相应的接口定义时,注入将被忽略。   static属性   在XML的定义中,还可以使用static属性。如果static属性被设置成true,那么注入将针对bean中的static方法和static属性进行。   2. 在代码中使用Annotation进行注入   Java代码 @Inject("xwork")protected ObjectFactory objectFpublic LightURLUnknownHandler(@Inject ObjectFactory objectFactory) {
this.objectFactory = objectF}@Injectpublic void setObjectFactory(ObjectFactory factory) {
this.objectFactory =}@Inject(required=false)public void setUnknownHandler(UnknownHandler handler) {
this.unknownHandler =}  在代码中,使用@Inject这样一个Annotation进行对象依赖注入。在上面的例子中,我们可以看到,@Inject这个Annotation,可以作用在属性上,也可以作用在方法上,甚至可以作用在方法的参数上。   在默认情况下,如果@Inject不指定value,那么XML配置定义中的name="default"或者name=""的实现类定义将被注入。   那么,在struts-default.xml中,Struts2到底选择了那些实现类,作为Struts2或者XWork内部接口的默认实现类呢?默认情况下,struts-default.xml中定义的bean的name="struts"的将被作为默认的接口实现类被注入。这些默认行为,是由org.apache.struts2.config.BeanSelectionProvider所决定的,有兴趣的读者可以参阅这个类的源码。3. 内部的Container机制完成一切背后工作   上面看到的,是现象。在内部,Struts2通过一个Container来实现所有的注入机制。   Java代码   public interface Container extends Serializable {
* Default dependency name.
String DEFAULT_NAME = "default";
* Injects dependencies into the fields and methods of an existing object.
void inject(Object o);
* Creates and injects a new instance of type {@code implementation}.
&T& T inject(Class&T& implementation);
* Gets an instance of the given dependency which was declared in
* {@link com.opensymphony.xwork2.inject.ContainerBuilder}.
&T& T getInstance(Class&T& type, String name);
* Convenience method.&Equivalent to {@code getInstance(type,
* DEFAULT_NAME)}.
&T& T getInstance(Class&T& type);
* Gets a set of all registered names for the given type
* @param type The instance type
* @return A set of registered names
Set&String& getInstanceNames(Class&?& type);
* Sets the scope strategy for the current thread.
void setScopeStrategy(Scope.Strategy scopeStrategy);
* Removes the scope strategy for the current thread.
void removeScopeStrategy();}在系统启动的时候,这个Container的实现类就会工作,把XML中定义的内容进行注入。有兴趣的读者可以继续探寻这个接口的实现类:com.opensymphony.xwork2.inject.ContainerImpl。   静态变量(Constant)的注入   @Inject这个Annotation不仅能够对接口的实现类进行注入,也能够对静态变量进行注入。   有关静态变量的声明和注入,在我的另外一篇专栏文章中已经详细阐述:《深入plugin》 ——  /wiki/struts2/1333-deep-into-plugin。在这里,我也就不详细介绍了。  package节点详解  package节点是整个配置的核心部分。每个package,从语义上讲,其实代表了每一个独立的模块。在这个模块中,你可以定义隶属于这个模块的行为方式,而与其他的模块没有关系。所以,每个package都有独立的interceptor、result-type和action的定义,绝大多数的Runtime配置定义都是通过package节点实现的。接下来我们就来详细讨论一下package中的属性和子节点。   基本属性   1. name   name属性为每个package设置一个唯一的标识,这个标识在所有的package定义中不能重复。   2. abstract   标识这个package的定义是一个抽象定义,也就是允许他仅包含声明式的定义,而不需要在package定义中包含action的定义。   3. extends   通过使用extends,你可以指定本package继承另外一个package的所有的配置。当某个package继承了另外一个package的所有配置,那么你就无需对父package中已经声明过的配置定义做再次的定义。   同时,如果重复定义父package中已声明过的配置定义,那么这些重复定义声明将覆盖父package中的相关定义。 4. namespace   Struts2 Reference 写道  The namespace attribute subdivides action configurations into logical modules, each with its own identifying prefix. Namespaces avoid conflicts between action names. Each namespace can have its own "menu" or "help" action, each with its own implementation.  这段来自Struts2的Reference的引用,基本上阐明了namespace的作用:对于action配置进行逻辑划分。   如果我们不为package节点指定namespace,Struts2默认使用一个空字符串作为默认的namespace。当然,也可以使用"/"等字符串来表示namespace。   Struts2在根据URL进行寻址的时候,使用以下的步骤:   1) 根据URL进行Namespace和ActionName的计算   2) 根据计算的得到的Namespace和ActionName查找package节点中相应配置   3) 如果查找失败,则查找Namespace为空,ActionName为整个URL的配置   有关上述3点的详细信息,请参考Struts2的Reference:http://struts.apache.org/2.0.14/docs/namespace-configuration.html   result-types节点   在result-types节点中,我们可以声明在本package中所支持的Result类型。这些Result类型,将在action节点中被引用到。   interceptors节点   在interceptors节点中有两类节点:&interceptor&和&interceptor-stack&。这两个节点都用于声明拦截器。前者的作用,是真正定义一个拦截器。而后者则通过引用已经定义的拦截器,指定他们的执行顺序。   当我们在试图在Action中引用拦截器时,我们实际上是为某个Action指定需要执行哪些拦截器,并且为这些拦截器指定执行顺序。所以Action所引用的,是某个&interceptor-stack&中的定义。   缺省配置指向   为了简化配置,我们可以在package节点中指定本package内的缺省配置指向。这可以通过&default-interceptor-ref&、&default-action-ref&、&global-results&等子节点来完成。   action节点   action节点是所有的Runtime配置的核心内容。它的主要作用就是指定URL与Action之间的映射关系。同时,在action节点中你也可以指定action执行时的相关配置,例如action所引用的interceptor等。   参考文档  上面所有的内容,实际上我只是做了一些简单的概括和归纳,至于每个节点语义和每个节点中具体属性的使用,我认为还是需要参考Struts2的Reference,因为Reference的讲解比任何教程都来的详细和正确,所以希望大家在了解了这些配置的基本分类之后,重新阅读Struts2的Reference的相关章节,从而更加深刻的理解Struts2配置文件的方方面面:http://struts.apache.org/2.0.14/docs/configuration-elements.html
zookeeper学习记要三(session,watcher,persit机制)
zookeeper学习记录三(session,watcher,persit机制)背景
继续前面的zookeeper学习的专题,这次主要是结合项目中遇到的一些问题,进一步学习了下zookeeper的一些内部机制。
针对以下几个问题:
1. zk是否可以保证watcher事件不丢失?
2. zk的EPHEMERAL节点的自动过期时间?
3. zk的如何保证节点数据不丢失?
如果你已经非常清楚这以上的几个问题,看官们可以不用往下看了。
persit机制
zookeeper中的persit机制主要是通过本地disk进行持久化,在本地disk上会有个memory数据对象保持同步。
持久化实现:
ZKDatabase
DataTree (内存树)
FileTxnSnapLog (disk持久化)
committedLog (FileTxnSnapLog的一份内存数据cache,默认存储500条变更记录)
DataTree(内存树)
zookeeper本身的数据结构就是一个树结构
数据模型(DataTree):
DataNode (1:n)
data WatchManager (1:1,处理node节点的CRUD的变更事件,发送Watcher事件)
child WatchManager (1:1,
处理node子节点的变更事件,发送Watcher事件)
sessions (ephemerals)
DataNode模型:
data byte[]
stat(审计信息)
整个实现相对比较简单,就是查找一个树节点后进行响应的操作
FileTxnSnapLog (disk持久化)
持久化数据分两类:
TxnLog (类似于mysql/oracle的binlog/redolog)
SnapShot (DataTree的数据镜像)
刚开始最容易搞不清楚就是Txnlog和SnapShot的区别,SnapShot主要是定期对DataTree的数据做一个本地备份,TxnLog只是一些历史的版本变更日志(每次由写事件变化,就会写入到该日志中)。
下面看一下,zookeeper在新节点启动后,是否如何保证数据一致:
首先节点启动后,尝试读取本地的SnapShot log数据(zkDb.loadDataBase()),反序列化为DataTree对象,并获取last zxid。
follower启动后会向leader发送自己的last zxid
leader收到zxid后,对比自己当前的ZKDatabase中的last zxid如果当前follower的zxid在内存committedLog中,直接将内存中的committedLog提取出来进行发送,否则将当前的DataTree直接发送给follower.(不再是发送变更记录)
数据同步完成后,follower会开始接收request请求
一致性机制
整个zk集群在处理数据变更过程中,会是先append变更信息到Txnlog中(此时会触发take snap操作),最后在FinalRequestProcessor中更新内存中的DataTree信息。
触发take snap的条件:
if (logCount & (snapCount / 2 + randRoll)) {
randRoll = r.nextInt(snapCount/2);
snapCount可以通过jvm参数zookeeper.snapCount指定,默认为100000。 这里zookeeper很巧妙的加两个随机处理,避免zk机器在同一时间点进行take snap处理,影响性能。
session机制
zookeeper会为每个client分配一个session,类似于web服务器一样。针对session可以有保存一些关联数据,zookeeper里针对session的一些关联数据主要就是EPHEMERAL节点。
EPHEMERAL的翻译为短命的,技术上理解就是session关闭后,其节点即消失,和session保持相同的生命周期。
创建EPHEMERAL节点:
zookeeper.create(path, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
我们能用EPHEMERAL节点做啥?
分布式集群是否存活监控. (每个节点启动后,注册一个EPHEMERAL节点到zookeeper中,注册一个Watcher获取EPHEMERAL节点的存在情况,消失即可代表集群节点dead)
分布式lock (每个锁竞争者,排队时都在zookeeper中注册一个EPHEMERAL节点,排队过程中有节点dead了,zookeeper可以自动将其剔除队列,避免出现deadlock)
替代web服务器的session,实现一个集中式session, session数据中的setAttribute都创建为EPHEMERAL节点,session关闭后即可自动删除,不会造成java中的"内存泄漏"
不扯远,下面看一下zookeeper中是如何处理session的管理? 因为用上了zookeeper,必然要考虑分布式常见的问题
zookeeper server挂了,对应的session是否会丢失?
zookeeper client发生了failover后(出现了Connection Loss异常),对应的session是否会丢失?
在服务端,zookeeper中session的存储是有进行持久化的, 具体可见perist机制的描述。 一个新节点启动后,会从leader中同步对应的session数据
在客户端,zookeeper在每次出现failover后(出现了Connection Loss异常),会重新带上sessionId,sessionPasswd发起一次链接请求。接收到该请求的server,会返回内存中的session信息
所以,session是有可靠性保证的。
session expired机制
zookeeper中session expired机制和node数据一致性的保证原理类似,对应的follower都是受控于leader。
follower接收到客户端链接请求,就会向leader发送一次createSession的操作请求,leader收到后进行广播通知给所有的follower/observer节点createSession
leader会通过内存版的(SessionTrackerImpl),定期扫描过期的session,发送一次closeSession的请求给所有的客户端
在2发送过程中,如果有follower接收到过期session的请求,会提交给leader进行仲裁,leader会直接返回session expired。
session expired几个参数:
服务端: minSessionTimeout (默认值为:tickTime * 2) , maxSessionTimeout (默认值为 : tickTime * 20) , ticktime的默认值为3000ms。所以session范围为6s ~ 60s
客户端: sessionTimeout, 无默认值,创建实例时必填。
一个误区: 很多人会按照hadoop文档中的建议,创建zookeeper客户端时设置了sessionTimeout为90s,而没有改变server端的配置,默认是不会生效的。
原因: 客户端的zookeeper实例在创建连接时,将sessionTimeout参数发送给了服务端,服务端会根据对应的minSessionTimeout/maxSessionTimeout的设置,强制修改sessionTimeout参数,也就是修改为6s~60s返回的参数。所以服务端不一定会以客户端的sessionTImeout做为session expire管理的时间。
int minSessionTimeout = zk.getMinSessionTimeout();
if (sessionTimeout & minSessionTimeout) {
sessionTimeout = minSessionT
int maxSessionTimeout = zk.getMaxSessionTimeout();
if (sessionTimeout & maxSessionTimeout) {
sessionTimeout = maxSessionT
Watcher机制
watcher是zookeeper实现分布式lock一个很重要的feature,在写分布式lock时一定要对其有所了解。
就会冒出如下问题:
什么情况下,会触发什么类型的watcher?
watcher信息出现failover是否会丢失?
watcher信息出现session expired是否会丢失?
针对第一个问题,需要在使用中详细阅读下对应Zookeeper类中方法的javadoc,注意几个点:
exists方法: 设置watcher时,如果对应服务端已经不存在node时,watcher是不会留在服务端,下次不会被触发。针对这种情况需要判断返回的stat == null来进行处理
getChildren方法: 和exist一样,需要处理节点不存在时watcher不会被记录。 还有一个点,当前的父node发生delete变化时,也可以得到触发
getData方法: 和exist一样,需要处理节点不存在时watcher不会被记录
要了解watcher是否会丢失,必须要清楚zookeeper整套watcher机制的实现:
Watcher是一个本地jvm的callback,在和服务端交互过程中是不会进行传递的。只是会将是否有watcher的boolean变量传递给server端
在服务端,在FinalRequestProcessor处理对应的node操作时,会根据客户端传递的watcher变量,添加到对应的zkDataBase中进行持久化存储,同时将自己NIOServerCnxn做为一个Watcher callback,监听服务端事件变化
leader通过投票通过了某次node变化请求后,通知给对应的follower,follower根据自己内存中的zkDataBase信息,发送notification信息给zookeeper 客户端
zookeeper客户端接收到notification信息后,找到对应变化path的watcher列表,挨个进行触发回调。
整个流程图:
可能存在的问题:
1. client向连接的server提交了watcher事件后,对应的server还未来得及提交给leader就直接出现了jvm crash,这时对应的watcher事件会丢失。(理论上正常关闭zookeeper server,不会存在该问题,需要客户端进行重试处理)
2. client在发生一次failover时,可以自动对新的server的notification使用watcher set,可以通过设置jvm变量:zookeeper.disableAutoWatchReset进行,默认为false。如果为true,则不进行自动使用的行为)
3. client出现session expired时,需要重新创建一个zookeeper client实例,此时对应的watcher set也会丢失,需要自己编码做一些额外的处理
zookeeper异常处理
官方文档:http://wiki.apache. org/hadoop/ZooKeeper/FAQ
主要处理两个系统异常:
KeeperException.ConnectionLossException (client与其中的一台server socket链接出现异常)
KeeperException.SessionExpiredException (client的session超过sessionTimeout为进行任何操作)
ConnectionLossException可以通过重试进行处理,在ClientCnxn会根据你初始化ZooKeeper时传递的服务列表,自动尝试下一个server节点
SessionExpiredException不能通过重试进行解决,需要应用重新new Zookeeper(),创建一个新的客户端,包括重新初始化对应的watcher,EPHEMERAL节点等。
思路可能写的有点乱,文中所有的内容均通过阅读源码所得,如有不对的地方,尽情拍砖。
如果您想提高自己的技术水平,欢迎加入本站官方1号QQ群:&&,&&2号QQ群:,在群里结识技术精英和交流技术^_^
本站联系邮箱:}

我要回帖

更多关于 struts1获取session 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信