Servlet组件
什么是servlet
Servlet是一种用于扩展服务器功能的服务器端组件技术,用于实现动态网页编程
- 是直接或者间接实现Servlet接口的类
- 有三种:Servlet Filter xxxListener
Hello Servlet
Servlet接口不在JavaSE中,需要因为servlet-api依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope> scope=provider表示该jar包在编译阶段使用,并不会打包到应用中
</dependency>
1、定义一个类实现Servlet接口
public class HelloServlet implements Servlet { //所谓的Servlet就是直接或者间接的实现Servlet接口
@Override
public void init(ServletConfig config) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { // Servlet组件用于对外提供服务的方法
//接收用户提交数据,不管是使用get还是post提交
String username=req.getParameter("username");
if(username==null ||username.trim().length()<1)
username="Servlet";
String message="Hello "+username+"!";
//使用rep生成针对客户端请求的响应信息
PrintWriter pw=res.getWriter();
pw.println("<h2>"+message+"</h2>");//生成html文档
pw.flush();
pw.close();
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
service方法实际上是由tomcat服务器负责调用执行,服务器调用方法时会传入两个参数,一个req用于封装所有的用户请求信息,一个res用于封装服务器向客户端的响应信息
-
req.getParameter用于获取用户提交的数据,例如get请求 hello.do?username=zhangsan
-
res.setContentType用于告知客户端的浏览器,如何处理响应信息
- res.setContentType(“text/html;charset=utf-8”);
- 遵循一个MIME协议(多用途互联网邮件协议),其中text表示是一个文本文档,html表示是一个html格式的文档文档
-
res.getWriter用于获取向客户端输出数据的输出流,在编码中向输出流中写出数据,实际上就是向客户端输出的内容
2、在web应用的核心配置文件web.xml中将Servlet和一个URL地址建立对应关系,当用户请求对应的URL地址时则触发Servlet运行
- Servlet类中没有main方法,所以程序的执行是由服务器按照规则进行调用的
- 由客户端对特定路径发起请求触发执行
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 将Servlet类和一个名称建立对应关系 -->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.yan.action.HelloServlet</servlet-class>
</servlet>
<!-- 将一个地址和名称建立对应关系 -->
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello.do</url-pattern>
</servlet-mapping>
</web-app>
3、启动Tomcat服务器
4、在浏览器中输入请求URL地址 http://localhost:8080/demo1_war/hello.do
5、使用get提交数据 http://localhost:8080/demo1_war/hello.do?username=zhangsan
IDEA控制台提示乱码
在Tomcat服务器下conf/logging.properties是日志输出的相关配置
java.util.logging.ConsoleHandler.encoding = GBK
Servlet的特点
Servlet用MIME过滤数据,由javax.servlet和javax.servlet.http两个包组成Java Servlet API框架
编写Servlet必须直接或间接实现作为Servlet的核心javax.servlet.Servlet接口,通常是实现javax.servlet.http.HttpServlet的子类,可以覆盖继承doGet()或者doPost()方法,两者分别对应于Http(s)中的Get请求和Post请求。
优点
- 可以移植性:由于Servlet是用Java语言编写的,因此它可以在不同的操作系统和服务器上移植
- 安全:Servlet具有类型检查特征,并利用Java的垃圾收集和没有指针的设计,使得Servlet避免了内存管理等问题
- 高效:Servlet加载执行后会常驻服务器内存中,当再次受到客户端的请求时,服务器会产生新的线程而不是进程为客户端服务,这样就提高了响应速度。 – Java是最差的内存性价比
缺点
- 不能所见即所得开发
- 生成html文档需要通过一系列的输出完成,非常的繁琐低效
应用场景
主要是处理客户端的请求并将其结果发送到客户端 ,由于生成html文档繁琐低效,所以一般不建议使用Servlet直接生成响应页面。建议考虑使用JSP
Servlet开发
Servlet编程
-
直接实现Servlet接口,需要实现5个方法
public interface Servlet { public void init(ServletConfig config) throws ServletException; //servlet对象创建后执行初始化操作,例如通过config读取配置信息 public ServletConfig getServletConfig(); //获取当前Servlet对象的相关ServletConfig对象。ServletConfig对象用于封装当前Servlet对象的配置信息 public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException; //对外提供服务。Servlet针对多用户会采用单实例多线程的方式对外提供服务 public String getServletInfo(); //获取当前Servlet的说明信息,一般供工具使用 public void destroy(); //在当前Servlet实例被GC回收前所运行的方法,一般用于资源回收 }
-
为了简化开发,所以Sun提供了一个通用抽象父类GenericServlet。针对大部分方法提供了默认的空实现,只需要Servlet提供service方法的实现即可,一般用于非通用协议开发中。采用的是适配器模式
public class Hello3Servlet extends GenericServlet { @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { } }
-
为了进一步简化开发,提供针对http协议的支持,Sun提供了一个GenericServlet的子类HttpServlet,这个父类专门用于http协议的应用开发中,是模板设计模式
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); //获取客户端的请求方法,例如get/post之类 if (method.equals(METHOD_GET)) { //如果是GET请求则调用doGet方法 ... doGet(req, resp); //转向调用自己的doGet方法 } 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); } }
默认的doGet方法实现
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String protocol = req.getProtocol(); //获取请求所使用的协议,例如http/1.1
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);
}
}
###Servlet的三生命周期
-
默认servlet不会自动加载,只有当第一次访问时才会被服务器自动加载,一般Servlet会采用单实例多线程的方式对外提供服务
-
加载完成立即执行初始化操作(new操作),构建对象完成后立即执行init方法,同时服务器会将当前Servlet的配置信息封装在一个ServletConfig对象中 。在一个Servlet的生命周期过程中init方法执行且只执行一次
<servlet> <servlet-name>hello2Servlet</servlet-name> <servlet-class>com.yan.action.Hello2Servlet</servlet-class> <!--初始化配置参数胡--> <init-param> <param-name>name</param-name> <param-value>lisi</param-value> </init-param> </servlet>
-
针对用于请求信息以多线程的方式运行service方法,同时服务器会自动封装request和response对象传入方法中
-
请求处理完毕,Servlet对象常驻内存。
-
只有当关闭服务器或者由于内存不足导致当前Servlet对象被调度销毁对象时,才会执行destroy方法,在一个Servlet的生命周期过程中destroy方法执行且只执行一次
###练习:用户登录
流程:
地址栏中输入/login.do打开输入页面,供用户输入对应的用户名和口令,再点击提交按钮则再服务器中执行数据库查询验证,成功则显示欢迎页,否则报错
- 地址栏中输入地址----Get请求
- 点击提交按钮,因为涉及敏感数据—Post提交
开发:
public class LoginServlet extends HttpServlet {
//针对get请求的处理,需要生成一个带有form表单的输入页面
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out=resp.getWriter();
out.println("<html><head><title>用户登录</title></head><body>");
out.println("<form><table>");
out.println("<tr><td>用户名称:</td><td><input name=‘username’/></td></tr>");
out.println("<tr><td>用户口令:</td><td><input type='password' name=‘passwird’/></td></tr>");
out.println("<tr><td colspan=2><input type='submit' value='登录系统'/><input type='reset' value='重置数据'/></td></tr>");
out.println("</table></form>");
out.println("</body></html>");
}
}
配置web.xml
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.yan.action.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bXbyHcYp-1618486504386)(images/login页面.png)]
点击提交按钮的处理
-
如果针对
<form>
表单不设置method,则默认采用get请求;- http://localhost:8080/demo1_war/login.do?username=adfasd&password=adfasd
- 如果定义一个
<form>
没有action,则提交到当前页面
-
如果需要使用post提交,则必须设置method=post
package com.yan.action;
import com.yan.util.JdbcUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
public class LoginServlet extends HttpServlet {
//针对get请求的处理,需要生成一个带有form表单的输入页面
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //对应get请求的具体处理
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out=resp.getWriter();
out.println("<html><head><title>用户登录</title></head><body>");
out.println("<form method='post'><table>");
out.println("<tr><td>用户名称:</td><td><input name='username'/></td></tr>");
out.println("<tr><td>用户口令:</td><td><input type='password' name='password'/></td></tr>");
out.println("<tr><td colspan=2><input type='submit' value='登录系统'/><input type='reset' value='重置数据'/></td></tr>");
out.println("</table></form>");
out.println("</body></html>");
}
//针对post请求的处理,需要调用数据库查询判断用户提交的数据是否正确
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //针对post请求的具体处理,目前可以实现post请求的方法只有一种<form method=post>
String username=req.getParameter("username"); //获取用户在<input name="username"/>输入框中输入的数据
String password=req.getParameter("password");
boolean bb=false;
//这里实际上就是登录处理逻辑,直接写在Servlet类中是不合理的,应该参照JavaEE的三层开发的方式来实现
Connection conn=null;
ResultSet rs=null;
JdbcUtil ju=JdbcUtil.getInstance();
try{
conn= ju.getConnection();
String sql="select * from t_users where username=? and password=?";
rs=ju.executeQuery(conn,sql,username,password);
bb=rs.next();
}catch(Exception e){
throw new ServletException(e);
}finally{
try {
ju.close(rs,null,conn);
} catch (Exception e) {
e.printStackTrace();
}
}
resp.setContentType("text/html;charset=utf-8");
PrintWriter out=resp.getWriter();
if(bb){
//成功页
out.println("success!");
}else{
//失败页
out.println("failure!");
}
out.flush();
out.close();
}
}
注意:需要在MySQL中创建一个数据库和对应的数据表,表中包含数据才能登录成功