Servlet

Servlet

Servlet是一个用于扩展服务器端功能的服务器端组件技术

  • 运行在服务器端,所以调用执行是由服务器负责
    • 在web.xml中需要进行配置,就是将servlet和一个请求地址建立对应关系,当浏览器对地址
      发起请求时,服务器则按照规则调用serlvet类中的方法
  • 服务器端的组件技术
    • 直接或者间接的实现Servlet接口
  • 用于扩展服务器端功能,可以实现动态网页的开发

Servlet接口

1、定义类实现Servlet接口

public class HelloServlet implements Servlet {
	//在Servlet类实例化后自动执行的方法,这个方法在servlet对象的整个生命周期中运行且只运行一次。主要用于实现Servlet的初始化操作,例如读取配置文件中当前Servlet的初始化配置信息。当服务器调用该方法时会将当前servlet的初始化配置存储在cnfig对象中
	public void init(ServletConfig config) throws ServletException {
		this.config=config; //缓存config对象
	}
	//用于供其它位置获取config对象时进行调用
	private ServletConfig config;
	public ServletConfig getServletConfig() {
		return config;
	}
	
	//在init方法执行后,服务器调用service方法用于生成针对客户端的响应信息。服务器采用多线程的方式运行service方法,一个客户端请求对应一个线程。服务器调用service方法时会传入2个参数对象,req用于封装客户端的请求信息,resp用于封装服务器的响应信息。Servlet默认采用单实例多线程的方式对客户端浏览器请求提供服务,service执行完成后不会自动销毁,而是常驻内存
	public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException {
	
	}

	//一般供获取当前servlet对象的说明信息
	public String getServletInfo() {
		return "当前servlet对象的说明信息";
	}
//在servlet对象销毁之前执行,用于进行资源回收。一个servlet对象在整个生命周期运行且只运行一次。servlet对象默认是常驻内存,只有在服务器关闭或者内存严重不足而当前Servlet对象被GC机制选中时才会被销毁
	public void destroy() {
	}
}

2、在web.xml中针对servlet类进行配置,将servlet和一个或者多个地址建立对应关系

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
id="WebApp_ID" version="4.0"> xml文件头用于说明当前xml文件的语法规则
	<servlet> 将servlet类和一个名称建立对应关系
		<servlet-name>hello</servlet-name>
		<servlet-class>com.yan.action.HelloServlet</servlet-class>
	</servlet>
	<servlet-mapping> 将一个servlet名称和请求地址建立对应关系
		<servlet-name>hello</servlet-name>
		<url-pattern>/hello.do</url-pattern>
	</servlet-mapping>
</web-app>

Servlet三生命周期

  • init
  • service
    • 单实例多线程的方式对外提供服务
    • 常驻内存
  • detroy

Servlet开发

  • 直接实现Servlet接口
  • 抽象类GenericServlet用于非标准协议开发,例如游戏的服务器端
  • 抽象类HttpServlet是GenericServlet的子类,用于http协议应用开发,例如网站

HttpServlet的运行原理

//public abstract class HttpServlet extends GenericServlet,针对Servlet接口的5种方法都提供了默认实现
//按照Servlet的运行规则,服务器会调用service方法对外提供服务。
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
	String method = req.getMethod(); //获取请求方法,http协议定义了7种请求,例如get/post/put/delete,...
	if (method.equals(METHOD_GET)) { //如果请求方法为GET,则调用doGet方法进行处理
		doGet(req, resp);
	} else if (method.equals(METHOD_HEAD)) {
		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 {
		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);
	}
}
//针对不同的请求方法提供了对应的扩展点,运行子类进行覆盖定义
protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {
	String protocol = req.getProtocol(); //获取请求协议,例如http
	String msg = lStrings.getString("http.method_get_not_supported");
//根据参数获取默认的报错信息
	if (protocol.endsWith("1.1")) { 判断http协议的版本号,发送报错信息
		resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
	} else {
		resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
	}
}

Servlet初始化配置

在服务器调用Servlet的 init方法时会传入一个ServletConfig对象,其中包含当前Servlet的所有初始化

public void init(ServletConfig config) throws ServletException {}

//GenericServlet中的方法实现
public void init(ServletConfig config) throws ServletException {
	this.config = config; //缓存服务器提供的config对象
	this.init(); //提供扩展点
}

配置位于web.xml文件

<servlet>
	<servlet-name>hello3</servlet-name>
	<servlet-class>com.yan.action.Hello3Servlet</servlet-class>
	<init-param> 初始化参数,可以配置多个
		<param-name>rows</param-name> 配置的参数名称
		<param-value>3</param-value> 对应配置参数名称的配置值
	</init-param>
	<init-param>
		<param-name>width</param-name>
		<param-value>120</param-value>
	</init-param>
</servlet>

在servlet中获取配置参数

可以在GenericServlet提供的扩展点中获取配置参数

@Override
public void init() throws ServletException {
	//获取对应名称的初始化参数值,如果没有配置则返回为null
	//这个获取初始化参数的方法也可以在其它方法中进行调用,从而获取配置参数
	String rows = this.getServletConfig().getInitParameter("rows");
	System.out.println(rows);
	//获取当前servlet的配置名称,对应web.xml中的<servletname>hello3</servlet-name>
	System.out.println(this.getServletConfig().getServletName()):
	//获取所有当前servlet的初始化配置参数名称的迭代器,类似Iterator
	Enumeration<String> initParameterNames =this.getServletConfig().getInitParameterNames();
	while(initParameterNames.hasMoreElements()){
		String name=initParameterNames.nextElement(); //获取每个初始化配置参数名称
		String value=this.getServletConfig().getInitParameter(name);//根据配置参数名称读取对应的参数配置值
		System.out.println(name+"-->"+value);
	}
	//可以通过config对象获取当前应用的上下文
	final ServletContext context =this.getServletConfig().getServletContext();
}

接收客户端请求

  • request对象中封装的有客户端的请求参数信息
    • getParamter(" 参数名称"):String
    • getParameterValues(“参数名称”):String[]
    • getParameterMap():Map
  • request对象是HttpServletRequest接口类型,由服务器提供的实现,服务器在调用service方法时
    传入
  • request中的数据有3个不同的部分,请求头信息header【浏览器发送的请求信息】、请求参数
    parameter【用户发送的相关参数信息】、属性信息attribute【一般用于编程中的数据传递】,前
    两个只是用于读取,并没有提供修改方法,属性信息提供了读写操作方法
  • 特殊方法
    • getReader()或者getInputStream()获取输入流,直接操作流

请求头

一般数据是传送给服务器使用的,不是给应用直接使用的。只有在特殊需求时使用,例如收集客户端所使用的浏览器信息【User-Agent】

public class AddServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 操作请求头参数
		final String host = request.getHeader("Host"); // 根据名称获取请求头中指定的参数值
		System.out.println(host);//localhost:8080
		//另外根据发送数据的类型 request.getIntHeader("名称"):Int request.getDateHeader():long
		//获取所有的请求头参数名称的枚举对象
		final Enumeration<String> headerNames = request.getHeaderNames();
		while(headerNames.hasMoreElements()){
			String headerName=headerNames.nextElement();
			String headerValue=request.getHeader(headerName);
			System.out.println(headerName+"-->"+headerValue);
		}
	
		String name="Accept-Encoding"; //遍历一个名称对应的多个配置值的情形
		final Enumeration<String> enumeration = request.getHeaders("AcceptEncoding");//请求头中一个名称对应多个参数值
		while(enumeration.hasMoreElements()){
			String tmp=enumeration.nextElement();
			System.out.println(name+"<-->"+tmp);
		}
	}
}

请求参数

无论是get请求还是post请求,获取请求参数的方法一致,只是编程时需要注意对应的doXxx方法

  • 如果针对请求没有对应的doXxx方法,则会从父类中继承得到默认实现。实现是报错405
  • 如果get请求和post请求处理逻辑一致,则需要自行编码实现
public class AddServlet extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		this.doPost(request,response); //将get请求转发给doPost进行处理
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	}
}
获取请求参数数据的方法
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
	//根据参数名称获取对应的参数值
	String username=request.getParameter("username");//在网络数据传输中只有
	String类型,其它类型需要自行编码实现数据类型转换
	//因为DateFormat不是线程安全的,所以DateFormat不能定义为属性
	System.out.println(username);
	//获取用于遍历所有请求参数名称的迭代器对象
	final Enumeration<String> parameterNames =request.getParameterNames();
	while(parameterNames.hasMoreElements()){
		String parameterName=parameterNames.nextElement();
		String parameterValue=request.getParameter(parameterName);
		System.out.println(parameterName+"-->"+parameterValue);
	}

	//获取所有请求参数
	final Map<String, String[]> parameterMap =
	request.getParameterMap();
	for(String tmpName:parameterMap.keySet()){
		String[] params=parameterMap.get(tmpName);
		System.out.println(tmpName+"<===>"+ Arrays.toString(params));
	}
	//针对checkbox获取数据的方法
	final String[] hobbies = request.getParameterValues("hobby");
	for(String tmp:hobbies)
		System.out.println(tmp);
}
针对一个名称传递多个数据的情形
<form action="add.do" method="post">
	用户名称:<input name="username"/><br/>
	<!-- checkbox复选框允许用户选中多个,name是提交参数名称,value是选中这个选项的提交值,checked默认选中状态 -->
	用户爱好:<input type="checkbox" name="hobby" value="篮球">篮球
			<input type="checkbox" name="hobby" value="111">足球
			<input type="checkbox" name="hobby" value="222" checked>乒乓球
			<input type="checkbox" name="hobby" value="333">其它<br/>
			<input type="submit" value="提交数据"/>
</form>

针对checkbox使用一个名称传递多个数据则必须使用getParameterValues获取数据,如果使用
getParameter则会有数据丢失

//针对checkbox获取数据的方法
final String[] hobbies = request.getParameterValues("hobby");
for(String tmp:hobbies)
	System.out.println(tmp);

常见问题1:类型转换

数据传输都是String类型获取数据后需要进行类型转换,但是转换会有异常
实际处理中有2种不同的处理方案:方案1记录日志并继续向上抛出;方案2采用默认值

String sage=request.getParameter("age");
Integer age=null;
try {
	age = Integer.parseInt(sage); //NumberFormatException运行时异常
} catch(Exception e){
	//记录日志,如果需要程序中断,可以不做异常处理,因为NumberFormatException运行时异常throw new ServletException(e);
	age=18;//设定默认值。不是任何时候都可以采用自定义默认值的方式进行容错
}

常见问题2:一个名字对应多个数据的存储和显示

MySQL中并不建议使用enum枚举类型和set集合类型

多个选项的存储方案:

-- 一个专门用于存放各种常量的独立的表,字典表
create table tb_dict(
	id bigint primary key auto_increment comment '代理主键',
	type_ varchar(32) not null comment '用于标识数据的具体类型,例如sex表示用于性别信息',
	val_ varchar(20) not null comment '显示的数据' 
	-- key_ comment '存放具体提交数据'
)engine=innodb default charset utf8;

insert into tb_dict(type_,val_) values('sex','男');
insert into tb_dict(type_,val_) values('sex','女');
-- 注意具体存储对应数据时可以采用id编号值

具体入库数据

一种直接存储选项的显示值,优势在于方便显示
另外的存储方式存储的时字典表中对应的id值,优势在于方便修改,最大的缺陷在于额外查询

请求参数的中间问题

为了重现问题使用tomcat7插件

<plugin>
	<groupId>org.apache.tomcat.maven</groupId>
	<artifactId>tomcat7-maven-plugin</artifactId>
	<version>2.2</version>
	<configuration>
		<path>/pp</path> 上下文路径名称,也就是访问应用的路径为/pp
		<port>9090</port> 端口号的配置,localhost:9090/pp/
		<uriEncoding>UTF-8</uriEncoding> 默认的编码字符集
	</configuration>
</plugin>

因为在整个数据提交过程中会涉及多处编码字符集问题,1、提交所采用的编码字符集。 <meta charset="UTF-8"> 2、在互联网上传输数据默认编码字符集为ISO-8859-1。3、服务器接收数据后
的编码处理

请求参数的中文乱码问题有3种解决方案

  • 针对get请求,在具体应用中基本不可用
以tomcat为例的解决方案: conf/server.xml
需要掌握的配置参数1:修改请求的端口号,默认端口8080

<Connector port="8080连接端口号,默认http端口号为80,https默认443"
protocol="所支持的协议HTTP/1.1" connectionTimeout="20000连接超时时长,默
认单位为ms" redirectPort="8443重定向端口号" />

针对连接子 <Connector> 添加一个配置参数URIEncoding="GBK",这里的GBK来自于页面编
码字符集
  • 针对post请求

在所有的接收请求参数方法执行之前,先调用方法 request.setCharacterEncoding("UTF- 8"); ,注意UTF-8就是页面提交所采用的编码字符集

  • 通用解决方案

//根据参数名称获取对应的参数值
String username=request.getParameter(“username”);//在网络数据传输中只有
String类型,其它类型需要自行编码实现数据类型转换
//使用ISO8859-1进行解码转换,转换为原始的字节数组
byte[] arr=username.getBytes(StandardCharsets.ISO_8859_1);
//再次使用UTF-8进行编码
username=new String(arr, Charset.forName(“UTF-8”));
System.out.println(username);

理论上来说采用通用解决方案可以处理get和post的中文乱码问题,但是一般不采用这种方式,因为处理比较繁琐。具体开发实践中注意一些小细节即可,不用通用解决方案

  • 一般不采用get提供中文,如果需要传递数据有限考虑采用代码值之类的方式。例如查询教学部的
    所有雇员,并不传递中文的【教学部】参数,而是传递教学部对应的编号值
  • 提交数据的html页面编码字符集统一为UTF-8
  • 如果需要传递中文信息,可以采用 <form method='post'> 提交数据, 在所有接收数据
    request.getPamrameter 之前先进行编码转换 request.setCharacterEncoding("UTF-8");

属性操作

request的属性操作就是操作attribute,主要用于实现在两个页面之间之间使用编程的方式进行数据传

//向request的attribute种存储数据,如果名称已经存在则后盖前,否则新增
request.setAttribute("name","具体数据Object");

//从request的attribute中获取数据
Object obj=request.getAttribute("name");

//按照key值删除指定的数据
request.removeAttribute("name");

//遍历attribute中的所有数据对应的key值
Enumeration<String> attributeNames = request.getAttributeNames();
while(attributeNames.hasMoreElements()){
	String attrName=attributeNames.nextElement();
	Object attrValue=request.getAttribute(attrName);
	System.out.println(attrName+"<-->"+attrValue)
}

请求转发和动态包含

其他方法

生成响应消息

响应头

重定向

输出流

异常处理

Web页面之间的3种关系

包含

重定向和请求转发

典型应用

跟踪用户的4种方法

会话对象

具体应用
工作原理

数据校验

JSP

基础语法

注释
页面组成
jsp页面的三生命周期

3大页面指令

page指令

6大页面动作

9大默认对象

request
response
out

数据的4大范围

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值