回话记得有session,以及集中模式介绍

对request.getSession(false)的理解(附程序员常疏忽的一个漏洞)
【前面的话】

在网上经常看到有人对request.getSession(false)提出疑问,我第一次也很迷惑,看了一下J2EE1.3 API,看一下官网是怎么解释的。

【官方解释】

  getSession 

public HttpSessiongetSession(boolean create)

Returns the current HttpSession associated with this request or, if if there is no current session and create is true, returns a new session.

If create is false and the request has no valid HttpSession, this method returns null.

To make sure the session is properly maintained, you must call this method before the response is committed. If the container is using cookies to maintain session integrity and is asked to create a new session when the response is committed, an IllegalStateException is thrown.

Parameters:true - to create a new session for this request if necessary; false to return null if there's no current session

Returns: the HttpSession associated with this request or null if create is false and the request has no valid session

译:

getSession(boolean create)意思是返回当前reqeust中的HttpSession ,如果当前reqeust中的HttpSession 为null,当create为true,就创建一个新的Session,否则返回null;

简而言之:

HttpServletRequest.getSession(ture) 等同于 HttpServletRequest.getSession()

HttpServletRequest.getSession(false) 等同于 如果当前Session没有就为null;

【问题和bug】:

我周围很多同事是这样写的;

HttpSession session = request.getSession();   // a new session created if no session exists, 哈哈!完蛋啦!如果session不存在的话你又创建了一个!  String user_name = session.getAttribute("user_name"); 
HttpSession session = request.getSession(); // a new session created if no session exists, 哈哈!完蛋啦!如果session不存在的话你又创建了一个! String user_name = session.getAttribute("user_name");
需要注意的地方是request.getSession() 等同于 request.getSession(true),除非我们确认session一定存在或者sesson不存在时明确有创建session的需要,否则尽量使用request.getSession(false)。在使用request.getSession()函数,通常在action中检查是否有某个变量/标记存放在session中。这个场景中可能出现没有session存在的情况,正常的判断应该是这样:

HttpSession session = request.getSession(false);  if (session != null) {      String user_name = session.getAttribute("user_name");  } 
HttpSession session = request.getSession(false); if (session != null) { String user_name = session.getAttribute("user_name"); }

【投机取巧】:

如果项目中用到了Spring(其实只要是Java的稍大的项目,Spring是一个很好的选择),对session的操作就方便多了。如果需要在 Session中取值,可以用WebUtils工具(org.springframework.web.util.WebUtils)的 getSessionAttribute(HttpServletRequest request, String name)方法,看看高手写的源码吧:哈哈。。

/**  * Check the given request for a session attribute of the given name.  * Returns null if there is no session or if the session has no such attribute.  * Does not create a new session if none has existed before!  * @param request current HTTP request  * @param name the name of the session attribute  * @return the value of the session attribute, or <code>null</code> if not found  */  public static Object getSessionAttribute(HttpServletRequest request, String name) {      Assert.notNull(request, "Request must not be null");      HttpSession session = request.getSession(false);      return (session != null ? session.getAttribute(name) : null);  } 
/** * Check the given request for a session attribute of the given name. * Returns null if there is no session or if the session has no such attribute. * Does not create a new session if none has existed before! * @param request current HTTP request * @param name the name of the session attribute * @return the value of the session attribute, or <code>null</code> if not found */ public static Object getSessionAttribute(HttpServletRequest request, String name) { Assert.notNull(request, "Request must not be null"); HttpSession session = request.getSession(false); return (session != null ? session.getAttribute(name) : null); }
注:Assert是Spring工具包中的一个工具,用来判断一些验证操作,本例中用来判断reqeust是否为空,若为空就抛异常。

上面的代码又可以简洁一下啦,看吧:

HttpSession session = request.getSession(false);  String user_name = WebUtils.getSessionAttribute(reqeust, "user_name"); 



2.9.8 session对象

session对象也是一个非常常用的对象,这个对象代表一次用户会话。一次用户会话的含义是:从客户端浏览器连接服务器开始,到客户端浏览器与服务器断开为止,这个过程就是一次会话。

session通常用于跟踪用户的会话信息,如判断用户是否登录系统,或者在购物车应用中,用于跟踪用户购买的商品等。

session范围内的属性可以在多个页面的跳转之间共享。一旦关闭浏览器,即session结束,session范围内的属性将全部丢失。

session对象是HttpSession的实例,HttpSession有如下两个常用的方法:

setAttribute(String attName , Object attValue):设置session范围内attName属性的值为attValue。

getAttribute(String attName):返回session范围内attName属性的值。

下面的示例演示了一个购物车应用,以下是陈列商品的JSP页面代码。

程序清单:codes\02\2.9\jspObject\shop.jsp

<%@ page contentType="text/html; charset=gb2312" language="java" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>选择物品购买</TITLE>
</HEAD>
<BODY>
<FORM METHOD=POST ACTION="processBuy.jsp">
书籍:<INPUT TYPE="checkbox" NAME="item" value="book"><br>
电脑:<INPUT TYPE="checkbox" NAME="item" value="computer"><br>
汽车:<INPUT TYPE="checkbox" NAME="item" value="car"><br>
<INPUT TYPE="submit" value="购买">
</FORM>
</BODY>
</HTML>

这个页面几乎没有动态的JSP部分,全部是静态的HTML内容。该页面包含一个表单,表单里包含3个复选按钮,用于选择想购买的物品,表单由processBuy.jsp页面处理,其页面的代码如下:

程序清单:codes\02\2.9\jspObject\processBuy.jsp

<%@ page contentType="text/html; charset=gb2312" language="java" import="java.
util.*"%>
<%
//取出session范围的itemMap属性
Map<String,Integer> itemMap = (Map<String,Integer>)session
.getAttribute("itemMap");
//如果Map对象为空,则初始化Map对象
if (itemMap == null)
{
itemMap = new HashMap<String,Integer>();
itemMap.put("书籍" , 0);
itemMap.put("电脑" , 0);
itemMap.put("汽车" , 0);
}
//获取上个页面的请求参数
String[] buys = request.getParameterValues("item");
//遍历数组的各元素
for (String item : buys)
{
//如果item为book,表示选择购买书籍
if(item.equals("book"))
{
int num1 = itemMap.get("书籍").intValue();
//将书籍key对应的数量加1
itemMap.put("书籍" , num1 + 1);
}
//如果item为computer,表示选择购买电脑
else if (item.equals("computer"))
{
int num2 = itemMap.get("电脑").intValue();
//将电脑key对应的数量加1
itemMap.put("电脑" , num2 + 1);
}
//如果item为car,表示选择购买汽车
else if (item.equals("car"))
{
int num3 = itemMap.get("汽车").intValue();
//将汽车key对应的数量加1
itemMap.put("汽车" , num3 + 1);
}
}
//将itemMap对象放到设置成session范围的itemMap属性
session.setAttribute("itemMap" , itemMap);
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>购买的物品列表</TITLE>
</HEAD>
<BODY>
您所购买的物品:<br>
书籍:<%=itemMap.get("书籍")%>本<br>
电脑:<%=itemMap.get("电脑")%>台<br>
汽车:<%=itemMap.get("汽车")%>辆<p>
<a href="shop.jsp">再次购买</a>
</BODY>
</HTML>

上面页面中粗体字代码使用session来保证itemMap对象在一次会话中有效,这使得该购物车系统可以反复购买,只要浏览器不关闭,购买的物品信息就不会丢失,图2.31显示的是多次购买后的效果。

 
图2.31  利用session记录购物车信息

考虑session本身的目的,通常只应该把与用户会话状态相关的信息放入session范围内。不要仅仅为了2个页面之间交换信息,就将该信息放入session范围内。如果仅仅为了2个页面交换信息,可以将该信息放入request范围内,然后forward请求即可。

关于session还有一点需要指出,session机制通常用于保存客户端的状态信息,这些状态信息需要保存到Web服务器的硬盘上,所以要求session里的属性值必须是可序列化的,否则将会引发不可序列化的异常。

session的属性值可以是任何可序列化的Java对象。



9.3.1 单例模式

有些时候,允许自由创建某个类的实例没有意义,还可能造成系统性能下降(因为创建对象所带来的系统开销问题)。例如整个系统只有一个窗口管理器,只有一个假脱机打印设备;在Java EE应用中可能只需要一个数据库引擎访问点,Hibernate访问时只需要一个SessionFactory实例,如果在系统中为它们创建多个实例就没有太大的意义。

如果一个类始终只能创建一个实例,则这个类被称为单例类,这种模式就被称为单例模式。

对Spring框架而言,可以在配置Bean实例时指定scope="singleton"来配置单例模式。不仅如此,如果配置<bean .../>元素时没有指定scope属性,则该Bean实例默认是单例的行为方式。

Spring推荐将所有业务逻辑组件、DAO组件、数据源组件等配置成单例的行为方式,因为这些组件无须保存任何用户状态,故所有客户端都可共享这些业务逻辑组件、DAO组件,因此推荐将这些组件配置成单例模式的行为方式。

如果不借助Spring框架,我们也可手动实现单例模式。为了保证该类只能产生一个实例,程序不能允许自由创建该类的对象,而是只允许为该类创建一个对象。为了避免程序自由创建该类的实例,我们使用private修饰该类的构造器,从而将该类的构造器隐藏起来。

将该类的构造器隐藏起来,则需要提供一个public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用static修饰(因为调用该方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类)。

除此之外,该类还必须缓存已经创建的对象,否则该类无法知道是否曾经创建过实例,也就无法保证只创建一个实例。为此该类需要使用一个静态属性来保存曾经创建的实例,且该属性需要被静态方法访问,所以该属性也应使用static修饰。

基于上面的介绍,下面程序创建了一个单例类。

程序清单:codes\09\9.3\Singleton\TestSingleton.java

class Singleton
{
//使用一个变量来缓存曾经创建的实例
private static Singleton instance;
//将构造器使用private修饰,隐藏该构造器
private Singleton(){}
//提供一个静态方法,用于返回Singleton实例
//该方法可以加入自定义的控制,保证只产生一个Singleton对象
public static Singleton getInstance()
{
//如果instance为null,表明还不曾创建Singleton对象
//如果instance不为null,则表明已经创建了Singleton对象,将不会执行该方法
if (instance == null)
{
//创建一个Singleton对象,并将其缓存起来
instance = new Singleton();
}
return instance;
}
}
public class TestSingleton
{
public static void main(String[] args)
{
//创建Singleton对象不能通过构造器,只能通过getInstance方法
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//将输出true
System.out.println(s1 == s2);
}
}

上面程序中第一行粗体字代码使用了一个静态属性来保存已创建的Singleton实例,程序第二段粗体字代码用于判断系统是否已经创建过Singleton实例——如果已经创建过Singleton实例,则直接返回该Singleton实例即可。

正是通过上面第二段粗体字代码提供的控制逻辑,从而保证了Singleton类只能产生一个实例。所以在TestSingleton类的main方法中看到两次产生的Singleton对象实际上是同一个对象。

在Java EE应用中,单例模式是一种应用非常广泛的设计模式,应用中许多组件都只需要单个实例,下面介绍的工厂模式里的工厂也只需要单个实例……

使用单例模式主要有如下两个优势:

减少创建Java实例所带来的系统开销。

便于系统跟踪单个Java实例的生命周期、实例状态等。



1. Object getAttribute( String name ) ;
获取与指定名字相关联的session属性值。
2. Enumeration getAttributeNames() ;
取得session内所有属性的集合。

3. long getCreationTime() ;
返回session的创建时间,最小单位千分之一秒。

4. String getId() ;
取得session标识。

5. long getLastAccessedTime() ;
返回与当前session相关的客户端最后一次访问的时间,由1970-01-01算起,单位毫秒。

6. int getMaxInactiveInterval( int interval ) ;
返回总时间,以秒为单位,表示session的有效时间(session不活动时间)。-1为永不过期。

7. ServletContext getServletContext() ;
返回一个该JSP页面对应的ServletContext对象实例。

8. HttpSessionContext getSessionContext() ;


9. Object getValue( String name ) ;
取得指定名称的session变量值,不推荐使用。

10. String[] getValueNames() ;
取得所有session变量的名称的集合,不推荐使用。

11. void invalidate() ;
销毁这个session对象。

12. boolean isNew() ;
判断一个session是否由服务器产生,但是客户端并没有使用。

13. void pubValue( String name, Object value ) ;
添加一个session变量,不推荐使用。

14. void removeValue( String name ) ;
移除一个session变量的值,不推荐使用。

15. void setAttribute( String name, String value ) ;
设置指定名称的session属性值。

16. void setMaxInactiveInterval( int interval ) ;
设置session的有效期。

17. void removeAttribute( String name ) ;
移除指定名称的session属性。



在回答问题之前,跟你简单介绍Session的工作原理:

不需要写手动写SessionID:
代码如下:
HttpSession sesion = Request.getSesion();
if(session!=null){
//如果sessionID不等于空,则说明是第二次访问
//写第二次访问时的代码
}else{
//写第一次访问的代码
}

java web怎么通过sessionid得到session这个我们不需要了解,就是第二次客户端发出请求时,将sessionid也发给了服务器,服务器根据这个唯一的ID找到相应的session(session都是保存在服务器的数据库中,每个session用唯一一个ID所标识),就像是数据库中根据关键字查找数据一样,找到之后就可以直接用这个session里面的数据了。

你可能感兴趣的:(session、模式介绍)