一、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);
}
}
解决方案:
- 让当前Servlet实现 javax.servlet.SingleThreadModel 接口。但是这个方式已过时(性能太低)
- 在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还是存在一定缺陷的:
- 当多个人使用同一台电脑,可以互相查看浏览器的Cookie,导致隐私泄露;
- Cookie存储中文较麻烦,要先编码再解码;
- 一个Cookie只能存储一个数据,若要存储多个数据就要使用多个Cookie(cookie的value类型是String类型,只能存储一个数据);
- 一个站点对Cookie有限制:
- Cookie大小限制4KB;
- 一台服务器在一个客户端最多保存20个Cookie;
- 一个浏览器最多可以保存300个Cookie,等;
- 在设计上就存在一定的问题:
- 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的细节与规范
细节:
- 一般地,存储在Session的属性名称要唯一,我们习惯用 XXX_IN_SESSION 表示属性名。
- 例:session对象.setAttribute("UESR_IN_SESSION","XXX");
- 若需要把多个数据存放到Session中,就要用setAttribute方法多次,很不方便。一般地,我们需要把需要存储的数据封装到一个对象当中;
- 如果多台服务器需要共享Session,此时Session中的对象必须实现 java.io.Serializable 接口(才能在网络上传输)
- 序列化:把对象信息存储为二进制
- 反序列化:把对象信息恢复成对象
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