JavaWeb笔记
JavaWeb笔记
Servlet
什么Servlet?
servlet 是运行在 Web 服务器中的小型 Java 程序(即:服务器端的小应用程序)。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。
编写一个servlet程序:
a、写一个java类,实现servlet接口
b、修改web.xml文件,给servlet提供一个可访问的URI地址
c、部署应用到tomcat服务器
d、测试:http://locahost:8080/day08_servlet/demo1
执行过程
Servlet生命周期(重要)
实例化-->初始化-->服务->销毁
出生:(实例化-->初始化)第一次访问Servlet就出生(默认情况下)
活着:(服务)应用活着,servlet就活着
死亡:(销毁)应用卸载了servlet就销毁。
小知识:
如何让servlet在服务器启动时就创建。
Servlet的三种创建方式
-
实现javax.servlet.Servlet接口(参见:编写一个servlet程序:)
-
继承javax.servet.GenericServlet类(适配器模式)
-
继承javax.servlet.http.HttpServlet类(模板方法设计模式)
(开发中常用方式)
Servlet --> GenericServlet --> HttpServlet (继承HttpServlet)
曾祖父 爷爷 爸爸 孙子
servet映射细节
servet映射细节1:
servet映射细节2: 通配符* 代表任意字符串
url-pattern: *.do 以*.字符串的请求都可以访问 注:不要加/
url-pattern: /* 任意字符串都可以访问
url-pattern: /action/* 以/action开头的请求都可以访问
匹配规则:
优先级:从高到低
绝对匹配--> /开头匹配 --> 扩展名方式匹配
如果url-pattern的值是/,表示执行默认映射。所有资源都是servlet
Servlet的线程安全
单实例:每次访问多线程
解决线程安全问题的最佳办法,不要写全局变量,而写局部变量。
Servlet获取配置信息
ServletConfig的使用
作用1:可以获取servlet配置信息
<servlet>
<servlet-name>demo3</servlet-name>
<servlet-class>com.love.servletcontext.ServletDemo3</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
方式1:
方式2:
方式3:
作用2:可以获得ServletContext对象
//获取 ServletContext 的方式
ServletContext servletContext = this.getServletContext();
ServletContext servletContext2 = this.getServletConfig().getServletContext();
ServletContext(重要)
ServletContext: 代表的是整个应用。一个应用只有一个ServletContext对象。单实例。
作用:
域对象:在一定范围内(当前应用),使多个Servlet共享数据。
ServletContext的获取方式
-
通过request对象获取
ServletContext sc = request.getServletContext(); -
通过HttpServlet获取
ServletContext sc = this.getServletContext();
两种方式获取到的对象是一样的。
常用方法:
void setAttribute(String name,object value);//向ServletContext对象的map中添加数据
Object getAttribute(String name);//从ServletContext对象的map中取数据
void rmoveAttribute(String name);//根据name去移除数据
获取全局配置信息:
修改web.xml文件:
<!-- 配置当前应用的全局信息,用ServletContext.getInitParameter(xx) 获取 -->
<context-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</context-param>
<context-param>
<param-name>encoding2</param-name>
<param-value>UTF-82</param-value>
</context-param>
String getInitParameter(String name) //根据配置文件中的key得到value
获取文件MIME类型
String img = "a.png";
String type = sc.getMimeType(img);
// image/png
获取资源路径:
String getRealPath(String path);//根据资源名称得到资源的绝对路径.
可以得到当前应用任何位置的任何资源。
实现Servlet的转发
RequestDispatcher getRequestDispatcher(String path) ;//参数表示要跳转到哪去
Request / Respones
学好的关键:理解HTTP协议
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
- get_request请求原始字符串
GET /login.html?uname=aaaa&pwd=shsxt HTTP/1.1 --》请求行,下面都是请求头 key:value
Host: localhost:8888
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8
- post_request请求原始字符串
POST /aaaa?uname=laopei HTTP/1.1 --》请求行,下面都是请求头 key:value,最后是请求正文
Host: localhost:8888
Connection: keep-alive
Content-Length: 22
Cache-Control: max-age=0
Origin: null
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.8
//注意正文前有一个空行
pwd=soeasy&name=jj
- response 响应返回原始
HTTP/1.1 200 OK --> 响应行,下面是响应头,最后是响应正文
Date:Tue Dec 26 11:47:16 CST 2023
Server:shsxt Server/0.0.1;charset=UTF-8
Content-type:text/html;charset=UTF-8
Content-length:114 --> 正文的字节数
//正文前注意有一个空行
//正文
<html><head><title>服务器响应成功</title></head><body>shsxt server终于回来了。。。。</body></html>
HttpServlet
HttpServletResponse
响应行
HTTP/1.1 200 OK
setStatus(int sc) 设置响应状态码
响应头
//sendRedirect(String location) //(※)请求重定向
//setHeader(String name, String value) // 设置响应头信息
// ------------ 使用 --------------
//告知浏览器使用什么码表
response.setHeader("content-type", "text/html;charset=UTF-8");
//告知客户端不缓存
response.setHeader("pragma", "no-cache");
response.setHeader("cache-control", "no-cache");
response.setDateHeader("expires", 0);
Referesh刷新
响应正文(主体)
getWrite(); 字符输出流
getOutputStream(); 字节输出流
setCharacterEncoding(String charset) 告知服务器使用什么编码
setContentType(String type)
//告诉服务器用什么编码解析文本
// resp.setCharacterEncoding("UTF-8");
//告诉客户端(浏览器)用什么编码显示
// resp.setHeader("content-type", "text/html;charset=UTF-8");
//这句顶上面两句,即告诉服务器用什么编码解析文本,又告诉客户端(浏览器)用什么编码显示
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.write("你好,中国!");
HttpServletRequest
请求行
Get http://localhost:8080/day09/servlet/req1?username=zs http/1.1
http://localhost:8080/HttpServlet/d6?username=tom&pwd=123
getMethod(); 获得请求方式 GET
***getRequestURL();返回客户端发出请求时的完整URL。http://localhost:8080/HttpServlet/d6
***getRequestURI(); 返回请求行中的资源名部分。/HttpServlet/d6
*****getContextPath(); 当前应用的虚拟目录。 /HttpServlet
getQueryString() ; 返回请求行中的参数部分。username=tom&pwd=123
请求消息头
String getHeader(String name) 根据头名称得到头信息值
Enumeration getHeaderNames() 得到所有头信息name
Enumeration getHeaders(String name) 根据头名称得到相同名称头信息值
请求正文(重要)
- 与获取表单数据相关的方法
<input type="text" name="username" />
*** getParameter(name) 根据表单中name属性的名,获取value属性的值方法
*** getParameterValues(String name)专业为复选框取取提供的方法
getParameterNames() 得到表单提交的所有name的方法
*** getParameterMap 得到表单提交的所有值的方法 //做框架用,非常实用
getInputStream 以字节流的方式得到所有表单数据
- 与操作非表单数据相关的方法(request也是一个域对象)
*** void setAttribute(String name, Object value);
*** Object getAttribute(String name);
void removeAttribute(String name);
- 与请求转发相关的方法
//得到请求转发或请求包含的协助对象
RequestDispatcher getRequestDispatcher(String path)
*** forward(ServletRequest request, ServletResponse response) //转发的方法
include(ServletRequest request, ServletResponse response) //请求包含
forward与include的区别:
- forward 是若转发前已经使用respond.getWriter()输出了内容,那么只要没有flush() ,forward依然可以成功,并会自动清除刚才的输出内容。但当调用了flush() ,则会报错。值得注意的是,执行了forward方法后,程序还会回来继续执行,此时再使用response输出已经没有作用了。建议可以在forward语句后加上if(true) return; 避免执行后面的程序。
- include 是服务器的动态加载,执行完第二个页面后可以回到第一个页面继续输出,只需要注意第二个页面不能更改response的头部信息。
总结:forward 是转发的另一个页面,include 是把另一个页面加载到当前页面。
请求与响应的乱码解决
解决请求的乱码
- 与请求编码相关的方法:
//解决post方式编码
request.setCharacterEncoding("UTF-8"); //告诉服务器客户端什么编码,只能处理post请求方式
//解决get方式编码
String name = new String(name.getBytes("iso-8859-1"),"UTF-8");
解决响应的乱码
- 与响应编码相关的方法
// ------ response ------
//告诉服务器用什么编码解析文本
// resp.setCharacterEncoding("UTF-8");
//告诉客户端(浏览器)用什么编码显示
// resp.setHeader("content-type", "text/html;charset=UTF-8");
//这句顶上面两句,即告诉服务器用什么编码解析文本,又告诉客户端(浏览器)用什么编码显示
resp.setContentType("text/html;charset=UTF-8");
重定向和转发的区别
转发
/**
* 请求转发必须要以斜杠打头,/ 斜杠表示地址为:http://ip:port/工程名/ , 映射到代码的 web 目录
*/
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2");
requestDispatcher.forward(req,resp);
重定向:
请求重定向的第一种方案:
// 设置响应状态码 302 ,表示重定向,(已搬迁)
resp.setStatus(302);
// 设置响应头,说明 新的地址在哪里
resp.setHeader("Location", "http://localhost:8080");
请求重定向的第二种方案(推荐使用):
resp.sendRedirect("http://localhost:8080");
区别
web 中 / 斜杠的不同意义
在 web 中 / 斜杠 是一种绝对路径。
- / 斜杠 如果被浏览器解析,得到的地址是:http://ip:port/
<a href="/">斜杠</a>
- / 斜杠 如果被服务器解析,得到的地址是:http://ip:port/工程路径/
1、/servlet1
2、servletContext.getRealPath("/");
3、request.getRequestDispatcher("/");
特殊情况: response.sendRediect("/");
把斜杠发送给浏览器解析。得到 http://ip:port/
会话Session
会话概述
什么是会话?
如同打电话
会话解决的问题?
保持各个客户端自己的数据
Cookie对象
由于cookie是由客户端浏览器保存和携带的,所以称之为客户端技术
属性:
- name: 名称不能唯一确定一个cookie。路径可能不同
- value: 不能存中文
- path: 默认是写入cookie那个应用的访问路径
如:http://localhost:8080/day10/servlet/cookieDemo1
Path: /day10/servlet/
当客户端访问服务器其它资源时,根据访问路径来决定是否带着cookie到服务器
当访问的路径是以cookie中path开头的路径,就带cookie,否则就不带。 - maxAge: cookie的保存时间。默认是-1(表示保存在浏览器的内存中)。单位是秒
- 负数:cookie存在浏览器的内存中。
- 0:删除。路径要保持一致,否则会删错了。
- 正数:缓存(持久化到磁盘中)的时间。
Session对象
Session的状态:
为什么要学HttpSession?
- 它也是一个域对象: session servletContext request
- 同一个会话下,可以使一个应用的多个资源共享数据
- cookie客户端技术,只能存字符串。HttpSession服务器端的技术,它可以存对象。
常用方法
把数据保存在HttpSession对象中,该对象也是一个域对象。
void setAttribute(String name,Object value);
Object getAttribute(String name);
void removeAttribute(String name);
HttpSession.getId():
setMaxInactiveInterval(int interval) 设置session的存活时间
invalidate() 使此会话无效
getSession():内部执行原理
HttpSession request.getSession():内部执行原理
1、获取名称为JSESSIONID的cookie的值。
2、没有这样的cookie,创建一个新的HttpSession对象,分配一个唯一的SessionID,并且向客户端写了一个名字为JSESSIONID=sessionID的cookie
3、有这样的Cookie,获取cookie的值(即HttpSession对象的值),从服务器的内存中根据ID找那个HttpSession对象:
找到了:取出继续为你服务。
找不到:从2开始。
HttpSession request.getSession(boolean create):
参数:
true:和getSession()功能一样。
false:根据客户端JSESSIONID的cookie的值,找对应的HttpSession对象,找不到返回null(不会创建新的,只是查询)。
客户端禁用Cookie后的会话数据保存问题
客户端禁用cookie:浏览器永远不会向服务器发送cookie的请求消息头
解决方案:
方案一:在主页上给出提示:请不要禁用您的cookie
方案二:URL重写。必须对网站的所有地址都重写。
http://url--->http://url;JSESSIONID=111
response.encodeURL(String url);
看浏览器有没有发送cookie请求消息头,没有就重写URL,有就不重写。
request.getSession();必须写
例子
JSP (Java Server Page)
请看:https://www.cnblogs.com/htj10/p/13197965.html
事务
事务:
事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。
Mysql中的事务
a、mysql引擎是支持事务的
b、mysql默认自动提交事务。每条语句都处在单独的事务中。
c、手动控制事务
开启事务:start transaction | begin
提交事务:commit
回滚事务:rollback
JDBC如何控制事务
事务的特性(面试题)
- 原子性(Atomicity):指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
- 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。转账前和转账后的总金额不变。
- 隔离性(Isolation):事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
- 持久性(Durability):指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
事务的隔离级别
- 赃读:指一个事务读取了另一个事务未提交的数据。
- 不可重复读:在一个事务内读取表中的某一行数据,多次读取结果不同。一个事务读取到了另一个事务提交后的数据。(update)
- 虚读(幻读):是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。 (insert)
数据库通过设置事务的隔离级别防止以上情况的发生:
- 1、READ UNCOMMITTED: 赃读、不可重复读、虚读都有可能发生。
- 2、READ COMMITTED: 避免赃读。不可重复读、虚读都有可能发生。(oracle默认的)
- 4、REPEATABLE READ:避免赃读、不可重复读。虚读有可能发生。(mysql默认)
- 8、SERIALIZABLE: 避免赃读、不可重复读、虚读。
级别越高,性能越低,数据越安全
mysql中:
查看当前的事务隔离级别:SELECT @@TX_ISOLATION;
更改当前的事务隔离级别:SET TRANSACTION ISOLATION LEVEL 四个级别之一。
设置隔离级别必须在事务之前
练习:设置事务的隔离级别为 read uncommitted
JDBC控制事务的隔离级别
Connection接口:
设置隔离级别:必须在开启事务之前。
Connection.setTransactionIsolation(int level);
连接池
数据库连接池
1、连接池原理:(面试)
目的:解决建立数据库连接耗费资源和时间很多的问题,提高性能。
2、编写标准的数据源
自定义数据库连接池要实现javax.sql.DataSource接口,一般都叫数据源。
3、编写数据源时遇到的问题及解决办法
a、装饰设计模式:使用频率很高
目的:改写已存在的类的某个方法或某些方法,装饰设计模式(包装模式)
口诀:
1、编写一个类,实现与被包装类相同的接口。(具备相同的行为)
2、定义一个被包装类类型的变量。
3、定义构造方法,把被包装类的对象注入,给被包装类变量赋值。
4、对于不需要改写的方法,调用原有的方法。
5、对于需要改写的方法,写自己的代码。
b、默认适配器:装饰设计模式一个变体
常用的数据源配置(日后都使用数据源,一定要配置一下)
DBCP
DBCP:Apache推出的Database Connection Pool
使用步骤:
- 添加jar包 commons-dbcp-1.4.jar commons-pool-1.5.6.jar
- 添加属性资源文件
- 编写数据源工具类
dbcpconfig.properties文件:
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/shop
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
C3P0
使用步骤:
1、添加jar包
2、编写配置文件
c3p0-config.xml,放在classpath中,或classes目录中
点击查看代码
<?xml version="1.0" encoding="UTF-8"?>
<c3po-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/shop</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="minitialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements>200/property>
</default-config>
</c3p0-config>
3、编写工具类:
用JavaWeb服务器管理数据源:Tomcat
开发JavaWeb应用,必须使用一个JavaWeb服务器,JavaWeb服务器都内置数据源。
Tomcat:(DBCP)
数据源只需要配置服务器即可。
配置数据源的步骤:
1、拷贝数据库连接的jar到tomcatlib目录下
2、配置数据源XML文件
a)如果把配置信息写在tomcat下的conf目录的context.xml中,那么所有应用都能使用此数据源。
b)如果是在当前应用的META-INF中创建context.xml, 编写数据源,那么只有当前应用可以使用。
3、使用连接池
- JNDI:java nameing directory interface
JNDI容器就是一个Map
key(String) | value(Object) |
---|---|
path+name | 对象 |
path+"jdbc/day16" | DataSource对象 |
自定义JDBC框架(练技术)
1、数据库元信息的获取(很简单、很无聊、很重要)
a、元信息:(Meta Data)指数据库或表等的定义信息。
2、自定义JDBC框架
反射;策略设计模式;
DBUtils
一、DBUtils介绍 apache
什么是dbutils,它的作用
DBUtils是java编程中的数据库操作实用工具,小巧简单实用。
DBUtils封装了对JDBC的操作,简化了JDBC操作。可以少写代码。
- 对于数据表的读操作,他可以把结果转换成List,Array,Set等java集合,便于程序员操作;
- 对于数据表的写操作,也变得很简单(只需写sql语句)
- 可以使用数据源,使用JNDI,数据库连接池等技术来优化性能--重用已经构建好的数据库连接对象
二、DBUtils的三个核心对象
QueryRunner类
ResultSetHandler接口
DBUtils类
- QueryRunner类
QueryRunner中提供对sql语句操作的API.
它主要有三个方法
query() 用于执行select
update() 用于执行insert update delete
batch() 批处理 - ResultSetHandler接口
用于定义select操作后,怎样封装结果集. - DbUtils类
它就是一个工具类,定义了关闭资源与事务处理的方法
三、Dbutils快速入门
导入jar包
创建QueryRunner对象
使用query方法执行select语句
使用ResultSetHandler封装结果集
使用DbUtils类释放资源
Dbutils快速入门实现步骤:
创建数据库及表
点击查看代码
CREATE DATABASE day14;
USE day14;
create table account(
id int primary key auto_increment,
name varcar(50),
money float
);
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
SELECT * FROM account;
第一步:导入jar包
注意: c3p0与mysql驱动jar也要导入。
//创建QueryRunner对象
//使用query方法执行select语句
//使用ResultSetHandler封装结果集
//使用DbUtils类释放资源
四、QueryRunner对象
1.1 构造函数:
- new QueryRunner(); 它的事务可以手动控制。
也就是说此对象调用的方法(如:query、update、batrch)参数中要有Connection对象。 - new QueryRunner(DataSource ds); 它的事务是自动控制的。一个sql一个事务。
此对象调用的方法(如:query、update、batrch)参数中无需Connection对象。
1.2 方法
进行基本的CRUD操作:练一下
五、ResultSetHandler接口
ResultSetHandler下的所有结果处理器:
//ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]
//ArrayListHandler:适合取多条记录。把每条记录的每列值封装到一个数组中Object[],把数组封装到一个List中
//ColumnListHandler:取某一列的数据。封装到List中。
//KeyedHandler:取多条记录,每一条记录封装到一个Map中,再把这个Map封装到另外一个Map中,key为指定的字段值。
//MapHandler:适合取1条记录。把当前记录的列名和列值放到一个Map中
//MapListHandler:适合取多条记录。把每条记录封装到一个Map中,再把Map封装到List中
//ScalarHandler:适合取单行单列数据
BeanHandler //获取一条数据封装到Javabean
BeanListHandler //获取多条数据封装到 List<Javabean>
六、DBUtils控制事务的开发
七、ThreadLocal 线程局部变量
模拟ThreadLocal的设计,让大家明白他的作用。
public class ThreadLocal{
private Map<Runnable,Object> container = new HashMap<Runnable,Object>();
public void set(Object value){
container.put(Thread.currentThread(),value);//用当前线程作为key
}
public Object get(){
return container.get(Thread.currentThread());
}
public void remove(){
container.remove(Thread.currentThread());
}
}
总结:调用该类的get方法,永远返回当前线程放入的数据。线程局部变量。
来自网上的教程进行编辑,侵权联系。
常记溪亭日暮,沉醉不知归路。兴尽晚回舟,误入藕花深处。争渡,争渡,惊起一滩鸥鹭。
昨夜雨疏风骤,浓睡不消残酒。试问卷帘人,却道海棠依旧。知否?知否?应是绿肥红瘦。