Tomcat
conf文件夹中的Server.xml是核心配置文件,如规定了主机名、端口号,这些可以修改。
Maven
管理jar包的工具,核心思想:约定大于配置
安装步骤
- 下载解压
- 环境变量
- 配置文件中修改镜像地址
- 建立本地仓库,在配置文件中修改本地仓库默认位置
在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版本 状态码 状态码描述信息
响应头:
响应体:
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的步骤:
- 从请求中获取浏览器端传来的cookie对象组,保存到数组变量Cookie[]
- 遍历找到对应名称的cookie对象
- 对相应的cookie对象修改值、删除或者添加新cookie对象,删除是通过cookie.setMaxAge(0) 设置目标cookie对象的存活时长0达到删除目的。
- 对任意对象操作都需要执行写回response.addCookie(cookie) ,因为在servlet中改动了cookie对象只是改动了Cookie[]中的,还需要将变化的cookie添加进响应头中返回给浏览器,覆盖掉之前的同名cookie(如果是新增的则直接添加)。
Session对象
为每个浏览器访问站点时都创建一个Session,不同的浏览器被认为是不同的用户。
session对象是放在服务器端,它的存活时长不受浏览器关闭而结束:关闭浏览器,只会是浏览器端内存里的session cookie消失,但不会使保存在服务器端的session对象消失,到了默认失效期会被清除。sessionID这个cookie在浏览器关闭时会消失,下次访问的时候请求中没有sessionID服务器就会再次创建一个新的session使用。
session对象由服务器创建,至于获取时用req.getsession()是因为请求中带着sessionID,并不是直接从请求中拿session对象。
使用Session的步骤:
-
req.getsession()获取Session,实际上是利用从请求中获取浏览器端传来的SessionID拿到在服务器端存放着的Session对象。
-
增
setAttribute(String,object)
删removeAttribute(Sting)
查getAttribute(String)
改get到相应Attribute后直接修改其值
。 -
手动删除
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的区别是什么
理解 IntelliJ IDEA 的项目配置和Web部署(project structure面板中的东西)
idea tomcat服务器日志输出乱码(红字):tomcat与idea的编码不一致
idea tomcat控制台输出请求中提取的数据乱码(白字):浏览器与idea的编码不一致
关于idea编码的相关设置:
怎么理解TCP是面向连接的,HTTP基于TCP却是无连接的?
仔细对照一下,两种通信方式大同小异,问HTTP是不是连接,就好比问罗密欧与朱丽叶是不是连接的? 事实上,罗密欧朱丽叶只是借助底层的电话连接来交换信息而已,他们无需连接,HTTP也是一样,借助底层的TCP虚拟连接Q,采用的QA的方式对话,无需连接
无论是电话连接,还是TCP连接,都是虚拟连接,只有水管这样看得见摸的着的才算物理连接。如果一个会话需要多个层级的连接,会造成延迟大、资源浪费,所以如果一个层级已经提供了可靠连接,则其它层级完全没有必要连接,只需要交流信息即可,比如这里的HTTP。