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的获取方式

  1. 通过request对象获取
    ServletContext sc = request.getServletContext();

  2. 通过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的区别:

  1. forward 是若转发前已经使用respond.getWriter()输出了内容,那么只要没有flush() ,forward依然可以成功,并会自动清除刚才的输出内容。但当调用了flush() ,则会报错。值得注意的是,执行了forward方法后,程序还会回来继续执行,此时再使用response输出已经没有作用了。建议可以在forward语句后加上if(true) return; 避免执行后面的程序。
  2. 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操作。可以少写代码。

  1. 对于数据表的读操作,他可以把结果转换成List,Array,Set等java集合,便于程序员操作;
  2. 对于数据表的写操作,也变得很简单(只需写sql语句)
  3. 可以使用数据源,使用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方法,永远返回当前线程放入的数据。线程局部变量。


来自网上的教程进行编辑,侵权联系。

posted @ 2023-12-26 15:07  htj10  阅读(17)  评论(0编辑  收藏  举报
TOP