Java学习笔记day37

Servlet深入

体系

请求流程

生命周期

init: 初始化Servlet对象		注意: 一个Servlet类自始至终只会创建一个对象
	当没有配置loadOnStartup=1, 第一次访问该Servlet时会创建Servlet的对象, 后面访问该Servlet时将不会创建该Servlet对象, 而是直接执行service方法, 对其进行服务
	如果配置了loadOnStartup=1, 当服务器启动时就会创建该Servlet对象, 以后不管第几次访问该Servlet都不会再创建该Servlet对象

配置loadOnStartup
方式1: web.xml配置
<servlet>
	<servlet-name>TestServlet</servlet-name>
	<servlet-class>com.demo.TestServlet</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>TestServlet</servlet-name>
	<url-pattern>/test</url-pattern>
</servlet-mapping>

方式2: 注解配置
@WebServlet(name = "TestServlet",value = "/test",loadOnStartup = 1)

service: 进行服务,在进行服务时会先判断其请求方式,然后再调用其对应的方法
destroy: 销毁,当服务器关闭时,Serlvet类的实例会被销毁

线程安全问题

问题原因

因为Servlet实例是单例模式,当多个客户端并发访问同一个Servlet类时,Tomcat会创建多个线程,多个线程会使用同一个Servlet实例,会导致线程安全问题

解决方案

方案1: 实现SingleThreadModel接口
	让我们的Servlet类实现SingleThreadModel接口,每个线程都会创建servlet实例,避免了多线程使用同一个Servlet实例的情况,但是使用这种方式会导致对客户端的请求响应效率变低,增加了服务器因频繁创建和销毁Servlet实例的开销,因此此种方式不建议使用,已经过时。

方案2: 使用锁

示例: 
package com.demo;

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("/us")
public class UserServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		synchronized (this){
		}
	}
    
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	}
}

建议:Servlet实例中尽量不使用成员变量(属性)

Servlet核心类

请求

类名: HttpServletRequest
作用: 客户端向服务器发送的请求信息都会被封装到request对象
方法: 

//getParameter(String name): 根据参数名称获取参数值
String value=request.getParameter(key);
/**
* 作用:设置请求数据编码格式
* 注意:
* 对get请求无效
* Tomcat8以前默认编码格式为ISO8859-1.所以此时上传会乱码
*/
request.setCharacterEncoding("utf-8");
//get请求防止乱码1
value=new String(value.getBytes("ISO8859-1"),"UTF-8");
//get请求防止乱码2
//修改Tomcat的conf/server.xml中配置URL的编码方式
/*
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="utf-8" />
*/
//getMethod:获取客户端请求方式
String method = request.getMethod();
//getRequestURL:获取客户端的请求URL(不包含url上的参数)
String url = request.getRequestURL().toString();
//getProtocol:获取客户端提交数据的协议及版本
String protocol = request.getProtocol();
//getHeaderNames:获取请求头中所有的key
Enumeration<String> en = request.getHeaderNames();
while(en.hasMoreElements()){
	String key = en.nextElement();
	//getHeader:根据请求头中的key获取对应value
	String value = request.getHeader(key);
	System.out.println(key+":"+value);
}
// 获取请求正文(*)
// getInputStream :获取客户端请求的输入流
ServletInputStream inputStream = request.getInputStream()

响应

类名: HttpServletResponse
作用: 用于响应客户端请求(给客户端发数据)
方法: 

//设置响应状态行
//setStatus : 设置状态行中的状态码
response.setStatus(200);
//设置响应头
//setContentType(*): 设置响应头中的Content-Type属性,设置响应客户端的数据格式
//等价于: response.setHeader("Content-Type","text/html")
//同时设置服务端的编码格式和客户端响应的文件类型及响应时的编码格式
response.setContentType("text/html;charset=UTF-8");
//setContentLength: 设置响应客户端的数据长度(一般无需设置)
response.setContentLength(1024);
//setHeader: 设置其他的响应头属性
response.setHeader("Content-Type","text/html");
//设置响应正文
//setCharacterEncoding: 设置响应客户端的数据编码格式
response.setCharacterEncoding("utf-8");
// 通过response对象获取输出流
// 字节流(*)(如果要响应文件数据给客户端,则需要使用字节流)
ServletOutputStream outputStream = response.getOutputStream();
// 字符流(*)(如果响应文本数据-HTML文档,则使用字符流)
PrintWriter out = response.getWriter();

转发与重定向

当客户端请求到了某个Servlet类之后,Servlet类进行处理,但是并不使用这个Servlet来响应客户端,而是要使用另一个Servlet来响应。

转发

流程

特点
转发是在服务器端,两个Servlet之间的请求行为
浏览器只对服务器进行了一次请求
浏览器的地址不会改变,浏览器请求ServletA,ServletA转到ServletB由ServletB响应浏览器,浏览器显示的是ServletA的访问地址
转发过程中,可以通过request对象传递数据
语法
//将ServletA中的请求转发给ServletB
//不带数据
//注意:需要带/,/表达地址为:http://ip:端口/项目名/
request.getRequestDispatcher("ServletB的URL").forward(request,response);
//带数据
request.setAttribute("stuNum","10001");
request.setAttribute("stuAge",21); //如果直接给简单类型数据,则会自动装箱为对应的封装类对象
request.setAttribute("stu",new Student(...));
request.getRequestDispatcher("ServletB的URL").forward(request,response);
//ServletB中接收A的数据
String snum = (String)request.getAttribute("stuNum");//注意取值时key与存值时要相同
int sage = (Integer)request.getAttribute("stuAge");
Student stu = (Studennt)request.getAttribute("stu")

注意:转发前请求方式是什么,转发后请求方式不变

重定向

流程

特点
重定向是客户端的行为
浏览器对服务器发送了两次请求
重定向是由浏览器再次发送请求,浏览器地址会改变为转发的地址
不能通过request对象将ServletA中的数据传递给ServletB
语法
//将ServletA重定向到ServletB
//不带值
response.sendRedirect("/ServletB访问URL");
//带值
//ServletA: 在响应浏览器重定向地址的时候,在URL声明参数
response.sendRedirect("/项目名/ServletB?key=value");
//ServletB: 获取浏览器请求的参数
String value = request.getParameter("key");
//注意: 
//1.重定向时传递中文会乱码需要使用UrlUtil类进行编码与解码
//编码:UrlUtil.encode(username,"utf-8");
//解码:UrlUtil.decode(info,"utf-8");
//2.路径中需要写项目名
示例
@WebServlet("/s1")
public class Servlet1 extends HttpServlet {
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	doGet(request,response);
	}
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	request.setCharacterEncoding("utf-8");
	response.setContentType("text/html;charset=utf-8");
	String username = request.getParameter("username");
	username = UrlUtil.encode(username,"utf-8");
	String info = UrlUtil.encode("Servlet1传递的信息","utf-8");
	response.sendRedirect(request.getContextPath()+"/s2?username="+username+"&key="+info);
	}
}

@WebServlet("/s2")
public class Servlet2 extends HttpServlet {
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	doGet(request,response);
	}
    
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	request.setCharacterEncoding("utf-8");
	response.setContentType("text/html;charset=utf-8");
	String username = request.getParameter("username");
	String info = request.getParameter("key");
	username = UrlUtil.decode(username,"utf-8");
	info = UrlUtil.decode(info,"utf-8");
	PrintWriter writer = response.getWriter();
	writer.println("<html>");
	writer.println("<head>");
	writer.println("<title>Servlet2</title>");
	writer.println("<mate charset='utf-8'>");
	writer.println("</head>");
	writer.println("<body>");
	writer.println("<p>"+username+"</p>");
	writer.println("<p>"+info+"</p>");
	writer.println("</body>");
	writer.println("</html>");
	writer.flush();
	writer.close();
	}
}

经验: 
	当两个Servlet需要传递数据时,选择forward转发,不建议使用sendRedirect进行传递
	重定向的请求方式是get

Cookie与HttpSession

Cookie

简介

Cookie是在浏览器访问web服务器上的某个资源时,由web服务器在响应浏览器时通过响应头附带的传送给浏览器并存储在浏览器端的一小段数据
一旦web浏览器保存了来自于某个服务器的Cookie,那么当浏览器再次访问这个服务器的时候,会通过请求头将cookie传递给web服务器
浏览器访问服务器的时候,只会携带由当前服务器存储在客户端的cookie
Cookie中缓存的数据是以键值对形式存储的(name-value)
方法
//创建Cookie
Cookie ck=new Cookie(key, value);
ck.setPath("/当前项目名");//设置Cookie的路径,如果不设置当前Tomcat下所有webapp项目皆可使用
ck.setMaxAge(-1);//内存存储,取值有三种: >0 有效期,单位秒; =0 立即删除; <0 浏览器关闭时销毁.默认-1
//添加cookie
//服务器可以同时写多个Cookie存储到客户端
response.addCookie(ck);//添加到response对象中,响应时发送给客户端
//获取所有的Cookie
Cookie[] cks=request.getCookies();
//遍历Cookie
for(Cookie ck:cks){
	//检索出自己的Cookie
	if(ck.getName().equals(key)){
		//记录Cookie的值
		String value=ck.getValue();
		break;
	}
}
//修改cookie
//需要保证Cookie的名和路径一致即可修改
Cookie ck=new Cookie(key, value);
ck.setPath("/当前项目名");
ck.setMaxAge(0);
response.addCookie(ck);

注意:Cookie不支持存储中文,所以如果需要使用Cookie存储中文需要对其进行编码与解码

//存入cookie
//使用中文的 Cookie. name 与 value 都使用 UTF-8 编码.
Cookie cookie = new Cookie(
	URLEncoder.encode("姓名", "UTF-8"),
	URLEncoder.encode("张三", "UTF-8"));
//发送到客户端
response.addCookie(cookie);
//取出cookie
for(Cookie cc : request.getCookies()){
	String cookieName = URLDecoder.decode(cc.getName(), "UTF-8");
	String cookieValue = URLDecoder.decode(cc.getValue(), "UTF-8");
    response.getWriter.println(cookieName + "=" + cookieValue + ";");
}
优点和限制
优点: 
    可以灵活的配置过期规则
    简洁,cookie中存储的是最简单的键值对格式的文本数据
    数据的持久性,保存到浏览器中的cookie在过期之前是持久存储的

限制: 
    存储数据的大小有限,大多浏览器支持4k、8k的数据存储
    用户可以通过浏览器设置禁用cookie,因此限制了cookie的使用场景
    cookie是存储在客户端浏览器中的,有被纂改的风险,有潜在的安全隐患

HttpSession

简介
Session对象,就是当浏览器向服务器发送请求建立连接后,由服务器创建的存储在服务器端的用于记录用户状态对象。
原理

特性
服务器会为每个客户端连接分配一个Session对象,存储在服务器上
同一个浏览器发送多次请求,同属于一次会话,共享同一个Session对象
首次使用到Session时,服务器会自动创建Session,并创建Cookie存储SessionId发送回客户端

作用域:拥有存储数据的空间,作用范围是一次会话有效
    一次会话是使用同一浏览器发送的多次请求。一旦浏览器关闭,则结束会话
    可以将数据存入Session中,在一次会话的任意位置进行获取
    可传递任何数据(基本数据类型、对象、集合、数组)

生命周期
    开始:第一次使用到Session的请求产生,则创建Session
    结束:
    浏览器关闭,则失效
    Session超时,则失效
    	session.setMaxInactiveInterval(seconds);//设置最大有效时间(单位:秒)
    手工销毁,则失效
    	session.invalidate();//登录退出、注销
方法
//1.获取session对象
// getSession() : 获取当前用户连接
HttpSession session = request.getSession();
// getId() : 获取sessionID
String sessionId = session.getId();
//2.将数据保存到session对象,session对象的数据结构底层维护的就是一个Map,可以存放多个键值对
session.setAttribute("key1","Hello");
session.setAttribute("key2","Java");
//3.从session对象中取数据,根据key获取value
String s1 = (String) session.getAttribute("key1");
System.out.println("Servlet:"+s1);
//4.修改session数据,如果key存在,则表示修改session中这个key的值
session.setAttribute("key1","Hi");
//5.删除session中 key1的键值对
session.removeAttribute("key1");
Session与Request的区别
request是一次请求有效,请求改变,则request改变
session是一次会话有效,浏览器改变,则session改变

ServletConfig

简介: Servlet的配置信息类
注意:Tomcat创建,一个Servlet对应一个ServletConfig
作用:
    1.获取servlet-name的值
    2.获取初始化参数init-param
    3.获取ServletContext对象

public class HelloServlet extends HttpServlet {
	@Override
	public void init(ServletConfig config) throws ServletException {
	//注意:不能删除super.init(config);否则ServletConfig对象为null
	super.init(config);
	//可以在Servlet任何一处使用getServletConfig()方法获取ServletConfig对象
	//1.获取servlet-name的值
	System.out.println(config.getServletName());
	//2.获取init-param
	System.out.println(config.getInitParameter("key1"));
	System.out.println(config.getInitParameter("key2"));
	//3.获取ServletContext对象
	ServletContext context = config.getServletContext();
	}
}

//web.xml文件
<servlet>
	<servlet-name>Hello</servlet-name>
	<servlet-class>com.demo.HelloServlet</servlet-class>
	<init-param>
		<param-name>key1</param-name>
		<param-value>1</param-value>
	</init-param>
	<init-param>
		<param-name>key2</param-name>
		<param-value>2</param-value>
	</init-param>
</servlet>

<servlet-mapping>
	<servlet-name>Hello</servlet-name>
	<url-pattern>/hello</url-pattern>
</servlet-mapping>

ServletContext

概念

全局对象,也拥有作用域,对应一个Tomcat中的Web应用
当Web服务器启动时,会为每一个Web应用程序创建一块共享的存储区域(ServletContext)。
ServletContext在Web服务器启动时创建,服务器关闭时销毁。
作用: 全局共享数据

方法

//ServletContext对象 —— Servlet上下文对象
//1.获取ServletContext对象
ServletContext servletContext = getServletContext();
//2.通过ServletContext对象获取当前web应用的上下文路径
//就是当前web应用在web服务器上的访问路径
String contextPath = servletContext.getContextPath();
//contextPath: /day38_war_exploded
//3.通过ServletContext对象获取web项目中的目录在服务器上的绝对路径
String realPath = servletContext.getRealPath("/");
//realPath: D:\IdeaProjects\day38\out\artifacts\day38_war_exploded\
//4.将数据存储到全局对象
servletContext.setAttribute("ckey",value);
//5.根据key从全局对象取数据
Object v = servletContext.getAttribute("ckey");
//6.从全局对象移出数据
servletContext.removeAttribute("ckey");
//7.获取全局参数
//web.xml配置全局参数
<!-- 配置上下文参数 : 键值对 -->
<context-param>
	<param-name>key1</param-name>
	<param-value>Hello</param-value>
</context-param>
<context-param>
	<param-name>key2</param-name>
	<param-value>World</param-value>
</context-param>
//getInitParameterNames():获取web.xml中全局参数所有的key[key1,key2]
Enumeration<String> keys = servletContext.getInitParameterNames();
//getInitParameter(key): 根据key获取value
String v1 = servletContext.getInitParameter("key1");

Filter

概念

处于客户端与服务器目标资源之间的一道过滤技术

作用

执行地位在Servlet之前,客户端发送请求时,会先经过Filter,再到达目标Servlet中;响应时,会根据执行流程再次反向执行Filter
可以解决多个Servlet共性代码的冗余问题(例如:乱码处理、登录验证)

创建

1.创建一个类实现Filter接口
2.重写方法

示例: 
public class MyFilter implements Filter {
	//初始化过滤器
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("过滤器初始化了........init..."+filterConfig);
	}

	//执行过滤
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		System.out.println("过滤前........doFilter");
		//放行
		//如果不允许通行则可以通过servletResponse直接响应客户端(转发、重定向)。
		chain.doFilter(request, response);
		System.out.println("过滤后.......doFilter");
	}

	//销毁
	@Override
	public void destroy() {
		System.out.println("销毁了.....destroy");
	}
}

过滤器配置

方案1: web.xml中配置
<!--过滤器的xml配置 -->
<filter>
<!--名称-->
<filter-name>MyFilter</filter-name>
<!--过滤器类全称-->
<filter-class>com.demo.filter.MyFilter</filter-class>
</filter>
<!--映射路径配置-->
<filter-mapping>
<!--名称-->
<filter-name>MyFilter</filter-name>
<!--过滤的url匹配规则和Servlet类似-->
<url-pattern>/*</url-pattern>
</filter-mapping>
方案2: 注解配置
@WebFilter("/*")
public class MyFilter implements Filter {
	//...
}
路径规则
精确过滤: 配置过滤器拦截指定的请求url
例如: /FirstServlet, /index.html
后缀过滤: 配置过滤器拦截指定的后缀的请求url
例如: *.jsp 、 *.html
通配符过滤: 
- /* 拦截所有请求
- /aaa/bbb/* 拦截项目名后 当前项目下/aaa/bbb/FirstServlet 或者 当前项目下/aaa/bbb/a.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值