目录
Tomcat
Get与Post请求的区别
Servlet
体系
请求流程
生命周期
配置loadOnStartup
线程安全问题
问题原因
解决方案
Servlet核心类
请求
响应
转发与重定向
转发
重定向
Cookie与HttpSession
Cookie
HttpSession
ServletConfig
ServletContext
总结
Filter
作用:
过程,结构:
Tomcat
免费,性能可以。
bin:Tomcat使用的二进制文件,包括启动(startup.bat)与关闭(shutdown.bat)
conf:Tomcat的配置文件
webapps:存储服务的项目
使用:
1.下载解压到层级较浅的路径
2.将前端项目拷贝到Tomcat的webApps文件夹下
3.启动(startup.bat)
Get与Post请求的区别
get: 会将上传的数据追加到网址尾部,?后表示的是参数,参数以key=value形式展示,多个参 数直接使用&号连接
上传数据最大4kb或8kb(根据浏览器决定) 会进入到doGet方法中
post: 不会将上传的数据追加的网址尾部
上传数据大小无限制 会进入到doPost方法中
Servlet
体系
1.
Servlet 接口:实现HTTP规范
void init(ServletConfig var1); 初始化方法
ServletConfig getServletConfig(); 获取Servlet的Config对象
void service(ServletRequest var1, ServletResponse var2)
String getServletInfo();
void destroy(); 销毁方法
ServletConfig 接口:Servlet配置信息
String getServletName(); 获取Servlet名字
ServletContext getServletContext(); 获取Servlet上下文
String getInitParameter(String var1); 获取Servlet初始化参数
Enumeration<String> getInitParameterNames();获取初始化参数名
2.GenericServlet主要实现了Servlet, ServletConfig两个接口的方法
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
}
3,HttpServlet继承于GenericServlet,对其进行了扩展,用于接收所有用户请求,根据不同的请求调用不同的方法,并处理
public abstract class HttpServlet extends GenericServlet {
}
void service(ServletRequest req, ServletResponse res){
}接收所有用户请求
void service(HttpServletRequest req, HttpServletResponse resp){
}根据请求不同调用不同的方法
此外还有具体的方法用来处理不同的方法
4.自定义的Servlet,继承于HttpServlet,需要重写doGet与doPost方法(主要这两个)用来实现与客户端的交互
public class MyServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
请求流程
Servlet请求流程
生命周期
init:初始化Servlet对象
注意:一个Servlet类自始至终只会创建一个对象 当没有配置loadOnStartup=1时,第一次访
问该Servlet时创建给Servlet的对象,后面访问该 Servlet时将不会创建该Servlet对象,而是直接执行service,对其进行服务
如果配置了loadOnStartup=1时,当服务器启动时就会创建该Servlet对象,以后不管第几次访 问该Servlet都不会在创建该Servlet对象
配置loadOnStartup
第一种:打开web-info文件夹下web.xml文件 配置以下内容
<servlet>
<servlet-name>Servlet名称</servlet-name>
<servlet-class>自建Servlet类所在位置</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet名称</servlet-name> //跟上面的Servlet名称一样
<url-pattern>/访问地址</url-pattern>
</servlet-mapping>
第二种
Servlet3.0以后可以在Servlet类上使用@WebServlet("/访问地址")
@WebServlet(name = "TestServlet",value = "/test",loadOnStartup = 1)
线程安全问题
问题原因
因为Servlet实例是单例模式,当多个客户端并发访问同一个Servlet类时,Tomcat会创建多 个线程,多个线程会使用同一个Servlet实例,会导致线程安全问题
解决方案
第一种实现SingleThreadModel接口
让自己创建的的Servlet类实现SingleThreadModel接口,每个线程都会创建servlet实例,避免了多线程使用通过Servlet实例的请求,但是使用这种方式会导致对客户端的请求响应效率变低,增加了服务器因频繁创建和销毁Servlet实例的开销,因此此种方式一般使用,并且其已经过时。
第二种使用锁
在需要的地方使用锁,但这样会影响效率
第三种
在Servlet实例中在避免使用成员变量属性,只使用局部变量
Servlet核心类
请求
类名:HttpServletRequest
作用:客户端向服务器发送的请求信息都会被封装到request对象
方法:
1.getParameter() 获取html表单提交的name
String num = request.getParameter("num");
<input type="text" name = "num"/>
2.setCharacterEncoding() 设置请求数据编码格式
注意: 对get请求无效 ,Tomcat8以前默认编码格式为ISO8859-1.所以此时上传会乱码
request.setCharacterEncoding("utf-8");
3.getMethod() 获取客户端请求返回格式
String method = request.getMethod();
4.getRequestURL() 获取客户端的请求URL,(不包含url上的参数)
String url = request.getRequestURL().toString();
5.getProtocol() 获取客户端提交数据的协议及版本
String protocol = request.getProtocol();
6.getHeaderNames() 获取请求头中所有的key
getHeader(key) 根据请求头中的key获取对应value
Enumeration<String> en = request.getHeaderNames();
while(en.hasMoreElements()){
String key = en.nextElement();
String value = request.getHeader(key);
System.out.println(key+":"+value);
}
7.getInputStream() 获取请求正文 ,获取客户端请求的输入流
ServletInputStream inputStream = request.getInputStream()
响应
HttpServletResponse
作用:用于响应客户端请求(给客户端发数据)
方法:
设置响应状态行:
1.setStatus() 设置状态行中的状态码
1xx、2xx、3xx 为成功响应吗,4xx、5xx 为失败响应码
设置响应头:
2. response.setContentType("text/html;charset=UTF-8");
//setContentType(*): 设置响应头中的Content-Type属性,设置响应客户端的数据
格式
//等价于: response.setHeader("Content-Type","text/html")
//同时设置服务端的编码格式和客户端响应的文件类型及响应时的编码格式
3. response.setContentLength(1024);//setContentLength:设置响应客户端的数据长度(一般无需设置)
4. response.setHeader("Content-Type","text/html");//setHeader:设置其他的响应头属性
设置响应正文
5.setCharacterEncoding(); 设置响应客户端的数据编码格式
response.setCharacterEncoding("utf-8");
6.getOutputStream(); 字节流 如果要响应文件数据给客户端,则需要使用字节流
ServletOutputStream outputStream = response.getOutputStream();
7.getWriter(); 字符流 如果响应文本数据-HTML文档,则使用字符流***
PrintWriter out = response.getWriter();
例:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
//设置请求的文本类型
resp.setContentType("text/html;charset=utf-8");
//设置服务端
String num = req.getParameter("num");//通过请求对象获取用户输入的内容
Chaxun.getStudent(Integer.parseInt(num));//这可以写gdbc
PrintWriter writer = resp.getWriter();//响应一下,写入一个界面
writer.println("<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Title</title>\n" +
"</head>\n" +
"<body>\n" +
"成功"+
"</body>\n" +
"</html>");
System.out.println("测试----get");
}
转发与重定向
当客户端请求到了某个Servlet类之后,Servlet类进行处理,但是并不使用这个Servlet来响 应客户端,而是要使用另一个Servlet来响应。这样一个接收,一个响应
转发
特点:
1. 转发是在服务器端,两个Servlet之间的请求行为
2. 浏览器只对服务器进行了一次请求 浏览器的地址不会改变,浏览器请求ServletA,ServletA转到ServletB由ServletB响应浏览器,浏览器显示的是ServletA的访问地址
3. 转发过程中,可以通过request对象传递数据
方法:
a:
request.setAttribute() key---value 传值
request.getRequestDispatcher(" /转发的地址").forward(request,response); 转发
b:
request.getAttribute()获取到了
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("进a了");
request.setCharacterEncoding("utf-8");//
response.setContentType("text/html;charset=utf-8");//统一编码
request.setAttribute("key","Servlet携带的数据");
request.setAttribute("username",1);
//对应的key --- value
//如果直接给简单类型数据,则会自动装箱为对应的封装类对象,也可以传递其它对象
request.getRequestDispatcher("/B").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("到b了");
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
int username = (int) request.getAttribute("username");
String info = (String) request.getAttribute("key");
//这时就可以把数据响应到客户端
System.out.println("username"+username);
System.out.println("key"+info);
转发前请求方式是什么,转发后请求方式不变
重定向
流程:
特点:
1. 重定向是客户端的行为
2.浏览器对服务器发送了两次请求;
3.重定向是由浏览器再次发送请求,浏览器地址会改变为转发的地址;
4.不能通过request对象将ServletA中的数据传递给ServletB
关键方法:a跳向b
a中写 response.sendRedirect("/ServletB访问URL")
String path = request.getContextPath();得拼一个项目的地址
import com.sun.jndi.toolkit.url.UrlUtil;
导入这个包,万一传值,需要中文编码解码
//将ServletA重定向到给ServletB
//不带值
response.sendRedirect("/ServletB访问URL");
//带值
//ServletA:在响应浏览器重定向地址的时候,在URL声明参数
response.sendRedirect("/项目名/ServletB?key=value");
//ServletB: 获取浏览器请求的参数
String value = request.getParameter("key");
//注意:
//1,重定向时传递中文会乱码需要使用UrlUtils类进行编码与解码
//编码:UrlUtil.encode(username,"utf-8");
//解码:UrlUtil.decode(info,"utf-8");
//2,路径中需要写项目名
例:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
System.out.println("进入a");
String path = request.getContextPath();
System.out.println(path);
response.sendRedirect(path+"/B");//得拼接地址,项目的地址
//如果要传参数,得自己拼,例如:
/**
* String str1 = UrlUtil.encode("你好","utf-8");
*中文将其编码,才可以拼接
* response.sendRedirect(path+"/B?key=str1&name=qwer")
* 多个值用&符号连接
*
*/
}
经验:两个Servlet传数据时,选择转发,实现页面跳转用重定向。
1.转发的两个请求方式不会变,
是服务器之间的交互,
只有一次请求,
使用request
可以转发对象
2.重定向后面那个绝对是get,
是两次请求,
使用response
只能拼字符串
Cookie与HttpSession
Cookie
Cookie是在浏览器访问web服务器上的某个资源时,由web服务器在响应浏览器时通 过响应头附带的传送给浏览器并存储在浏览器端的一小段数据
一旦web浏览器保存了来自于某个服务器的Cookie,那么当浏览器再次访问这个服务 器的时候,会通过请求头将cookie传递给web服务器
浏览器访问服务器的时候,只会携带由当前服务器存储在客户端的cookie
Cookie中缓存的数据数据是以键值对形式存储的(name-value)
使用:
cookie能new出来
通过响应的方法 response.addCookie(cookie); 添加
通过请求获取 request.getCookies();因为是获取所有,所以返回的为cookie数组;
中文得编码
Cookie 自带的方法:
setPath() 设定使用的范围,不设置的话,默认此服务器所有项目都可以用
setMaxAge() 设定最大时效性,默认-1,浏览器关闭就清空,单位为s
getName() 获取name值
getValue() 获取value值
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
Cookie cookie = new Cookie("name", "value");
//cookie.setPath("/当前项目名");不设定地址,此服务器所有项目都能用
Cookie cookie2 = new Cookie(URLEncoder.encode("纳西妲","UTF-8"),URLEncoder.encode("布耶尔","UTF-8"));
//中文得编码
response.addCookie(cookie);//响应时发给客户
response.addCookie(cookie2);
//cookie.setMaxAge(-1); 不写时效性,浏览器关闭会自动清空,默认为-1 ,单位为s
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
Cookie[] requestCookies = request.getCookies();//通过请求获取cookies
for (Cookie cookie : requestCookies) {
String key = URLDecoder.decode(cookie.getName(),"utf-8");
String value = URLDecoder.decode(cookie.getValue(),"utf-8");
//有中文先解码
//取key
//取value
System.out.println(key + "------" + value);
}
response.getWriter().print("完成");
第二次直接访问/S2,cookie也能被取到,因为浏览器没关闭
cookie的优点:
可以灵活的配置过期规则
简洁,cookie中存储的是最简单的键值对格式的文本数据
数据的持久性,保存到浏览器中的cookie在过期之前是持久存储的
缺点:
不同浏览器所支持存储数据的大小,cookie数目,各个浏览器有自己的标准
用户可以通过浏览器设置禁用Cookie,因此限制了cookie的使用场景
cookie是存储在客户端浏览器中的,有被纂改的风险,有潜在的安全隐患
HttpSession
Session对象,就是当浏览器向服务器发送请求建立连接后,由服务器创建的存储在服务器 端的用于记录用户状态对象。
所以不同浏览器,就是不同客户端
1.原理
1.当浏览器第一次请求服务器时,服务器为这个连接创建一个Sess对象,当服务器响应浏览器请求时,自动通过response(响应)将sessionID写入到浏览器的cookie中
2.服务器再次接收到浏览器请求时,先读取cookie,判断cookie中的jsessionid,通过jsession的值到服务器中找session对象,如果可以找到,不会创建新的session,找到了,直接用
2.特性
服务器会为每个客户端连接分配一个Session对象,存储在服务器上
同一个浏览器发送多次请求,同属于一次会话,共享同一个Session对象
首次使用到Session时,服务器会自动创建Session,并创建Cookie存储SessionId发送回客户端
作用域:
拥有存储数据的空间,作用范围是一次会话有效
一次会话是使用同一浏览器发送的多次请求。一旦浏览器关闭,则结束会话
可以将数据存入Session中,在一次会话的任意位置进行获取
可传递任何数据(基本数据类型、对象、集合、数组)
生命周期
开始:第一次使用到Session的请求产生,则创建Session
结束: 浏览器关闭, Session超时 ,手工销毁
session.setMaxInactiveInterval(seconds);//设置最大有效时间(单位:秒) , session.invalidate();//登录退出、注销
方法:
request.getSession()//使用请求获取session对象
session的方法:
getId(); //获取sessionID
setAttribute("key","Hello");
将数据保存到session对象,session对象的数据结构底层维护的就是一个Map,
可以存放多个键值对
getAttribute("key");//取数据
setAttribute("key","word");//改数据,如果有的话
//中文依旧会乱码
removeAttribute("key");//删除key键值对
Session与Request应用区别
request是一次请求有效,请求改变,则request改变
session是一次会话有效,浏览器改变,则session改变
举例:
发送
HttpSession session = request.getSession();
session.setAttribute("username","z_oo7");
request.setAttribute("password","123456");
response.sendRedirect(request.getContextPath()+"/S4");//重定向到s4
System.out.println(session.getId());
接收
HttpSession session = request.getSession();
String use = (String) session.getAttribute("username");
String pa = (String) request.getAttribute("password");
System.out.println("session 获取---------"+use);
System.out.println("request 获取---------"+pa);//重定向不是同一个请求了
ServletConfig
简介:Servlet的配置信息类
注意:由Tomcat创建,一个Servlet对应一个ServletConfig
作用:
1,获取servlet-name的值
getServletName()
2,获取初始化参数init-param
getInitParameterNames()
getInitParameter(key)
3,获取ServletContext对象
web.xml配置:
<servlet>
<servlet-name>Dome</servlet-name>
<!-- 起个别名Dome-->
<servlet-class>com03.Servlet05</servlet-class>
<!-- 所在位置-->
<init-param>
<param-name>name</param-name>
<param-value>纳西妲</param-value>
</init-param>
<init-param>
<param-name>sex</param-name>
<param-value>女</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>500</param-value>
</init-param>
<!-- 这些都是ServletConfig里面的值-->
</servlet>
<servlet-mapping>
<servlet-name>Dome</servlet-name>
<!-- 名字得和上面一样-->
<url-pattern>/my</url-pattern>
<!-- 所在路径-->
</servlet-mapping>
使用获取:
super.doPost(request, response);//必须写得
String servletName = getServletName();//获取servlet名字,自己配的
System.out.println("别名"+servletName);
Enumeration<String> names = getInitParameterNames();//获取所有的name
while (names.hasMoreElements()){//类似于迭代器
String key = names.nextElement();//获取key值
String value = getInitParameter(key);//通过key获取value
System.out.println(key+"------"+value);
}
作用:搞一些默认值,如果直接用属性的话,可能产生线程安全问题
ServletContext
全局对象,也拥有作用域,对应一个Tomcat中的Web应用
当Web服务器启动时,会为每一个Web应用程序创建一块共享的存储区域
ServletContext在Web服务器启动时创建,服务器关闭时销毁。
关键方法:
ServletContext servletContext = getServletContext();//获取对象
setAttribute(key,value) //存值
getAttribute(key) //取值
removeAttribute(key) //删值
作用:全局共享数据
//ServletContext对象 —— Servlet上下文对象
//1.获取ServletContext对象
ServletContext servletContext = getServletContext();
//2.通过ServletContext对象获取当前web应用的上下文路径
// 就是当前web应用在web服务器上的访问路径
String contextPath = servletContext.getContextPath();
//3.通过ServletContext对象获取web项目中的目录在 服务器上的绝对路径
String realPath = servletContext.getRealPath("/");
//4.将数据存储到全局对象
servletContext.setAttribute("ckey",value);
//5.根据key从全局对象取数据
Object v = servletContext.getAttribute("ckey");
//6.从全局对象移出数据
servletContext.removeAttribute("ckey");
也可以将默认值配到web.xml中,直接定义在web-app
<context-param>
<param-name>name</param-name>
<param-value>布耶尔</param-value>
</context-param>
ServletContext context = getServletContext();//直接获取对象
String contextPath = context.getContextPath();//获取项目路径
String realPath = context.getRealPath("/");//项目的绝对路径,从盘符开始
context.setAttribute("name","存的值:纳西妲");//存到服务器中,只要是这个服务器的都能取到值
ServletContext context = getServletContext();
String name =(String)context.getAttribute("name");
System.out.println(name);
不同浏览器都能获取到,这个值就属于所有用户了
ServletContext在整个web应用程序生命周期内存在,用来保存全局对象,整个web应用都可以使用其获取context参数。当启动服务器后,servlet容器将读取web.xml中配置的context参数,进行初始化工作。ServletContext可以获取该参数。比如我们可以配置编码信息供web程序使用。
ServletConfig对象保存着servlet的配置信息,当servlet被实例化后,web容器读取初始化参数,保存到config对象中,每一个servlet都有一个servletconfig对象保存着自己的参数信息。
总结
存值
1.request 在一个请求内
2.cookie 存在浏览器端,只要浏览器不关,cookie生命周期没到,每次请求都会把值传给服务器
3.Session存在服务器中,服务器中有多个session
4.Config直接在web.xml中配的,一般用于对变量的初始化值。
5.Context存在于整个服务器端
Filter
作用:
执行地位在Servlet之前,客户端发送请求时,会先经过Filter,再到达目标Servlet中; 响应时,会根据执行流程再次反向执行Filter
既可以对请求进行拦截,也可以对响应进行处理,可以解决多个Servlet共性代码的冗余问题(例如:乱码处理、登录验证)
过程,结构:
web应用启动时,web服务器将创建Filter的实例对象,并调用init方法,读取web.xml的配置,完成对象的初始化功能,
从而为后续的用户请求做好拦截的准备工作
filter对象只会创建一次,init方法也只会执行一次,可通过init的参数获得代表当前filter配置信息的FilterConfig对象
@WebFilter("/*")//在这配置那些东西都需要过滤
public class Filter01 implements Filter {
public void destroy() {
/**
* filter创建后会保存在内存中,当web应用移除或者服务器停止时才销毁,
* 该方法在Filter的生命周期中仅执行一次,在这个方法中可以释放过滤器使用的资源
*
*/
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
chain.doFilter(req, resp);
//这个方法完成实际的过滤操作,
}
public void init(FilterConfig config) throws ServletException {
//初始化工作
}
}
web.xml的配置:谁定义在上边,谁先执行
:配置
<filter>
<filter-name>sf</filter-name><!--名称-->
<filter-class>com05.Filter01</filter-class> <!--过滤器类全称-->
</filter>
<!--映射路径配置-->
<filter-mapping>
<filter-name>sf</filter-name><!--名称-->
<url-pattern>/*</url-pattern><!--过滤的url匹配规则和Servlet类似-->
</filter-mapping>
路径规则:
精确过滤: 配置过滤器拦截指定的请求url
例如:/FirstServlet,/index.html
后缀过滤: 配置过滤器拦截指定的后缀的请求url
例如:*.jpg 、 *.html
通配符过滤:
/* 拦截所有请求
/user/* 访问/user下的所有资源时,过滤器都会被执行
创建过滤器:
1. 创建一个类实现Filter接口
2. 重写方法
例:不登录直接访问界面,可以使用过滤器
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//这个方法完成实际的过滤操作,
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse) resp;
HttpSession session = request.getSession();
Object login = session.getAttribute("login");
if(login==null){//如果为空说明没登录
//跳转到登录页面,使用转发就行
return;
}else{
//如果正常的话就访问就行
chain.doFilter(request,response);
}
}