#Servlet简介
Servlet全称 Server Applet ,服务端小应用
Servlet运行在支持java的应用服务器中,主要用作Web服务器,处理客户端(浏览器)的请求,并响应给客服端,一种B/S模型
Servlet项目部署
结构
package com.test.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("Hello Servlet");
}
}
配置web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.test.servlet.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/myServlet</url-pattern>
</servlet-mapping>
</web-app>
运行项目
Servlet继承结构
Servlet 接口
Defines methods that all servlets must implement.
定义所有servlet必须实现的方法
ServletConfig接口
A servlet configuration object used by a servlet container to passinformation to a servlet during initialization.
一个servlet容器用来传递的servlet配置对象在初始化期间向servlet发送信息。
servlet运行期间需要的一些辅助信息,这些信息可以在web.xml文件中,使用一个或多个元素,进行配置
当初始化一个Servlet时,会将改Servlet配置信息,封装到一个ServletConfig中,通过调用init(ServletConfig config)方法,将ServletConfig对象传递给Servlet
GenericServlet类
Defines a generic, protocol-independent servlet. To write an HTTP servlet for use on the Web, extend {@link javax.servlet.http.HttpServlet} instead.
定义一个通用的、独立于协议的servlet。编写一个HTTP servlet 在Web上使用,扩展{@link javax.servlet.http。HttpServlet}。
GenericServlet实现了Servlet接口,是一个抽象类。
我们可以通过继承GenericServlet来编写自己的Servlet对象。
HttpServlet类
Provides an abstract class to be subclassed to create
an HTTP servlet suitable for a Web site. A subclass of
HttpServlet must override at least
one method, usually one of these:
-
doGet, if the servlet supports HTTP GET requests -
doPost, for HTTP POST requests -
doPut, for HTTP PUT requests -
doDelete, for HTTP DELETE requests -
initanddestroy, to manage resources that are held for the life of the servlet -
getServletInfo, which the servlet uses to provide information about itself
提供要创建的子类的抽象类
适合于Web站点的HTTP servle的一个子类HttpServlet必须至少覆盖一种方法,通常是下面的一种:
-
doGet, 如果servlet支持HTTP GET请求 -
doPost, 对于HTTP POST请求 -
doPut, 对于HTTP PUT请求 -
doDelete, 对于HTTP删除请求 -
initanddestroy, 管理在servle生命周期中保留的资源 -
getServletInfo, servlet使用哪个来提供关于自身的信息
HttpServlet类方法
1.Init() 创建servlet对象后立即调用该方法完成其他初始化工作
2.service(),处理客户端请求,执行业务操作,利用Response对象响应客户端请求
3.destroy(),在销毁Servlet对象之前调用该方法,释放资源
4.getServletConfig(),ServletConfig是容器向servlet传递参数的载体。
5.getServletInfo(),获取servlet相关信息。
Servlet生命周期
当浏览器第一次请求一个Servlet对象时,Servlet容器会实例这个Servlet对象(只实例一次)。然后调用Servlet对象init方法(运行时只初始化一次),并在新线程(每个请求都是一个单独线程)执行Servlet对象service()(执行多次)方法,service()方法执行完,这时Servlet对象会在缓存在Servlet容器中以单例的形式存在,直到Servlet容器被销毁之前,调用一次destory()方法(只执行一次),释放Servlet对象
实例
public class Servlet1 extends HttpServlet {
public Servlet1(){
System.out.println("Servlet1被构造了");
}
@Override
public void init() throws ServletException {
System.out.println("Servlet1初始化");
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet1服务方法");
}
@Override
public void destroy() {
System.out.println("Servlet1销毁方法");
}
}
线程安全问题
每一次请求都会产生一个线程,每个Servlet实例只有一次并且是唯一的。
多个线程去请求一个Servlet实例对象中的成员变量,就有可能会造成线程不安全
解决思路:将变量尽量写在方法体中,少用成员变量
Servlet处理请求的过程
没有重写sevice方法
如果自定义的Servlet没有重写service方法,程序运行时请求这个Servlet会调用HttpServlet类中service中方法,service方法会判断浏览器请求方式,响应给浏览器HTTP 405 方法不允许(或HTTP 400客户端请求的语法错误,服务器无法理解,这里不会报400错误)
public class Servlet2 extends HttpServlet {
}
HttpServlet中service方法
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
HttpServlet中doGet()方法和doPost()方法
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
HttpServletResponse部分源码
/ * *
* 状态码(404),指示请求的资源不是可用
* /
public static final int SC_BAD_REQUEST = 400;
/ * *
* 状态码(405),指示
* <code><em>Request-Line</em></code>对于资源是不允许的
* 由<code><em>Request-URI</em></code>识别。
* /
public static final int SC_METHOD_NOT_ALLOWED = 405;
重写了service方法
如果自定义的Servlet重写了Service方法,程序运行时请求这个Servlet,HttpServlet对象中执行service方法,service方法中调用Servlet重写的Service方法执行,处理请求并响应给浏览器
注意于上面service方法参数不同:
- service(HttpServletRequest req, HttpServletResponse resp)
- +service(ServletRequest req, ServletResponse res)
HttpServlet中部分源码
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
service(request, response);
}
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//响应给浏览器
resp.getWriter().println("Hello Servlet");
}
}
自定义处理请求2中方式
1.重写sevice() 不需要区分Get 或 Post 方法处理方式不一样
2.重写 doGet()和doPost() 适用于明确区分Get 或 Post 方法处理方式不一样
public class Servlet2 extends HttpServlet {
/* @Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//响应给浏览器
resp.getWriter().println("Hello Servlet");
}*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("doGet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("doPost");
}
}
HttpReuquest
封装客户端请求数据
HttpReuquest生命周期
浏览器请求一次,实例化一次HttpReuquest对象,Servlet容器调用servlet中service方法HttpReuquest对象作为service方法参数传递,service 方法处理请求并响应给浏览器,service方法执行完毕。HttpReuquest对象销毁
获取请求信息
public class Servlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("URI:"+req.getRequestURI());
//返回客户端浏览器发出请求时的完整URL。
System.out.println("URL:"+req.getRequestURL());
//返回发出请求的客户机的IP地址。
System.out.println("客户端IP地址:"+req.getRemoteAddr());
System.out.println("客户端Host:"+req.getRemoteHost());
System.out.println("客户端Port:"+req.getRemotePort());
System.out.println("协议名:"+req.getScheme());
System.out.println("Serve主机名:"+req.getLocalName());
//返回WEB服务器的IP地址。
System.out.println("Serve主机IP:"+req.getLocalAddr());
//返回WEB服务器处理Http协议的连接器所监听的端口。
System.out.println("Server主机Port:"+req.getLocalPort());
System.out.println("当前项目部署名:"+req.getContextPath());
System.out.println("请求方式:"+req.getMethod());
//设置Post请求数据的编码
req.setCharacterEncoding("UTF-8");
//根据请求头名,获取请求头对应值
System.out.println(req.getHeader("Accept"));
//获取请求头中所有请求名
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()){
String name=headerNames.nextElement();
System.out.println(name+": "+req.getHeader(name) );
}
}
}
获取请求参数
public class Servlet4 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理Post乱码
req.setCharacterEncoding("UTF-8");
//获取提交过来的参数
//根据参数名接收单个参数
System.out.println("用户名:" + req.getParameter("username"));
System.out.println("星期" + req.getParameter("week"));
System.out.println("籍贯: " + req.getParameter("pro"));
System.out.println("介绍: " + req.getParameter("introduce"));
//获取复选框提交过来的数据,多个参数值使用同一个参数名
String[] hobbies = req.getParameterValues("hobby");
System.out.println("爱好:" + Arrays.toString(hobbies));
//获取所有参数名
/*username [aaa]
hobby [1, 2, 3, 4]
week [2]
pro [3]
introduce [一个小白]*/
Enumeration<String> parameterNames = req.getParameterNames();
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
System.out.println(name + " " + Arrays.toString(req.getParameterValues(name)));
}
//获取参数map集合
/*username=[aaa]
hobby=[1, 2, 3, 4]
week=[2]
pro=[3]
introduce=[一个小白]*/
Map<String, String[]> parameterMap = req.getParameterMap();
Set<String> strings = parameterMap.keySet();
for (String name : strings) {
String[] value = parameterMap.get(name);
System.out.println(name + "=" + Arrays.toString(value));
}
}
}
HttpResponse
封装响应给客户端的数据
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置响应状态码
//resp.setStatus(405);
//指定的状态码发送错误响应到客户端,并清除缓冲区
//resp.sendError(405,"请求方法发不允许");
//设置响应头
//resp.setHeader("Content-Type","text/html;");
//设置响应类型默认是text/html
//设置响应类型为html,浏览器以utf-8编码解析数据
resp.setContentType("text/html;charset=UTF-8");
//设置响应编码
resp.setCharacterEncoding("UTF-8");
// resp.setHeader("Content-Disposition", "attachment; filename=text.text");
PrintWriter writer = resp.getWriter();
writer.println("hello");
}
}
ServletContext
ServletContext官方叫Servlet上下文。服务器会为每一个Web应用创建一个ServletContext对象。这个对象全局唯一,而且Web应用中的所有Servlet都共享这个对象。所以叫全局应用程序共享对象。
ServletContext生命周期
当容器启动时会创建ServletContext对象并一直缓存该对象,直到容器关闭,该对象生命周期结束。
ServletContext对象生命周期非常长,所以使用全局容器时不建议存放业务数据
作用:
- 全局容器
- 相对路径转绝对路径
- 读取配置信息
- 获取容器附加信息
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ServletContext 获取方式
//一个web应用只用一个ServletContext对象
ServletContext servletContext = this.getServletContext();
/*ServletContext servletContext1 = req.getServletContext();
ServletContext servletContext3 = getServletConfig().getServletContext();
System.out.println(servletContext==servletContext1);
System.out.println(servletContext1==servletContext3);
*/
//获取web服务名 项目部署名 路径
String contextPath = servletContext.getContextPath();
System.out.println(contextPath);
//项目真实路径 + /upload
String realPath = servletContext.getRealPath("/upload");
System.out.println(realPath);
//返回Servlet容器的名称和版本
System.out.println(servletContext.getServerInfo());
//返回Servlet容器所支持的Servlet主版本号
System.out.println(servletContext.getMajorVersion());
//返回Servlet容器所支持Servlet小版本
System.out.println(servletContext.getMinorVersion());
//获取<context-param>标签中的参数
System.out.println(servletContext.getInitParameter("username"));
System.out.println(servletContext.getInitParameter("password"));
//获取全部参数名
Enumeration<String> pnames = servletContext.getInitParameterNames();
while(pnames.hasMoreElements()){
String pname = pnames.nextElement();
System.out.println(pname+":"+servletContext.getInitParameter(pname));
}
//向servletContext中追加数据
servletContext.setAttribute("hello","你好呀");
//获取servletContext中追加数据
Object hello = servletContext.getAttribute("hello");
System.out.println(hello);
//移除servletContext中追加数据
servletContext.removeAttribute("hello");
}
}
web.xml配置信息
<!--配置全局信息-->
<context-param>
<param-name>username</param-name>
<param-value>test</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</context-param>
ServletConfig对象
封装Servlet配置信息
一个Servlet对象有一个ServletConfig对象,
public class Servlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*ServletContext servletContext = this.getServletContext();
//获取全部参数名
Enumeration<String> pnames = servletContext.getInitParameterNames();
while(pnames.hasMoreElements()){
String pname = pnames.nextElement();
System.out.println(pname+":"+servletContext.getInitParameter(pname));
}*/
//获取ServletConfig
//每个Servlet独有信息
ServletConfig config = this.getServletConfig();
System.out.println(config.getInitParameter("computer"));
//ServletConfig对象读取全部参数
Enumeration<String> initPNames = config.getInitParameterNames();
while (initPNames.hasMoreElements()){
String name =initPNames.nextElement();
System.out.println(name+":"+config.getInitParameter(name));
}
}
}
web.xml配置信息
<servlet>
<servlet-name>servlet3</servlet-name>
<servlet-class>com.test.servlet.Servlet3</servlet-class>
<!--
servlet初始参数
每个Servlet独有信息
-->
<init-param>
<param-name>computer</param-name>
<param-value>微星</param-value>
</init-param>
<init-param>
<param-name>CPU</param-name>
<param-value>i9</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>servlet3</servlet-name>
<url-pattern>/servlet3.do</url-pattern>
</servlet-mapping>
Servlet乱码处理
POST乱码处理
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*设置post方式提交的数据包字符集为UTF-8
该字符集必须和当前项目编码一致*/
req.setCharacterEncoding("UTF-8");
}
GET乱码处理
方式一:
byte[] bytes = req.getParameter("username").getBytes("ISO-8859-1");
String str = new String(bytes, "UTF-8");
方式二:
设置Tomcat/conf/servler.xml URIEncoding="UTF-8"
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8" />
响应信息乱码
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置响应头
//resp.setHeader("Content-Type","text/html;");
//设置响应类型默认是text/html
//设置响应类型为html,浏览器以utf-8编码解析数据
resp.setContentType("text/html;charset=UTF-8");
//设置响应编码
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
writer.write("<h2>你好</h2>");
}
Servlet注解开发
在Servlet3.0以及之后的版本中支持注解开发Servlet。
对一Servlet配置不依赖于web.xml配置文件,使用@WebServlet 将一继承javax.servlet.http.HttpServlet的类定义为一个Servlet组件
@WebServlet注解中属性
| 属性名 | 类型 | 作用 |
|---|---|---|
| initParams | WebInitParam[] | Servlet的init参数 |
| name | String | Servlet的名称 |
| urlPatterns | String[] | Servlet的访问URL,支持多个 |
| value | String[] | Servlet的访问URL,支持多个 |
| loadOnStartup | int | 自启动Servlet |
| description | String | Servlet的描述 |
| displayName | String | Servlet的显示名称 |
| asyncSupported | boolean | 声明Servlet是否支持异步操作模式 |
@WebServlet(
description = "hello",
displayName = "servlet2",
name = "servlet2",
loadOnStartup = 6,
initParams = {
@WebInitParam(name="test",value = "test"),
@WebInitParam(name="test2",value = "test2")
},
urlPatterns = {"/servlet2.do","servlet2"},
asyncSupported = false
)
实例
/**
* loadOnStartup 项目是启动时加载顺序
* @WebServlet 设置url映射路径
* */
@WebServlet(value = {"/servlet1.do","/servlet1"},loadOnStartup = 6)
@WebInitParam(name="key",value = "value")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet1服务方法");
}
}
请求转发
请求转发是通过HttpServletRequest或者ServletContext对象获取一个转发器ResquestDispatcher,再由请求作出转发处理,通过请求转发器,当前请求可以转方法给下一个web资源
- RequestDispatcher.forward(req,resp);
- requestDispatcher.include(req,resp);
特点
- 请求参数可以传递
- 浏览器地址栏不会发生变化,浏览器只发送一次请求,只负责接收服务端的响应
- 可以跳转页面、跳转到另一个Servlet进行响应
- 可以通过转发访问项目中WEB-INF安全目录资源
- 不能通过转发(跨域)访问外部资源
- 请求转发是服务器后台内部进行处理,对浏览器屏蔽
RequestDispatcher两种获取方式区别:
- ServletContext获得转发器Path比需要“/”字符开头
- HttpServletResponse获取的转发器, 相对路径或绝对路径都可访问
@WebServlet("/servlet3.do")
public class Servlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求转发器方式
/*ServletContext获得转发器,Path必需要“/”字符开头
java.lang.IllegalArgumentException: 路径[aaa.html]不以“/”字符开头*/
RequestDispatcher requestDispatcher = this.getServletContext().getRequestDispatcher("/aaa.html");
//HttpServletResponse获取的转发器 可写相对路径或绝对路径
RequestDispatcher requestDispatcher1 = req.getRequestDispatcher("aaa.html");
requestDispatcher.forward(req,resp);
//requestDispatcher1.forward(req,resp);
}
}
forword处理流程
1.清空Response存放响应正文数据的缓冲区
2.如果源Servlet在请求转发之前,已经提交响应(flushBuffer() ,close() 方法),forword()方法会抛出异常IllegalStateException 提交响应后无法转发。为了避免该异常,不应该在源Servlet中提交响应结果。
include处理流程
1.跳转资源为Servlet或jsp,跳转到另一个service方法的响应正文添加到源Servlet中,如果是HTML只添加到源响应结果中
2.返回到源Servlet的service方法发中,继续执行后续代码
forword 和 include 区别
include,会将跳转的资源包含到源servlet中,执行service方法,响应给浏览器
forword,会清空源Servlet中response中数据,跳转目标资源,源Servlet中对Response对象操作不会被保存,目标资源响应给浏览器
实例
@WebServlet("/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String money = req.getParameter("money");
System.out.println("Servlet1接收到请求,"+username+"要借钱"+money+"元 ");
//java.lang.IllegalStateException: 提交响应后无法转发
//resp.getWriter().close();
//resp.flushBuffer();
//获取请求转发器
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2.do");
//由转发器调用forward或者include作出请求转发动作
// requestDispatcher.forward(req,resp);
resp.getWriter().write("hello");
requestDispatcher.include(req,resp);
}
}
@WebServlet("/servlet2.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String money = req.getParameter("money");
System.out.println("Servlet2接收到请求,"+username+"要借钱"+money+"元 ");
resp.getWriter().write("收到"+money+"元");
resp.getWriter().write("bye");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8 ");
}
}
注意:请求转发只能访问当前项目中的资源,无法访问外部资源
//请求装法只能获取当前项目内部资源,不能访问外部资源
//HTTP状态 404 - 未找到 请求的资源[/servletDemo4_2/https://www.baidu.com]不可用
req.getRequestDispatcher("https://www.baidu.com").forward(req,resp);
请求转发访问当前项目WEB-INF
WEB-INF是项目中安全目录不能直接访问,可以通过转发访问
@WebServlet("/servlet4.do")
public class Servlet4 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// WEB-INF是项目中安全目录不能直接访问,可以通过转发访问
req.getRequestDispatcher("WEB-INF/ccc.html").forward(req,resp);
}
}
响应重定向
原理
1.浏览器向服务器发送请求,访问服务器某个Servlet
2.服务器就收到请求,向浏览器响应HTTP 302 状态码(让浏览器请求访问另一个web资源)
3.浏览器接收到响应后,立即自动访问另一个web资源。这个web资源可能是同一个web服务,也可能是不同web服务器资源,意味着可以访问的不是当前服务器的外部资源
4.浏览器接收到另一个web资源响应结果
特点
- 不能共享Request对象,请求参数不能直接传递
- 浏览器地址栏发生变化,浏览器发送多次请求
- 不能访问当前项目WEB-INF安全目录资源
- 能跨域访问外部资源
- 响应重定向是浏览器自主访问其他资源的行为
实例
@WebServlet("/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String money = req.getParameter("money");
System.out.println("Servlet1接收到请求,"+username+"要借钱"+money+"元 ");
//响应重定向到Servlet2
//resp.sendRedirect("servlet2.do?username"+username+"&money="+money);
resp.sendRedirect("servlet2.do");
@WebServlet("/servlet2.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username=req.getParameter("username");
String money = req.getParameter("money");
System.out.println("Servlet2接收到请求,"+username+"要借钱"+money+"元 ");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().write("收到"+money+"元");
}
}
public class Servlet3 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取项目主路径。 /项目名
String base = req.getServletContext().getContextPath();
//resp.sendRedirect(base +"/servlet2.do");
// resp.sendRedirect("aaa.html");
//重定向不能定向到项目中WEB-INF中的资源
//resp.sendRedirect("WEB-INF/bbb.html");
//重定向可以访问(跨域)外部资源
resp.sendRedirect("https://www.baidu.com/");
}
}
转发和重定向区别
转发:
请求参数可以传递
可以访问WEB-INF中资源
只能访问当前项目资源
浏览器产生一次请求
浏览器地址栏没有变化
重定向:
不能定向访问WEB-INF中资源
可以访问当前项目资源,也可以访问外部资源
浏览器产生多次请求
浏览器地址栏发生变化
产生一个新的请求,原来的请求参数不可以传递
* 4.转发和重定向的不同点
* 1)语句不同
* 转发: request.getRequestDispatcher("/WEB-INF/test.jsp").forward(request, response);
* 重定向: response.sendRedirect("/myservlet2/login/success.jsp");
* 2)添加后的地址栏路径不同
* 转发:跳转之前的地址
* 重定向:跳转之后的地址 /login/success.jsp
* 3)是否可以获取保存在request中的数据
* 转发:可以
* 重定向:无法获取
* 4)原理不同
* 转发 :相当于110
* 重定向:呼叫转移
* 5)效率不同
* 转发:效率高
* 重定向:效率低
* 6)跳转的范围不同
* 转发:仅限当前项目跳转(最大也是当前服务器)
* 重定向:可以跳转到互联网任意位置
* 7)跳转时的路径不同
* 绝对路径
* 转发:不支持(只能在当前项目跳转)
* 重定向:支持
* 根路径 /
* 转发:不需要写上下文路径
* 重定向:需要写上下文路径
* 相对路径
* 1.重定到其他服务器,只能使用绝对路径
* 2.跳转到当前服务器的其他项目,建议使用根路径
* 3.不建议使用相对路径
* 8) 跟路径的含义不同
* 转发: /代表当前项目
* 重定向: /代表当前服务器
* 9)刷新是否导致表单重复提交
* 重定向:不会
* 转发:会
*
* 10)是否经过过滤器
* 转发:不经过
* 重定向:经过
Servlet处理路径
相对路径:相对当前文件身路径作为标准定位符
绝对路径:在跟路径为标准定位资源
绝对路径
<h3>绝对路径 ,以HTTP开始,包含主机名、端口号、项目上下文、文件名
可以访问不同服务器的不同项目资源
</h3>
<a href="http://localhost:8081/myservlet2/path1/select.jsp">select.jsp</a>
<a href="http://localhost:8081/myservlet2/path1/sub1/update.jsp">update.jsp</a>
<a href="http://localhost:8081/myservlet2/path2//insert.jsp">insert.jsp</a>
<a href="http://localhost:8081/myservlet2/index.jsp">index.jsp</a>
<a href="http://localhost:8081/myservlet2/login/loginServlet">访问同一服务器同一个应用</a>
<a href="http://localhost:8081/myservlet/firstServlet">访问同一服务器同不同应用</a>
<a href="https://www.test.com:443/news/11662.html"> 不畏将来</a>
根路径
<h3>根路径 以/开始,可以访问当前服务器的所有项目资源。需要写上下文context</h3>
<a href="/myservlet2/path1/select.jsp">select.jsp</a>
<a href="/myservlet2/path1/sub1/update.jsp">update.jsp</a>
<a href="/myservlet2/path2//insert.jsp">insert.jsp</a>
<a href="/myservlet2/index.jsp">index.jsp</a>
<a href="/myservlet2/login/loginServlet">访问同一服务器同一个应用</a>
<a href="/myservlet/firstServlet">访问同一服务器同不同应用</a>
相对路径:相对当前文件自身
<a> </a>
<h3>相对路径1 :相对当前文件自身</h3>
<a href="./select.jsp">select.jsp</a>
<a href="sub1/update.jsp">update.jsp</a>
<a href="../path2/insert.jsp">insert.jsp</a>
<a href="../index.jsp">index.jsp</a>
<a href="../login/loginServlet">访问同一服务器同一个应用</a>
<a href="../../myservlet/firstServlet">访问同一服务器同不同应用</a>
相对路径:相对基准路径
<h3>相对路径2 :相对基准路径</h3>
<a href="path1/select.jsp">select.jsp</a>
<a href="path1/sub1/update.jsp">update.jsp</a>
<a href="path2/insert.jsp">insert.jsp</a>
<a href="index.jsp">index.jsp</a>
<a href="login/loginServlet">访问同一服务器同一个应用</a>
<a href="../myservlet/firstServlet">访问同一服务器同不同应用</a>
实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--
如果写了base标签会自动在相对路径默认补充href中的路径,简化相对路径
如果没有定义base标签,默认就是当前文件所在路径
-->
</head>
<base href="http://127.0.0.1:8080/servletDemo5/a/a2/">
<body>
this is a
<!--
相对路径不以/开头,以当前资源本省所在路径为基准路径
绝对路径以/开头,以当前项目所在目录作为根路径(项目上级目录,项目部署目录)
-->
<a href="a2.html">相对路径找a2</a>
<a href="/servletDemo5/a/a2/a2.html">绝对路径找a2</a>
<a href="../b/b2/b.html">相对路径找b</a>
<a href="/servletDemo5/b/b2/b.html">绝对路径找b</a>
<!--基于base处理相对路径-->
<a href="a2.html">相对路径找a</a>
<a href="b/b2/b.html">相对路径找b</a>
</body>
</html>
请求转发路径处理
- 以 / 开头的路径是绝对路径,以当前当前项目部署名径作为根路径
- 相对路径处理,以当前Servlet自身路径作为基准路径定位其他资源,受当前Servlet映射路径影响
- ../代表向上一层的路径
@WebServlet("/myServlet1.do")
public class MyServlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//请求转发路径处理
/*
* 以绝对路径处理 /开头 以当前当前项目部署名径作为根路径
* 请求转发只能转发服务器内部资源
* http://localhost:8080/servletDemo5 /b/b2/b.html
*
* */
//req.getRequestDispatcher("/b/b2/b.html").forward(req,resp);
/*
* http://localhost:8080/servletDemo5/myServlet1.do
* 以相对路径处理,以当前Servlet自身路径作为基准路径定位其他资源
* http://localhost:8080/servletDemo5/b/b2/b.html
* 受当前Servlet映射路径影响
* */
req.getRequestDispatcher("b/b2/b.html").forward(req,resp);
}
}
响应重定向路径处理
- 绝对路径 /开头 以项目的上一层作为根路径
- 相对路径 相对于url-pattern当前Servlet相对路径的基准路径,受当前Servlet映射路径影响
- ../ 代表向上一层的路径
@WebServlet("/myServlet2.do")
public class MyServlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//响应重定向路径处理方式
/*
* 绝对路径 /开头 以项目的上一层作为根路径
* 响应重定向可以跳转到外部资源
* http://localhost:8080 + /servletDemo5/b/b2/b.html
* */
//resp.sendRedirect("/servletDemo5/b/b2/b.html");
/*
* 相对路径 相对于url-pattern 当前Servlet相对路径的基准路径
* http://localhost:8080/servletDemo5/ + b/b2/b.html
* 受当前Servlet映射路径影响
* */
resp.sendRedirect("b/b2/b.html");
}
}
请求转发处理和和响应重定向路径处理不同处?
绝对路径
- 请求转发: /开头 以项目的上一层作为根路径
- 响应重定向:/开头 以当前当前项目部署名径作为根路径
Cookie
Cookie是一种保存少量信息至浏览器的一种技术
1.浏览器请求服务器
2.服务器响应给浏览器一组Cokie信息
3.浏览器将Cookie信息保存到本地计算机,以备使用
4.下一次请求浏览器会将Cokie信息,发送到服务器
通过这种机制可以实现在浏览器端保留一些用户信息.为服务端获取用户状态获得依据
Cookie特点
- Cookie使用字符串存储数据
- Cookie使用key和Value结构存储数据
- 单个Cookie存储数据大小限制在4097字节
- Cookie存储的数据中不支持中文,Servlet4.0中支持
- Cookie对象保存在客户端浏览器或系统磁盘中
- Cookie是与域名绑定所以不支持跨一级域名访问
- 浏览器在保存同一域名所返回Cookie的数量是有限的。不同浏览器支持的数量不同
- Cookie分为持久化Cooke与状态Cookie
- 浏览器每次请求时都会把与当前访问的域名相关的Cookie在请求中提交到服务端
Cookie: Hm_lvt_1391140a1403a41e22164b7215f536b7=1589980724; Hm_lpvt_1391140a1403a41e22164b7215f536b7=1589980724; _CS_USER_TAGS=s-t-172323c7f78-28f7a69f58fa41858d0253bd4fda8544; JSESSIONID=1522AE97AEAC14B45FC1507FA36C13F3
Cookie持久化和状态Cookie
状态Cookie:浏览器会缓存Cookie对象。浏览器关闭后Cookie对象销毁
持久化Cookie:浏览器会对Cookie做持久化处理,基于文件形式保存在系统的指定目录中,浏览器会将Cookie对象持久化到磁盘中。当失效时间到达后文件删除
Cookie安全性
Cookie对于存储内容是基于明文的方式存储的,所以安全性很低。不要在Cookie中存放敏感数据
在数据存储时,虽然在Servlet4.0中Cookie支持中文,但是建议对Cookie中存放的内容做编码处理,也可提高安全性
Cookie对象方法
setMaxAge()设置Cookie持久化时间(存活时间)setPath()设置Cookie提交路路径限定getCookies()获取一组CookieaddCookie()Cookie写回给客户端浏览器getName()返回 cookie 的名称getValue()cookie 关联的值getPath()获取路径
实例
@WebServlet("/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建Cookie
Cookie cookie = new Cookie("username", "lisi");
//设置Cookie持久化时间(存活时间)
cookie.setMaxAge(30);
Cookie cookie2 = new Cookie("pwd", "lisi");
//设置Cookie提交路路径限定 当访问/aaa 提交这个Cookie
cookie2.setPath("/aaa");
Cookie cookie3 = new Cookie("age", "11");
Cookie cookie4 = new Cookie("gender", URLEncoder.encode("男","UTF-8"));
//将Cookie响应给浏览器
resp.addCookie(cookie);
resp.addCookie(cookie2);
resp.addCookie(cookie3);
resp.addCookie(cookie4);
}
}
@WebServlet("/servlet2.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取Cookie
Cookie[] cookies = req.getCookies();
for (Cookie c:cookies){
if("gender".equals(c.getName())){
System.out.println(c.getName()+':'+URLDecoder.decode(c.getValue(),"UTF-8"));
}else{
System.out.println(c.getName()+':'+c.getValue());
}
}
}
HttpSession
由于HTTP是一种无状态协议,客户端请求服务器,服务器不会记录当前客户端浏览器的状态
HTTPSession是一种保存少量信息至服务器端的技术
原理
1.浏览器第一次请求时,服务器会创建HttpSession保存用户信息
2.服务器将HttpSession的JSESSIONID以Cookie形式响应给浏览器
3.浏览器下一次请求会携带之前的JSESSIONID的Cookie发送给服务器
4.服务器根据JSESSIONID获取对应HttpSession对象
通过这种技术可以解决HTTP协议本身无法记录用户状态情况
特点
- HttpSession保存在服务端
- HttpSession可以存储任何数据
- HttpSession使用Key和Value结构存储数据
- HttpSession存储数据无大小限制
HttpSession生命周期
HttpSession对象生命周期中没有固定的创建时间和销毁时间
何时创建,第一次调用getSession() 或getSession(true)(每次创建Session)
HttpSession对象取决于是否达到超时时间或调用用了Invalidate()方法
如果没有超时或者没有调用Invalidate()方法,HttpSession对象会一直存储在服务器。
Tomcat配置文件web.xml 中配置了默认超时时间为30分钟
HttpSession对象方法
getSession()获取HttpSessiongetId()获取JSESSIONIDsetAttribute()将数据存储到HttpSessionsetAttribute()根据Key修改HttpSession中的数据removeAttribute()根据key删除HttpSession中的数据getCreationTime()HttpSession创建时间getLastAccessedTime()HttpSession最近访问时间getMaxInactiveInterval()HttpSession最大不活动时间invalidate()销毁HttpSession
@WebServlet("/servlet1.do")
public class Servlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取HttpSession 如果不存在则创建一个Session
HttpSession session = req.getSession();
session.setAttribute("username", "123456");
session.setAttribute("password", "123456");
session.setAttribute("gender", "boy");
session.setAttribute("age", "12");
/*销毁session
* 调用改方法后不能对Session进行CURD(增删改查)操作
* */
session.invalidate();
//修改
//session.setAttribute("age","13");
//移除
// session.removeAttribute("age");
}
}
@WebServlet("/servlet2.do")
public class Servlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取HttpSession
HttpSession session = req.getSession();
System.out.println(session.getAttribute("username"));
Enumeration<String> attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()){
String name = attributeNames.nextElement();
System.out.println(name+":"+session.getAttribute(name));
}
System.out.println("JSESSIONID"+session.getId());
System.out.println("创建时间"+new Date(session.getCreationTime()));
System.out.println("最近访问时间"+new Date(session.getLastAccessedTime()));
System.out.println("最大不活动时间"+new Date(session.getMaxInactiveInterval()));
}
}
HttpSession 销毁
HttpSession与Cookie的区别
存储方式不同
- Cookie数据保存在客户端或系统文件中
- HttpSession中的数据存放在服务器中
安全性
- Cookie 数据不安全,浏览器中可以看到Cookie数据内容
- HttpSession 数据安全,浏览器中只能看到Cookie数据中JSESSIONID
数据容量大小
- 单个Cookie保存数据不能超过4K,不同浏览限制Cookie数量
- HttpSession没有容量以及数量的限制
功能不同
- Cookie是通过浏览器实现会话维持
- HttpSession是通过服务器来实现会话状态
域对象
域代表区域,域对象指存储数据的区域
Request域
对应HttpServletRequest对象,也叫请求域
有效范围:一次请求内有效,请求转发数据可以传递
生命周期
创建: 每发生一次请求创建一个独立的请求域
使用: service方法中或者请求转发有效
销毁: 请求结束,已经向浏览器响应数据
Session域
对应HttpServletSession对象,也叫请求域
有效范围:单次会话内有效,可以跨多个请求
生命周期
创建: 会话的产生,第一次发生请求,会话的开始
使用 :本次会话之内,浏览器和服务器之间发生多次请求和响应有效
销毁 :会话结束,如:浏览器失去JSESSIONID、到达最大不活动时间、手动清除
Application域
对应ServletContext对象,也叫应用域
有效范围:当前web服务内,跨会话,跨请求
生命周期
创建: 项目启动
使用: 项目运行任何时间有效
销毁: 项目关闭
Filter(过滤器)
Filter称之为过滤器,它是Servlet技术中最实用的技术,
Filter技术,拦截用户请求,对用户请求进行预处理,服务器响应进行处理
例如:文件拦截、URL级别权限访问控制,过滤敏感词汇,压缩响应信息
过滤器如何实现功能
- 在HttpServletRequest到达Servlet之前,拦截客户端的HttpServletRequest。根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据
- 在HttpServletResponse到达客户端之前,拦截HttpServletResponse。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
- Fliter 接口有一个方法doFilter,通过实现Filter接口方法,编写代码对web资源进行拦截。用户每发送一个请求,Servlet 中service方法中会先调用Fliter中doFilter方法,执行定义好的过滤请求和过滤响应功能代码
过滤器生命周期
- 在web应用启动进行类加载
- web容器创建完毕
- Filter对象实例化后调用
init()方法后,保存到web容器中。等待用户访问资源 - 当用户请求资源中正好时过滤器的过滤路径,调用Filter中
dofFllter方法 - 当web服务关闭,调用Filter中
destroy方法
public class FilterA implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//初始化方法--> 执行1次
System.out.println("FilterA初始化方法");
System.out.println(filterConfig.getInitParameterNames());
}
/**
* 功能描述:过滤器请求方法 符合路径每次请求都会执行 执行多次*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//到达目标资源之前的预处理操作
System.out.println("执行预处理程序FilterA");
//执行下一个过滤或者目标资源
chain.doFilter(request,response);
//离开服务器之前的处理操作
System.out.println("离开服务器之前处理操作FilterA");
}
@Override
public void destroy() {
//销毁--> 执行1次
System.out.println("FilterA销毁方法");
}
}
web.xml
<!--配置过滤器过滤的路径-->
<filter>
<!--过滤器明-->
<filter-name>filterA</filter-name>
<!--指定过滤器-->
<filter-class>com.test.filter.FilterA</filter-class>
</filter>
<filter-mapping>
<filter-name>filterA</filter-name>
<!--过滤器筛选路径-->
<!--<url-pattern>*.jsp</url-pattern>
<url-pattern>*.html</url-pattern>-->
<!--所有请求经过过滤器-->
<!--<url-pattern>/*</url-pattern>-->
<url-pattern>/test/</url-pattern>
<url-pattern>*.do</url-pattern>
</filter-mapping>
使用@WebFilter注解开发
@WebFilter(urlPatterns = {"/test/*"})
public class FilterB implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//初始化方法--> 执行1次
}
/**
* 功能描述:过滤器请求方法 符合路径每次请求都会执行 执行多次
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//到达目标资源之前的预处理操作
System.out.println("执行预处理程序FilterB");
//执行下一个过滤或者目标资源
chain.doFilter(request,response);
//离开服务器之前的处理操作
System.out.println("离开服务器之前处理操作FilterB");
}
@Override
public void destroy() {
//销毁--> 执行1次
System.out.println("销毁方法");
}
}
多个过滤器执行顺序
不使用注解开发情况下Filter按照web.xml中filter-mapping标签先后顺序
使用注解开发情况下Filter按照文件先后顺序
注意:对Filter顺序由严格的要求使用web.xml中配置方式
案例:使用过滤器处理Post乱码
web.xml配置
<!--全局参数-->
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<filter>
<filter-name>encFilter</filter-name>
<filter-class>com.test.filter.EncFilter</filter-class>
<init-param>
<param-name>encoding2</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
过滤器代码
public class EncFilter implements Filter {
private static String en2;
private static String encoding;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//读取初始化参数和全局参数
//读取初始化参数
en2 = filterConfig.getInitParameter("encoding2");
//读取全局参数
ServletContext servletContext = filterConfig.getServletContext();
encoding = servletContext.getInitParameter("encoding");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//到达目标资源之前的处理操作
request.setCharacterEncoding(en2);
response.setCharacterEncoding(encoding);
response.setContentType("text/html;charset=UTF-8");
//执行下一个过滤器或者目标资源
chain.doFilter(request, response);
//离开服务器之前的处理操作
}
}
案例:使用过滤器用户是否登录过验证
web.xml配置
<filter>
<filter-name>loginFilter</filter-name>
<filter-class>com.test.filter.LoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>/test/*</url-pattern>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.html</url-pattern>
</filter-mapping>
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//从Session获得user
HttpServletRequest req=(HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse)response;
Object user = req.getSession().getAttribute("user");
//如果用户访问的是login.jsp 直接放行 如果不是进行过滤
String requestURI = req.getRequestURI();
if(requestURI.contains("login.jsp")||requestURI.contains("test/UserServlet")){
chain.doFilter(req,resp);
return;
}
//判断是否为空
if(user!=null){
//直接放行
chain.doFilter(req,resp);
}else{
//跳转到登录页面
resp.sendRedirect(req.getContextPath()+"/login.jsp");
}
}
}
Listener(监听器)
什么是监听器?
web应用中对某些对象、信息的创建、创建销毁、增加、修改、删除等动作发生后,做出相应的响应处理。
当范围对象的状态发生变化的时候,服务器自动调用监听对象中的方法。
常用于统计在线人数和在线用户,系统加载时进行信息初始化,统计网站的访问量等等
有哪些监听器?
一共有8个监听器
按对象划分分别是:ServletContext对象监听器、HttpSession对象监听器、ServletRequest对象监听器
Request域2个监听器
ServletRequestListener 处理Request对象创建和销毁
| 方法 | 描述 |
|---|---|
| requestInitialized() | Request对象初始化被触发方法发 |
| requestDestroyed() | Request对象销毁化被触发方法 |
ServletRequestAttributeListener 处理Request中数据添加、替换、删除
| 方法 | 描述 |
|---|---|
| attributeAdded() | setAttribute() Request中数据第一次添加会触发该方法 |
| attributeRemoved() | removeAttribute()Request,中数据被移除时触发该方法 |
| attributeReplaced() | setAttribute(),Request中数据被修改会触发该方法 |
Session域4个监听器
HttpSessionListener 处理Session对象创建和销毁
| 方法 | 描述 |
|---|---|
| sessionCreated() | Sessiond对象被创建会触发该方法 |
| sessionDestroyed() | Session销毁时触发方法 |
HttpSessionAttributeListener 处理Session对象数据添加、修改、删除
| 方法 | 描述 |
|---|---|
| attributeAdded | setAttribute(),Session数据第一次添加数据时触发方法 |
| attributeRemoved | removeAttribute() , Session数据被移除时触发方法 |
| attributeReplaced | setAttribute(), Session数据被修改时触发方法 |
HttpSessionBindingListener 针对1个对象 处理Session对象绑定和解绑定
| 方法 | 描述 |
|---|---|
| valueBound() | session绑定对象触发方法 |
| valueUnbound() | session解除绑定对象触发方法 |
HttpSessionActivationListener
| 方法 | 描述 |
|---|---|
| sessionWillPassivate() | Session序列化(保存到硬盘,持久化)触发方法 |
| sessionDidActivate() | Session反序列化(把字节序列转化为对象的过程,存放于内存)触发方法 |
Application域监听器2个
ServletContextListener 处理ServletContext 创建和销毁
| 方法 | 描述 |
|---|---|
| contextInitialized | ServletContext初始化触发方法 |
| contextDestroyed | ServletContext 销毁时触发方法 |
ServletContextAttributeListener 处理ServletContext 中数据添加、修改、删除
| 方法 | 描述 |
|---|---|
| attributeAdded | setAttribute(),ServletContext第一次添加数据触发方法 |
| attributeRemoved | removeAttribute(),ServletContext被移除触发方法 |
| attributeReplaced | setAttribute(),ServletContext数据被修改触发方法 |
Request域实例
public class LoginListener implements ServletRequestListener, ServletRequestAttributeListener {
@Override
public void requestDestroyed(ServletRequestEvent sre) {
//req对象销毁会执行的方法
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
//req创建的时候执行的方法
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
//获取访问当前程序客户端
String ip =request.getRemoteAddr();
//获取当前时间
String time = new Date().toLocaleString();
//访问当前程程序访问位置
StringBuffer requestURL = request.getRequestURL();
//把日志信息保存到本地文件
PrintWriter out = null;
try {
out = new PrintWriter(new FileOutputStream("Z:/IDEProjects/TomCatProject/flower2/src/log.txt",true));
String str = "用户IP:"+ip+"在时间"+time+"访问了"+requestURL;
out.println(str);
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
out.close();
}
}
/*attribute 第一次添加*/
@Override
public void attributeAdded(ServletRequestAttributeEvent srae) {
HttpServletRequest request = (HttpServletRequest)srae.getServletRequest();
System.out.println(request.getContextPath()+"attributeAdded被执行");
}
/*attribute 被移除会执行该方法*/
@Override
public void attributeRemoved(ServletRequestAttributeEvent srae) {
HttpServletRequest request = (HttpServletRequest)srae.getServletRequest();
System.out.println(request.getContextPath()+"attributeRemoved被执行");
}
/*同一个请求中再次设置同名key 会执行改方法 attribute 被替换*/
@Override
public void attributeReplaced(ServletRequestAttributeEvent srae) {
HttpServletRequest request = (HttpServletRequest)srae.getServletRequest();
System.out.println(request.getContextPath()+"attributeReplaced被执行");
}
}
Session域实例
public class OnLine implements HttpSessionListener, HttpSessionAttributeListener {
@Override
public void attributeAdded(HttpSessionBindingEvent se) {
//session.setAtt
//获取session中key
String name = se.getName();
if("user".equals(name)){
//在线人数加1
//获得全局对象 用户保存当前在线人数
ServletContext servletContext = se.getSession().getServletContext();
//获得当前在线人数
Integer online = (Integer)servletContext.getAttribute("online");
if(online==null){
//是否第一个在线人数
online=1;
}else{
online++;
}
servletContext.setAttribute("online",online);
}
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
//session销毁时触发方法
//人数减1
ServletContext servletContext = se.getSession().getServletContext();
Integer online = (Integer) servletContext.getAttribute("online");
if(online==null){
online=0;
}else{
online--;
}
servletContext.setAttribute("online",online);
}
@Override
public void sessionCreated(HttpSessionEvent se) {
//session创建
}
@Override
public void attributeRemoved(HttpSessionBindingEvent se) {
//session.removeAttribute是被触发
}
@Override
public void attributeReplaced(HttpSessionBindingEvent se) {
}
}
/**
* HttpSessionBindingListener 针对于具体的对象,监听指定的对象从session上进行绑定和解绑的过程
* 注意:该监听器不需要配置web.xml文件
* */
public class User implements Serializable, HttpSessionBindingListener, HttpSessionActivationListener {
private static final long serialVersionUID = -3532883031405078802L;
private Integer uid;
private String uname;
private String pwd;
private String alternative;
@Override
public void valueBound(HttpSessionBindingEvent event) {
System.out.println("user对象绑定到session");
}
@Override
public void valueUnbound(HttpSessionBindingEvent event) {
/*
对象从session 中解绑
session失效
session.invalidate() 超过30分钟
session.removeAttribute("user")*/
System.out.println("user对象从session解绑");
}
@Override
public void sessionWillPassivate(HttpSessionEvent se) {
System.out.println("序列化");
}
@Override
public void sessionDidActivate(HttpSessionEvent se) {
System.out.println("反序列化");
}
}
Application域实例
@WebListener
public class ApplicationListener implements ServletContextListener, ServletContextAttributeListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
//Application
System.out.println("Application初始化");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("Application销毁");
}
@Override
public void attributeAdded(ServletContextAttributeEvent sce) {
System.out.println("Application attributeAdded");
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
System.out.println("Application attributeRemoved");
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
System.out.println("Application attributeReplaced");
}
}
TheradLocal处理来SqlSession
public class DBUtil {
private static SqlSessionFactory factory;
private static ThreadLocal<SqlSession> tl = new ThreadLocal<>();
static {
InputStream inputStream = null;
try {
//[1]解析myBatis.xml文件
inputStream = Resources.getResourceAsStream("mybatis.xml");
//[2]获得sqlsession工厂
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获得sqlsession对象
public static SqlSession getSqlSession() {
//获得ThreadLoacl中的sqlsession对象
SqlSession sqlSession = tl.get();
if (sqlSession == null) {
sqlSession = factory.openSession(true);
//把创建好的对象放到ThreadLoacl
tl.set(sqlSession);
}
return tl.get();
}
//关闭sqlsession
public static void closeAll() {
SqlSession sqlSession = tl.get();
if (sqlSession != null) {
sqlSession.close();
}
tl.set(null);
}
}
分页
什么是分页?
当大量数据无法一次性全部显示在网页上,只能将数据分成几段。每一段用一个网页显示,也就是一页。
分页就是将大量数据分成很多页的一种处理手段
分页有什么好处?
- 减少数据库的IO数据量传输,降低数据库读写压力,提高数据库响应速度不用一次性将所有数据查出来,只需要查一部分。
- 减少浏览器和服务器之前大量数据的IO传输,从而提高服务器响应速度
- 占用内存少
- 分页可以减少资源浪费
小案例
mapper层
public interface StuMapper {
//分页查询操作
List<Stu> selectPage(Integer start,Integer size,String name,String age);
//查询数据库总条数
Integer getCount(String name,String age);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.mapper.StuMapper">
<select id="selectPage" resultType="stu">
select * from stu
<where>
<if test="param3!=null and param3!=''">
sname=#{param3}
</if>
<if test="param4!=null and param4!=''">
and age>#{param4}
</if>
</where>
limit #{param1},#{param2}
</select>
<select id="getCount" resultType="int">
select COUNT(*) from stu
<where>
<if test="param1!=null and param1!=''">
sname=#{param1}
</if>
<if test="param2!=null and param2!=''">
and age>#{param2}
</if>
</where>
</select>
util
package com.test.util;
import java.util.List;
/**
* 分页的三个基本属性
* 1.每页几条记录size 可以有默认值5
* 2.当前页号 index 可以有默认值1
* 3.记录总数totalCount:不可能有默认值,需要查询数据库获取真正的记录总数
*
* 4.一共多少页 :totalPageCount=totalCount/size+1
* 5 30 31 32 33 34 35
* 5.上一页 index-1 当前页1,上一页1
* 6.下一页 index+1 当前页是最后一页 下一页:还是最后一页
*
* 扩展
* 分页Bean还可以放要查询的数据 protected List<T> list;
* 分页Bean还可以放页码列表 [1] 2 3 4 5 private int[] numbers;
*
* @author Administrator
*
* @param <T>
*/
public class PageBean<T> {
private int size = 2;//每页显示记录 //ok
private int index = 1;// 当前页号 ok
private int totalPageCount = 1;// 总页数 ok
private int totalCount = 0;// 记录总数 //ok 2
private int[] numbers;//展示页数集合
protected List<T> list;//要显示到页面的数据集
/**
* 得到开始记录
* @return
*/
public int getStartRow() {
return (index - 1) * size;
}
/**
* 得到结束记录
* @return
*/
public int getEndRow() {
return index * size;
}
/**
* @return Returns the size.
*/
public int getSize() {
return size;
}
/**
* @param size
* The size to set.
*/
public void setSize(int size) {
if (size > 0) {
this.size = size;
}
}
/**
* @return Returns the currentPageNo.
*/
public int getIndex() {
if (totalPageCount == 0) {
return 0;
}
return index;
}
/**
* @param currentPageNo
* The currentPageNo to set.
*/
public void setIndex(int index) {
if (index > 0) {
this.index = index;
}
}
/**
* @return Returns the totalCount.
*/
public int getTotalCount() {
return totalCount;
}
/**
* @param totalCount
* The totalCount to set.
*/
public void setTotalCount(int totalCount) {
if (totalCount >= 0) {
this.totalCount = totalCount;
setTotalPageCountByRs();//根据总记录数计算总页�?
}
}
public int getTotalPageCount() {
return this.totalPageCount;
}
/**
* 根据总记录数计算总页?
* 5
* 20 4
* 23 5
*/
private void setTotalPageCountByRs() {
if (this.size > 0 && this.totalCount > 0 && this.totalCount % this.size == 0) {
this.totalPageCount = this.totalCount / this.size;
} else if (this.size > 0 && this.totalCount > 0 && this.totalCount % this.size > 0) {
this.totalPageCount = (this.totalCount / this.size) + 1;
} else {
this.totalPageCount = 0;
}
setNumbers(totalPageCount);//获取展示页数集合
}
public int[] getNumbers() {
return numbers;
}
/**
* 设置显示页数集合
*
* 默认显示10个页码
* 41 42 43 44 [45 ] 46 47 48 49 50
*
*
* 1 2 3 [4] 5 6 7 8 9 10
*
* 41 42 43 44 45 46 47 [48] 49 50
* @param totalPageCount
*/
public void setNumbers(int totalPageCount) {
if(totalPageCount>0){
//!.当前数组的长度
int[] numbers = new int[totalPageCount>10?10:totalPageCount];//页面要显示的页数集合
int k =0;
//
//1.数组长度<10 1 2 3 4 .... 7
//2.数组长度>=10
// 当前页<=6 1 2 3 4 10
// 当前页>=总页数-5 ......12 13 14 15
// 其他 5 6 7 8 9 当前页(10) 10 11 12 13
for(int i = 0;i < totalPageCount;i++){
//保证当前页为集合的中�?
if((i>=index- (numbers.length/2+1) || i >= totalPageCount-numbers.length) && k<numbers.length){
numbers[k] = i+1;
k++;
}else if(k>=numbers.length){
break;
}
}
this.numbers = numbers;
}
}
public void setNumbers(int[] numbers) {
this.numbers = numbers;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
pojo、service 层省略
controller层
@WebServlet("/test/StuFindPage")
public class StuFindPage extends HttpServlet {
private StuService stuService = new StuServiceImpl();
private Integer size=2;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收前台页面中的数据
String sname = req.getParameter("sname");
String age = req.getParameter("age");
//从前台获得当前页数
String ind = req.getParameter("index");
int index =1;
if(null!=ind&&!"".equals(ind)){
index = Integer.parseInt(ind);
}
//从前台获得size
String si = req.getParameter("size");
if(null!=si&&!"".equals(si)){
size = Integer.parseInt(si);
}
PageBean pg = new PageBean();
//把当前的页数设置到PageBean 中
pg.setIndex(index);
pg.setSize(size);
//总条数设置到PageBean 中
pg.setTotalCount(stuService.getCount(sname,age));
//数据的处理 调用service层返回结果
List<Stu> list = stuService.findPage(pg.getStartRow(),pg.getSize(),sname,age);
//根据返回结果做出响应
req.setAttribute("list",list);
req.setAttribute("pg",pg);
req.setAttribute("sname",sname);
req.setAttribute("age",age);
req.getRequestDispatcher("/main.jsp").forward(req,resp);
}
}
view层
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
<style>
table{
margin: auto;
border: 1px solid ;
width: 40%;
}
table th,td{
border: 1px solid;
}
</style>
<base href="<%=request.getContextPath()+'/'%>">
<script type="text/javascript">
function chang(index,size) {
var sname= document.getElementById("sname").value
var age=document.getElementById("age").value
window.location.href="test/StuFindPage?size="+size+"&index="+index+"&sname="+sname+"&age="+age;
}
</script>
</head>
<body>
<h3 align="center">学生信息</h3>
<center>
<form action="">
姓名<input type="text" value="${sname}" name="sname" id="sname"/>
年龄<input type="text" value="${age}" name="age" id="age"/>
<input type="submit" value="查询">
</form>
</center>
<hr>
<table cellspacing="0px" cellpadding="0px">
<tr>
<th>学生ID</th>
<th>学生姓名</th>
<th>学生年龄</th>
<th>学生头像</th>
<th>操作</th>
</tr>
<c:forEach items="${requestScope.list}" var="stu">
<tr>
<td>${stu.sid}</td>
<td>${stu.sname}</td>
<td>${stu.age}</td>
<td><img src="upload/${stu.filename}" width="90px" /></td>
<td><a href="test/FileDownLoad?filename=${stu.filename}&filetype=${stu.filetype}" >下载</a></td>
</tr>
</c:forEach>
<tr>
<th colspan="5">
<a href="javascript:chang(1,${pg.size})">首页</a>
<c:if test="${pg.index > 1}">
<a href="javascript:chang(${pt.index-1},${pg.size})">上页</a>
</c:if>
<c:forEach items="${pg.numbers}" var="i">
<c:if test="${i==pg.index}">
[<a href="javascript:chang(${i},${pg.size})">${i}</a>]
</c:if>
<c:if test="${i!=pg.index}">
<a href="javascript:chang(${i},${pg.size})">${i}</a>
</c:if>
</c:forEach>
<c:if test="${pg.index<pg.totalPageCount}">
<a href="javascript:chang(${pg.index+1},${pg.size})">下页</a>
</c:if>
<a href="javascript:chang(${pg.totalPageCount},${pg.size})">尾页</a>
每页显示<select onchange="chang(1,this.value)">
<c:forEach begin="2" end="10" step="2" var="i">
<c:if test="${pg.size==i}">
<option value="${i}" selected="selected" >${i}</option>
</c:if>
<c:if test="${pg.size!=i}">
<option value="${i}" >${i}</option>
</c:if>
</c:forEach>
</select>条记录
,总条数:${pg.totalCount}
</th>
</tr>
</table>
</body>
</html>
文件上传和下载
使用Comm组件上传文件
/*
* f.getName() 文件名
* f.getFieldName() 表单name属性
* f.getSize() 文件大小
* f.getContentType() 文件类型
* isFormField() 是file表单项 false 不是则true
* getString 获取表单项值
* getString("UTF-8") 获取表单项值,并解决中文乱码
* */
@WebServlet("/test/FileUpLoad")
public class FileUpLoad extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//中文乱码解决
//req.setCharacterEncoding("UTF-8");
//创建文件上传工厂对象
FileItemFactory factory = new DiskFileItemFactory();
//创建文件上组件对象
ServletFileUpload upload = new ServletFileUpload(factory);
//解决上传文件 文件名中文乱码问题
upload.setHeaderEncoding("UTF-8");
//设置文件大写 异常无法捕获
//upload.setSizeMax(20*1024);
String sname = "";
Integer age = 0;
String filename = "";
String filetype = "";
try {
//接收前台提交的所有的表单对象
List<FileItem> list = upload.parseRequest(req);
for (FileItem f : list) {
System.out.println(f.getName() + "--" + f.getFieldName() + "--" + f.getSize() + "--" + f.getContentType() + "--" + f.isFormField());
if (!f.isFormField()) {
String substring = f.getName().substring(f.getName().lastIndexOf("."));
//获取文件类型
filetype = f.getContentType();
//指定上传文件类型
if (!".jpg".equals(substring) || !".jpg".equals(substring) || !".jpg".equals(substring)) {
req.setAttribute("msg", "文件只能是图片");
req.getRequestDispatcher("/save.jsp").forward(req, resp);
return;
}
//指定文件大小
if (f.getSize()>20*1024) {
req.setAttribute("msg", "文件最大为20K");
req.getRequestDispatcher("/save.jsp").forward(req, resp);
return;
}
//重新命名 UUIID
//文件名称
String uuid = UUID.randomUUID().toString();
filename = uuid + substring;
//动态获取服务器目录
String realPath = req.getServletContext().getRealPath("/upload");
//判断上传文件目录是否存在
File file = new File(realPath);
if (!file.exists()) {
file.mkdirs();
}
//文件表单项
f.write(new File(file, filename));
} else {
if ("sname".equals(f.getFieldName())) {
//获取表单现内容,解决中文乱码
sname = f.getString("UTF-8");
}
if ("age".equals(f.getFieldName())) {
//获取表单内容
age = Integer.parseInt(f.getString());
}
}
}
Stu stu = new Stu(sname, age, filename, filetype);
//添加
StuService stuService = new StuServiceImpl();
int save = stuService.save(stu);
if (save > 0) {
resp.sendRedirect(req.getContextPath() + "/test/StuFindPage");
} else {
req.setAttribute("msg", "注册失败");
req.getRequestDispatcher("/save.jsp");
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
基于Part接口文件的上传
/**
* 基于Part接口方式实现文件上传
* */
@WebServlet("/test/FileUpLoad2")
@MultipartConfig
public class FileUpload2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收前台的图片
Part part = req.getPart("image");
//form-data; name="image"; filename="111111.jpg"
String header = part.getHeader("content-disposition");
//对上传文件重命名
//获取后缀
String su = header.substring(header.lastIndexOf("."),header.length()-1);
String uuid = UUID.randomUUID().toString();
String filename = uuid + su;
//把 part 写到文件
//part.write("D:/img/"+filename);
part.write(req.getServletContext().getRealPath("/upload/")+filename);
System.out.println(header);
}
}
文件下载
@WebServlet("/test/FileDownLoad")
public class FileDownLoad extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取需要下载的文件
String filename = req.getParameter("filename");
//把需要下载的文件读取
//获得服务器目录
String realPath = req.getServletContext().getRealPath("/upload");
File file = new File(realPath+'/'+filename);
InputStream input = new FileInputStream(file);
//把文件写入到本地
//OutputStream output = new FileOutputStream("");
//写到客户端
OutputStream output = resp.getOutputStream();
//设置下载文件大小
resp.setContentLength((int)file.length());
//设置下载文件类型
resp.setContentType(req.getParameter("filetype"));
//设置响应头
resp.setHeader("Content-Disposition","attachment;filename="+filename);
IOUtils.copy(input,output);
//关闭资源
output.close();
input.close();
}
}
异步(Asynchronous)
什么是异步(Asynchronous)?
异步是指:当程序1调用程序2时,程序1径自继续自己的下一个动作,不受程序2的的影响。
异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
什么是同步(Synchronous)?
同步是指:当程序1调用程序2时,程序1停下不动,直到程序2完成回到程序1来,程序1才继续执行下去。
同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。
同步和异步的区别?
举例 :普通B/S模式(同步) Ajax技术(异步)
同步:提交请求--->等待服务器处理--->处理完毕响应给浏览器(这期间客户端浏览器不能干任何事)
异步:请求通过事件触发--->服务器处理(这期间浏览器可以做其他事情) --->处理完毕
举个例子 打电话时同步 发消息是异步
Ajax
- Ajax 即 Asynchronous javascript And XML(异步JavaScript 和XML)
- Ajax是一种无需重新加载整个页面情况下,能够更新部分网页的技术
- AJAX的最大的特点: 异步访问,局部刷新
AJAX异步原生js提交请求的步骤为:
1 获取XMLHTTPRequest对象xhr=new XMLHttpRequest();
2打开链接 xhr.open("GET","loginServlet?uname="+uname,true);
3设置回调函数xhr.onreadystatechange=showRnturnInfo;
4提交数据 xhr.send(data)
XMLHTTPequest的几种状态
readyState代表请求的状态或者形情
0:请求没有发出(在调用open()之前)
1:请求已经建立但是还没有发出(调用send()之前)
2:请求已经发出正在处理之中(这里通常可以从响应的到内容头部)
3请求已经处理,响应中有数据可用,但是服务器还没有完成响应
4:响应已经完成,可以访问服务器响应并使用它
在AJAX中有以上五种状态,但是通常只使用状态4
status代表响应状态码
200
404
Ajax案例-验证用户名是否被占用
页面代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户注册</title>
<base href="<%=request.getContextPath()+'/'%>">
<script type="text/javascript">
function checkZh() {
var zh= document.getElementById("zh").value
if(null==zh||""==zh){
document.getElementById("zh_sapn").innerText="用户名不能为空"
}else{
var xhr
//发送ajax请求 判断用户名是否可用
if(window.ActiveXObject){
xhr= new ActiveXObject("Microsoft.XMLHTTP")
}else{
//创建 XMLHttpRequest
xhr= new XMLHttpRequest();
}
//和服务器建立连接
xhr.open("post","test/CheckName",true)
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded")
//执行回掉函数
xhr.onreadystatechange= function () {
if(xhr.status==200){
//接收响应数据
var text=xhr.responseText
//把响应内容给span标签
document.getElementById("zh_sapn").innerText=text
}else if(xhr.status==404){
alert("路径找不到");
}else if(xhr.status==500){
alert("运行异常");
}else{
alert("ajax响应失败");
}
};
//发送请求数据
xhr.send("zh="+zh)
}
}
/*
老版IE创建方式
xhr= new ActiveXObject("Microsoft.XMLHTTP")
xhr.open(method,url,是否异步,服务器用户,服务器密码)
get方式
xhr.open("get","test/CheckName?zh="+zh,true)
xhr.send(null);
post 方式
xhr.open("post","test/CheckName",true)
xhr.send("zh="+zh)
前台接收服务器响应数据
var text=xhr.responseText
status 状态码
*/
</script>
</head>
<body>
<h1>用户注册页面</h1>
<hr>
<form action="test/UserServlet" method="post">
<p>
用户名:<input type="text" name="uname"/>${requestScope.msg}
</p>
<p>
账号名:<input type="text" name="zh" id="zh" onblur="checkZh()"/>${requestScope.msg}
<span id="zh_sapn"></span>
</p>
<p>
密码:<input type="password" name="pwd"/>
</p>
<p>
<input type="submit" value="提交" />
<input type="reset" value="清空"/>
</p>
</form>
</body>
</html>
后台Servlet代码
@WebServlet("/test/CheckName")
public class CheckName extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8;");
//接收前台页面数据
String zh = req.getParameter("zh");
//System.out.println(zh);
boolean flag = false;
if("test".equals(zh)){
flag=true;
}
//根据返回结果 给用户做出响应
if(flag){//账号在数据中存在
resp.getWriter().println("账号被占用");
}else{
resp.getWriter().write("账号可以使用");
}
}
}
AJAX数据格式处理
- 响应普通文本
- 响应json
- 响应xml
页面代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Ajax响应数据类型</title>
<base href="<%=request.getContextPath()+'/'%>">
<script type="text/javascript">
function demo1() {
var xhr = new XMLHttpRequest();
xhr.open("get","test/AjaxResponseType",true)
xhr.onreadystatechange=function () {
if(xhr.readyState==4&xhr.status==200){
//接收响应的数据
//var text = xhr.responseText;
//console.log(text);
/* //方式1:把字符串转为可识别js对象
eval("var user="+text)
console.log(user.uid);
/!*方式2:字符串转JSON对象
* 注意 字符串中key必须用""括住
* *!/
var user2 = JSON.parse(text);
console.log(user2.uid);*/
//接收XML格式的数据
var xml = xhr.responseXML;
alert(xml.getElementsByTagName("sname")[0].innerHTML)
}
}
xhr.send(null)
}
function jsonTst(){
//json 是一种数据格式
var json1 ={sid:1,sname:'zs',age:18}
console.log(json1.sid)
var json2 ={"sid":1,"sname":'zs',"age":18}
console.log(json2.sname)
var json3={
sid:1,
sname:'zs',
age:18,
study:function () {
console.log("--study--")
}
}
console.log(json3.study)//函数体
console.log(json3.study())//执行函数 获得方法返回值没有输出undefined
//多个json
var json4=[{sid:1,sname:'zs',age:18},
{sid:2,sname:'zs2',age:18},
{sid:3,sname:'zs3',age:18}]
for (var i in json4) {
console.log(json4[i].sname)
}
}
</script>
</head>
<body>
<button onclick="demo1()">ajax请求</button>
<button onclick="jsonTst()">jsonTest</button>
</body>
</html>
后台代码
/**
* 响应数据类型
* 普通文本
*
* json
* 1.手动更改toString方法
* 2.将对象属性拼接成json字符串
* 3.使用gson包
*
* xml
*/
@WebServlet("/test/AjaxResponseType")
public class AjaxResponseType extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//resp.setContentType("text/html;charset=utf-8");
/*响应普通文本*/
//resp.getWriter().write("ajax响应成功");
/******************************************************/
/*响应JSON*/
User user = new User(10, "ls", 19);
//resp.getWriter().println(user);
/* String json = '{' + "uid:" + user.getUid() + ',' + "uname:" + user.getUname() + ',' + "age:" + user.getAge() + '}';
resp.getWriter().println(json);*/
/* Gson gson = new Gson();
String json = gson.toJson(user);
resp.getWriter().println(json);*/
/******************************************************/
/*响应XML*/
String xml = "<students>" +
"<student>" +
"<sid>1</sid>"+
"<sname>zs</sname>"+
"<age>19</age>"+
"</student>" +
"<student>" +
"<sid>2</sid>"+
"<sname>ls</sname>"+
"<age>19</age>"+
"</student>"+
"</students>";
resp.setContentType("text/xml;charset=utf-8");
resp.getWriter().println(xml);
}
}
jQuery Ajax使用
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>ajax结合jQuery使用</title>
<base href="<%=request.getContextPath()+'/'%>">
<script type="text/javascript" src="js/jquery-1.12.3.min.js"></script>
<script type="text/javascript">
$(function () {
$("#but").click(function () {
$.ajax({
//发送请求是否试异步的
async:true,
//文件类型
//contentType:"application/x-www-form-urlencoded",
//请求路径
url:"test/AjaxJQ",
//请求方式
type:"get",
//请求参数
data:"name=zs&pwd=123",
//json格式
//data:{name:'zs',pwd:123},
//服务器返回数据类型
dataType:"json",
//回调函数接收响应数据
success:function(e){
//e 响应结果
console.log(e)
},
//在创建xhr对象之前进行修改
boforeSend:function (xhr) {
},
error:function () {
alert("ajax响应失败")
}
})
})
})
</script>
</head>
<body>
<button id="but">ajax请求发送</button>
</body>
</html>
$(function () {
$("#but").click(function () {
//加载js文件中代码
$.getScript("js/my.js")
//获得json格式数据
$.getJSON("","",function (result) {
})
})
})
$(function () {
$("#but").click(function () {
//Ajax请求加载数据 请求默认get
/*
// URL请求路径 data发送参数 callback回调函数,成功时执行
$("#div1").load("test/AjaxLoad", "uname=zs", function () {
})
*/
/*$("#div1").load("test/AjaxLoad", "uname=zs")*/
/*****************************************************/
/*
简单版 ajax请求 get方式
URL 请求路径
data 请求参数
callback 回调函数 ,成功时执行
type 返回内容格式
*/
$.load(
"test/Task",
"",
function (re) {
}
//, "json"
)
/*****************************************************/
/*$.post(
"test/AjaxLoad",
"uname=zs",
function (result) {
alert(result)
}
, "json"
)*/
})
})
jsonp跨域处理
/*不能跨域*/
$(function () {
$("#bu1").click(function () {
$.ajax({
url:"http://127.0.0.1:8080/ajax01/test/AjaxKY",
type:"get",
//jsonp 代表支持跨域请求
dataType:"jsonp",
data:{funName:"h1"},
success:function () {
alert("123");
}
})
})
})
function h1(te) {
alert(te);
}
web.xml配置文件
设置默认访问页面
<!--
设置默认访问页面
如果没有配置 <welcome-file-list>
默认会使用全局配置文件TomCat\conf\web.xml中 <welcome-file-list>配置
-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>login.html</welcome-file>
</welcome-file-list>
服务启动时加载顺序
<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>com.test.servlet.Servlet1</servlet-class>
<!--服务启动加载顺序-->
<load-on-startup>6</load-on-startup>
</servlet>
servlet上下文初始化参数
<!--配置全局信息-->
<context-param>
<param-name>username</param-name>
<param-value>test</param-value>
</context-param>
<context-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</context-param>
设置描述信息
<servlet>
<!--描述信息-->
<description>test servlet life cycle</description>
<!--显示名字-->
<display-name>LifeServlet</display-name>
<servlet-name>lifeServlet</servlet-name>
<servlet-class>com.test.servlet.LifeServlet</servlet-class>
</servlet>
设置初始化参数
<servlet>
<servlet-name>servlet3</servlet-name>
<servlet-class>com.test.servlet.Servlet3</servlet-class>
<!--
servlet初始参数
每个Servlet独有信息
-->
<init-param>
<param-name>computer</param-name>
<param-value>微星</param-value>
</init-param>
<init-param>
<param-name>CPU</param-name>
<param-value>i9</param-value>
</init-param>
</servlet>
url-pattern请求映射路径
- 精确匹配 /servlet3.do
- 扩展名匹配 *.abc
- 路径匹配 /aaa/*
- 任意匹配 /
- 匹配所有 /*
优先级:精确匹配 >长路径> 扩展名匹配
精确匹配
精确匹配是指中配置的值必须与url完全精确匹配。
<servlet-mapping>
<servlet-name>servlet3</servlet-name>
<url-pattern>/servlet3.do</url-pattern>
</servlet-mapping>
扩展名匹配
“*”表示匹配任意字符。在扩展名匹配中只要扩展名相同都会被匹配和路径无关
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<!--后缀名.d的访问的是servlet2-->
<url-pattern>*.d</url-pattern>
</servlet-mapping>
路径匹配
根据请求路径进行匹配,在请求中只要包含该路径都匹配。“*”表示任意路径以及子路径。
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<!--以/aaa/开头访问的都是(不包含web.xml配置的Servlet路径)servlet2-->
<url-pattern>/aaa/*</url-pattern>
</servlet-mapping>
任意匹配
匹配所有但不包含JSP页面、web.xml中配置的其他servlet路径
<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<!--/匹配所有映射路径 不包括jsp
/后面在web.xml中没有配置servlet的路径
一律访问的是当前servlet1
例如/servlet2.do 访问的是servlet2
/aaa.html 访问的是servlet1
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
匹配所有
以/开头访问的都是(不包含web.xml配置的Servlet路径)访问当前servlet
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<!--以/开头访问的都是(不包含web.xml配置的Servlet路径)servlet2-->
<url-pattern>/*</url-pattern>
</servlet-mapping>
URL映射方式
在web.xml文件中支持将多个URL映射到一个Servlet中,但是相同的URL不能同时映射到两个Servlet中
方式一:在一个标签中添加多个url映射
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<!--后缀名.d的访问的是servlet2-->
<url-pattern>*.d</url-pattern>
<!--以/abc开头的访问的都是servlet2-->
<url-pattern>/abc*</url-pattern>
<!--以/开头访问的都是(不包含web.xml配置的Servlet路径)servlet2-->
<url-pattern>/*</url-pattern>
<url-pattern>/aaa/*</url-pattern>
<url-pattern>/servlet2.do</url-pattern>
</servlet-mapping>
方式二::在多个标签中添加1个url映射
<servlet-mapping>
<servlet-name>servlet3</servlet-name>
<url-pattern>/servlet3.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servlet3</servlet-name>
<url-pattern>/servlet3</url-pattern>
</servlet-mapping>
设置Session会话超时
HttpSession超时后销毁
<session-config>
<!--最大不活动时间-->
<!--Session超过1分钟自动销毁-->
<session-timeout>1</session-timeout>
</session-config>
设置过滤器
<!--配置过滤器过滤的路径-->
<filter>
<!--过滤器明-->
<filter-name>filterA</filter-name>
<!--指定过滤器-->
<filter-class>com.test.filter.FilterA</filter-class>
</filter>
<filter-mapping>
<filter-name>filterA</filter-name>
<!--过滤器筛选路径-->
<!--<url-pattern>*.jsp</url-pattern>
<url-pattern>*.html</url-pattern>-->
<!--所有请求经过过滤器-->
<!--<url-pattern>/*</url-pattern>-->
<url-pattern>/test</url-pattern>
<url-pattern>*.do</url-pattern>
</filter-mapping>
配置监听器
<!--声明当前监听器-->
<listener>
<listener-class>com.test.listener.LoginListener</listener-class>
</listener>
<listener>
<listener-class>com.test.listener.OnLine</listener-class>
</listener>
设置错误提示页面
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.html</location>
</error-page>