Servlet
1、BS和CS的区别
CS:客户端服务器架构模式 优点:充分利用客户端机器的资源,减轻服务器的负荷(一部分安全要求不高的计算任务存储任务放在客户端执行,不需要把所有的计算和存储都在服务器执行,从而能够减轻服务器的压力,也能够减轻网络负荷) 举例:如果把所有的计算都放在服务器端计算,然后再发送给客户端,过程需要网络来传输,如果这部分直接放到客户端计算就可以直接得到结果了,因此可以减轻网络负荷 缺点:需要安装;升级维护成本较高
BS:浏览器服务器架构模式 优点:客户端不需要安装;维护成本较低 缺点:所有的计算和存储任务都是放在服务器端,服务器的负荷较重;再服务器计算完成之后把结果再传输给客户端,因此服务端和客户端会进行非常频繁的数据通信,从而网络负荷较重
2、什么是Servlet
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器)端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web。
狂神的课程中更是把实现了servlet接口的java程序叫做servlet
3、Servlet的作用
首先了解到web开发的本质就一句话:客户端和服务器交换数据。于是使用Java的Socket套接字进行编程,去处理客户端来的tcp请求,经过编解码处理读取请求体,获取请求行,然后找到请求行对应的处理逻辑步入服务器的处理中,处理完毕把对应的结果返回给当前的Socket链接,响应完毕,关闭 Socket。
上述过程中, 建立连接、传输数据、关闭连接等过程是tomcat容器帮你做了这些事情, 而拿到请求行之后去找对应的url路由,这一部分就是Servlet来做的!更简单来说Servlet就是一段处理web请求的逻辑。
4、Servlet和Servlet容器的区别
首先需要了解容器的概念,生活中容器用来装水、装粮食,则对应的Servlet容器用来装类,装对象。
而编写的Servlet类为什么需要Servlet容器来管理?因为我们编写的Servlet类没有main()函数,不能独立运行,只能作为一个模块被载入到Servlet容器,然后由Servlet容器来实例化,并调用其中的方法。
一个动态页面对应一个Servlet类,开发一个动态页面就是编写一个Servlet类,当用户请求到达时,Servlet容器会根据配置文件(web.xml)来决定调用哪个类。
了解后可得到以下定义Servlet容器就是Servlet代码的运行环境(运行时),它除了实现Servlet规范定义的各种接口和类,为Servlet的运行提供底层支持,还需要管理由用户编写的Servlet类,比如实例化类(创建对象)、调用方法、销毁类等。
而本次学习采用的Servlet容器则是tomcat(tomcat是一个免费的开放源代码的Servlet容器,很多资料上也称为Web服务器,这里暂不对web容器和web服务器进行区别解释)
5、Servlet的三大作用域
request(请求):它的作用范围是一次请求和响应,是三个作用域中最小的。 session(会话):它的作用比request要大一点,一次会话过程中,它的作用域就一直存在,(默认是30分钟) servletcontext:它作用范围最大,作用于整个服务器中。(Application)
6、生命周期
Servlet生命周期分为四个部分: 实例化==>初始化==>执行处理==>销毁。
// JDK中的Servlet是一个接口
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
(1)生命周期:从出生到死亡的过程就是生命周期。对应Servlet中的三个方法:init(),service(),destroy() (2)默认情况下:第一次接受请求时,这个Servlet会进行实例化(调用构造方法;tomcat在底层用反射做的实例化)、初始化(调用init())、然后服务(调用service()) 然后从第二次请求开始,每一次都服务 当容器关闭时候,其中的所有的servlet实例会被销毁,调用销毁方法 (3)通过案例我们发现:Servlet实例tomcat只会创建一个,所有的请求都是这个实例来响应。 默认情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务。 这样的好处是什么?提高系统的启动速度。 这样的缺点是什么?第一次请求时,耗时较长 因此得出结论:如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置Servlet的初始化时机——这里应该是讲反了把,提高启动速度应该是设置初始化时机,提高响应速度应该是默认 (4)Servlet的初始化时机: 默认是第一次接受请求时,实例化,初始化 我们可以通过来设置servlet启动的先后顺序,数字越小,启动越靠前,最小值0 (5)Servlet在容器中是:单例的、线程不安全的 单例:所有的请求都是同一个实例去响应 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化 我们已经知道了servlet是线程不安全的,给我们的启发是:尽量的不要再servlet中定义成员变量。如果不得不定义成员变量,那么就不要修改成员变量的值,并且不要根据成员变量的值做一些逻辑判断
7、Servlet工作原理
Servlet处理请求的过程
1、首先简单解释一下Servlet接收和响应客户请求的过程:
客户发送一个请求,Servlet是调用service()方法对请求进行响应,service()方法中对请求的方式进行了匹配。选择调用doGet,doPost等这些方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。在Servlet接口和GenericServlet中是没有doGet()、doPost()等等这些方法的,HttpServlet中定义了这些方法,但是都是返回error信息,所以,我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。
2、每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承HttpServlet即可。
3、Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以HttpServlet中实现了service()方法,并将请求ServletRequest、ServletResponse 强转为HttpRequest 和 HttpResponse。
客户端发送请求给服务器 容器根据请求及web.xml判断对应的Servlet是否存在,如果不存在则返回404。 容器根据请求及web.xml判断对应的Servlet是否已经被实例化,若是相应的Servlet没有被实例化,则容器将会加载相应的Servlet到Java虚拟机并实例化。 调用实例对象的service()方法,并开启一个新的线程去执行相关处理。调用servce方法,判断是调用doGet方法还是doPost方法。 业务完成后响应相关的页面发送给客户端。
简而言之:Servlet 容器接收到请求以后,会根据配置文件(web.xml)找到对应的 Servlet 类,将它加载并实例化,然后调用其中的方法来处理用户请求;处理结束后,Servlet 容器将处理结果再转交给 Web 服务器,由 Web 服务器将处理结果进行封装,以 HTTP 响应的形式发送给最终的用户。
8、小结
(1)继承关系:HttpServlet -> GenericServlet -> Servlet (2)Servlet中的核心方法:init(),service(),destroy() (3)服务方法:当有请求过来时,service方法会自动响应(tomcat容器调用)在HttpServlet中会分析请求的方式:到底是get、post、head还是delete等等 然后再去调用的是哪个do开头的方法 那么在HttpServlet中这些do方法默认都是405的实现风格-要我们子类去实现对应的方法,否则默认会报405错误 (4)因此,我们在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法(父类中的方法就是报405错误) (5)Tomcat与Servlet如何工作的?
- Web Client 向Servlet容器发出Http请求;
- Servlet容器接收Web Client 的请求; 3.Servlet容器创建一个Http Request对象,将Web Client请求信息封装到这个对象中; 4.Servlet容器创建一个HttpResponse对象; 5.Servlet容器调用HttpServlet对象的Service方法,把HttpRequest对象与HttpResponse对象作为参数传给HttpServlet对象; 6.HttpServlet调用HttpRequest对象相关方法获取Http请求信息; 7.HttpServlet调用HttpResponse对象的相关方法生成响应数据; 8.Servlet容器把HttpServlet的响应结果传给WebClient;
9、实践
9.1 Mapping问题
1、一个Servlet可以指定一个映射路径
2、一个Servlet可以指定多个映射路径
<!--Servlet的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello4</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello5</url-pattern>
</servlet-mapping>
3、一个Servlet可以指定通用映射路径
// 这种写法优先级很高,直接访问servlet了,不去访问index.jsp了
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello5/*</url-pattern>
</servlet-mapping>
4、指定一些后缀或者前缀等等
<!--可以自定义后缀实现请求映射-->
<!--只要以这个结尾即可即/hello.wang都可以-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.wang</url-pattern>
</servlet-mapping>
5、优先级问题
制定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求
public class ErrorServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 这里可以写成resp.setContentType("text/html;charset=utf-8");合并成一行
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.print("<h1>404</h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
public class HelloServlet extends HttpServlet {
// 由于get或者post只是请求实现的不同的方式,可以互相调用,业务逻辑都一样
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入了doGet方法");
//ServletOutputStream outputStream = resp.getOutputStream();
// 响应流
PrintWriter writer = resp.getWriter();
writer.print("Hello,Serlvet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
<!--注册Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.wang.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<!--404-->
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.wang.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
9.2 ServletContext
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用
1、共享数据:
我在这个Servlet中保存的数据,可以在另外一个Servlet中拿到
// 放置数据的类
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// this.getInitParameter();初始化参数
// this.getServletConfig();Servlet配置
// this.getServletContext();Servlet上下文
ServletContext context = this.getServletContext();
String username = "秦疆"; // 数据
// 将一个数据保存在了ServletContext,名字为username,值为username
context.setAttribute("username", username);
System.out.println("hello");
}
}
// 读取数据的类
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");
resp.setContentType("text/html;charset=utf-8");
resp.getWriter().print("名字" + username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getc</servlet-name>
<servlet-class>com.kuang.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getc</servlet-name>
<url-pattern>/getc</url-pattern>
</servlet-mapping>
</web-app>
2、获取初始化参数
<!--配置一些web应用初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
<servlet>
<servlet-name>gp</servlet-name>
<servlet-class>com.kuang.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gp</servlet-name>
<url-pattern>/gp</url-pattern>
</servlet-mapping>
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
3、请求转发(用的少了解)
请求的是sd4,但是显示的页面是gp,转发的时候路径不会发生变化,之后的重定向路径会变
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println("进入了sd4");
// 获得请求转发,里面参数就是转发的请求路径
// 这里的路径是相对路径
RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
// 调用forward实现请求转发
requestDispatcher.forward(req, resp);
}
<servlet>
<servlet-name>sd4</servlet-name>
<servlet-class>com.kuang.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd4</servlet-name>
<url-pattern>/sd4</url-pattern>
</servlet-mapping>
转发是站内请求,重定向是站外请求
4、读取资源文件
Properties
- 在java目录下新建properties
- 在resources目录下新建properties
发现:都被打包到了同一路径下:classes,我们俗称这个路径为classpath
<servlet>
<servlet-name>sd5</servlet-name>
<servlet-class>com.kuang.servlet.ServletDemo05</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd5</servlet-name>
<url-pattern>/sd5</url-pattern>
</servlet-mapping>
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 此处是把资源变成流,因为prop.load需要流
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/kuang/servlet/aa.properties");
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().print(user + ":" + pwd);
}
maven由于他的约定大于配置,我们之后可能会遇到我们写的配置文件,无法被导出或者生效的问题,解决方案:
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
#classespath是类路径,java和resources
外部应用就是classes
9.3 HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse;
- 如果要获取客户端请求过来的参数:找HttpServletRequest
- 如果要给客户端响应一些信息:找HttpServletResponse
1、简单分类
负责向浏览器发送数据的方法
// 其他流
ServletOutputStream getOutputStream() throws IOException;
// 中文
PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
响应的状态码
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
2、常见应用
-
向浏览器输出消息
-
下载文件
- 要获取下载文件的路径
- 下载的文件名是啥
- 设置想办法让浏览器能够指出下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端
3、验证码功能
<?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_3_1.xsd"
version="3.1"
metadata-complete="true">
<servlet>
<servlet-name>ImageServlet</servlet-name>
<servlet-class>com.wang.servlet.ImageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ImageServlet</servlet-name>
<url-pattern>/image</url-pattern>
</servlet-mapping>
</web-app>
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 如何让浏览器5秒自动刷新一次
resp.setHeader("refresh", "3");
// 在内存中创建一个图片
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
// 得到图片
Graphics2D g = (Graphics2D) image.getGraphics();
// 设置图片的背景颜色
g.setColor(Color.BLACK);
g.fillRect(0,0,80,20);
// 给图片写数据
g.setColor(Color.BLUE);
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),0,20);
// 告诉浏览器这个请求用图片的方式打开
resp.setContentType("image/jpeg");
// 网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires", -1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
// 把图片写给浏览器
ImageIO.write(image,"jpg",resp.getOutputStream());
}
// 生成随机数
private String makeNum(){
Random random = new Random();
String num = random.nextInt(9999999) + "";
StringBuffer sb = new StringBuffer();
// fo循环是保证这个数一定是7位的
for (int i = 0; i < 7 - num.length(); i++) {
sb.append("0");
}
num = sb.toString() + num;
return num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
4、实现重定向
一个web资源B收到客户端A请求后,B他会通知客户端A去访问另外一个web资源C,这个过程叫重定向
常见场景:
-
用户登录
void sendRedirect(String var1) throws IOException;
// 所以此处要加上/res
resp.sendRedirect("/res/image");
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 重定向的原理
// resp.setHeader("Location", "/res/image");
// resp.setStatus(302);
// 所以此处要加上/res
resp.sendRedirect("/res/image");
}
面试题:请你聊聊重定向和转发的区别?
相同点
- 页面都会实现跳转
不同点
- 请求转发的时候,url不会产生变化
- 重定向时候,url地址栏会发生变化
总结: 1.转发是一次请求,重定向是两次 2.转发不会改变url,重定向会改变 3.请求转发是在服务器内部完成的,而重定向是在客户端完成的 4.转发的url必须是当前web工程内部的地址,重定向可以是任意地址
9.4 HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法获得客户端的所有信息
1、获取前端传递的参数
2、请求转发
之前讲过一种转发和一种重定向页面
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println("进入了sd4");
// 获得请求转发,里面参数就是转发的请求路径
RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
// 调用forward实现请求转发
requestDispatcher.forward(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 重定向的原理
// resp.setHeader("Location", "/res/image");
// resp.setStatus(302);
// 所以此处要加上/res!!!!!!!!
resp.sendRedirect("/res/image");
}
以下为常用的请求转发方式
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("***********************");
// 后台接受中文乱码问题
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbys));
System.out.println("***********************");
// 通过请求转发
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录i</title>
</head>
<body>
<h1>登录</h1>
<div style="text-align: center">
<%--这里表单表示的意思是以post方式提交表单,提交到我们的login请求--%>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:
<input type="checkbox" name="hobbys" value="女害">怒海
<input type="checkbox" name="hobbys" value="代码">代码
<input type="checkbox" name="hobbys" value="唱歌">唱歌
<input type="checkbox" name="hobbys" value="电影">电影
<br>
<input type="submit">
</form>
</div>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登陆成功</h1>
</body>
</html>
面试题:请你聊聊重定向和转发的区别?
相同点
- 页面都会实现跳转
不同点
- 请求转发的时候,url不会产生变化:307
- 重定向时候,url地址栏会发生变化:302
\