引入:服务器在接收到浏览器的请求后,会自动地调用对应的逻辑代码进行请求处理。但是逻辑代码是由程序员编写并放到服务器中,那么服务器怎么知道该怎么调用并调用哪个类和哪个方法来进行请求处理?
解决:程序员在编写代码的时候如果能够按照服务器能识别的规则进行编写,浏览器按照指定的规则进行发送请求,那么服务器就可以调用并执行相应的逻辑代码进行请求处理了。
实现:Servlet技术。
Servlet
狭义的Servlet是指Java语言实现的一个接口;广义的Servlet是指任何实现了这个servlet接口的类。
Servlet运行于支持java的应用服务器中。
从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下,Servlet只用来扩展基于Http协议的Web服务器。
使用
- 创建普通的Java类,并继承HttpServlet;
- 覆写service方法(由服务器调用);
- 在service方法中书写逻辑代码;
- 在webRoot下的WEB-INF文件夹下的web.xml文件中配置Servlet。
运行流程
浏览器发送请求到服务器,服务器根据请求URL地址中的URI信息在webapps目录下找到对应的项目文件夹,然后在web.xml中检索对应的servlet,找到后调用并执行Servlet。
URL组成:服务器地址:端口号/虚拟项目名/servlet别名(eg:http://localhost:8080/.../...)
URI:虚拟项目名/servlet别名。
Servlet的生命周期
- 从第一次调用(被加载到内存),到服务器关闭;
- 如果在web.xml文件中配置了load-on-startup,则是从服务器开启到服务器关闭。
注意:服务器启动,web.xml文件就会被加载到内存。
方法
初始化方法init():在servlet第一次加载到内存时被调用;
service方法:真正处理请求的方法。
service方法 | doGet方法 | doPost方法 |
get和post方式的请求都可以处理 | 处理get方式的请求 | 处理post方式的请求 |
三个方法同时存在时,优先执行service方法进行请求处理。
注意:如果在覆写的service方法中调用了父类的service方法,(super.service(arg0,arg())),则service方法处理完后,会再次根据请求方式响应的doGet和doPost方法执行。此时,必须覆写doGet和doPost方法。所以,一般情况下,我们是不在覆写的service方法中调用父类的service方法的,避免出现405错误。
Servlet常见的错误
- 404错误:资源未找到;
- 500错误:内部服务器错误。错误1:java.lang.ClassNotFoundException。解决方法:仔细检查你的web.xml文件的全限定路劲是否正确。错误2:Service方法体的代码执行错误。解决方法:检查service方法里面的代码是否全部正确。
- 405错误:请求方式不支持。
request对象的介绍和获取请求头信息
服务器接收到浏览器的请求后会创建一个request对象,对象中存储了此次请求相关的请求数据。服务器在调用Servlet时会将创建的request对象作为实参传递给Servlet的方法。
request对象是由服务器创建,并作为实参传递给处理请求的servlet的service方法。
使用:
- 获取请求头数据:
- 获取请求方法:String method = req.getMethod();
- 获取请求的URL:StringBuffer url = req.getRequestURL();
- 获取请求的URI:String uri = req.getRequestURI();
- 获取协议:String h = req.getScheme();
- 获取请求行数据(键值对):
- 获取指定的请求行信息:String value = req.getHeader(key);
- 获取所有的请求行的键的枚举+所有的请求行信息;req.getHeaderNames();
- 获取用户数据(部分get / post方式):req.getParameter(key);(如果获取的请求数据不存在不会报错,返回null)
Respose对象
服务器在调用指定的Servlet进行请求处理的时候,会给Servlet的方法传递两个实参request和respond。其中,request中封存了请求相关的请求数据,而respond则是用来响应的一个对象。
作用:用来响应数据到浏览器的一个对象。
使用:
- 设置响应头:
- 在响应头中添加信息,但同键会被覆盖:SetHeader(String name,String value);
- 在响应头中添加信息,同键不会被覆盖:addHeader(String name,String value);
- 设置响应状态:sendError(int num,String msg);
- 设置响应实体,响应具体的数据给浏览器:resp.getWrite().write(String str);
- 设置响应编码格式:
- resp.setHeader("content-type","text/html;charset=utf-8");
- resp.setContentType("text/html;charset=utf-8);
请求乱码的解决
浏览器的默认编码格式:iso8859-1;设置的servlet的编码格式:utf-8;
- (万能方式)使用String进行重新编码:
- String uname = req.getParameter("uname");
- uname = new String(uname.getBytes("iso8859-1"),"utf-8");
- 使用公共配置(有可能不同浏览器会出现问题)
- get方式:
- req.setCharacterEncoding("utf-8");
- 在tomcat的目录下的conf目录中修改server.xml文件,在下图中添加 useBodyEncodingForURI="true" 这句话。
- post方式:req.setCharacterEncoding("utf-8");
- get方式:
Servlet流程总结:
- 浏览器发起请求到服务器(请求);
- 服务器接收浏览器的请求,进行解析,创建request对象存储请求数据;
- 服务器调用对应的servlet进行请求处理,并将request对象作为实参传递给servlet的方法;
- servlet的方法执行进行请求处理:
- 设置请求编码格式;
- 设置响应编码格式;
- 获取请求信息;
- 处理请求信息;
- 处理响应结果。
请求转发
作用:实现多个servlet联动操作处理请求,这样避免代码冗余,让servlet的职责更明确。
特点:一次请求;地址栏信息没有发生改变。
使用:req.getRequestDispatcher("要转发的地址").forward(req,resp);
请求转发结束之后直接return结束即可。
Request对象的作用域
引入:使用请求转发后,不同的servlet之间怎么进行数据的共享?或者说,数据怎么从一个servlet流转给另一个servlet?
解决:使用request对象的作用域。
- request.setAttribute(object name,object value);
- request.setAttribute(object obj);
作用:解决了一次请求内的不同servlet的数据共享问题。
作用域:基于请求转发,一次请求中的所有servlet共享。
重定向
引入:如果使用请求转发,造成表单数据重复提交怎么办?如果当前的请求,servlet无法进行处理怎么办?
解决:重定向。
使用:respone.sendRedirect("路径");(本地路径:uri;网络路径:定线资源的URL信息)
特点:
- 重定向包含两次请求,即两个request对象;
- 地址栏信息改变。
Cookie学习
在后台请求处理时,怎样让不同的请求处理拿到相同的数据?使用Session,而Session依赖于Cookie。
作用:解决了发送的不同请求的数据共享问题。
使用:
- 创建Cookie对象:Cookie c = new Cookie(String name,String value);
- 设置Cookie(可选):有效期,路径...
- 设置Cookie信息给客户端:resp.addCookie();
注意:一个Cookie对象存储一条数据,多条数据可创建多个Cookie对象进行存储。
特点:浏览器端的数据存储技术;存储的数据声明在服务器端。
- 临时存储:存储在浏览器的运行内存中,浏览器关闭即失效。
- 定时存储:存储了Cookie的有效期,存储在客户端的硬盘中。在有效期内符合路径要求的请求都会附带该信息。
- 默认Cookie信息存储好之后,每次请求都会附带,除非设置有效路径:c.setPath("路径");
- 有效期:c.setMaxAge(int seconds);
Session学习
Session技术是依赖Cookie技术的服务器端的数据存储技术。
原理:用户第一次访问服务器,服务器会创建一个Session对象给此用户,并将该session对象的JSESSIONID使用Cookie技术存储到浏览器中,保证用户的其他请求能够获取到同一个session对象,也保证了不同请求能够获取到共享数据。
特点:存储在服务器端;服务器创建;依赖Cookie技术;作用域一次会话;默认存储时间为30分钟。
使用:创建Session对象 / 获取Session对象:HttpSession hs = req.getSession();
- 如果请求中拥有session的标识符JSESSIONID,则返回其对应的session对象;
- 如果请求中没有session的标识符,则创建新的session对象,并将其JSESSIONID作为Cookie数据存储到浏览器中。
- 如果Session对象失效了,也会重新创建一个session对象,并将其JSESSIONID存储到浏览器的内存中。
注意:JSESSIONID存储在Cookie的临时存储空间中,浏览器关闭即失效。
设置session的存储时间:he.setMaxInactiveInterval(int seconds);
注意:在指定的时间内session对象没有被使用则销毁;如果使用了则重新计时。
设置session强制失效:hs.invalidate();
存储和获取数据:
- 存储:hs.setAttribute(String name,objct value);
- 获取:hs.getAttribute(String name);//返回的数据类型为object.
- 存储和获取的动作发生在不同的请求中,但是存储要先于获取执行。
使用时机:一般用户在登录web项目时会将用户的个人信息存储到session中,供该用户的其他请求使用。
总结:session解决了一个用户的不同请求的数据共享问题,前提在JSESSIONID和session对象不失效的情况下。
ServletContex对象
作用:不同用户使用相同数据。
特点:服务器创建;共享数据。
作用域:整个项目内。
生命周期:服务器启动到服务器关闭。
获取ServletContex对象
- ServletContex sc = this.getServletContex();
- ServletContex sc2 = this.getServletConfig().getServletContex();
- ServletContex sc3 = req.getSession().getServletContex();
使用ServletContex对象完成数据共享
- 数据存储:sc.setAttribute(String name,object value);
- 数据获取:sc.getAttribute(String name);//返回的是object类型。
- 不同的用户可以给ServletContex对象进行数据存储。
当获取的数据不存在时返回null。
返回项目中web.xml文件中的全局配置数据
- 根据键名返回web.xml配置的全局数据的值,如果不存在则返回null:sc.getInitparameter(String name);
- 返回键的枚举:sc.getInitparameterNames();
配置方式:任意一组<context-param>标签只能存储一组键值对数据,多组可以声明多个进行存储。
<context-param>
<param-name>name</param-name>
<param-value>value</param-value>
</context-param>
作用:将静态数据和代码进行解耦。
获取webroot下资源的绝对路径
String path = sc.getRealPath(String path);
获取的路径为项目根路径,path参数为项目根目录中的路径。(动态获取)
获取webroot下资源的流对象
InputStream is = sc.getResourceAsStream(String path);
注意:此方式只能获取项目根目录下的资源流对象,class文件的流对象需要使用类加载器获取。
ServletConfig对象
引入:如何获取在web.xml中给每个servlet单独配置的数据?
解决:使用ServletConfig对象。
作用:ServletConfig对象时Servlet的专属配置对象,每个Servlet都单独拥有一个ServletConfig对象,用来获取web.xml中的配置信息。
使用:
- 获取ServletConfig对象:ServletConfig sc = this.getServletConfig();
- 获取web.xml中的配置数据:String code = sc.getInitParameter("config");