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
@Override
public void init () throws ServletException {
// 获取对应名称的初始化参数值,如果没有配置则返回为 null
// 这个获取初始化参数的方法也可以在其它方法中进行调用,从而获取配置参数
String rows = this . getServletConfig (). getInitParameter ( "rows" );
System . out . println ( rows );
// 获取当前 servlet 的配置名称,对应 web.xml 中的 <servlet
name>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 ();
}
public class AddServlet extends HttpServlet {
@Override
protected void doGet ( HttpServletRequest request , HttpServletResponse
response ) throws ServletException , IOException {
请求参数
无论是 get 请求还是 post 请求,获取请求参数的方法一致,只是编程时需要注意对应的 doXxx 方法
如果针对请求没有对应的 doXxx 方法,则会从父类中继承得到默认实现。实现是报错 405
如果 get 请求和 post 请求处理逻辑一致,则需要自行编码实现
获取请求参数数据的方法
// 操作请求头参数
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 ( "Accept
Encoding" ); // 请求头中一个名称对应多个参数值
while ( enumeration . hasMoreElements ()){
String tmp = enumeration . nextElement ();
System . out . println ( name + "<-->" + tmp );
}
}
}
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 );
// 获取用于遍历所有请求参数名称的迭代器对象
针对一个名称传递多个数据的情形
针对 checkbox 使用一个名称传递多个数据则必须使用 getParameterValues 获取数据,如果使用
getParameter 则会有数据丢失
常见问题 1 :类型转换
数据传输都是 String 类型获取数据后需要进行类型转换,但是转换会有异常
实际处理中有 2 种不同的处理方案:方案 1 记录日志并继续向上抛出;方案 2 采用默认值
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 获取数据的方法
final String[] hobbies = request.getParameterValues("hobby");
for(String tmp:hobbies)
System.out.println(tmp);
4 常见问题 2 :一个名字对应多个数据的存储和显示
MySQL 中并不建议使用 enum 枚举类型和 set 集合类型
多个选项的存储方案:
具体入库数据
一种直接存储选项的显示值,优势在于方便显示
另外的存储方式存储的时字典表中对应的 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 请求,在具体应用中基本不可用
针对 post 请求
通用解决方案
//根据参数名称获取对应的参数值
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") ;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值