介绍
Servlet是sun公司提供的一门用于开发动态web资源的技术。
Servlet是JavaWeb的三大组件之一,它属于动态资源。Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要:
接收请求数据;
【1.获取信息】
处理请求;
【2.调用方法】Dao中的方法
完成响应
【3.页面跳转】
例如客户端发出登录请求,或者输出注册请求,这些请求都应该由Servlet来完成处理!Servlet需要我们自己来编写,每个Servlet必须实现javax.servlet.Servlet接口。
使用
Sun公司在其API中提供了一个servlet接口,用户若想要开发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
1. 编写一个Java类,实现servlet接口。然后把开发好的Java类部署到web服务器中。
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1. PrintWriter out = response.getWriter();//获取打印流对象 out.println("hello_Servlet");//向浏览器打印数据 out.flush();//刷新 out.close();//关流 }
2.servlet需要进行注册(在web.xml中)(手动部署)
<servlet> <servlet-name>Servlet1</servlet-name> <servlet-class>com.zym.servlet.Servlet1</servlet-class> </servlet> <servlet-mapping> <servlet-name> Servlet1</servlet-name> <url-pattern>/Servlet1</url-pattern> </servlet-mapping>
还可以直接在生成的servlet文件中配置
package com.zym.servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "Servlet2",urlPatterns = "/Servlet2") public class Servlet2 extends HttpServlet { @Override public void init(ServletConfig config) throws ServletException { System.out.println("start............."); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
Servlet的编写及配置
URL-Pattern的三种配置方式
1、完全路径匹配 以/开始 不能包含统配符* 例如: /hello /init
2、目录匹配 以/开始, /*结尾 例如: /* /aa/* /aaa/bbb/*
3、扩展名匹配 不能以/开始,以*开始 例如: *.do *.action
优先级
优先级:完全匹配 > 目录匹配 > 扩展名匹配
example
Servlet1 映射到 /abc/* Servlet2 映射到 /* Servlet3 映射到 /abc Servlet4 映射到 *.do
当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应 Servlet引擎将调用Servlet1。 当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应 Servlet引擎将调用Servlet3。 当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应 Servlet引擎将调用Servlet1。 当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应 Servlet引擎将调用Servlet2. 当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应 Servlet引擎将调用Servlet2。
编写注意事项
Servlet初始化时覆盖init() ,无需覆盖init(config)
根据Http请求的方式,覆盖相应的doGet或者doPost方法,无需覆盖Service方法
当doGet和doPost代码逻辑相同时,可以相互调用,简化编程
Servlet的执行过程
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。
装载并创建该Servlet的一个实例对象。
调用Servlet实例对象的init()方法。
创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
Servlet线程安全
当多个servlet的线程同时访问了servlet的共享数据,如成员变量,可能会引发线程安全问题。
解决方法:
1把使用到共享数据的代码块进行同步(使用synchronized关键字进行同步)
2建议在servlet类中尽量不要使用成员变量。如果确实要使用成员,必须同步。而且尽量缩小同步代码块的范围。(哪里使用到了成员变量,就同步哪里!!),以避免因为同步而导致并发效率降低。
ServletConfig对象和ServletContext对象(了解)
ServletConfig
在Servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。
当servlet配置了初始化参数后,通过ServletConfig对象得到初始化信息,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
<servlet> <servlet-name>ConfigDemo</servlet-name> <servlet-class>gz.itcast.f_config.ConfigDemo</servlet-class> <!-- 初始参数: 这些参数会在加载web应用的时候,封装到ServletConfig 对象中 --> <init-param> <param-name>path</param-name> <param-value>e:/b.txt</param-value> </init-param> </servlet> 在一个web应用可以存在多个ServletConfig对象(一个Servlet对应一个ServletConfig对象) 在哪个servlet里面配置的初始化参数,只能在该servlet里面获取
servletConfig对象获取
ServletConfig config=this.getServletConfig();
Servlet初始化参数
//获取servletConfig对象 ServletConfig config = this.getServletConfig(); //得到初始化值 String name = config.getInitParameter("name"); String age = config.getInitParameter("age"); String address = config.getInitParameter("address"); System.out.println(name+" "+age+" "+address);
ServletContext
WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
ServletContext代表是一个web应用的环境(上下文)对象,ServletContext对象 内部封装是该web应用的信息,ServletContext对象一个web应用只有一个
一个web项目只会有一个ServletContext对象
ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。
对象获取方法
由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。
了解ServletContext对象的功能
web.xml中配置初始化参数 <context - param> <param -name>driver</param - name> <param -value>com.mysql.jdbc.Driver</param - value> </context - param> 通过context对象获得参数 获取servletContext对象 ServletContext context = getServletContext(); 获取初始化参数 String initParameter = context.getInitParameter("driver"); System.out.println(initParameter);
context.getRealPath(“路径”) //得到web应用中的资源文件
context.getResourceAsStream("路径");
servletContext对象获取
1)ServletContext servletContext = config.getServletContext();
2)ServletContext servletContext = this.getServletContext();
HttpServletRequest(重点)
Request获取客户端请求信息
getRequestURL方法返回客户端发出请求完整URL
getRequestURI方法返回请求行中的资源名部分
getQueryString 方法返回请求行中的参数部分
getRemoteAddr方法返回发出请求的客户机的IP地址
getMethod得到客户机请求方式
getContextPath 获得工程虚拟目录名称
Request获取请求头
获得客户机请求头 getHeader(name)方法 --- String getHeaders(String name)方法 --- Enumeration<String> getHeaderNames方法 --- Enumeration<String>
获得具体类型客户机请求头 getIntHead(name)方法 --- int getDateHead(name)方法 --- long(日期对应毫秒)
Request获取请求参数
getParameter(name) --- String 通过name获得值 getParameterValues --- String[ ] 通过name获得多值 checkbox getParameterNames --- Enumeration<String> 获得所有name getParameterMap --- Map<String,String[ ]> key :name value: 多值
Request请求参数乱码
POST请求乱码 :request.setCharacterEncoding("utf-8"); GET请求乱码 解决方案一: 修改server.xml ,不建议使用此种解决方案 <Connector port="80" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="utf-8"/> * 必须有修改tomcat服务器配置文件权限 解决方案二:逆向编解码 username = URLEncoder.encode(username, "ISO-8859-1"); username = URLDecoder.decode(username, "utf-8"); 简化 username = new String(username.getBytes("ISO8859-1"),"utf-8");
URL特殊字符转义规则
常用转义规则 空格换成加号(+)
+换成%2B 正斜杠(/)分隔目录和子目录 换成%2F 问号(?)分隔URL和查询 换成%3F 百分号(%)制定特殊字符 换成%25 #号指定书签 换成%23 &号分隔参数 换成%26 java.net.URLEncoder和 java.net.URLDecoder
请求和重定向的区别
RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。 如果传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于服务器的根目录;如果创建RequestDispatcher对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。 调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。 HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。 RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。
HttpServletResponse (重点)
Response设置响应头和状态码
状态码(Status Code)
setStatus(int)
常用状态码:200、302、304、404、500
头信息 (Header Info)
addHeader(String,String) 在原有值添加
setHeader(String,String) 替换原有值
Response实现页面刷新
//隔1秒之后,刷新到一个新页面 resp.setHeader("Refresh", "1;/day02/a.html"); //设置当前页面每隔1秒刷新一次 resp.setHeader("Refresh", "1");
Response实现重定向
方案1: resp.setStatus(304); resp.setHeader("Location", "/FirstWeb/aa.jsp"); 方案2: resp.sendRedirect(“/FirstWeb/aa.jsp”); //推荐使用
Response向页面输出内容
response.getWriter().write(); 发送字符实体内容 response.getOutputStream().write() 发送字节实体内容 字节流输出中文 乱码和浏览器的编码有关 设置浏览器默认打开的时候的编码集 获得字节数组的时候,传入一个编码集 字符流输出中文 response的字符流的缓冲区是ISO-8859-1编码. 设置response缓冲区的编码. 设置浏览器的默认打开的时候字符集编码.
Response开发细节
向客户端输出字符中文的简写方式 response.setContentType("text/html;charset=UTF-8"); 字节流与字符流是互斥的 同一个页面只能使用一种流,不能同时向页面响应。 使用字符流输入数字,有可能存在问题 想输入数字时,使用字符串”1”
Response实现文件下载
设置头信息response.setHeader(“Content-Disposition”,” attachment;filename=”+文件名称);
通过response.getOutputStream()向浏览器端输出
Response禁用浏览器缓存
发送http头,控制浏览器禁止缓存当前文档内容 设置以下三个Http头信息禁用浏览器缓存 Cache-Control : no-cache Expires: Thu, 01 Dec 1994 16:00:00 GMT (非常特殊,转换特定日期格式才可以)
Javabean【实体类】 & servlet
将jsp中java代码和html代码进行分离,将java代码写入到servlet中
servlet主要负责业务逻辑,jsp只负责显示数据
请求->找到servlet的过程:
页面发送请求->web.xml->解析web.xml中url-pattern标签,获取值后
和请求进行匹配(equals())->获取servlet-mapping标签中的
servlet-name ->获取servlet标签中servlet-name,和mapping标签中
的name进行匹配->获取servlet-class中的值-->Class.forName("类的完整路径").newInstance()
baseServlet【加强反射】
创建一个BaseServlet继承HttpServlet【重写service方法】
这里的路径需要注意
http://localhost:8080/B/UserServlet?method=login&name=Colin&pass=123 这里的login就是方法的名字
在service方法中
package com.zym.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@WebServlet(name = "BaseServlet")
public class BaseServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String MethodName = req.getParameter("method");
System.out.println(this);
//com.zym.servlet.User_Servlet@163fc255
Class<? extends BaseServlet> clazz = this.getClass();
try {
//获取方法对象
Method method = clazz.getMethod(MethodName, HttpServletRequest.class, HttpServletResponse.class);
//执行方法
String path = (String ) method.invoke(this,req,resp);
System.out.println("路径:"+path);
if (path!=null) {
System.out.println("页面跳转");
req.getRequestDispatcher(path).forward(req,resp);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
package com.zym.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet(name = "User_Servlet") public class User_Servlet extends BaseServlet { public String login(HttpServletRequest request,HttpServletResponse response){ String name = request.getParameter("user"); String pwd = request.getParameter("pwd"); System.out.println(name+""+pwd); return "test.jsp"; }
Cookie
Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
Cookie会话技术的原理
服务器创建cookie对象,把会话数据存储到cookie对象中。 new Cookie("name","value"); Cookie cookieName = new Cookie("NAME","Colin") Cookie cookiePass = new Cookie("PASS","123456") 服务器发送cookie信息到浏览器 response.addCookie(cookieName); response.addCookie(cookiePass); 浏览器得到服务器发送的cookie,然后保存在浏览器端。 浏览器在下次访问服务器时,会带着cookie信息 服务器接收到浏览器带来的cookie信息 request.getCookies(); 返回Cookie[]
核心技术点
构造Cookie对象 Cookie(java.lang.String name, java.lang.String value) 设置cookie void setPath(java.lang.String uri) :设置cookie的有效访问路径 void setMaxAge(int expiry) : 设置cookie的有效时间,单位是秒 void setValue(java.lang.String newValue) :设置cookie的值 发送cookie到浏览器端保存 void response.addCookie(Cookie cookie) : 发送cookie 服务器接收cookie Cookie[] request.getCookies() : 接收cookie
Cookie会话技术的使用
显示用户上次访问时间
判断用户是否是第一次访问
如果是第一次访问,需要输出欢迎,并且记录当前的时间,保存到cookie中,再回写到浏览器端。
如果不是第一次访问,获取cookie中的时间,输出时间,并且记录当前的时间,保存到cookie中,再回写到浏览器端。
Cookie细节
一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
void setPath(java.lang.String uri) :设置cookie的有效访问路径。有效路径指的是cookie的有效路径保存在哪里,那么浏览器在有效路径下访问服务器时就会带着cookie信息,否则不带cookie信息。
Cookie数据类型只能保存非中文字符串类型的。可以保存多个cookie,但是浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
void setMaxAge(int expiry) : 设置cookie的有效时间。 正整数:表示cookie数据保存浏览器的缓存目录(硬盘中),数值表示保存的时间。 负整数:表示cookie数据保存浏览器的内存中。浏览器关闭cookie就丢失了!! 零:表示删除同名的cookie数据
Session
在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
Session的原理
1)第一次访问创建session对象,给session对象分配一个唯一的ID,叫JSESSIONID 2)把JSESSIONID作为Cookie的值发送给浏览器保存 3)第二次访问的时候,浏览器带着JSESSIONID的cookie访问服务器 4)服务器得到JSESSIONID,在服务器的内存中搜索是否存放对应编号的session对象。 5)如果找到对应编号的session对象,直接返回该对象 6)如果找不到对应编号的session对象,创建新的session对象,同时分配一个sessionId,然后继续走1的流程 结论:通过JSESSION的cookie值在服务器找session对象!!!!!
Session的核心技术点
创建或得到session对象 HttpSession getSession() 相当于 getSession(true) getSession(false) HttpSession getSession(boolean create) 设置session对象 void setMaxInactiveInterval(int interval) : 设置session的有效时间,设置为-1的时候表示session永远不过期; void invalidate() : 销毁session对象 java.lang.String getId() : 得到session编号 保存会话数据到session对象 void setAttribute(java.lang.String name, java.lang.Object value) : 保存数据 java.lang.Object getAttribute(java.lang.String name) : 获取数据 void removeAttribute(java.lang.String name) : 清除数据
Session需要注意的细节
三个getSession()方法的区别 getSession(true) / getSession() : 创建或得到session对象。没有匹配的session编号,自动创建新的session对象。 getSession(false): 得到session对象。没有匹配的session编号,返回null Session对象销毁的时间 a. 默认情况30分钟服务器自动回收 b.手动修改session回收时间,设置setMaxInactiveInterval(int interval) (如果设置-1的话,表示永远不过期) c. 全局修改session有效时间,在web.xml里面设置全局的session有效时间
Servlet的数据访问范围
application Scope servletContext (数据库连接池,配置, 线程池, 站点访问次数)
每一个Web应用对应一个ServletContext
存放所有用户都可以访问的数据
session Scope HttpSession (存放与用户相关数据)
存放每个用户自己会话过程中的数据
request Scope HttpServletRequest
(Servlet处理结果,JSP显示)
数据存放在request对象中
生成新的请求时,原request存放数据丢失
session和Cookie的区别
(1)保存位置:session-服务器,cookie--客户端
(2)保存数据不同:session存入后Object,Cookie--String
(3)运行周期:session--浏览器访问期间,Cookie可以永久保存
(4)安全性:session高于cookie