JavaWeb

JavaWeb概述

Web服务器

​ Web服务器的作用是接收客户端的请求,给客户端作出响应,一个连接了网络的应用程序,一定会有一个服务器

​ 做一个面向互联网的应用程序,你就需要一个24小时开启的服务器,以便于你的客户可以随时使用程序

​ Java的Web开发是基于B/S架构的,所以将在浏览器上编写程序

Tomcat

​ Tomcat是一个免费的开放源代码的Web应用服务器,我们的项目程序都会运行在服务器之上

安装与配置

Tomcat官网

​ 下载压缩包,解压

​ 使用Tomcat之前,要现在系统环境变量中添加JAVA_HOME变量,值为JDK目录

​ 再在Path变量下添加%JAVA_HOME%/bin,之后就可以正常运行

​ 进入bin目录,启动startup.bat以启动Tomcat,shutdown.bat关闭Tomcat

​ 使用Tomcat时,不要关闭启动的cmd界面,startup.bat相当于启动服务器,你把这个关了就等于把你服务器关了

Http

​ 超文本传输协议,是一个简单的请求-响应协议,运行于TCP之上

Http报文

​ Http通过发送Http报文,完成请求与响应

​ Http报文分为请求报文和响应报文两种

1.请求报文

​ 请求报文由请求行,首部和主题实体组成

  • 请求行:包括请求类型(get,post),URL和Http版本
  • 首部:用于浏览器和web服务器之间信息交换的附加信息
2.响应报文

​ 响应报文由状态行,首部和实体主体组成

  • 状态行:包括Http版本,状态码(404(找不到资源),200(表示成功))和状态短语
  • 首部:和请求报文基本相似

响应码

  • 1xx(信息性状态码):表示接收的请求正在处理。

  • 2xx(成功状态码):表示请求正常处理完毕。

    200:表示请求成功。一般用于GET与POST请求

  • 3xx(重定向状态码):需要后续操作才能完成这一请求。

  • 4xx(客户端错误状态码):表示请求包含语法错误或无法完成。

​ 404:找不到资源,一般是路径写错了

  • 5xx(服务器错误状态码):服务器在处理请求的过程中发生了错误。

​ 500:服务器内部错误,你代码写错了

Http标头

HTTP 标头 - HTTP | MDN (mozilla.org)

​ 标头(Header)控制着报文的具体信息

Maven

​ Maven是一种架构管理工具,用来帮助我们管理项目 是什么Maven?

​ 我们在实际开发时,必然会用到外部库,而且外部库的数量不小,如果没有合适的管理,使用起来就会异常杂乱

​ Maven就像一个仓库管理员,它会去管理你导入的外部库。在这里将外部库称之为依赖

​ Maven会提供以下等功能

  • 中央仓库:Maven有一个云端的中央仓库,包含着大量公开的依赖;并且会在本地部署一个本地仓库,用于存放依赖。当你声明要使用一个依赖时,会先从中央仓库查找是否有这个依赖,如果没有,去中央仓库下载
  • 依赖传递:如果你的依赖A必须要有另一个依赖B才能运行,那么Maven会当你使用依赖A时自动导入依赖B
  • 版本管理:你可以为每个依赖制定一个版本号,以确保项目使用了正确稳定的库版本
  • 范围:你可以为依赖制定范围,来确定依赖在何时可用,或者是否应该包含在最终的构建结果中

​ Maven的核心思想:约定大于配置。Maven会规定好该如何去设计项目,必须按照一定的规范进行

Maven父子工程

​ 如同类的继承关系一样,Maven为不同的Project赋予的继承的属性,子工程可以使用父工程的所有jar包,父工程可以通过范围来决定依赖是否提供给子工程

​ 在父工程的pom.xml文件中,moudle标签中会存放子项目列表

<modules>
  <module>web1</module>
  <module>web2</module>
</modules>

Maven环境配置

IDEA中配置Maven

​ 使用Maven前,先设置MAVEN_HOME和M2_HOME两个环境变量,一个是Maven的文件目录一个是bin目录,再在Path里添加上%MAVEN_HOME%\bin

​ 然后找到conf的settting.xml文件,将mirror修改为国内的阿里云maven镜像,百度就能找到

<mirror>
  <id>alimaven</id>
  <name>aliyun maven</name>
  <url>https://maven.aliyun.com/nexus/content/groups/public</url>
  <mirrorOf>central</mirrorOf>
</mirror>
本地仓库

​ 在settting.xml文件中,找到localRepository,可以修改本地仓库位置

<localRepository>本地仓库路径</localRepository>
IDEA中设置

​ file->settings->build,execution,deployment->build tools->maven

将用户设置文件和本地仓库的路径设置为自己的Maven所在路径

image-20240815091600989

​ 再回到IDEA主页面,点击所有设置,同样的修改以上设置。这一步是全局配置

Junit

Junit是每个Maven项目创建时自动添加的依赖

    <!--junit-->
    <!--该依赖提供单元测试,通常只有main方法才能运行,通过junit,在要运行的方法添加注解@Test,即可运行该方法-->
	
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
    </dependency>

Maven导致的配置文件失效问题

​ 由于Maven的约定大于配置,会导致配置文件(如.xml文件)无法导出或无法生效的问题。解决方案如下:

​ 在pom.xml文件内,添加以下build标签,将资源暴露出来

<build>
        <resources>
            
            <!--使directory路径下的include类型文件能够被使用-->
            
            <resource>
                <directory>src.main.resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>

            <resource>
                <directory>src.main.java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>

        </resources>
    </build>

创建Web项目

Tomcat提供了Web服务器,Maven是架构管理工具,通过这二者我们就能部署一个基本的Web程序

创建带有Maven的Web项目

image-20240815144126106

​ 创建完Maven项目后,就会自动下载Maven相关文件

​ 下载完成后,删除pom.xml中15行的< scope>test< /scope>,再重构一次Maven项目

部署Tomcat

设置运行/调试配置

image-20240815144424133 image-20240815144629261

Servlet

​ 目前的B/S架构应用都是用Http协议进行数据传输,Http协议通过请求/应答来完成数据的传输

​ Servlet是Tomcat提供的一个接口,用来处理请求的业务逻辑。

导入Servlet资源

​ 要使用Servlet,就要先导入Servlet包;导入外部资源,就要用到Maven

​ 在pom.xml文件中,添加以下依赖

  <!--Servlet-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

​ 添加完后,重构Maven即可

Servlet的运行过程

​ Servlet接口提供了几个方法,源码如下

package javax.servlet;
import java.io.IOException;

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;
    ServletConfig getServletConfig();
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    String getServletInfo();
    void destroy();
}
//init,service,destroy是生命周期方法
//init和destroy仅执行一次,即Servlet的创建和销毁
//service在每次有新请求时被调用。主要的业务逻辑代码就写在这里

​ 由于Tomcat将底层的TCP连接和Http请求/响应过程都已经完成,我们无需关注底层连接,只需要编写业务逻辑代码即可,集合到Servlet接口中,展现给我们的只有三个对象:ServletConfig,ServletRequest,ServletResponse。

  • ServletConfig

​ ServletConfig对象封装了servlet的一些参数信息。如果需要,我们可以从它获取。

  • Request/Response

​ 当Http请求到了Tomcat服务器后,Tomcat通过字符串解析,将Header,URL,Method等信息封装进Request对象中,等待Servlet调用

​ 对于Response,Tomcat将其传入Servlet时是一个空对象,通过Servlet一系列处理后才会有信息,Tomcat再将里面的信息组装成Http响应,发送给客户端

​ 浏览器发送请求最基本的方式有两种:Get和Post,那么不同的请求方法就一定要有不同的处理方式

​ 通过实现接口中的service方法,来区分不同的请求方式

​ Servlet在Java中有两个具体的实现类:GenericServlet 和 HttpServlet。其中HttpServlet类实现了service方法

//HttpServlet类中的service方法
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        long lastModified;
        if (method.equals("GET")) {//------------------Get方法
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if (ifModifiedSince < lastModified) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        } else if (method.equals("POST")) {//------------------Post方法
            this.doPost(req, resp);
        } else if (method.equals("PUT")) {//------------------其他方法
            this.doPut(req, resp);
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        } else if (method.equals("TRACE")) {
            this.doTrace(req, resp);
        } else {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[]{method};
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }

    }

​ 每当有请求时service方法都会被调用,根据请求的Method属性,找到对应的请求方法所要执行的业务逻辑代码

​ 如Get方法去执行doGet,Post方法执行doPost

​ 但是HttpServlet中的这些do方法,并不是直接使用的,到了这里就是实际的业务逻辑部分了,如果我们不去重写do方法,返回给我们的只有错误码。你不去设计具体怎么做,默认就是报错

//HttpServlet类中的doGet方法
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(405, msg);
        } else {
            resp.sendError(400, msg);//无论怎样,返回的都是错误码
        }

    }

​ 现在我们就要实现一个继承了HttpServlet的类,来重写do方法

public class MyServlet extends HttpServlet {
    //重写doGet,每当有一个Get类型的请求时,最终会转到执行这个方法
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DoGet");
        PrintWriter writer=resp.getWriter();
        writer.println("thsiisget");
    }

    //重写doPost,每当有一个Post类型的请求时,最终会转到执行这个方法
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("DoPost");
        PrintWriter writer=resp.getWriter();
        writer.println("thsiispost");
    }
}

​ 总之,service方法只是判断并转发请求,具体的业务逻辑在do方法里实现

将Servlet类部署到Web服务器中

​ 打开Web.xml文件,添加

<!--注册Servlet-->
<servlet>
    <!--请求名-->
    <servlet-name>hello</servlet-name>
    <!--执行请求的Servlet类-->
    <servlet-class>MyServlet</servlet-class>
</servlet>

<!--Servlet的请求路径-->
<servlet-mapping>
    <!--与上面的请求名一致-->
    <servlet-name>hello</servlet-name>
    <!--映射路径-->
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

后记:这样注册Servlet类,会造成JSP页面显示源码,注释掉Servlet和Servlet-Mapping又会恢复正常。

注册Servlet类还可以通过注解形式

首先要将Web.xml文件开头的metadata-complete改为"false"

<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"
         metadata-complete="false">
		<!-- 如果该值为true,表示整个项目部署时配置已经完全,则只会使用web.xml文件里的配置-->
    	<!--为false,则会扫描所有文件的注解,并且加载配置-->
</web-app>

然后再Servlet类前添加注解,以注册Servlet

//name相当于<servlet-name>,,urlPatterns=<url-pattern>,loadOnstartup为加载优先级,越小越高
//经过测试,如果name=jsp的话,会显示源码,url=jsp的话,第一次显示源码,在请求一次就正常了
//所以不要将值写成关键字
@WebServlet(name="jsp1",urlPatterns = "/jsp1",loadOnStartup=1)
public class JSPTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    }
}

​ 通过注解注册Servlet不会出现问题

Web开发 web.xml中metadata-complete属性

请求映射

​ 一个具体的http请求,实际操作就是在网址后加上其他路径,不同的路径请求的资源也不相同

​ 此时就需要一个请求映射,不同的请求映射到不同的操作上

​ 在上面我们部署Servlet类时,在servlet标签内定义了请求名和执行请求的类,还在servlet-mapping中设置了它的映射。

​ 当我们访问了url-pattern的路径后,就会跳转到对应请求名的类上,根据请求的Method类型调用do方法

image-20240902183614983

映射遵循以下规则:

1.一个Servlet类可以指定一个映射路径

2.一个Servlet类可以指定多个映射路径

3.一个Servlet类可以指定通用映射路径

即映射可以写成这样:

<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>MyServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <!-- 一个Servlet类提供给多个映射使用-->
    <servlet-name>hello</servlet-name>
    <!-- 指定通用映射路径,*为通配符,即请求/123+任意字符,都会跳转到hello-->
    <url-pattern>/123/*</url-pattern>
</servlet-mapping>

ServletContext对象

一个Web容器启动时,会为其中的每个Web项目创建一个通用的ServletContext对象,用于代表这些项目

​ Context对象能提供以下功能

1.共享数据

​ 一个Servlet类中的数据,可以共享给其他Servlet类使用

比如我定义了两个Servlet类,Servlet1和Servlet2

public class Servlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            String username="zhang";
            //向Context中存放了一个key=username,value=username字符串的值的信息
            context.setAttribute("username",username);
        PrintWriter writer=resp.getWriter();
        writer.println("thsiispost");
    }
}

public class Servlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        //从Context中提取key=username的值
        String string=(String)context.getAttribute("username");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("GBK");//编码格式

        resp.getWriter().print("名字="+string);
        System.out.println(string);
    }
}

​ 在Servlet1类中,我们使用setAttribute方法向Context对象中存放了一个值,在Servlet2类中,我们可以提取出来这个值

​ 设置好映射

<servlet>
  <servlet-name>GetContext</servlet-name>
  <servlet-class>Servlet2</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>GetContext</servlet-name>
  <url-pattern>/GC</url-pattern>
</servlet-mapping>

<servlet>
  <servlet-name>hello</servlet-name>
  <servlet-class>Servlet1</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>hello</servlet-name>
  <url-pattern>/hello</url-pattern>
</servlet-mapping>

​ 先去请求Servlet1类,将值存入Context对象中,然后请求Servlet2类,就可以得到这个值

2.获取初始化参数

​ 在web.xml里,可以设置一些初始化参数

<context-param>
    <!--key=url,value=value-->
  <param-name>url</param-name>
  <param-value>value</param-value>
</context-param>

​ 这些参数也可以通过Context对象获取到

public class Servlet3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        //获取key=url的初始化参数
        context.getInitParameter("url");
    }
}
3.请求转发

​ 有时候一个请求需要多个Servlet类执行,那么在Servlet1类执行完后,就要把这个请求转发到Servlet2类中

public class Servlet3 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        context.getInitParameter("url");
        RequestDispatcher rd = context.getRequestDispatcher("/s2");//设置转发路径,RequestDispatcher请求调度类
        rd.forward(req, resp);//将Request和Response对象转发到路径所在位置
    }
}
当请求/s3时,执行到forward后就会跳转到/s2的页面,请求也会转发到这里

请求转发有两种方式

getServletContext().getRequestDispatcher("/s2").forward(req,resp);
req.getRequestDispatcher("/s2").forward(req,resp);

4.读取资源文件
public class Servlet5 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //读取properties文件信息
        InputStream fis=this.getServletContext().getResourceAsStream("WEB-INF/classes/db.properties");
        Properties properties=new Properties();
       
        properties.load(fis);
        String username=properties.getProperty("username");
        String password=properties.getProperty("password");
        resp.getWriter().println(username);
        resp.getWriter().println(password);

    }
}
#properties文件
username=root
password=123456

Response对象

​ Web服务器接收到客户端的http请求时,Servlet就会创建一个代表请求的HttpServletRequest对象和一个代表响应的HttpServletResponse对象

  • HttpServletRequest包含着客户端请求过来的参数
  • HttpsResponse包含响应给客户端的信息
源码分析

源码信息可以大致分为以下几类

向浏览器输出的方法

ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;

向浏览器发送响应头的方法

void setCharacterEncoding(String var1);

void setContentLength(int var1);

void setContentLengthLong(long 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;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
1.向浏览器输出消息
resp.getWriter().println("");
2.下载文件

​ 下载文件的过程如下

  • 获取下载文件的路径,确定下载的文件名
  • 设置content-disposition响应头,控制浏览器以下载的形式打开文件
  • 获取下载文件输入流
  • 创建缓冲区
  • 获取OutputStream对象
  • 将FileOutputStream流写入到Buffer缓冲区
  • 使用OutputStream对象将缓冲区中数据输出到客户端
public class Servlet1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        //获取下载文件的路径,确定下载的文件名
        String realpath="E:\\WorkSpaces\\IdeaProjects\\MavenWeb\\ResponseTest\\src\\main\\resources\\1.jpg";
        String filename=realpath.substring(realpath.lastIndexOf("\\")+1);
        
		//设置content-disposition响应头,控制浏览器以下载的形式打开文件
        resp.setHeader("Content-disposition","attachment;filename"+ filename);
        
        //到这一步,文件将以字节流的形式下载下来(一个txt文件)。我们要通过IO流将其转化为具体的媒体格式
        
 		//获取下载文件输入流,输出流对象
        FileInputStream fis=new FileInputStream(realpath);
        ServletOutputStream out=resp.getOutputStream();

        //创建缓冲区
        int len=0;
        byte[] buffer=new byte[1024];

        //输入流将文件信息读入缓冲区,输出流将缓冲区内信息写出
        //从Response对象获取的输出流,输出对象是客户端,即是输出到客户端下载的文件
        //经典的文件复制过程,看不懂回去看IO流
        while((len=fis.read(buffer))>0){
            out.write(buffer,0,len);
        }
        //关闭流
        fis.close();
        out.close();
    }
}
3.验证码

验证码实际上就是长度固定的随机数

4.实现重定向

​ 一个Web资源收到客户端请求后,会通知客户端去访问另外一个Web资源,这个过程叫重定向

​ 例如用户登录后,从登录页面跳转到其他页面

resp.sendRedirect("/file");//重定向到/file

重定向实际上是进行了以下两步

//Location首部指定的是需要将页面重新定向至的地址
//一般在响应码为 3xx 的响应中才会有意义
resp.setHeader("Location","/file");
//将响应码设置为302,使页面跳转
resp.setStatus(302);

重定向与转发的区别:

相同:

  • 页面都会实现跳转

不同

  • 转发是服务器行为,服务器内部资源只能将请求转发给其他内部资源,实现跳转
  • 重定向是客户端行为,客户端请求一个新的url,可以跳转到外部资源

后记:

当你重定向到jsp后出现404时,看这个

jsp重定向与请求转发的路径404问题

//sendRedirect方法必须添加虚拟路径:
resp.sendRedirect(req.getContextPath()+"/success.jsp");

Request对象

获取参数

Request对象可以获取表单内信息

<html>
    <body>
        <h2>Hello World!</h2>
        <div style="align-items: center">
            <%--action:表单提交到action内的请求--%>
            <form action="${pageContext.request.contextPath}/login" method="post">
                <%--Request对象将根据name找值--%>
                username:<input type="text" name="username"><br>
                password:<input type="password" name="password"><br>

                <input type="checkbox" name="c1" value="apple">apple<br>
                <input type="checkbox" name="c1" value="banana">banana<br>
                <input type="checkbox" name="c1" value="orange">orange<br>

                <input type="submit" value="submit">
            </form>
        </div>
    </body>
</html>
public class RequestText extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		
        // req.getParameter("")获取参数方法,传入一个String,找到表单内对应name的值,以String返回
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        //对于checkbox,由于有多个值,要用getParameterValues(方法),返回一个String数组
        String[] checkbox = req.getParameterValues("c1");

        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(checkbox));

        resp.sendRedirect(req.getContextPath()+"/success.jsp");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
请求转发

​ Request对象也可以进行请求转发,详情见ServletContext的请求转发

Cookie,Session

会话,Cookie,Session

会话:会话是浏览器和服务器之间的多次请求和响应

​ 从浏览器访问服务器开始,到访问服务器结束,浏览器关闭为止的这段时间内容产生的多次请求和响应,合起来叫做浏览器和服务器之间的一次会话

​ Http是一种无状态的协议

//    HTTP无状态协议,是指协议对于交互性场景没有记忆能力
//    你在一次请求中所产生的所有数据不会被服务器记录下来,在下一次请求就会被彻底忘记
//    举例来说,你在一个网站完成了登录,但当你刷新了页面,服务器就忘记了你的登录信息,需要你重新登录
//    这种情况是不能接受的,例如你在淘宝买东西,进淘宝登录,选商品登录,买商品还要登录。
//    为了解决这个问题,Cookie和Session应运而生

​ Cookie和Session是两种用于保持HTTP连接状态的技术:

  • ​ Cookie相当于一个通行证,当客户端请求服务器时,服务器就会通过Response对象返回给客户端一个Cookie,之后每次请求时客户端会将Cookie放在Http的Header里,服务器检查该Cookie,以此辨别用户状态

  • ​ Cookie是存放在客户端的,一般当你在网站登录时选择记住我,服务器就会发给你一个Cookie,只要你不删除它,就不用再次登录了

  • ​ Cookie具有跨域名性,不同网站的Cookie不会混淆。例如百度的Cookie不会在请求其他网站时使用

  • Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上

  • 服务器端为每个用户创建了唯一的session,当服务器接受到请求时,从Request对象中获取到Cookie信息,再去查找Session

​ Cookie有三个主要信息:name,value个Maxage

Cookie cookie=new Cookie("name","value");//name和value可以通过get和set方法设置。不建议修改name
cookie.setMaxAge(1);//设置cookie的最大生命周期,超过此时间cookie会被删除。1表示1秒

Cookie[] cookies = req.getCookies();//可以获取到Request对象中的Cookie(可能有多个)

  • 一个Cookie只能保存一个信息(value)
  • 一个Web站点可以给客户端浏览器发送多个Cookie,最多存放20个
  • Cookie大小限制4kb
  • 浏览器最多存放300个Cookie

Session

​ 一个浏览器访问Web网站时,就会产生一个Session,只要浏览器不关闭(会话不结束),Session就一直存在

​ 不同的浏览器会创建不同的Session(相当于不同的客户端)

​ 现在将Session理解为存储Cookie即可

​ 当一个Session被创建时,执行了以下步骤:

HttpSession session=req.getSession();
Cookie cookie = new Cookie("name",session.getID());
resp.addCookie(cookie);//将Cookie添加到Response对象中
//addCookie后,就可以通过req.GetCookie方法获得
image-20240910083511570

Session可以添加参数

session.setAttribute("name","value");
session.getAttribute("name");
session.removeAttribute("name");

可以注销Session,但会立刻产生一个新的Session

session.invalidate();
//如果没有Session去请求服务器,会产生500

可以在Web.xml文件里修改Session的持续时间

<session-config>
    <!--以分钟为单位-->
    <session-timeout>1</session-timeout>
</session-config>

每一个Session都有一个唯一的ID,用于标识Session

session.getID();

当客户端访问服务器时,服务器执行以下步骤:

  • 创建一个唯一的Session
  • 返回给客户端一个Cookie,值为Session的id

JSP

浅显来说,JSP就是写在HTML文件里的Java代码

但是JSP用起来时不适合写Java代码,现在更多的是前后端分离,JSP更多用于输出一些东西

​ JSP(JavaServerPage),java服务器端页面,也是用来开发动态Web的技术

​ 动态Web的定义是可以自己产生页面的。例如一个视频网站,你不可能用户上传一个视频,你去专门做一个该视频的页面。动态Web页面会自动生成

现在我们接触到的向页面写入的方法有

resp.getWriter().println("");

如果用printWriter来写页面,就会变成这样

PrintWriter writer = resp.getWriter();     
writer.println("<html>");
writer.println("<head>");
writer.println("<title>Session Test</title>");
writer.println("</head>");
writer.println("<body>");
writer.println("<h1>Session Test</h1>");
writer.println("</body>");
writer.println("</html>");

​ 手动来写这些显然是不可接受的,JSP应运而生

JSP和HTML的区别:

  • HTML只提供静态的数据
  • JSP页面中可以嵌入Java代码,提供动态数据
HttpJspPage jspPage;//一个JSP对象

​ HttpJspPage类继承了JspPage,JspPage继承了Servlet类,所以本质上JSP还是一个Servlet类

​ JSP在运行过程中会被转化为一个java类。从源码来看,JSP也是通过PrintWriter的print方法来写网页的

   //初始化
	public void _jspInit() {
  }
	//销毁
  public void _jspDestroy() {
  }
	//Service方法,判断请求
	public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
      throws java.io.IOException, javax.servlet.ServletException {

    if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
      final java.lang.String _jspx_method = request.getMethod();
      if ("OPTIONS".equals(_jspx_method)) {
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        return;
      }
      if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
        response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
        return;
      }
    }

​ JSP内置了一些对象

final javax.servlet.jsp.PageContext pageContext;//页面上下文
final javax.servlet.ServletContext application;//applicationContext ,实际上是ServletContext对象
final javax.servlet.ServletConfig config;//config,ServletConfig对象
javax.servlet.jsp.JspWriter out = null;//out,JspWriter
final java.lang.Object page = this;//page:当前页面
Jsp的工作流程

请求JSP页面→代码部分转换为java类→编译执行代码→代码执行后数据嵌入到页面中→最终页面响应给客户端

JSP基础语法

使用JSP前,先导入以下依赖

  <!--JSP-->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>

​ JSP支持Java中的所有语法

​ 注释形式:<%--注释体--%>

JSP表达式
<%--   <%   %>这种格式是写java代码的    --%>
<% 
int sum=0;
    for (int i = 0; i < =100; i++){
        sum+=i;
    }
%>

<%--  <%= 变量%>这种格式会向网页输出变量的值  --%>
<%=sum%>
<%-- 一样的效果,这称为EL表达式 --%>
${sum}

JSP代码里可以嵌入HTML代码,HTML代码可以嵌入JSP代码

<%
    for (int i = 0; i <10; i++) {
%>
<%--这个p标签会被循环写出10次--%>
<p>HELLOWORLD<%=i%></p>
<%
    }
%>

效果图:

image-20240910193151663

可以声明方法

<%--  <%! %>这个格式里面声明方法   --%>
<%!
    public void menthod(){
    System.out.println(1);
    }
%>

<%
    menthod();
%>
<%   %>		<%--java代码--%>
<%=  %>		<%--输出变量值--%>
<%!  %>		<%--声明方法--%>
<%--注释--%>
JSP指令

JSP指令通常是用于修改一些设置

<%@ %>这个格式写指令

例如JSP文件生成时自带的指令

<%--指令类型page	上下文类型:html文件,UTF-8编码  所用语言:java   --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="" %>               <%--导包--%>
<%@ page errorPage="/页面路径"%>      <%--确定错误页面,出现错误时转到此页面--%>
<%@ page isErrorPage="true" %>      <%--是否是一个错误页面--%>
<%@ page pageEncoding="UTF-8" %>    <%--页面编码--%>
<%@ include file="index.jsp"%>      <%--如同C语言的include一样,将其他文件放到include位置,合二为一--%>

JSP内置对象

介绍这些对象的方法

  • pageContext

可以找到其他对象的Attribute

<body>
    <%
    pageContext.setAttribute("name1","value1");
    session.setAttribute("name2","value2");
    request.setAttribute("name3","value3");
    application.setAttribute("name4","value4");
    %>
    
    <%
        String s1=(String)pageContext.findAttribute("name1");
        String s2=(String)pageContext.findAttribute("name2");
        String s3=(String)pageContext.findAttribute("name3");
        String s4=(String)pageContext.findAttribute("name4");
    %>
    
    name1=<%=s1%><br>
    name2=<%=s2%><br>
    name3=<%=s3%><br>
    name4=<%=s4%><br>

</body>

不同对象的Attribute有不同的生命周期:

pageContext:保存的数据只在一个页面中有效,切换页面数据就消失

request:数据只在一次请求中有效,转发会携带这个数据,重新请求消失

session:数据只在一次会话中有效,会话结束数据消失

application:数据保存在服务器中,服务器关闭才失效

pageContext的findAttribute()方法,采用从低向上的方法查找Attribute,即顺序为page→Request→Session→applicantion

  • request

  • response

  • session

  • applicantion(ServletContent)

这几个对象用来承载产生的数据:

request:客户端向服务器发送请求,产生的数据。使用一次就没用了,例如新闻文档

session:客户端向服务器发送请求,产生的数据。一个用户使用多次,例如购物车

applicantion:客户端向服务器发送请求,产生的数据。一个用户用完其他用户还可能使用。例如聊天数据

  • config(ServletConfig)

  • out:数据

  • page:几乎不再使用

  • exception:异常

EL表达式,JSP标签,JSTL标签

EL表达式

${ }

可以放在字符串中,最终代表一个值

功能:

  • 获取数据
<% String s1="string1"%>
${s1}
  • 执行运算
  • 获取Web开发常用对象
JSP标签
<%--执行转发,可以携带数据;相当于请求转发到localhost:8080/500.jsp?name1=value1&name2=value2--%>
<jsp:forward page="500.jsp">
    <jsp:param name="name1" value="value1"></jsp:param>
    <jsp:param name="name2" value="value2"></jsp:param>
</jsp:forward>
<%--从Request对象里可以取出来--%>
    <%=request.getParameter("name1")%>
    <%=request.getParameter("name2")%>

<%--setAttribute的数据用getAttribute--%>
<%--param数据用getParameter--%>
JSTL标签

​ JSTL即为JSP 标准标签库,封装了JSP应用的通用核心功能

​ JSTL的使用是为了弥补HTML标签的不足,包括一下标签:

核心标签,格式化标签,SQL标签,XML标签,JSTL 函数

​ 目前只了解部分核心标签即可

​ 使用前先引入依赖:

  <!--JSTL-->
    <dependency>
      <groupId>javax.servlet.jsp.jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
  <!--JSTL-api-->
    <dependency>
      <groupId>javax.servlet.jsp.jstl</groupId>
      <artifactId>jstl-api</artifactId>
      <version>1.2</version>
    </dependency>
  <!--JSTL-impl-->
    <dependency>
      <groupId>org.glassfish.web</groupId>
      <artifactId>jstl-impl</artifactId>
      <version>1.2</version>
      <scope>runtime</scope>
    </dependency>
  <!--jsp标准库-->
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>

JSP 标准标签库(JSTL)

​ 使用核心标签前,在需要使用的jsp文件头,先引入核心标签库

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

​ 如果使用中出现500错误,java.lang.NoClassDefFoundError错误。试着把这些依赖的jar包放到Tomcat的lib目录下

<c:out value="123"/><%--相当于out.print,向网页输出值--%>


<%--if判断,text内为判断条件(用EL表达式),var定义一个变量,值为判断条件的结果T/F,scope作用于,page,session等--%>
<%--若为true,执行标签内语句,var变量值为true--%>
<c:if test="${2>1}" var="isAdmin" scope="page">
    <c:out value="Yes"/>
</c:if>


<%--定义一个变量,名为number1值=100--%>
<c:set var="number1" value="100"/>


<%--选择标签,choose相当于switch,when相当于case--%>
    <c:choose>
        <c:when test="${}">
            <c:out value=""/>
        </c:when>

        <c:when test="${}">
            <c:out value=""/>
        </c:when>
    </c:choose>

JavaBean

JavaBean是一个自定义的实体类,要满足以下要求:

  • 必须有一个无参构造
  • 属性必须私有化
  • 每个属性有对应的Get/Set方法

一般用来和数据库的字段做映射

MVC三层架构

​ MVC:Model,View,Controller,将程序架构分为模型,显示,控制器三部分

Controller:接收用户请求,交给业务层执行请求,执行重定向或转发,例如Servlet

View:向用户展示数据模型,提供可以供用户操作的请求,例如JSP

Model:控制业务操作,增删改查数据(数据持久化),例如service方法,Dao层

​ Servlet和JSP都能写Java代码,为了利于维护,设置了以下规定:

  • Servlet专注于处理请求,以及控制视图跳转
  • JSP专注于处理数据

Filter

Filter,过滤器

​ 过滤器过滤的是请求和数据,例如垃圾请求和乱码

​ 乱码可以通过在类中设置编码解决,但是如果有成千上万个类就不能去一个个设置了

​ 通过继承Filter接口实现过滤器类

//使用javax.servlet包下的Filter
public class MyFliter implements Filter {
    //init和destroy,生命周期方法    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void destroy() {
    }

    //过滤
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

    }
}

​ 进入过滤器的请求会执行doFilter方法

​ doFilter有三个参数

  • servletRequest,servletResponse分别代表Request和Response对象,该有的方法都有
  • filterChain用于执行转发,让请求继续传递

​ 例如我们想过滤掉所有乱码,就在doFilter方法里设置Request和Response对象的编码

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("UTF-8");
        servletResponse.setCharacterEncoding("UTF-8");
        //该方法让请求继续向下传递,否则请求会被拦截在这里
        filterChain.doFilter(servletRequest, servletResponse);
    }

然后再Web.xml文件里配置Filter

<filter>
  <filter-name>filter</filter-name>
  <filter-class>MyFliter</filter-class>
</filter>

<filter-mapping>
  <filter-name>filter</filter-name>
  <url-pattern>/s1</url-pattern><!--s1请求将经过该过滤器-->
</filter-mapping>

过滤器还可以过滤请求,不让用户访问某些页面

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("GBK");
        servletResponse.setCharacterEncoding("GBK");
		
        
        String username = servletRequest.getParameter("username");
        String password = servletRequest.getParameter("password");
        if(username.equals("admin")) {
            //如果是admin,可以执行请求
            filterChain.doFilter(servletRequest, servletResponse);
        }else{
            //不是admin,请求被拦截在这里,不再执行
            PrintWriter out = servletResponse.getWriter();
            out.println("You are not allowed to access this resource");
        }
        
    }

Listener

Listener监听器,监听器有许多个接口,不同接口的实现类监听不同的事件

例如Session监听器

class listener implements HttpSessionListener {
    //Session创建和销毁时执行方法
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSessionListener.super.sessionDestroyed(se);
    }

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        HttpSessionListener.super.sessionCreated(se);
    }
}

监听器需要再web.xml文件里注册

<listener>
  <listener-class>listener</listener-class>
</listener>

JDBC

​ Java程序连接数据库时需要手动添加mysql驱动。Maven项目添加依赖即可

  <!--Mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>

​ 然后添加数据库连接类,此步移交JDBC笔记

事务

​ 详见SQL笔记

posted @ 2025-03-24 14:14  顾巧  阅读(28)  评论(0)    收藏  举报