tomcat(WEB服务器)
-
启动tomcat一定要配置java的环境变量,且环境变量的名称只能是JAVA_HOME,不然启动时会闪一下,然后启动失败
-
关闭tomcat要在使用bin目录下的shutdown.bat脚本,点击x关闭的是意外退出,不会有日志写入
tomcat目录的说明
servlet(小服务程序)
-
UML类图
-
servlet的特点
- 他是由服务器端调用和执行的
- 他是用java语言编写的,本质就是Java类
- 他是按照Servlet规范开发的
- 功能强大,可以完成几乎所有的网站功能
-
servlet3.0前使用web.xml,servlet3.0版本以后(包括3.0)支持注解,同时支持web.xml配置
-
web.xml文件主要用来配置web应用使用到Servlet
-
servlet是单例的,常驻内存,只能有一个
Servlet快速入门
- 开发servlet需要实现Servlet接口
- 实现Servlet接口需要实现Servlet的五个抽象方法
- init()方法
- 初始化servlet
- 当创建HelloServlet实例列时,就会调用init方法
- 该方法只会被调用一次
- ServletConfig()方法
- 返回ServletConfig对象
- 也就是返回Servlet的配置
- service()方法
- service方法处理浏览器的清求(包括get/post)
- 当浏览器每次清求Servlet/时,就会调用一次service
- 当tomcat调用该方法时,会把http请求的数据封装成实现ServletRequest接口的request对象
- 通过servletRequest.对象,可以得到用户提交的数据
- servletResponse对象可以用于返回数据给刘览器tomcat->刘览器
- getServletlnfo()方法
- 返回servlet的信息
- destroy()
- 该方法是在servlet销毁时被凋用
- 只会调用一次
- init()方法
- 当tomcat重新发布或重新加载时,会销毁HashMap集合中的Servlet对象,并调用destroy()方法,浏览器再次访问时还是会发生创建servlet
- 浏览器的每次http请求,tomcat都会开启一个新的线程处理
- 当web应用被终止,或者Servlet:容器终止运行,或者Servlet类重新装载时,会调用destroy()方法
- 配置web.xml文件开发Servlet代码演示(实现接口Servlet的方式)
package com.hspedu.servlet;
import javax.servlet.*;
import java.io.IOException;
/**
* @Projectname: servlet
* @Filename: HelloServlet
* @Author: 杨逸
* @Data:2023/4/2 20:00
* @Description: TODO
*/
/**
*1.开发servlet需要实现Servlet接口
* 2.实现Servlet接口需要实现Servlet的五个抽象方法
*/
public class HelloServlet implements Servlet {
/**
* 1,初始化servlet
* 2.当创建HelloServlet实例时,会调用init方法
* 3.该方法只会被调用一次
* @param servletConfig
* @throws ServletException
*/
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init()方法被调用");
}
/**
* 返回ServletConfig对象
* 也就是返回Servlet的配置
* @return
*/
@Override
public ServletConfig getServletConfig() {
return null;
}
/**
* 1.service方法处理浏览器的清求(包括get/post)
* 2,当浏览器每次清求Servlet时,就会调用一次service
* 3.当tomcat调用该方法时,会把http请求的数据封装成实现ServletRequest接口的request对象
* 4,通过servletRequest对象,可以得到用户提交的数据
* 5.servletResponse对象可以用于返回数据给浏览器(tomcat->浏览器)
* @param servletRequest
* @param servletResponse
* @throws ServletException
* @throws IOException
*/
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service");
//tomcat每次处理http请求,都会开一个新的线程处理
System.out.println("线程id=" + Thread.currentThread().getId());
}
/**
* 返回servlet的信息
* @return
*/
@Override
public String getServletInfo() {
return null;
}
/**
* 1.该方法是在servlet销毁时被调用
* 2.只会调用一次
*/
@Override
public void destroy() {
}
}
web.xm配置文件的配置
- web.xml主要用来配置web应用使用到Servlet
- :给Servlet取名,该名字唯一,不能重复
- :Servlet的类的全路径:Tomcat在反射生成Servlet需要使用到类的全路径
- :表示在tomcat启动时,会自动的加载servlet实例,标签的'1'表示自动加载的循序
- 中的内容一定要和中的一样
- :这个就是该servlet访问的UrL的配置(路径)
- 这时我们应该这样(http://localhost:8080/servlet/helloServlet)访问servlet
- 取名是程序员决定的
<?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">
<!--web.xml主要用来配置web应用使用到Servlet-->
<!--Filter过滤器的配置-->
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>com.hsp.housefurnishings.filter.AuthFilter</filter-class>
<init-param>
<param-name>exclude</param-name>
<param-value>/views/manage/manage_login.jsp,/views/member/login.jsp,</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<!--
1.在filter-mapping的url-pattern配置要拦截/验证url
2.对于我们不去拦截url,就不配置
3.对于要拦截的目录的某些要放行的资源,在通过init-param配置指定
-->
<url-pattern>/views/cart/*</url-pattern>
<url-pattern>/views/manage/*</url-pattern>
<url-pattern>/views/member/*</url-pattern>
<url-pattern>/views/order/*</url-pattern>
<url-pattern>/cartServlet</url-pattern>
<url-pattern>/manage/furnServlet</url-pattern>
<url-pattern>/orderServlet</url-pattern>
</filter-mapping>
<filter>
<filter-name>TransactionFilter</filter-name>
<filter-class>com.hsp.housefurnishings.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置HelloServlet-->
<servlet>
<!--
1.servlet-name:给Servlet取名,该名字唯一,不能重复
2.servlet-class:Servlet的类的全路径:Tomcat在反射生成Servlet需要使用到类的全路径
3.load-on-startup:表示在tomcat启动时,会自动的加载servlet实例,标签的'1'表示自动加载的循序
-->
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.hspedu.servlet.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!--
3.<servlet-mapping>中的<servlet-name>内容一定要和<servlet>中的<servlet-name>一样
4.url-pattern:这个就是该servlet访问的UrL的配置(路径)
5.这时我们应该这样访问servlet, http://localhost:8080/servlet/helloServlet
6.url-pattern取名是程序员决定的
-->
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/helloServlet</url-pattern>
</servlet-mapping>
<!--错误页面的配置-->
<error-page>
<error-code>404</error-code>
<location>/views/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/views/error/500.jsp</location>
</error-page>
</web-app>
浏览器请求Servlet的流程
-
如果是第一次请求(tomcat)
-
查询web.xml
-
看看请求的资源/helloServlet,在web.xml配置中的url-pattern
-
如果找到url-pattern,就会得到(servlet-name) HelloServlet
-
Tomcat维护了一个大的HashMap<id,Servlet>,查询该HashMap看看有没有这个Servlet实例
-
如果没有查询到该serletname对应的id,即没有这个Servlet实例
-
就根据servlet.name去得到servlet-classs(类的全路径)
-
使用反射技术,将servlet实例化(调用init()方法),并放入到Tomcat维护的HashMap<id,Servlet>
-
-
如果不是第一次请求(tomcat)
- 查询web.xml
- 看看请求的资源/helloServlet,在web.xml配置中的url-pattern
- 如果找到url-pattern,就会得到(servlet-name) HelloServlet
- Tomcat维护了一个大的HashMap<id,Servlet>,查询该HashMap看看有没有这个Servlet实例
- 如果找到了该实例servlet,就调用servlet的service()方法
-
总的流程就是,查询web.xml文件,得到url-pattern,HashMap集合存在对应的实例就调用,没有就创建并加入到HashMap集合
-
使用注解开发Servlet代码演示(继承HttpServlet的方式)
package com.hspedu.servlet.annotation;
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;
/**
* @Projectname: servlet
* @Filename: OkServlet
* @Author: 杨逸
* @Data:2023/4/3 14:38
* @Description: TODO
*/
/**
* 1.使用注解的方式配置Servlet,使用注解后就不需要配置web.xml文件
* 2.urlPatterns对应web.xml文件的<url-pattern></url-pattern>
* 3.{"/ok1","/ok2"}表示对应多个访问该Servlet的url
* 4.
*/
@WebServlet (urlPatterns = {"/ok1","/ok2"})
public class OkServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("注解 的 get");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("注解的 post");
}
}
-
重写init()方法时,如果在其他地方需要使用config对象,就需要在重写的init()方法中调用super.init()方法,将配置信息传递到父类中
-
使用注解配置urlPattern的四种方式
-
使用注解可以同时匹配多个url
-
精确匹配
@WebServlet (urlPatterns = {"/ok1","/ok2"})
-
目录匹配(就是在ok1目录下的任意匹配)
@WebServlet (urlPatterns = {"/ok1/*"})
-
扩展名(后缀)匹配(不能带斜杠"/")
@WebServlet (urlPatterns = {"*.java","*.html"})
-
任意匹配(使用"/"或"/*")
@WebServlet (urlPatterns = {"/"})
-
-
使用注解设置urlPattern为任意匹配时,会覆盖默认的DefaultServlet,该Servlet是用于管理静态资源的,一旦设置任意匹配就会拦截对静态资源的访问,使的静态资源不能被访问
-
尽量使用精准匹配
ServletConfig
- 用于获得Servlet程序的servlet-name的值
- 用于获得Servlet程序的init-param初始化参数的值
- 用于获得Servlet程序的ServletContent对象
package com.hspedu.servletConfig;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Projectname: servlet
* @Filename: DBServlet
* @Author: 杨逸
* @Data:2023/4/5 9:52
* @Description: TODO
*/
public class DBServlet extends HttpServlet {
@Override
public void init() throws ServletException {
//一系列操作后,调用super.init()方法,将配置信息传递到父类的config对象中
//如果后续相使用config对象,进行操作,就一定要调用super.init()方法
super.init();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过ServletConfig对象获得web.xml配置的信息
//获得config对象
ServletConfig servletConfig = getServletConfig();
//获得web.xml配置的init-param中的值
String username = servletConfig.getInitParameter("username");
String pwd = servletConfig.getInitParameter("pwd");
System.out.println("用户名" + username);
System.out.println("密码" + pwd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
ServletContext
- ServletContext是一个接口,它表示Servlet上下文对象
- 一个web工程,只有一个ServletContext对象实例,可以通过ServletContent对象统计每个Servlet被访问的次数
- ServletContext对象是在web程启动的时候创建,在web工程停止的时销毁
- ServletContext对象可以通过ServletConfig.getServletContext方法获得对ServletContext对象的引用,也可以通过this.getServletContext()来获得其对象的引用。
- 由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现多个Servlet间通讯。ServletContext对象通常也被称之为域对象.
- ServletContext的作用
- 获取web.xml中配置的上下文参数context-param[信息和整个web应用相关,而不是属于某个Servlet]
- 获取当前的工程路径,格式:/工程路径=》比如/servlet
- 获取工程部署后在服务器硬盘上的绝对路径
- ServletContext属性像Map一样存取数据,多个Servlet共享数据
- 使用SrevletContext统计网站访问次数的演示
- 创建了两个Servlet
- 创建一个WebUilts工具类,使用工具类进行次数统计
- 先获得ServletContext对象,然后获得统计次数的属性
- 第一次需要先判断统计次数属性是否为空,再进行次数的增加
- 最后响应到页面上
public class OrderServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获得ServletContext对象
ServletContext servletContext = getServletContext();
//将ServletContext对象传进去,对统计属性进行加一操作
Integer visit_count = WebUilts.webCount(servletContext);
//设置响应输出流的类型和编码
resp.setContentType("text/html;charset=utf-8");
//获得输出流
PrintWriter writer = resp.getWriter();
//写出内容
writer.print("网站被访问了"+visit_count+"次");
//刷新关闭
writer.flush();
writer.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
//工具类
public class WebUilts {
public static Integer webCount(ServletContext servletContext){
//获得统计网站被访问的次数的属性
Object visit_count = servletContext.getAttribute("visit_count");
//判断是否为空
if (visit_count == null){//如果为空就创建该属性
servletContext.setAttribute("visit_count",1);
visit_count = 1;
}else {
//次数加一,然后将属性重新设置
visit_count = Integer.parseInt(visit_count + "") + 1;
servletContext.setAttribute("visit_count",visit_count+"");
}
return Integer.parseInt(visit_count + "");
}
}
HttpServletRequest(http请求头类)
- 常用方法
- getRequestURI()获取请求的资源路径
- getRequestURL()获取请求的统一资源定位符(绝对路径)
- getRemoteHost()获取客户端的主机,getRemoteAddr()
- getHeader()获取请求头
- getParameter()获取请求的参数
- getParameterValues()获取请求的参数(多个值的时候使用),比如checkbox,返回的数组
- getMethod()获取请求的方式GET或PoST
- setAttribute(key,value);设置域数据
- getAttribute(key)i获取域数据
- getRequestDispatcher()获取请求转发对象,请求转发的核心对
- 在读取http或响应http时,要先设置编码格式,如果存在中文会出现乱码现象
- 演示获得http请求头的数据的和提交的数据
public class RegisterServletRequest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获得http请求的数据
//获得请求的资源路径URI
String requestURI = req.getRequestURI();
System.out.println("请求资源的URI=" + requestURI);
//获得请求的统计资源定位符URl
StringBuffer requestURL = req.getRequestURL();
System.out.println("URL=" + requestURL);
//获得请求客户端的ip地址
String remoteAddr = req.getRemoteAddr();
System.out.println("客户端的ip地址=" + remoteAddr);
//获得http请求头的host
String remoteHost = req.getRemoteHost();
System.out.println("http请求头的host=" + remoteHost);
//获得http的请求方式
String method = req.getMethod();
System.out.println("http的请求方式" + method);
//获得其他的http请求头字段可以使用getHeader()方法
String date = req.getHeader("Referer");
System.out.println("发起请求的页面" + date);
//获得客户端浏览器的类型
String header = req.getHeader("User-Agent");
String[] s1 = header.split(" ");
System.out.println("客户端使用的浏览器=" + s1[s1.length - 2].split("/")[0]);
//读取数据和写入数据时,一定要先设置编码格式,不然会出现乱码现象
req.setCharacterEncoding("utf-8");
//获得提交的数据,使用getParameter()方法,
//提交如果是复选框的数据使用getParameterValues()方法
String username = req.getParameter("username");
System.out.println("提交的用户名" + username);
String[] hobbies = req.getParameterValues("hobby");
for (String s :hobbies) {
System.out.println("表单提交的爱好=" + s);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
请求转发
- 一般用于对响应处理的分步处理(问题分解,解耦)
- req.getRequestDispatcher("/manage"),获得转发器并设置转发的目标Servlet
- requestDispatcher.forward(req,resp),正式转发,并把请求的实例和响应的实例作为参数传进去
- 请求转发的细节
- 浏览器的地址栏不会变化
- 在同一次HTTP请求中,进行多次转发,仍然是一次HTTP请求
- 在同一次HTTP请求中,进行多次转发,多个Servlet可以共享request.域/对象的数据,即reqeust.域/对象的数据(参数,属性)的作用域,是在一次HTTP请求有效
- 可以转发到WEB-INF目录下(一般情况下半年访问到WEB-INF目录)
- 不能访问当前WEB工程外的资源(只能在服务区的内部进行转发)
- 因为浏览器地址栏会停止在第一个servlet,如果你刷新页面,会再次发出请求(并且会带数据),所以支付页面情况下,不要使用请求转发,否则会造成重复支付(可以使用重定向)
- 请求转发的演示
//转发请求的Servlet
public class CheckServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获得http请求的信息
//获得提交的用户名
String username = req.getParameter("username");
//根据用户名,在设置ServletContext设置用户的权限
if ("tom".equals(username)){
getServletContext().setAttribute("role","管理员");
}else {
getServletContext().setAttribute("role","普通用户");
}
//开始请求转发
//转发到用户管理的Servlet,响应具体的请求
//获得转发器
//getRequestDispatcher("/manage"),获得转发器的方法,参数是转发的目的Servlet地址
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/manage");
//把HttpServletRequest和HttpServletResponse放到转发器中开始转发
//使用forward()方法正式转发
requestDispatcher.forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
//请求转发的接受Servlet
public class ManageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//这里根据转发的请求进行具体的响应
//获得servletContext
ServletContext servletContext = getServletContext();
//获得用户的身份信息
String role = (String) servletContext.getAttribute("role");
//获得提交的用户名
String username = req.getParameter("username");
//设置响应的类型和编码
resp.setContentType("text/html;charset=utf-8");
//获得输出流
PrintWriter writer = resp.getWriter();
writer.print("<h1>用户"+username+"的身份是"+role+"</h1>");
writer.flush();
writer.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
-
HttpServletRequest的作业
-
将表单提交的数据,返回显示
-
注意读取数据和写入数据前都要设置编码格式
<!--表单--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册表单</title> </head> <body> <form action="http://localhost:8080/servlet/register_homework" method="post"> <p>用户注册信息</p> 用户名称:<input type="text" name="username" value="默认testuser"><br> 用户密码:<input type="password" name="pwd1"><br> 确认密码:<input type="password" name="pwd2"><br> <p>请选择你喜欢的运动项目</p> <span>篮球</span><input type="checkbox" name="sport" value="篮球" checked> <span>足球</span><input type="checkbox" name="sport" value="足球"> <span>羽毛球</span><input type="checkbox" name="sport" value="羽毛球"><br> 选择你的性别:男<input type="radio" name="sex" value="男" checked>女<input type="radio" name="sex" value="女"><br> 选择你喜欢的城市: <select name="city"> <option>选择你喜欢的城市</option> <option value="深圳">深圳</option> <option value="北京">北京</option> <option value="广州">广州</option> </select><br> 自我介绍:<br> <input type="textarea" name="自我介绍" ><br> 选择你喜欢的文件<input type="file" name="文件"><br> <input type="submit" value="提交"> <input type="reset" value="重置表单"> </form> </body> </html>
//处理表单的HttpServlet package com.hspedu.request; 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; /** * @Projectname: servlet * @Filename: ServletRequestHomework * @Author: 杨逸 * @Data:2023/4/5 18:16 * @Description: TODO */ public class ServletRequestHomework extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //设置编码格式 req.setCharacterEncoding("utf-8"); //获得用户名 String username = req.getParameter("username"); //获得密码 String pwd1 = req.getParameter("pwd1"); String pwd2 = req.getParameter("pwd2"); //获得喜欢的运动 String[] sports = req.getParameterValues("sport"); //获得性别 String sex = req.getParameter("sex"); //获得喜欢的城市 String city = req.getParameter("city"); //获得自我介绍 String introduce = req.getParameter("自我介绍"); //拼接要响应的字符串 String sport = ""; for (String s : sports) { sport += s + ","; } String s = "用户名是" + username + "\n第一次密码是" + pwd1 + "\n第二次密码是" + pwd2 + "\n用户的性别是" + sex + "喜欢的运动是" + sport + "\n喜欢的城市是" + city + "\n自我介绍是" + introduce ; //设置编码 resp.setContentType("text/plain;charset=utf-8"); //获得输出流 PrintWriter writer = resp.getWriter(); writer.print(s); writer.flush(); writer.close(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
HttpServletResponse
-
响应http请求时,中文出现乱码问题的两种解决方法
-
设置服务端的编码,再给响应头指定类型和编码
//给服务端设置编码格式 resp.setCharacterEncoding("utf-8"); //设置响应头的资源类型和编码 resp.setHeader("ContextType","text/html;utf-8");
-
给输出流设置类型和编码
//设置编码 resp.setContentType("text/plain;charset=utf-8"); //获得输出流 PrintWriter writer = resp.getWriter();
-
-
重定向的两种方式
-
直接重定向
//参数是重定向的资源位置(URI),是web项目的资源的具体路径 resp.sendRedirect("servlet/newServet");
-
先设置状态码,然后设置响应头
resp.setStatus(302); resp.setHeader("Location","/servlet/newServlet");
-
-
重定向的细节
- 最佳应用场景:网站迁移,比如原域名是WWw.hsp.com迁移到www.hsp.cn,但是百度抓取的还是原来网址.
- 浏览器地址栏会发生变化,本质是两次http请求
- 重定向后由浏览器解析
- 不能共享Request域中的数据,本质是两次http请求,会生成两个HttpServletRequest对象
- 不能重定向到/WEB-INF下的资源(转发可以访问到/WEB-INF下的资源)
- 可以重定向到Web工程以外的资源,比如到www.baidu.com
- 重定向有两种方式,推荐使用第1种.
- 使用getServletContext().getContextPath()方法可以动态获得WEB项目的路径(/servlet)