JavaWeb世界(四):Servlet映射与线程、Cookie、Session

一、Servlet映射细节

二、注解配置

一开始我们在xml文件中配置,例如初始化参数,配置资源文件等。但是在Servlet文件太多的情况下,代码显得非常臃肿,开发效率低。因此我们可以使用注解(WebServlet)取代xml配置。类似这样:

@WebServlet("/web")

使用注意:在web.xml文件的根元素中,存在属性 metadata-complete 表示是否忽略扫描注解。因此在使用注解时将其改为false,或者直接删掉这一行。

若在注解中使用初始化参数,代码如下:

@WebServlet(value = "/mapping", 
	initParams = {
			@WebInitParam(name = "encoding", value = "UTF-8"), 
			@WebInitParam(name = "name", value = "Bobbui") 
		}
)

XML和注解的优缺点:

三、启动加载Servlet

如果现在某一个Servlet(核心Servlet:初始化全局信息)需要在启动服务器的时候就创建出来,该怎么办?

XML配置:

	<servlet>
		<servlet-name>MappingServlet</servlet-name>
		<servlet-class>com.Bryan.Cookie_Session._01_mapping.MappingServlet</servlet-class>
		<!-- 在启动服务器的时候就加载,值越小越优先加载 -->
		<load-on-startup>1</load-on-startup>
	</servlet>	

在注解中设置:

@WebServlet(value="/mapping",loadOnStartup=1)

比如在Struts1中的核心Action的本质就是Servlet,既然核心Action是用来处理所有请求的,就应该在服务器启动的时候就初始化,而不是等到第一个请求过来再启动。

四、线程不安全问题

造成Servlet线程不安全的根本原因是:Servlet是单例的,Servlet中的非static的成员变量只有一份,多个客户端好比是多个线程,都访问的是同一个空间。

@WebServlet("/m")
public class MappingServlet extends HttpServlet {

	private static final long serialVersionUID = -981629810207965375L;
	
	private String name;
	protected void service(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();
		name = req.getParameter("name");
		name = new String(name.getBytes("ISO-8859-1"),"UTF-8");
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		out.println("你输入的名字:" + name);

	}
}

解决方案:

  1. 让当前Servlet实现 javax.servlet.SingleThreadModel 接口。但是这个方式已过时(性能太低)
  2. 在Servlet中不要使用成员变量,使用局部变量。因为每一个请求都会调用service方法,局部变量在service方法中,每一次都是新的空间。

Struts1,Spring,MVC都是线程不安全的,都是单例的,和Servlet类似

Struts2是线程安全的,因为每一个线程(请求)都是一个新的Action对象。


五、HTTP协议的无状态连接

一次会话:简单的理解为,打开一个浏览器,访问某一个站点,在网址内部查看信息,点击超链接等相关操作,然后关闭浏览器的整个过                    程,称之为一次会话。

HTTP协议:

有一个特点:

       无状态连接。服务端不知道上一次是哪一个客户端请求了自己。

无状态连接带来的问题:

       再一次会话中,我们可以查看多个资源,每个资源都会发送请求,再响应。由客户端发出请求,但是HTTP是无状态的,它不知道上一次是谁请求了自己,也就是说,在一次会话中,多个请求之间无法共享数据,无法跟踪用户的会话信息。

如下的例子(实例来源于网络)

解决方案:

LoginServlet.java

//处理登录请求并输出欢迎页面
@WebServlet("/param/login")
public class LoginServlet extends HttpServlet{

	private static final long serialVersionUID = 5220291018992262334L;

	protected void service(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		req.setCharacterEncoding("UTF-8");
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();
		String name = req.getParameter("username");
		out.println("欢迎" + name + "<br/>");
		out.println("<a href='/param/list?username=" + name +"'>收件箱<a/>");
		
	}
}

 ListServlet.java

//输出收件箱界面
@WebServlet("/param/list")
public class ListServlet extends HttpServlet{

	private static final long serialVersionUID = 5220291018992262334L;

	protected void service(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		req.setCharacterEncoding("UTF-8");
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();
		String name = req.getParameter("username");
		out.println("欢迎" + name + "<br/>");
		for (int i = 0; i < 6; i++) {
			out.println("<a href='/param/get?username=" + name +"'>邮件<a/>" + "<br/>");
		}	
	}
}

GetServlet.java

//输出收件箱界面
@WebServlet("/param/get")
public class GetServlet extends HttpServlet{

	private static final long serialVersionUID = 5220291018992262334L;

	protected void service(HttpServletRequest req, HttpServletResponse resp) 
			throws ServletException, IOException {
		req.setCharacterEncoding("UTF-8");
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();
		String name = req.getParameter("username");
		out.println("欢迎" + name + "<br/>");
		out.print("放暑假啦~~~");	
	}
}

每一个页面结果:

六、Cookie的简单应用

在没有使用Cookie的请求头和响应头:

 什么是Cookie?

Cookie操作:

我们编写Cookie:

//创建Cookie,存储数据
Cookie c = new Cookie("currentName", username);
//把Cookie响应响应给浏览器
resp.addCookie(c);

使用Cookie之后 Login 的响应头:

List 的请求头:

List和Get中的获取Cookie:

        //获取多个Cookie
	Cookie[] cookie = req.getCookies();
	for (Cookie c : cookie) {
		String name = c.getName();
		String value = c.getValue();
		System.out.println(name + " " + value);
		if ("currentName".equals(name)) {
			username = value;
		}
	}

七、Cookie的细节

1.Cookie的中文问题

在Cookie中属性名和属性值都不能使用中文

public class URLEncoderDemo {
	
	public static void main(String[] args) throws UnsupportedEncodingException {
		
		String str = "克里斯保罗";
		//编码
		String ret = URLEncoder.encode(str,"UTF-8");
		System.out.println(ret);
		//解码
		String ret1 = URLDecoder.decode(ret,"UTF-8");
		System.out.println(ret1);
	}
}

我们仿照这样的方式应用到Servlet中

        //创建Cookie,存储数据
        Cookie c = new Cookie("currentName", URLEncoder.encode(username,"UTF-8"));
        //把Cookie响应响应给浏览器
        resp.addCookie(c);
        //获取多个Cookie
	Cookie[] cookie = req.getCookies();
	for (Cookie c : cookie) {
		String name = c.getName();
		String value = URLDecoder.decode(c.getValue(),"UTF-8");
		System.out.println(name + " " + value);
		if ("currentName".equals(name)) {
			username = value;
		}
	}

这样就解决了Cookie的中文问题

2.修改Cookie(修改指定属性名的属性值)

方式一:创建一个同名的新Cookie(覆盖)

方式二:获取该Cookie对象,通过 setValue() 方法,重新设置新的value值

最后还要重新把Cookie放入响应中resp.addCookie();

3.Cookie的分类

会话Cookie:关闭浏览器Cookie销毁(缺省状态

持久化Cookie:Cookie可以保存一定时间

设置Cookie的最大生存时间:cookie.setMaxAge(int seconds)

若参数为负,相当于是会话Cookie

若参数为零,则清除该Cookie

若参数为正,则保留参数所指的一段时间

4.删除Cookie

cookie.setMaxAge(0)

八、Cookie的路径和Cookie的缺陷

当我们把两个资源名称修改后,如:/cookie/login  和  /abc/list  这样的话,Cookie是无法传递的:

like this:

c.setPath("/");

当然这样的Cookie还是存在一定缺陷的:

  1. 当多个人使用同一台电脑,可以互相查看浏览器的Cookie,导致隐私泄露;
  2. Cookie存储中文较麻烦,要先编码再解码;
  3. 一个Cookie只能存储一个数据,若要存储多个数据就要使用多个Cookie(cookie的value类型是String类型,只能存储一个数据);
  4. 一个站点对Cookie有限制:
    1. Cookie大小限制4KB;
    2. 一台服务器在一个客户端最多保存20个Cookie;
    3. 一个浏览器最多可以保存300个Cookie,等;
  5. 在设计上就存在一定的问题:
    1. Cookie是浏览器和服务器之间数据交互的凭证。在生活中我们是把识别数据存储到服务端,避免Cookie丢失

九、Session

1.Session的原理

Session本质就是一个会话Cookie(浏览器关闭后,Session就失效了)

2.Session的简单应用

HttpSession session = req.getSession();

查看响应头:

可见Session确实是特殊的Cookie

3.Session的创建与获取

HttpSession session = req.getSession();
session.setAttribute("currentName", username);

HttpSession session = req.getSession();
username = (String) session.getAttribute("currentName");

查看请求头:

4.Session的细节与规范

细节:

  1. 一般地,存储在Session的属性名称要唯一,我们习惯用 XXX_IN_SESSION 表示属性名。
    1. 例:session对象.setAttribute("UESR_IN_SESSION","XXX");
  2. 若需要把多个数据存放到Session中,就要用setAttribute方法多次,很不方便。一般地,我们需要把需要存储的数据封装到一个对象当中;
  3. 如果多台服务器需要共享Session,此时Session中的对象必须实现 java.io.Serializable 接口(才能在网络上传输)
    1. 序列化:把对象信息存储为二进制
    2. 反序列化:把对象信息恢复成对象
    HttpSession session = req.getSession();
    session.setAttribute("USER_IN_SESSION", user);
		
    HttpSession session = req.getSession();
    User user = (User) session.getAttribute("USER_IN_SESSION");

    out.println("欢迎" + user.getUsername() + "<br/>");
    for (int i = 0; i < 6; i++) {
    	out.println("<a href='/session/get'>邮件<a/>" + "<br/>");
    }

out.println("<a href='/session/list;jsessionid=" + session.getId() + "'>收件箱<a/>");

String url = resp.encodeURL("/session/list");
out.println("<a href='" + url + "'>收件箱<a/>");

但是在开发中都不会取消接收Cookie

发布了40 篇原创文章 · 获赞 17 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42650988/article/details/95788277