JavaWeb

文章详细介绍了在Idea中使用Maven创建Tomcat项目的过程,阐述了HTTP请求响应的基本原理,讲解了Servlet的工作机制和映射,以及ServleContext、HttpServletRequest和HttpServletResponse对象的使用。此外,还涉及了会话技术(Cookie和Session)、JDBC、PreparedStatement和数据库连接池的概念,以及过滤器的实现。文章最后提到了一些开发中的小贴士,如文件结构、编码问题和TCP与HTTP的连接特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tomcat

conf文件夹中的Server.xml是核心配置文件,如规定了主机名、端口号,这些可以修改。

在这里插入图片描述

Maven

管理jar包的工具,核心思想:约定大于配置

安装步骤

  1. 下载解压
  2. 环境变量
  3. 配置文件中修改镜像地址
  4. 建立本地仓库,在配置文件中修改本地仓库默认位置

在Idea中创建一个Maven项目

​ (2022.1.3)

​ 创建父子工程:1.创建父Maven项目时不要勾选任何模板 2.右击建好的父项目选择new-Module,创建带模板的子项目。

​ 在父项目的pom.xml中会出现标签显示子项目信息,在子项目的pom.xml中会出现标签显示父项目信息

在这里插入图片描述

在这里插入图片描述

​ 创建步骤与IDEA版本有关,接下来修改Maven、Maven配置文件和Maven本地仓库的位置。

在这里插入图片描述

​ 以上根据maven-archetype-webapp模板创建的项目缺少2个文件夹需要补齐。所以创建java和resource文件夹并赋予其文件类型:右击-Mark Directory as,也可以在File-Project Structure的Modules中设置

在这里插入图片描述

​ 配置Tomcat:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

一般是/项目名,因为如果同时启动多个项目的话,要区分不同的项目,不然就都是http://localhost:8080了。

HTTP

请求

​ 请求行:请求方式 资源路径 http版本。get携带参数数量和大小都有限制,明文不安全;post携带的参数数量和大小无限制,安全但不高效。

​ 请求头:

在这里插入图片描述

响应

​ 响应行:Http版本 状态码 状态码描述信息

​ 响应头:

在这里插入图片描述

​ 响应体:

HTTP请求头与响应头各字段含义(较全)

Servlet

基本原理

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

​ GenericServlet抽象类实现了Servlet接口中的init()、 getServletInfo()、getServletConfig()和ServletConfig接口中的所有方法。

​ HttpServlet类实现了Servlet接口中剩余未实现的方法,这个类首先补充了doGet()、doPost()等8个,然后在service()中根据不同http请求中的信息来决定调用哪种处理。我们在继承HttpServlet后实际上就是要重写doGet()、doPost()方法,来自定义我们的逻辑处理步骤。

映射

​ 写的java程序若要能被浏览器访问,需要在web.xml中注册相应的servlet。

​ 一个servlet可以映射多个虚拟路径。

在这里插入图片描述

​ 将404请求的映射放到最后面,根据顺序优先级,凡是没有在web.xml中注册的请求路径最后都会被以下代码捕捉:

	<servlet-mapping>
	    <servlet-name>error</servlet-name>
	    <url-pattern>/*</url-pattern>
	</servlet-mapping>

ServleContext对象

在这里插入图片描述

​ 可以通过继承来的方法得到servlet对象的配置信息对象(ServleConfig)和全局的上下文对象(ServleContext)

1.数据共享(主要)

​ 通过ServleContext.(get/set/remove)Attribute() 键值对形式保存处理全局信息。

​ 另一种方式:在web.xml中配置全局信息:

 <context-param>
        <param-name>参数名</param-name>
        <param-value>参数值</param-value>
</context-param><!-- 以上参数的信息(键值对形式)被保存在ServleContext对象中 --!>
2.请求转发

dispatcher(调度员)

ServletContext context = this.getservletContext();
RequestDispatcher requestDispatcher = context.getReguestDispatcher("/hello");
requestDispatcher.fo rward(reg ,resp) ;
3.获取资源路径

在这里插入图片描述

关于文件结构

classpath

​ classpath是编译打包之后项目的路径,而不是对源代码的路径描述。打包方式有package和build两种,但都不会把项目中使用的第三方jar包一起打进去。

在这里插入图片描述

war和war exploded

​ 1.war模式是先打成war包再发布到tomcat里的webapps

在这里插入图片描述

​ 2.war exploded模式是直接把文件夹、jsp页面 、classes等等移到Tomcat 部署文件夹里面,进行加载部署。因此这种方式支持热部署,一般在开发的时候也是用这种方式

读取资源文件(xx.properties)

1.确定properties文件所在位置(一般在resource文件夹里),服务器启动项目被打包发布后properties文件在classes文件夹里

在这里插入图片描述

​ 如果properties文件是在java文件夹里,那么会出现打包后导不出properties文件的情况,这样一来在servlet中的使用会失效,所以需要在pom.xml的标签中加入以下代码:

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

2.在servlet中读取资源文件里的内容。创建一个输入流,此输入流的源头路径是打包后properties文件在项目中的位置;调用Properties对象的加载方法;之后就可以通过getProperty(xxx)方法读取properties文件中的参数值了。

public class ServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");

        Properties prop = new Properties();
        prop.load(is);
        String user = prop.getProperty("username");
        String pwd = prop.getProperty("password");

        resp.getWriter().print(user+":"+pwd);

    }

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

HttpServletResponse对象

设置响应行、头、体的方法(常用)

​ 设置状态码方法

​ 设置响应头字段方法addHeader(String name,String value),setHeader(String name,String value)等等,这俩区别是前者可以增加同名的字段,后者直接覆盖。

​ 设置响应体方法:

​ 1.ServletOutputStream getOutputStream() 二进制格式

​ 2.PrintWriter getWriter() 字符格式

请求重定向

​ “重”:地址会变。使用场景:登录后自动跳转。

​ resp.sendRedirect(“/servlet_war_exploded/hello”)等价于——resp.setHeader(“Location”,“xxxx”);//通知浏览器请求的新地址,绝对路径表示

​ resp.setStatus(302); //设置状态码,这只是一个功能性提示 即使设置404也不会真的返回找不到页面

下载文件

​ 之前访问某个页面或者想要在页面里显示某个静态资源如图片,可以请求对应的路径,这是属于浏览器直接处理响应的实体内容。但是如果要下载文件,需要写单独一个servlet实现下载逻辑,用到Content-Disposition头字段,其指定了处理数据内容的方式:

​ Content-Type:application/octet-stream(汉意字节流) //不写这个也可以

​ Content-Disposition:attachment;filename=xxxx

​ attachment(附件):这个值表示要求用户干预并控制如何处理数据内容(非浏览器处理)

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // 1. 要获取下载文件的路径
    String realPath = "xxx";
    System.out.println("下载文件的路径:"+realPath);
    // 2. 下载的文件名是啥?
    String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
    // 3. 设置想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode编码,否则有可能乱码
    resp.setHeader("Content-Disposition","attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));
    // 4. 获取下载文件的输入流
    FileInputStream in = new FileInputStream(realPath);
    // 5. 创建缓冲区
    int len = 0;
    byte[] buffer = new byte[1024];
    // 6. 获取OutputStream对象
    ServletOutputStream out = resp.getOutputStream();
    // 7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端!
    while ((len=in.read(buffer))>0){
        out.write(buffer,0,len);
    }

    in.close();
    out.close();
}

HttpServletRequest对象

1.获取请求行的信息

​ String getContentPath() 获取URL(浏览器地址栏中的内容)中,Web应用的路径(根目录)

在这里插入图片描述

2.获取前端传来的参数

​ String getParameter(String s)

​ String[] getParameterValues(String s) //接受如多选框这样多值的参数

3.请求转发

之前利用ServleContext对象获取requestDispatcher对象,在这里直接req.getRequestDispatcher()获取

req.getRequestDispatcher("/新目的地相对址").forward(req,resp);

会话技术

产生的原因

​ HttpServletRequest对象和ServletContext对象都可以对数据保存,但是由于以下两点原因,会话技术依然有必要存在:

​ 第一,每次http请求都会创建一个对应的HttpServletRequest对象保存此次请求的数据,一个完整的流程比如购买流程需要很多次请求,完成某个完整的流程需要这N次请求的所有数据共同协作,但是新请求发生后,旧请求包中的数据不会保存。无状态

​ 第二,同一个Web应用中的所有Servlet共享有且仅有一个的ServletContext对象,不满足不同用户信息分别保存的要求。

Cookie对象组

​ 每一次访问Servlet由服务器返回一组cookie对象并保存在浏览器端。Cookie通过构造方法初始化cookie名称和值,只能存String类型不能存object,有局限性。

​ cookie对象只能存放一个String类型值, cookie对象还有众多属性包括名称、值、在哪些路径下有效、有效期是多长时间以及Cookie的长度大小等,除Name属性外都有set方法。

​ cookie有大小限制,大小一般是4k,每网站可用数量30个,浏览器共300个。

​ cookie中的path是cookie生效的范围,默认根目录,只有在其path规定的路径下共享。一般场景下cookie是服务器返回给客户端的一段数据,并且在该cookie的作用域内,每次请求都会在请求头中自动带上该cookie。(简而言之,不再path内,看不到也带不过去到服务器)。

​ 如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中(每个浏览器存储的位置不一致)。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效;-1为默认值,浏览器关闭后cookie删除;0为立即删除。

“属性是从服务器发送到浏览器的报头的一部分;但它们不属于由浏览器返回给服务器的报头“但是在cookie类源码中有例如getPath获取属性的方法(实践证明get不到),如果属性不参与发给服务器那么为什么要有这些方法

使用Cookie的步骤:

  1. 从请求中获取浏览器端传来的cookie对象组,保存到数组变量Cookie[]
  2. 遍历找到对应名称的cookie对象
  3. 对相应的cookie对象修改值、删除或者添加新cookie对象,删除是通过cookie.setMaxAge(0) 设置目标cookie对象的存活时长0达到删除目的。
  4. 对任意对象操作都需要执行写回response.addCookie(cookie) ,因为在servlet中改动了cookie对象只是改动了Cookie[]中的,还需要将变化的cookie添加进响应头中返回给浏览器,覆盖掉之前的同名cookie(如果是新增的则直接添加)。

Session对象

​ 为每个浏览器访问站点时都创建一个Session,不同的浏览器被认为是不同的用户。

session对象是放在服务器端,它的存活时长不受浏览器关闭而结束:关闭浏览器,只会是浏览器端内存里的session cookie消失,但不会使保存在服务器端的session对象消失,到了默认失效期会被清除。sessionID这个cookie在浏览器关闭时会消失,下次访问的时候请求中没有sessionID服务器就会再次创建一个新的session使用。

​ session对象由服务器创建,至于获取时用req.getsession()是因为请求中带着sessionID,并不是直接从请求中拿session对象。

使用Session的步骤:

  1. req.getsession()获取Session,实际上是利用从请求中获取浏览器端传来的SessionID拿到在服务器端存放着的Session对象。

  2. setAttribute(String,object)removeAttribute(Sting)getAttribute(String)get到相应Attribute后直接修改其值

  3. 手动删除session:HttpSession.invalidate(),也可以写在配置文件中:以下代码设置了一个失效时间(默认20min),当距离客户上一次使用session的时间超过了这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

    <session-config>
         <session-timeout>15</session-timeout>
     </session-config>
    
    

JDBC

是一套操纵数据库的接口,不同的数据库有对应的驱动,数据库厂商在生产驱动时去考虑实现JDBC的接口。

String url ="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
协议://主机地址:端口号/数据库名?参数1&参数2&参数3
Oracle   1521
jdbc:oralce:thin:@localhost:1521:sid

在这里插入图片描述

实际工作中要把jdbc相关代码抽出来成工具类

PreparedStatement

​ Statement的子类, 防止SQL注入 ,效率更高。

​ 不需要像Statement那样先create出来,直接connection

//使用问号占位符代替参数
String sql = "insert into users(id,`NAME`) values(?,?)";
//预编译sql,此时此刻pstm是一个等待拿到参数便可执行特定sql语句的对象。
pstm = connection.prepareStatement(sql); //手动赋值
//index为1的参数赋予整数8,index从1开始
pstm.setInt(1,8);
//index为2的参数赋予字符串sanjin
pstm.setString(2,"SANJIN");
//执行
pstm.executeUpdate()

数据库连接池

​ 每执行一条SQL都会与数据库建立/断开连接,池化技术是设置一些预先准备好的连接资源,有最小、最大连接数。若池满则队列等待,等待超过预订等待时间则返回对应信息。

​ 当前流行的开源数据源:c3p0、dbcp…

​ 实现此技术需要用的包取决于使用上面哪种数据源:比如dbcp 需要commons-dbcp-xx、commons-pool-xx,c3p0需要c3p0-xx、mchange-commons-java-xx。

​ 可以将连接池的配置信息拿出来放到配置文件中,配置文件中各项的名字都必须按照约定来命名,因为后续创建数据源对象时会加载配置文件自动读信息。

​ 依然写成工具类,引入配置文件—创建数据源—获取连接—释放连接

在这里插入图片描述

在这里插入图片描述

JavaBean

三要素:无参构造函数、属性全部私有、getset函数。一般用来做ORM

在java项目中bean类的包可以叫pojo、vo、entity

过滤器

实现了Filter接口的类叫过滤器

package com.zyq.filters;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class CharacterEncodingFilter implements Filter {
    /**
     * 初始化
     * web服务器启动的时候,初始化就会执行
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("CharacterEncodingFilter 初始化。。。");

    }

    /**
     * chain 链
     * 1.过滤器中的代码,在过滤特定请求的时候会执行
     * 2.必须让过滤器继续通行
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        request.setCharacterEncoding("GBK");
        response.setCharacterEncoding("GBK");
        System.out.println("doFilter执行前。。。。");
        //让请求继续走,如果不写程序到这里就被拦截停止了!
        chain.doFilter(request, response);
        System.out.println("doFilter执行后。。。。");
    }

    /**
     * 销毁
     * web服务器关闭的时候,过滤器会销毁
     */
    public void destroy() {
        System.out.println("CharacterEncodingFilter 销毁。。。");

    }
}

​ 需要实现三个方法:init、doFilter、destroy。重点看一下doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)。因为过滤器是在Servlet之前拦截对请求做一下预处理,所以这个方法中需要接收Request和Response参数,预处理完成后交给下一个过滤器或者servlet。过滤器在服务器启动的时候就已经被初始化了,服务器关闭时销毁。

​ 过滤器也需要在xml中配置路径,作用是访问哪些路径时会触发此过滤器

<filter>
   <filter-name>CharacterEncoding</filter-name>//过滤器名
   <filter-class>com.lwq.filter.CharacterEncoding</filter-class>//过滤器对应的完整类名
 </filter>
 <filter-mapping>
   <filter-name>CharacterEncoding</filter-name>
   <url-pattern>/*</url-pattern> //映射的Servlet的路径
 </filter-mapping>

Tips

在Tomcat中部署war和war exploded的区别是什么

package打包和build之间的关系

理解 IntelliJ IDEA 的项目配置和Web部署(project structure面板中的东西)

idea tomcat服务器日志输出乱码(红字):tomcat与idea的编码不一致

idea tomcat控制台输出请求中提取的数据乱码(白字):浏览器与idea的编码不一致

关于idea编码的相关设置:

在这里插入图片描述

encoding和charset的区别:

在这里插入图片描述

怎么理解TCP是面向连接的,HTTP基于TCP却是无连接的?

​ 仔细对照一下,两种通信方式大同小异,问HTTP是不是连接,就好比问罗密欧与朱丽叶是不是连接的? 事实上,罗密欧朱丽叶只是借助底层的电话连接来交换信息而已,他们无需连接,HTTP也是一样,借助底层的TCP虚拟连接Q,采用的QA的方式对话,无需连接
​ 无论是电话连接,还是TCP连接,都是虚拟连接,只有水管这样看得见摸的着的才算物理连接。如果一个会话需要多个层级的连接,会造成延迟大、资源浪费,所以如果一个层级已经提供了可靠连接,则其它层级完全没有必要连接,只需要交流信息即可,比如这里的HTTP。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TuringQi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值