目录
二、Servlet详解
1、Servlet
1.1servlet的概念
1.2 servlet的继承关系
1.3Servlet访问
1.4 servlet方法处理请求响应
案例(绝对路径、相对路径):
1.5 转发和重定向
1.6 servletContext
二、Servlet详解
1、Servlet
1.1servlet的概念
Servlet是运行在服务端的小程序,用来处理客户端的请求并给予相应的java类。jsp文件相当于就是servlet文件。
1.2 servlet的继承关系
自定义的servlet是继承自HttpServlet的;
1.2.1servlet接口
Servlet的框架是由两个Java包组成的:javax.servlet与javax.servlet.http。
1) 在javax.servlet包中定义了所有的Servlet类都必须实现或者扩展的通用接口和类。
init()、getServlet()、service()、getServlet()、destory()
2) 在javax.servlet.http包中定义了采用Http协议通信的HttpServlet类。
Servlet的框架的核心是javax.servlet.Servlet接口,所有的Servlet都必须实现这个接口
1.2.2 GenericServlet抽象类
自定义的Servlet需要通过实现Servlet接口来编写Servlet,但是我们每次都必须为Servlet中的所有方法都提供实现,还需要将ServletConfig对象保存到一个类级别的变量中,GenericServlet抽象类就是为我们省略一些模板代码,实现了Servlet和ServletConfig。
1.3Servlet访问
servlet无法通过类名直接访问servlet,需要通过映射的路径去访问(urlPatterns或者value),有两种方法:
1)通过urlPatterns注解方式访问
@WebServlet(name = "TestServlet", value = "/TestServlet",loadOnStartup = 1,initParams = {@WebInitParam(name = "driver",value = "com.mysql.cj.jdbc.driver")})
2) web.xml中配置路径映射
<servlet>
<!– 当地址栏请求之后,运行过程是111-222-333-444
111:匹配要运行的映射路径
222:找到要运行的映射文件名
333:找到对应的文件名
444:通过文件的绝对路径,找到要运行的文件 –>
<!–333相当于注解方式的name字段,建议类名相同,但第一个字母小写–>
<servlet-name>testServlet</servlet-name>
<!–444servelt对应的:包名+类名–>
<servlet-class>com.lwl.controller.TestServlet</servlet-class>
<!–load-on-startup:该元素标记是否在servlet启动(init初始化)时就加载
servlet初始化时就加载,中间的数字越小,优先级越高,
配置时,数字要大于等于0的正整数,也可以为0
若不配置,默认在servlet请求时才会加载–>
<load-on-startup>1</load-on-startup>
</servlet>
<!–配置servlet的映射–>
<servlet-mapping>
<!–222一定要和上面的名字一样–>
<servlet-name>testServlet</servlet-name>
<!–111映射的路径–>
<url-pattern>/TestServlet</url-pattern>
</servlet-mapping>
<!–可以写多个–>
<servlet>
<servlet-name>testServlet2</servlet-name>
<servlet-class>com.lwl.controller.TestServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<!–配置servlet的映射–>
<servlet-mapping>
<servlet-name>testServlet2</servlet-name>
<url-pattern>/TestServlet2</url-pattern>
</servlet-mapping>
1.3.1 servlet工作原理
当Web服务器接收到一个HTTP请求时,它会先判断请求内容——如果是静态网页数据,Web服务器将会自行处理,然后产生响应信息;如果牵涉到动态数据,Web服务器会将请求转交给Servlet容器。此时Servlet容器会找到对应的处理该请求的Servlet实例来处理,结果会送回Web服务器,再由Web服务器传回用户端。
Servlet是单例模式的: 针对同一个Servlet,Servlet容器会在第一次收到http请求时建立一个Servlet实例,然后启动一个线程(从容器线程池中获取一个)。第二次收到http请求时,Servlet容器无须建立相同的Servlet实例,而是启动第二个线程来服务客户端请求。所以多线程方式不但可以提高Web应用程序的执行效率,也可以降低Web服务器的系统负担。
1.3.2 servlet工作流程
1、Web Client 向Servlet容器(Tomcat)发出Http请求
2、Servlet容器接收Web Client的请求
3、Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中
4、Servlet容器创建一个HttpResponse对象
5、Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数传给 HttpServlet对象
6、HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息
7、HttpServlet调用HttpResponse对象的有关方法,生成响应数据
8、Servlet容器把HttpServlet的响应结果传给Web Client
1.3.3 工作周期
Servlet生命周期,是指Servlet从加载、初始化、处理请求、返回响应、直到销毁的过程。
//@WebServlet(name = "TestServlet", value = "/TestServlet",loadOnStartup = 1)
//也可以在此直接配置参数,通过initParams来配置参数,在init方法中调用
@WebServlet(name = "TestServlet", value = "/TestServlet",loadOnStartup = 1,initParams = {@WebInitParam(name = "driver",value = "com.mysql.cj.jdbc.driver")})
public class TestServlet extends HttpServlet {
//service服务可以调用很多次,用来处理各种请求的方法
//service相当于doGet、doPost的集合体
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("GBK");
PrintWriter writer = resp.getWriter();
writer.print("hello");
writer.flush();
writer.close();
}
//初始化只调用一次
@Override
public void init() throws ServletException {
//通过key值调用
String driver = getInitParameter("driver");
System.out.println(driver);
System.out.println("初始化");
}
//servlet是单例模式,
//创建servlet对象:
//1、每次启动servlet容器,servlet会判断内存中是否有指定的servlet对象,如果没有则创建,如果有则创建Httprequest、Httpresponse对象,从而调用service方法
//2、如果在web.xml文件或注解中指定了load-on-startup的值,则在servlet启动时,就会按照顺序创建并初始化servlet对象。
//3、当servlet文件修改更新后,会重新创建servlet
public TestServlet() {
System.out.println("实例化");
}
//只销毁一次,当关闭servlet或者重启程序是调用销毁servlet的方法
@Override
public void destroy() {
System.out.println("摧毁这个服务");
}
}
servlet类2:
@WebServlet(name = "TestServlet2", value = "/TestServlet2" , loadOnStartup = 2)
public class TestServlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
public void init() throws ServletException {
System.out.println("初始化222");
}
public TestServlet2() {
System.out.println("实例化222");
}
@Override
public void destroy() {
System.out.println("摧毁这个服务222");
}
}
通过观察load-on-startup存在和不存在的控制台情况,可以看到它的作用。
其中,在servlet运行过程中,在控制台tomcat会调用jdk的路径生成自己的文件路径,C:\Users\雷文林\AppData\Local\JetBrains\IntelliJIdea2022.2\tomcat\fe69fecd-5ad6-41f3-95ab-4978e8ca2575 里面存放了配置文件,日志,还有工作的servlet文件和jsp文件
1.3.4 配置初始化参数
1)通过web.xml文件配置中初始化
<servlet>
<servlet-name>testServlet</servlet-name>
<servlet-class>com.lwl.controller.TestServlet</servlet-class>
<init-param>
<param-name>driver</param-name>
<param-value>com.mysql.cj.jdbc.Driver</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/test?setverTimezone=Asia/Shanghai</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>testServlet</servlet-name>
<url-pattern>/checkServlet</url-pattern>
</servlet-mapping>
2)通过注解方式初始化参数
@WebServlet(name = "TestServlet", value = "/TestServlet",loadOnStartup = 1,initParams = {@WebInitParam(name = "driver",value = "com.mysql.cj.jdbc.driver")})
获取初始化参数:要在初始化servlet时就调用参数
//初始化只调用一次,必须要在初始化的方法中调用
@Override
public void init() throws ServletException {
//通过key值调用
String driver = getInitParameter("driver");
System.out.println(driver);
System.out.println("初始化");
}
1.4 servlet方法处理请求响应
对于每一个HTTP请求,servlet容器会创建一个封装了HTTP请求的ServletRequest实例传递给servlet的service方法,ServletResponse则表示一个Servlet响应,其隐藏了将响应发给浏览器的复杂性。
通过ServletRequest的方法你可以获取一些请求相关的参数,而ServletResponse则可以将设置一些返回参数信息,并且设置返回内容。
HttpServletResquest常用方法:
String getParameter(String); //根据名字获取参数的值:表单中的参数,地址栏中的参数
String[] getParameterValues(String);//根据名字获取一组参数的值(复选框)
Map<String,Object>[] getParameterMap()://获得一个Map<String, String[]>对象,所有的参数值都放在字符串数组中
getAttribute(String);//根据名字获取属性的值
setAttribute(String,Object);//根据属性设置值
getRequestDispatcher(String);//请求转发
setCharactorEncoding(); //设置编码方式
HttpServletResponse常用方法:
sendRirect();//重定向:重新请求指定的地址
setCharacterEncoding();//设置响应的编码方式;
addCookie(); //添加cookie
//所用登录界面为下面的.jsp文件
@WebServlet(name = "CheckServlet", value = "/checkServlet")
public class CheckServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求的编码格式
req.setCharacterEncoding("utf-8");
//获取表单中的元素,name="username",name属性的值一定要与表单元素的名字一致
String username = req.getParameter("username");
String password = req.getParameter("password");
//判断得到的名字和密码是否正确
if (username.equals("admin")&&password.equals("123456")){
System.out.println("登录成功");
//根据属性设置值,将值存储到request中
req.setAttribute("user",username);
//指明请求转发的路径
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/SecondServlet");
//请求转发,将值通过request转发,与接收值的一方共用一个request及里面的值
requestDispatcher.forward(req,resp);
}else{
System.out.println("登陆失败");
//假如映射路径是双层路径,那么在使用重定向时,重定向只会更换最后一个/后的内容,
//如果登陆失败,是跳不回登录界面的,
// 有两种解决方案
// 1、使用../index.jsp返回上级目录,就能顺利跳回登录界面
// 登录失败后路径http://localhost:8080/JavaWebDay02_war_exploded/index.jsp
//2、把项目名称改为只剩一个/,对于双层路径这样的话用绝对路径也能顺利跳回登录界面
//而对于单层路径,无论是相对路径还是绝对路径都可以调回登录界面
resp.sendRedirect("index.jsp");
}
}
}
案例(绝对路径、相对路径):
登录界面(.jsp):
<%--
对于单层路径而言
加斜杠(404 not found)绝对路径:http://localhost:8080/checkServlet
绝对路径:相对于localhost:8080而言的
不加斜杠/ 相对路径:http://localhost:8080/JavaWebDay02_war_exploded/checkServlet
相对路径:是相对于当前的项目 localhost:8080/项目名字/映射路径
--%>
<form action="UserServlet" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="提交">
</form>
实体类:
public class User {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
接口及实现类
public interface IUserDao {
List<Map<String,Object>> findOne(User user);
}
public class UserDaoImpl extends BaseDao implements IUserDao {
@Override
public List<Map<String,Object>> findOne( User user) {
String sql = "select * from user where username=? and password=?";
List<Map<String, Object>> query = query(sql, user.getUsername(), user.getPassword());
return query;
}
}
检查登录界面:
@WebServlet(name = "UserServlet", value = "/UserServlet")
public class UserServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
IUserDao iUserDao = new UserDaoImpl();
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
User user = new User();
user.setUsername(username);
user.setPassword(password);
List<Map<String, Object>> one = iUserDao.findOne(user);
if (one.size()>0){
req.setAttribute("user1",username);
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/SecondServlet");
requestDispatcher.forward(req,resp);
resp.sendRedirect("/SecondServlet");
System.out.println("登录成功");
}else {
resp.sendRedirect("/index.jsp");
}
}
}
书城(接收数据):
@WebServlet(name = "SecondServlet", value = "/SecondServlet")
public class BookServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
IBookDao iBookDao = new BookDaoImpl();
List<Map<String, Object>> all = iBookDao.findAll();
response.setCharacterEncoding("GBK");
PrintWriter writer = response.getWriter();
//接收request中传来的数据
//通过request获取里面存放的元素,
Object user = request.getAttribute("user1");
writer.print("<html><body>");
writer.print("<span>欢迎您,"+user+"</span>");
writer.print("<table border='1' rules=all > "+
"<tr><td>id</td><td>bookname</td><td>price</td><td>author</td></tr>");
int size = all.size();
for (int i = 0; i < size; i++) {
writer.print("<tr>");
writer.print("<td>"+all.get(i).get("id")+"</td>");
writer.print("<td>"+all.get(i).get("bookname")+"</td>");
writer.print("<td>"+all.get(i).get("price")+"</td>");
writer.print("<td>"+all.get(i).get("author")+"</td>");
writer.print("</tr>");
}
writer.print("</table>");
writer.print("</body></html>");
writer.flush();
writer.close();
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
}
1.5 转发和重定向
1.5.1 转发和重定向区别
服务器转发:发出了一次请求,地址栏不发生变化。可以携带数据,在服务器端完成,效率高且安全。
携带数据可以在Servlet之间进行传递
//请求转发,服务器转发
request.setAttribute("msg","欢迎你登陆成功");//request存储数据
request.getRequestDispatcher("/welcomeServlet").forward(request,response);
客户端重定向:发出了两次请求,地址栏发生变化,不可以携带数据
客户端重新发送新的请求。
//客户端重定向
//服务器会向客户端浏览器发送一个响应:url, 客户端会重新请求该URL
request.setAttribute("msg","欢迎你登陆成功");//request存储数据
response.sendRedirect("/login.html");
1.6 servletContext
ServletContext是代表了Servlet应用程序。每个Web应用程序只有一个context。在分布式环境中,一个应用程序同时部署到多个容器中,并且每台Java虚拟机都有一个ServletContext对象。
servletcontext对象就相当于是一个全局变量域,当在这个应用存在的时候,里面的内容会一直存在。
1.6.1 获取servletContext内容
先声明一个变量放在servletcontext内容中:
可以通过配置文件,也可以通过注解方式
<context-param>
<param-name>gongfu</param-name>
<param-value>太极拳</param-value>
</context-param>
设置内容:
ServletContext servletContext = this.getServletContext();
String gongfu = servletContext.getInitParameter("gongfu");
System.out.println(gongfu);
servletContext.setAttribute("gf",gongfu);
获取内容:
ServletContext servletContext = this.getServletContext();
Object gf = servletContext.getAttribute("gf");
System.out.println("========="+gf);
配置好内容后,必须要先执行设置内容的程序,再执行获取内容的程序才能取到值。