JavaWeb
1. MySQL 数据库
常用名词 | 简称及英文翻译 |
---|---|
数据库 | DB --database |
数据库系统 | DBS --database system |
数据库管理员 | DBA --database administrator |
数据库管理系统 | DBMS --database manage system |
1.数据类型
-
整型
数据类型 含义 tinyint(m) 1个字节表示(-128~127) smallint(m) 2个字节表示(-32768~32767) mediumint(m) 3个字节表示(-8388608~8388607) int
(m)4个字节表示(-2147483648~2147483647) bigint(m) 8个字节表示(±9.22*10的18次方) -
浮点型
数据类型 含义 float(m,d) 4字节,8位精度,m:十进制的数字个数,d:小数点的个数 double(m,d) 8字节,16位精度 m 只影响显示效果,不影响精度,d 却不同,会影响到精度
-
字符串
数据类型 含义 char
(n)固定长度
的字符串,最多255个字符varchar
(n)固定长度
的字符串,最多65535个字符tinytext 可变长度的字符串,最多255个字符 text 可变长度的字符串,最多655535个字符 mediumtext 可变长度的字符串,最多(2的24次方-1)个字符 longtext
可变长度的字符串,最多(2的32次方-1)个字符 -
日期时间
数据类型 含义 date 日期:‘2021-12-2’ time 时间:‘12:25:36’ datetime 日期时间:‘2021-12-2 12:25:36’ timestamp 时间戳,不固定 timestamp
:比较特殊,如果定义一个字段的类型为timestamp
,这个字段的时间
会在其他字段修改
时自动刷新
,所以这个数据类型的字段可存放这条数据最后被修改的时间,而不是真正用来存放时间的。
2. MySQL 常用运算符
类型 | 运算符 |
---|---|
算术运算符 | +、-、*、/、% |
比较运算符 | >、>=、<、<=、=、!=不等于、<>不等于 、in、not in、between and 、like、exist、not exist、is null、not null |
逻辑运算符 | and、or、not、xor(异或) |
3. 数据库操作
MySQL 数据库登录
mysql [-h 目的ip地址] -u 用户名 -p 密码
注:如果在本地, -h 目的ip地址
可以省略
3.1 数据库级别的操作
show databases; # 查看所有数据库
use 数据库名; # 使用某个数据库
create database [if not exists] 数据库名; # 创建数据库
drop database [if exists] 数据库名; # 删除数据库
show variables like 'char%'; # 查看字符集
3.2 表级别的操作
alter 修改,modify 修改,change 改变,add 添加,drop 删除,after 在…之后,rename…to 重命名
primary key 主键,unique 唯一约束,index 索引
-
表
-
创建表
create table teacher( t_id int(11), t_name varchar(20), t_age int(10) ); create table tea( t_id int(11) primary key auto_increment, t_name varchar(20) not null, t_age int(10) );
-
表的其他操作
drop table teacher; # 删除表 desc teacher; # 查看表结构 show tables; # 查看所有表 show create table teacher; # 查看建表信息 alter table 旧表名 rename 新表名; # 重命名表名 rename table 旧表名 to 新表名; # 重命名表名
-
-
表字段
-
添加表字段
alter table teacher add(t_sex char(3)); # 给表添加字段 alter table teacher add t_job varchar(20) after t_name; # 给某个字段后添加字段
-
删除表字段
alter table teacher drop t_sex;
-
修改字段类型
alter table teacher modify t_sex varchar(3); alter table teacher modify t_sex varchar(3) not null;
-
修改字段类型/字段名
# alter table 表名 change 旧字段名 新字段名 新字段类型; alter table teacher change t_job t_len VARCHAR(10);
-
4. 约束:主键、唯一约束、外键
-
主键
主键
唯一标示一条记录,不能有重复,不允许为空
,用来保证数据的完整性。一个表只能有一个主键。alter table teacher add primary key(t_id); # 添加主键 alter table teacher drop primary key; # 删除主键
-
唯一约束
alter table teacher add unique(t_name); # 添加约束 alter table teacher drop unique t_name; # 删除约束
-
外键
一个表的外键是另外一个表的
主键
或者是唯一约束
的列,外键可以重复,外键可以为空,用来和其他表建立联系。一个表可以有多个外键。
5. 索引
索引
alter table teacher add index index_name(t_age);# 添加索引并命名
alter table teacher add index(t_age); # 添加索引
alter table teacher drop index t_age; # 删除约束/索引
6. MySQL 常用函数
-
字符串函数
length(str); # 单位是字节 char_length(str); # 单位是字符 select length('你好'); # UTF:6个字节 select char_length('你好'); # 2个字符
-
聚合函数
avg(col); # 返回指定列的平均值 count(col); # 返回指定列中非 null 的个数 select avg(t_age) from tea; select count(t_name) from tea;
-
求最大值,最小值,总和
max(col),min(col),sum(col) select max(t_age) as '最大年龄' from tea; --as 可以省略(作用是起别名) select min(t_age) '最小年龄' from tea; select sum(t_age) '年龄总和' from tea;
-
日期和时间函数
now(); # 获取当前时间 select now(); select now() from dual; # 为一个假的表名
-
格式化时间
date_format(date,format); # 将时间转化为按指定格式 select date_format(now(),'%Y-%m-%d');
7. sql 语句单表操作
在没有表被引用的情况下,允许使用 dual 作为一个假的表名
1. sql 语法
-
增:
insert into ...values
# 通常用第一种方式,效率更高 insert into 表名(字段名 1,字段名 2,字段名 3,...) values(值 1,值 2,值 3,...); insert into 表名 values(值 1,值 2,值 3,...); # 例如 insert into tea(tid,tname,tage,birthday) values(null,'王五',28,'2000-11-11'); insert into tea(tid,tname,tage,birthday) values(1,'王五',28,'2000-11-11'); insert into tea values(1,'王五',28,'2000-11-11');
-
删:
delete from
delete from 表名; delete from 表名 where 条件; # 例如 delete from tea; delete from tea where t_id=2;
-
改:
update set
update 表名 set 字段名 1='值 1',字段名 2='值 2',...; update 表名 set 字段名 1='值 1',字段名 2='值 2',... where 条件; # 例如 update tea set t_name='张三丰',t_sex='女'; update tea set t_name='张三',t_sex='男' where t_id=1;
-
查:
select from
select * from 表名; # 效率较低,开发时一般不用 select 字段名 1,字段名 2,... from 表名; # 例如 select * from tea; select t_id,t_name from tea;
2. 结合运算符
的单表查询
-
结合比较运算符查询
# >、>=、<、<=、=、!=或<>(不等于) # in、not in、between and、like、some、any、all、 # is null、is not null select * from tea where t_id > 5; select * from tea where t_sex='女' and birthday='2000-11-11'; select * from tea where t_sex='男' or t_id < 5; select * from tea where t_name like '%d'; select * from tea where t_name like '_d'; select * from tea where t_name like '%w%'; select * from 表名 where 字段 > any(值 1,值 2,...,值 n); # 大于任意一个值即条件成立 select * from 表名 where 字段 < all(值 1,值 2,...,值 n); # 小于所有值则条件成立. select * from 表名 where 字段 < some(值 1,值 2,...,值 n);# some 作用和 any 相同 select * from tea where t_job is not null; select * from tea where t_job is null;
-
结合逻辑运算符查询
# and、or select * from tea where t_age > 20 and t_age < 30; select * from tea where t_age between 20 and 30; select * from tea where t_age=22 or t_age=25 or t_age=28; select * from tea where t_age in (22,25,28);
注意:
-
在没有表被引用的情况下,允许使用 dual 作为一个
假的表名
-
可以使用 as 给字段起一个
别名
,可省略select count(t_id) '老师总数' from tea;
3. 单表查询的功能实现
-
排序:
order by
,asc
升序(默认) ,desc
降序select * from tea order by birthday desc; select * from tea order by birthday asc;
-
分组:
group by
,having
:分组的条件select t_sex,count(t_sex),avg(t_age) from tea group by t_sex; select t_sex,count(t_sex),avg(t_age) from tea group by t_sex having t_sex='女';
-
分页:
limit
:(参数1:位置
,默认从0开始,可省略,参数2:查询记录数
)select * from tea limit 0,3; select * from tea limit 3; # 位置参数为0,可以省略
功能的综合实现:先分组,在排序,最后分页
#查询各个部门员工的工资在200以上员工,平均薪水在300以上的部门的编号和平均薪资,按平均薪资逆序排列 select avg(salary) avg_sal,dept_id from emp where salary > 200 group by dept_id having avg_sal > 300 # 分组 order by avg_sal desc; # 排序
-
去重复:
distinct
select distinct 字段,字段 from 表名;
8. sql 语句联表查询
联表查询
:也叫多表查询,就是几个表一起查
1. 内连接:join on
# 语法:
select 字段名 from 表名1 [inner] join 表名2 on 连接条件 where 条件;
等价于
select 字段名 from 表名1,表名2 where 连接条件 and 条件;
# 1.查员工的姓名和其所对应的部门名
select e.name,d.name from emp e inner join dept d on e.dept_id = d.id;
等价于
select e.name,d.name from emp e,dept d where e.dept_id = d.id; # 简写形式
# 2.查询员工的姓名及其经理的姓名(内连接)
select e1.name '员工',e2.name '领导' from emp e1,emp e2 where e1.gmrid=e2.id;
2. (左/右)外连接:left/right join on
左外连接:不仅列出与连接条件相匹配的行
,还列出左表
所有符合
where 过滤条件的数据行
。
右外连接:不仅列出与连接条件相匹配的行
,还列出右表
所有符合
where 过滤条件的数据行
。
# 语法:
select 字段名 from 表名 1 left/right [outer] join 表名 2 on 连接条件 where 条件;
select d.name,e.name from dept d left join emp e on e.dept_id=d.id where d.name != 'ls';
select d.name,e.name from dept d right join emp e on e.dept_id=d.id;
# 列出部门名称和这些部门员工的信息,同时列出没有员工的部门(外连接)
select * from emp e right join dept d on e.dept_id=d.id;
select * from dept d left join emp e on e.dept_id=d.id;
注意:
- 左链接以 left join 左边表为基础表进行连接查询,则
左表中的信息全部展示
,
右表部分如果没有找到相关连接信息
,则以null
代替,即是右表中跟左表没关连的内容不展示。 - 右链接以 right join 右边表为基础表进行连接查询,则
右表中的信息全部展示
,
如果在左表中没有找到相关连接信息
,则以null
代替,即是左表表中跟右表没关连的内容不展示。
3. 交叉连接:cross join
没有 on 子句和 where 子句,返回连接表中所有数据行的笛卡尔积。
笛卡尔积: 1,2 a,b,c --> 1a,1b,1c,2a,2b,2c
# 语法:
select 字段名 from 表名1 cross join 表名2;
select * from 表1,表2,...,表n; # 多张表中的内容分别进行组合(笛卡尔积)
# 拓展:带条件的联合查询(交叉连接)
select * from 表1,表2 where 表1.字段 = 表2.字段; # 查询出2表中,符合条件的信息.(等同于内连接)
select 字段名 from 表1,表2 where 连接条件 and 条件;
等价于
select 字段名 from 表1 [inner] join 表2 on 连接条件 where 条件;
4. 自连接
自连接:参与连接的表是同一张表
。
# 语法:
select * from 表 别名 1,表 别名 2 where 别名 1.字段=别名 2.字段;
select * from emp em,emp em2 where em.mgr = em2.empno; # 展示员工和其直属部门领导的信息
# 查询出员工的姓名和其领导的姓名。 emp e1:员工表 emp e2:经理表
select e1.name,e2.name from emp e1 inner join emp e2 on e1.gmrid=e2.id; # 内连接
select e1.name,e2.name from emp e1 left join emp e2 on e1.gmrid=e2.id; # 左外连接
select e1.name,e2.name from emp e1 right join emp e2 on e1.gmrid=e2.id; # 右外连接
5. 临时表
将查询到的结果
当做一个临时表与其他表
进行联表查询
# 查询至少有一个员工的部门,显示部门的编号,名称和人数。
# 1.把员工表 emp 根据 dept_id 分组,统计每个部门的人数。 先分组
select dept_id,count(id) coun from emp group by dept_id having dept_id is not null;
# 2.将第一步中得到的结果当成一个临时表与部门表 dept 进行联合查询,得到部门的信息。 再联表查询
select d.id,d.name,em.coun from dept d,
(select dept_id,count(id) coun from emp group by dept_id having dept_id is not null) em
where d.id=em.dept_id and em.coun > 0;
6. 子查询
某些情况下,当进行查询的时候,需要的条件
是另外一个 select 语句的结果
,这时就要用子查询。
为了给主查询(外部查询)提供数据而首先执行的查询
就叫子查询(内部查询)。
用子查询的关键字有:**in、not in、exist、not exist、=、<>**等
select * from 表1 where 字段 in (select 字段 from 表2);
select * from 表1 where exists (select * from 表2 where 表1.字段=表2.字段);
select * from 表1 where not exists(select * from 表2 where 表1.字段=表2.字段);
# 查询月薪最高的员工的名字
select max(salary) from emp;
select name,salary from emp where salary=(select max(salary) from emp);
# 查询月薪比平均月薪高的员工的名字
select avg(salary) from emp;
select name,salary from emp where salary > (select avg(salary) from emp);
# 查询工资比剑圣高的员工(子查询)
select salary from emp where name='剑圣';
select * from emp where salary > (select salary from emp where name='剑圣');
9. mysql 要注意的地方
-
表名,字段名
在 windows 下不区分大小写,在 linux 系统下区分大小写 -
索引不可以更改,只能删除重建。
-
空值(’’)不占用空间,
null 的值是未知的,占用空间,不走索引
。建议在
建表的时候
最好设置字段为 not null 来避免这种低效率的事情发生。 -
count()统计某列
非空的
记录数,如果是 null 系统会自动忽略掉,但是空值('')会统计到其中
。 -
对于 timestamp 数据类型,如果往这个数据类型的列插入 null,则出现的是当前系统时间,如果往这个数据类型的列插入空值(’’),则会出现’0000-00-00 00:00:00’。
区别 空值 null 是否占空间 不占空间 占空间 是否走索引 走索引 不走索引 **count()**统计 要统计 忽略不统计 timestamp 类型 出现’0000-00-00 00:00:00’ 当前系统时间
2. JDBC
1. JDBC 简介
JDBC:Java Database Connection,表示数据库连接,是 java 中专门提供的一组用于操作数据库的标准
,所有数据库支持此标准
。既然是标准,所以说 JDBC 实际上是一套类库的接口。
主要的操作类和接口:DriverManager 类、Connection 接口,Statement 接口,PreparedStatement 接口,ResultSet 接口
2. JDBC 使用步骤
准备工作:
1.导包:mysql-connector-java-5.1.15-bin.jar
2.创建所用的表
-
加载数据库驱动程序
Class.forName("com.mysql.jdbc.Driver");//加载驱动
-
通过连接地址,用户名和密码获取数据库连接对象
Connection conn = DriverManager.getConnection(连接地址 url,用户名,密码); //连接路径url:jdbc:mysql://localhost:3306/数据库名
-
构造 sql 语句
String sql = "sql 语句";
-
获得 Statement 实例(其实就是 sql 语句的发送器)
Statement stmt=conn.createStatement();
-
执行 sql 语句
stmt.executeQuery(sql); //用于查询 stmt.executeUpdate(sql);//用于增,删,改
-
关闭连接
stmt.close(); conn.close();
3. JDBC 连接池
每次访问数据库时都需要创建一个 Connection 对象,得到一个数据库的连接,用完之后再给他销毁,关闭这个连接。Connection 对象创建和销毁的过程非常消耗资源的,可能代码比较简单,但内部执行的过程比较复杂。比如说,在 mysql 服务器执行 sql 语句可能就用了 10ms,而创建和销毁数据库连接就可能花了 1000ms。所以说,频繁的创建和销毁连接就可能影响程序的性能。因此我们的连接池就出现了,用连接池去管理这个数据库的连接,如果一个连接创建完了,可以重复的去利用这个连接,避免频繁的创建和销毁数据库的连接。编程的时候用的最多的连接池就是 C3P0,一个开源的组件,用起来比较简单,配置一下就行了。
C3P0 概述
C3P0 是一个开源的 JDBC 连接池。
使用它的开源项目有 Hibernate(是一个非常著名的持久化框架,专门做数据库操作的),Spring 等。
下载网址:http://sourceforge.net/projects/c3p0/
使用 C3P0 配置数据库连接池
-
导入 jar 包:
c3p0-0.9.2.1.jar
、mchange-commons-java-0.2.3.4.jar
-
创建一个 xml 文件保存
配置信息
,取名c3p0-config.xml
,直接在 src 文件夹下面创建 -
通过 java 代码创建 C3P0 数据库连接池
-
创建数据源
ComboPooledDataSource ds = new ComboPooledDataSource(); // 加载默认配置 ComboPooledDataSource ds = new ComboPooledDataSource("MySQL"); // 加载命名配置
-
获取连接
Connection conn = ds.getConnection(); // 使用JDBC连接池,获得connection连接对象
-
4. 数据库工具类
-
数据库连接工具类
public class JdbcUtil { public static String driver = "com.mysql.jdbc.Driver"; public static String url = "jdbc:mysql://localhost:3306/mysql"; public static String username = "root"; public static String password = "root"; public static Connection conn; public static PreparedStatement ps; public static ResultSet rs; public static int i; static{ //加载驱动程序 try { Class.forName(driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //获取数据库连接对象 public static Connection getConn() throws SQLException{ conn = DriverManager.getConnection(url, username, password); return conn; } //创建一个方法适用于所有的查询 public ResultSet select_for_allcases(String sql,Object[] obj) throws SQLException{ ps = conn.prepareStatement(sql); if(obj!=null){ for(int i=1;i<=obj.length;i++){ ps.setObject(i, obj[i-1]); } } rs = ps.executeQuery(); return rs; } //创建一个方法适用于所有的增,删,改 public int update_for_allcases(String sql,Object[] obj) throws SQLException{ ps = conn.prepareStatement(sql); if(obj!=null){ for(int i=1;i<=obj.length;i++){ ps.setObject(i, obj[i-1]); } } i = ps.executeUpdate(); return i; } //统一处理关闭资源 public static void close() throws SQLException{ if(rs != null){ rs.close(); } if(ps != null){ ps.close(); } if(conn != null){ conn.close(); } } }
-
数据库**
带连接池
的连接工具类**// 使用饿汉式的单例模式 public class DBUtil { private static ComboPooledDataSource ds = null; static{ //1.创建数据源 默认加载 src/c3p0-config.xml 中的配置 ds = new ComboPooledDataSource(); // 在配置文件中设置,在程序中还可以重新设置相关信息 /*try { ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql://localhost:3306/book_shop"); ds.setUser("root"); ds.setPassword("root"); ds.setInitialPoolSize(5); ds.setMinPoolSize(5); ds.setMaxPoolSize(20); } catch (PropertyVetoException e) { e.printStackTrace(); }*/ } public static Connection getConnection(){ try { return ds.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return null; } }
3. Web
网络模式:
- C/S 模式(Client/Server,客户端/服务器模式)
- B/S 模式(Browser/Server,浏览器/服务器模式)
URL 简介
URL(uniform resource locators):统一资源定位符。web 上的每一个资源都有唯一的地址,采用的就是 url 格式。
协议://域名:端口号/路径/请求资源的名字
http://www.weibo.com:80/images/canglaoshi.jpg
web 基本概述
-
web 介绍
web 是建立在
Internet
上的一种服务(service
)。web 是 Internet 上多种服务的一种
,Internet 上服务包括 email、流媒体、ftp等。web 作为一种服务,web 定义了web 客户端(浏览器)和web 服务器(tomcat)是如何通过
Internet
通信web 客户端和服务器是通过
“请求/响应”模型
(request/response model)与Internet
进行通信“请求/响应”模型(
http协议
)- 客户端发送给服务器的请求叫 http 请求(
request 对象
) - 服务器返回给客户端的响应叫 http 响应(
response 对象
)
请求一个特殊的资源,如 web 页面,服务器如果有就响应该请求。
所以,
进行 web 开发,就是频繁的处理 http 请求和 http 响应
- 客户端发送给服务器的请求叫 http 请求(
-
Tomcat(web 服务器)
tomcat 是 Apache 组织的 Jakarta 项目中的一个重要子项目,是 Sun 公司推荐运行
servlet
和jsp
的容器(引擎),其源代码完全公开。作用:管理和运行 web 应用程序
tomcat 下载地址: http://www.apache.org/
1. JSP
1. JSP 概述
JSP(Java Server Pages):在传统的 html 文件
中加入 java 程序片段和 JSP 标记,就构成了 JSP 网页
JSP 注释:<!--注释-->
或 <%--注释--%>
2. JSP 元素
-
脚本元素
-
声明:<%! %>,在 JSP 中声明合法的
变量和方法
<%! int i=10; //JSP 的声明片段,注意:不建议在 JSP 页面中定义变量或者方法 String name = "小黑"; public void show(){ System.out.println("你好"); } %>
-
表达式:<%=表达式 %>,计算该表达式,将其结果转换成
字符串插入到输出中
<%=1+1 %>
-
脚本:位于**<%和%>**之间的代码,它是合法的 java 代码
<% java 代码,一行或者多行,可以放入任何的 java 代码 %>
-
-
指令元素:<%@ %>
-
page 指令:是与 JSP 容器(指服务器,这里用的是 Tomcat)的沟通方式
<%@ page language="java" // JSP 支持的语言 import="引入的类库" // 是唯一可以使用多次的属性 contentType="text/html;charset=UTF-8" session="true/false" isThreadSafe="true/false" // 是否线程安全 errorPage="错误页面的路径" // 指定错误页面,本页面发生错误时要跳转到的页面 isErrorPage="true/false" // 是否是错误页面,如果为true,发生错误时,跳转到这个页面 pageEncoding="UTF-8" %>
-
include 指令:是
在 JSP 页面被转换成 Servlet时
将指定的页面包含进来,这种特性允许创建指定的
导航栏
,联系人信息
等<%@ include file="页面路径" %>
-
taglib 指令:用于导入标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/core"%>
-
-
动作元素
-
include 动作
<jsp:include page="被包含文件的路径"></jsp:include>
include
的静态包含与动态包含的区别:区别 静态包含(include 指令) 动态包含(include 动作) 包含时机 在 JSP 转换时包含文件 用户请求时包含文件 包含内容 只能包含 静态资源
可包含 动态资源
执行过程 把 file 属性设置的文件先包含进来,
再一起执行转换和编译工作,后形成一个文件各自编译后形成多个文件 执行速度 相对较快,灵活性较差 速度相对较慢,灵活性较高 -
param:传递参数
该动作元素不能单独使用,动态包含,可以传参(静态包含不具有这个功能)
<jsp:include page="被包含文件的路径" flush="true"> <jsp:param name="username" value="tom"/><!--将参数传入被包含的文件中,默认request--> </jsp:include>
-
forward:转发用户请求
服务器的跳转(转发带请求数据,URL 地址不变)
把用户提交的数据(在 request 对象中)原封不动的转发给其他页面,实际上传递了 request 对象。
<jsp:forward page="转发的页面"></jsp:forward>
-
useBean、setProperty、getProperty
创建一个 Bean 实例并指定他的名字和作用范围。
<!--相当于Person p = new Person()--> <jsp:useBean id="p" class="com.mypackage.Person"></jsp:useBean> <jsp:setProperty name="p" propety="name" value="张三"/><!--p.setName("张三")--> <jsp:getProperty name="p" propety="name"/><!--相当于p.getName()-->
javaBean:简单说,就是
一个 java 类,可以重复使用
它必须遵循以下规定:
- 是一个公共类,具有一个共有的无参构造器
- 每个属性必须定义一组 **getXXX()和 setXXX()**方法以便读取和存储它的属性值
-
3. JSP 九大内置对象
JSP 中一共预先定义了 9 个对象,可以不加声明和创建就可以在 JSP 页面中使用
request、response、session、application、page、pageContext、out、config、exception
-
request 对象
request 说明 类型 javax.servlet.http.HttpServletRequest 描述 该对封装了客户端的 请求信息
,主要用于接受
通过http 协议传送到服务器的数据
作用域 为一次请求 重要方法:
request.getParameter(key); // 获取提交表单的数据 request.getParameterValues(key); // 获取提交表单的一组数据 request.setAttribute(key,object); // 设置请求对象 request 的属性 request.getAttribute(key); // 获取请求对象 request 的属性 request.setCharacterEncoding("UTF-8"); // 对请求数据重新编码 request.getRequestDispatcher("test.jsp").forward(request,response);// 转发
-
response 对象
response 说明 类型 javax.servlet.http.HttpServletResponse 描述 它封装了 JSP 的 响应信息
,发送到客户端以响应客户端的请求作用域 page(只在 JSP 页面有效,一个页面一个响应) 重要方法:
response.sendRedirect("test.jsp"); // 重定向 response.setCharacterEncoding("UTF-8"); // 设置响应的编码 response.setContentType("text/html;charset=UTF-8"); // 设置内容类型
-
session 对象
session 说明 类型 javax.servlet.http.HttpSession 描述 表示 一个会话
,用来保存用户信息
,以便跟踪每个用户的状态
作用域 session 重要方法:
session.getId(); // 取得 session 的 id 号 session.isNew(); // 判断 session 是否是新建的 session.setAttribute(key,object); // 往当前会话中设置一个属性 session.getAttribute(key); // 获取当前会话中的一个属性 session.removeAttribute(key); // 删除当前会话中的一个属性 session.setMaxInactiveInterval(1000*60*60); // 设置当前会话的失效时间(单位是毫秒) session.invalidate(); // 重新初始化session,删除session中的数据,在退出系统的时候使用
session 会话:是指在
一段时间内
客户端和服务器之间一连串的相互交互的过程,同一个客户端与服务器在特定时间内
的多个请求构成一个 session 会话。- session 的过期时间默认是半个小时(半个小时是指,在这个时间内客户端与服务器
没有交互的情况下
,才会失效)。 - session 对象内部使用 Map 类来保存数据,因此保存数据的格式为
key/value
。
- session 的过期时间默认是半个小时(半个小时是指,在这个时间内客户端与服务器
-
application 对象
application 说明 类型 javax.servlet.ServletContext 描述 可将 信息
保存在服务器
中作用域 application application 对象可将信息保存在服务器中,直到服务器关闭,否则 application 对象中保存的信息
会在整个应用中都有效
。与 session 对象相比,application 对象生命周期更长,类似于系统的“全局变量“
-
page 对象
page 说明 描述 page 对象代表 JSP 本身,只有 在 JSP 页面内才是合法的
, J
page 对象本质上包含当前 Servlet 接口引用的变量,类似于Java 编程中的this 指针
作用域 page -
pageContext 对象
pageContext 说明 类型 javax.servlet.jsp.PageContext 描述 本 JSP 的页面上下文 作用域 page 作用 可以取得任何范围的参数,
可以获取 JSP 页面的 out、request、reponse、session、application对象创建和初始化 pageContext 对象的 创建和初始化
都是由容器(这里指 Tomcat)来完成的。<% pageContext.getRequest(); //通过 pageContext 上下文对象获取当前页面的其他内置对象 pageContext.getResponse(); pageContext.getSession(); pageContext.getServletConfig(); // 获得Config对象 %>
-
out 对象
out 说明 类型 javax.servlet.jsp.JspWriter 描述 主要用来向客户端输出数据 作用域 page(每一个单独的页面都有一个 out 对象) 重要方法 print()、println()、write() -
config 对象
config 说明 类型 javax.servlet.**ServletConfig ** 作用 取得 服务器
的配置信息作用域 page 通过 pageConext 对象的 **getServletConfig()**方法可以获取一个 config对象。当一个
Servlet 初始化
时,容器把某些信息通过 config 对象传递给这个 Servlet。可以在 web.xml 文件中为应用程序环境中的Servlet 程序
和JSP 页面
提供初始化参数
。 -
exception 对象
exception 说明 作用 显示异常信息 作用域 page 注意:
- 只在包含 isErrorPage=“true” 的页面中才可被使用,在一般的 JSP 页面中使用该对象将无法编译
- excepation 对象和 Java 的所有对象一样,都具有系统提供的
继承结构
。exception 对象几乎定义了所有异常情况。在 Java 程序中,可以使用 try/catch 关键字来处理异常情况; 如果在 JSP 页面中出现没有捕获到的异常
,就会生成 exception 对象,并把 exception 对象传送到在 page 指令中设定的错误页面中,然后在错误页面中处理相应的exception 对象
4. JSP 四大作用域
JSP 作用域范围从小到大顺序为:page > request > session > application
-
page
page 作用域
仅限于当前页面,类似于 java 的 this 对象,离开当前 JSP 页面(无论是 redirect 还是 forward),则 pageContext 中的所有属性值就会丢失如果把变量放到 pageContext 里,则它的作用域是 page,它的有效范围只在当前 jsp 页面里。
从把变量放到 pageContext开始,到 jsp 页面结束,都可以使用这个变量
-
request
request 作用域
是同一个请求之内,在页面跳转时,- 如果通过
forward
方式跳转,则 forward 目标页面仍然可以拿到 request 中的属性值。 - 如果通过
redirect
方式页面跳转,由于 redirect 相当于重新发出的请求,request 中属性值会丢失
如果把变量放到 request 里,则它的作用域是 request,它的有效范围是当前请求周期。
所谓请求周期,就是指从 http请求发起,到服务器处理结束,返回响应的整个过程。在这个过程中可能使用 forward 的方式跳转了多个 jsp 页面,在这些页面里你都可以使用这个变量
- 如果通过
-
session
session 的作用域
是在一个会话的生命周期内,会话失效,则 session 中的数据也随之丢失如果把变量放到 session 里,则它的作用域是 session,它的有效范围是当前会话。
所谓当前会话,就是指从用户打开浏览器开始,到用户关闭浏览器这中间的过程。这个过程可能包含多个请求响应。也就是说,只要用户不关浏览器,服务器就有办法知道这些请求是一个人发起的,整个过程被称为一个会话(session),而放到会话中的变量
-
application
application 作用域是最大的,它的有效范围是整个应用,只要应用不结束,则 application 对象中的数据就一直存在,并且为所有会话所共享
如果把变量放到 application 里,则它的作用域是 application,它的有效范围是整个应用。
整个应用是指从应用启动,到应用结束。我们没有说“从服务器启动,到服务器关闭”是因为一个服务器可能部署多个应用,当然你关闭了服务器,就会把上面所有的应用都关闭了。Application 作用域里的变量,它们的存活时间是最长的,如果不进行手工删除,它们就一直可以使用。 与上述三个不同的是,application 里的变量可以被所有用户共用。如果用户甲的操作 修改了 application 中的变量,用户乙访问时得到的是修改后的值
5. 请求方式 GET/POST
GET 请求和 POST 请求的区别:
-
在客服端的区别
区别 GET POST 是否显示 提交的信息都 显示
在地址栏中提交的信息 不显示
地址栏中封装位置 将信息封装到了请求消息的请求行中 将信息封装到了请求体中 安全性 对于敏感的数据信息 不安全
对于敏感信息 安全
数据体积 对于 大数据不行
,因为地址栏存储体积有限,大小不超过4KB可以提交 大体积数据
速度 相对较快 相对较慢 -
在服务端的区别(乱码处理方式)
如果提交中文到 tomcat 服务器会出现乱码,服务器默认会用 iso-8859-1 进行解码,解决方法:
-
对
get
提交和post
提交都有效-
通过 iso-8859-1 进行编码,再用
指定的中文码表
解码即可new String(queryValue.getBytes("ISO8859-1"),"UTF-8"); // 先编码,再解码
-
用
URLDecoder
.decode(user,“utf-8”) 解码 -
修改 tomcat 的配置文件 server.xml:加上
URIEncoding="UTF-8"
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8"/>
-
-
对于
post
提交,还有另一种解决办法,就是直接使用服务端一个 request 对象request
对象的 setCharacterEncoding 方法直接设置指定的中文码表就可以将中文数据解析出来。这个方法只对请求体中的数据进行解码// 在获取数据之前,设置请求的字符编码集 request.setCharacterEncoding("UTF-8");
-
和服务端交互的三种方式
位置 | 请求方式 |
---|---|
地址栏输入 url 地址 | get |
超链接 | get |
表单 | get 和 post |
数据校验
客服端 校验 | 服务端 校验 | |
---|---|---|
是否校验 | 如果在客户端进行增强型 的校验(只要有一个组件内容是错误,是无法继续提交的, 只有全对才可以提交) | 服务端收到数据后,也还需要校验 |
校验目的 | 提高用户的上网体验 效果,减轻服务器端的压力 | 为了安全性 |
5. 转发与重定向
转发(forward)与重定向( sendRedirect)区别:
-
浏览器地址栏 URL 是否改变
- forward 转发是在
容器内部
实现的同一个 Web 应用程序
的转发,所以 forward 方法只能转发到同一个 Web 应用程序
中的一个资源
,转发后浏览器地址栏 URL 不变 - sendRedirect 方法可以重定向到
任何 URL
, 是修改 http 头来实现的,URL 没什么限制,重定向后浏览器地址栏 URL 改变
- forward 转发是在
-
是否能传递 Request 对象
- forward 将
原始的 HTTP 请求对象(request)
从一个 servlet 实例传递到另一个实例,能传递 Request - sendRedirect 方式两者不是同一个 application,不能传递 Request 对象
- forward 将
-
参数的传递方式
-
forward 的 form 参数跟着传递,所以在转发后的实例中可以取得 HTTP 请求的参数
-
sendRedirect 只能通过链接传递参数
response.sendRedirect(“login.jsp?param1=a¶m2=b”)
-
-
能否处理相对 URL
-
forward 转发
不能处理相对 URL
-
sendRedirect 能够
处理相对 URL
,把它们自动转换成绝对 URL
-
如果地址是相对的,没有一个‘/’,那么 Web 容器就认为它是相对于当前的请求 URL 的
比如 response.sendRedirect("login.jsp"),则会从当前servlet的 URL 路径下找login.jsp 当前servlet的 URL 路径:http://10.1.18.8:8080/dms/servlet/Servlet 重定向后的 URL 路径: http://10.1.18.8:8080/dms/servlet/login.jsp
-
如果地址是绝对的,有一个‘/’,那么 Web 容器则会从当前应用路径下查找 url
比如 response.sendRedirect("/login.jsp"),则会从当前应用路径下查找login.jsp 当前应用路径为:http://10.1.18.8:8081/ 重定向后的URL路径:http://10.1.18.8:8081/login.jsp
-
-
6. Cookie
6.1 Cookie 简介
cookie:是一种 web 服务器通过浏览器
在访问者的硬盘
上存储信息
的手段。
cookie 的目的:就是为了给用户带来方便,为网站带来增值。
- 浏览器一般只允许存放
300 个 cookie
,每个站点最多存放 20 个 cookie
,每个 cookie 的大小限制为 4KB
,因此,cookie 不会塞满你的硬盘
cookie 带来的好处:
- cookie 能使站点跟踪特定访问者的
访问次数
,最后访问时间
等。 - cookie 能告诉在线广告商广告被点击的次数,从而可以更
精确的投放广告
。 - cookie 的期限未到时,cookie 能使用户不键入用户名和密码的情况下
自动登录网站
。 - cookie 可以帮助站点实现很多
个性化服务
。
cookie 涉及到很多你的隐私,因为可以通过 cookie 跟踪你访问过的网站。 浏览器也可以禁用 cookie 。所以说,不能用 cookie 来完成核心业务,只能做一些锦上添花的功能,比如说自动登录,有没有都可以
6.2 Cookie 的使用
Cookie 是 Web 服务器发给浏览器保存在客户端用户磁盘上
的一段文本。Cookie 允许一个 Web 站点在用户电脑上保存信息
并且随后再取回
它。一个 Web 站点可能会为每一个访问者产生一个唯一的 ID,然后以 Cookie 文件的形式保存在每个用户的机器上。
-
Cookie 对象的创建
Cookie cookie = new Cookie("username","john"); //Cookie 名字、Cookie 值
-
Cookie 常用方法
cookie.getMaxAge(); cookie.getName(); cookie.getValue(); cookie.setValue(String newValue); cookie.setMaxAge(secondsTime);// 设置 Cookie 对象的有效时间, // secondsTime 表示cookie的最大生存时间 // 负值:关闭浏览器时,清除该cookie // 0 :立即清除该cookie request.getCookies(); // 获得所有客户端传来的 Cookie 对象以数组的形式排列 response.addCookie(cookie); // 将封装好的 Cookie 对象加入到响应中,传送到客户端
-
Cookie 分类
-
session cookie: 临时的 cookie
随着浏览器的关闭而消失,通过 sessionid 建立起与 session 会话之间的联系,一旦禁用 cookie,
session cookie
就不能用了。 -
persistent cookie:持续性的 cookie,也就是自己创建的 Cookie 对象
-
Cookie 对象的典型应用是用来统计网站的访问人数。由于代理服务器、缓存等的使用,唯一能帮助网站精确统计来访人数的方法就是为每个访问者建立一个唯一 ID
。使用 Cookie,网站可以完成以下工作:
-
测定多少人访问过;
-
测定访问者有多少是新用户(即第一次来访),多少是老用户;
-
测定一个用户多久访问一次网站;
-
测定一个用户访问网站的次数;
当一个用户
第一次访问
时,网站在数据库中建立一个新的 ID
,并把 ID 通过 Cookie 传送给用户。用户再次来访时,网站把该用户 ID 对应的计数器加 1
,得到用户的来访次数
2. EL 表达式
1. EL 简介
EL(Expression Language):它是一种数据访问语言
它是一种简单的语言,基于可用的命名空间
(PageContext 属性,即页面上下文)、嵌套属性
和对集合、操作符(算术型、关系型和逻辑型)的访问符
、映射到 Java 类中静态方法
的可扩展函数以及一组隐式对象(就是内置对象的意思)。
EL 表达式 | 简述 |
---|---|
简介 | 它是一种数据访问语言,不是编程语言,甚至不是脚本编制语言 |
目的 | 为了使 JSP 写起来更加简单 |
作用 | 主要用于查找作用域中的数据,然后对它们执行简单操作 |
说明 | 1、JSP2.0 将 EL 表达式 添加为一种脚本编制元素,即是:在 JSP2.0 之后, JSP 可以直接使用 EL,Tomcat5 之后也支持 EL 表达式。 2、EL 表达式通常与 JSTL 标记一起使用, 能用简单而又方便的符号来表示复杂的行为,来实现在 jsp 中不出现 java 代码段 |
2. EL 表达式
1. EL 语法
EL 语法:${表达式或者变量}
2. EL 运算符
类型 | 运算符 |
---|---|
算术运算符 | +、-、*、/、% |
关系运算符 | ==、!=、>、>=、<、<= |
逻辑运算符 | &&、||、! |
验证运算符 | empty:验证值是否为空(null) |
特殊运算符 | "." 运算符与"[]" 运算符 |
3. EL 隐含对象(11个)
隐含对象 | 类型 | 说明 |
---|---|---|
pageContext | javax.servlet.ServletContext | 表示此JSP的pageContext |
pageScope | java.util.Map | 获得page 范围的属性名所对应的值 |
requestScope | java.util.Map | 获得request 范围的属性名所对应的值 |
sessionScope | java.util.Map | 获得session 范围的属性名所对应的值 |
applicationScope | java.util.Map | 获得application 范围的属性名所对应的值 |
param | java.util.Map | 如同request.getParameter(String name) |
paramValues | java.util.Map | 如同request.getParameters (String name) |
header | java.util.Map | 如同request.getHeader(String name) |
headerValues | java.util.Map | 如同request.getHeaders (String name) |
cookie | java.util.Map | 如同request.getCookies() |
initParam | java.util.Map | 如同application.getInitParameter (String name) |
3. JSTL 标签库
1. JSTL 简介
JSTL 的全称为 JavaServer Pages Standard Tag Library
,中文名称为 JSP 标准标签函数库。
JSTL 是由 JCP(Java Community Process)所指定的标准规格,它主要提供给 Java Web 开发人员一个标准通用的标签函数库。
web 程序开发人员能够利用 JSTL 和 EL 来开发 web 程序,取代传统直接在页面上嵌入 java 程序代码的做法,以提高程序可读性,维护性和方便性。
导包:使用 JSTL,则必须引用 jstl.jar 和 standard.jar 两个包。
2. JSTL 核心标签库
JSTL 所提供的标签函数库主要分为五大类
,最常用的就是核心标签库,其他四类基本上不用。
核心标签库
包含了 web 应用的常见功能,比如:循环、表达式赋值、基本输入输出等。
-
引入标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!--uri:命名空间-->
-
常见标签
-
表达式操作
<c:out> // 主要用来显示数据的内容,就像是<%=表达式%>一样 <c:set> // 主要用来将变量存储至 JSP 范围中或是 JavaBean 的属性中 <c:remove> // 主要用来移出变量 <c:catch> // 主要用来处理产生错误的异常状况,并将错误信息存储起来
-
流程控制标签
<c:if> // 用途和我们在一般程序中用的 if 一样(没有 else) <c:choose> // 本身只当做<c:when>和<c:otherwise>的父标签,相当于 java 里的 case <c:when> <c:otherwise> </c:choose>
-
迭代操作标签
<c:forEach> // 为循环控制,它可以将集合(Collection)中的成员循序浏览一遍 // 运作方式为当条件符合时,就会持续重复执行<c:forEach>的本体内容 <c:forTokens> // 用来浏览一字符串中所有的成员,其成员是由定义符号(delimiters)所分割的
<c:forEach var="stu" items="${requestScope.page.list}" varStatus="st"> <tr> <td>${st.count}</td> </tr> </c:forEach>
-
4. Servlet
1. Servlet 概述
Servlet 是一种独立于平台和协议
的服务端 Java 应用程序,可以生成动态的 Web 页面。
Servlet
是位于 Web 服务器内部的服务端 Java 应用程序
,Servlet 由 Web 服务器进行加载,该 Web 服务器(如 Tomcat)必须包含支持 Servlet
的 Java 虚拟机。(因为 Tomcat 就是运行 Java 应用程序的一个容器)
Servlet 的框架组成
Servlet 框架是由两个 Java 包组成:Javax.servlet 和 Javax.servlet.http。
Servlet 的框架核心是 Javax.servlet.Servlet 接口。
所有的 Servlet 都必须实现这一接口。通常我们也把实现了 Servlet 接口的 java 程序,称之为 Servlet。
创建 Servlet 的一般步骤
-
新建一个 Servlet(要继承 httpservlet 类-----Servlet 的子类)
-
web.xml 配置
-
配置 Servlet 访问路径
<url-pattern>/*</url-pattern> <url-pattern>/</url-pattern>
* :可以匹配任意的字符,所以此时可以用任意的 URL 去访问这个Servlet / :为当前 Web应用程序的缺省Servlet。 凡是在 web.xml 文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他 Servlet 都不处理的访问请求
2. Servlet 的运行方式
Servlet 没有 main 方法,受控于另一个 Java 应用,这个 Java 应用称为 Servlet 容器(Tomcat )。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nbcLuY7j-1648561558495)(link-picture\image-20220115213107312.png)]
Servlet 默认是以多线程模式执行的。
真正写代码的地方就是 Servlet。 http 引擎:封装转发请求数据
3. Servlet 的生命周期
Servlet 的生命周期定义了一个 Servlet
如何被加载,初始化,接受请求,响应请求,提供服务。
代表 Servlet 的生命周期的三个方法
:
- init 方法,负责初始化 Servlet 对象
- service 方法,负责响应客户的请求(调用 doGet 或 doPost 等方法)
- destroy 方法,当 Servlet 对象退出生命周期时,负责释放占用的资源
Servlet 的运行过程
Servlet 程序是由 web 服务器调用,web 服务器收到客户端的 Servlet 访问请求
后:
- Web 服务器首先检查是否已经装载并创建了该 Servlet 的实例对象。如果是,则直接执行第4步,否则,执行第2步。
- 装载并创建该 Servlet 的一个实例对象。
- 调用 Servlet 实例对象的 **init()**方法。
- 创建一个用于封装 HTTP 请求消息的 HttpServletRequest 对象和一个代表 HTTP 响应消息的 HttpServletResponse 对象,然后调用 Servlet 的 service()方法并将请求和响应对象作为参数传递进去,service 方法再根据请求方式分别调用 doXXX 方法。
- Web
应用程序被停止或重新启动
之前,Servlet 引擎将卸载 Servlet
,并在卸载之前调用 Servlet 的 **destroy()**方法。
注意:
- 在 Servlet 的整个生命周期内,Servlet 的 init 方法只被调用一次。
- 而对一个 Servlet 的每次访问请求都导致 Servlet 引擎调用一次 Servlet 的 service 方法。
- 对于每次访问请求,Servlet 引擎都会创建一个新的 HttpServletRequest 请求对象和一个新的HttpServletResponse 响应对象,然后将这两个对象作为参数传递给它调用的 Servlet 的 service()方法,service 方法再根据请求方式分别调用 doXXX 方法。
4. Servlet 线程安全
哪些情况下需要考虑线程安全问题???
- 访问类的成员变量时
- 访问共享资源时
多个客户端同时访问一个 Servlet 实例的时候怎么办呢???
Servlet 引擎采用多线程模式运行,它为并发的每个访问请求都是用一个独立的线程来进行响应,但带来了线程安全问题(因为多个线程访问一个 Servlet 实例),就会导致 Servlet 是线程不安全的。
两个方法解决这个问题:
-
SingleThreadMode 接口(已经被废了,虽然
解决了安全问题
,但带来了很大的性能问题
),Servlet 实现了 SingleThreadMode 接口,那么 Servlet 引擎将以单线程模式来调用 Servlet 的service
方法。对于实现了 SingleThreadMode 接口的 Servlet,Servlet 引擎仍然支持对该 Servlet 的多线程并发访问,其采用的方式是产生多个 Servlet 实例对象,并发的每个线程分别调用独立的一个 Servlet 实例对象。
-
使用同步代码块(一般情况下不需要去同步,当多线程同时访问一个数据或者临界区时,才需要同步)
在 servlet 中定义成员变量
,此时需要考虑线程安全问题
,但是一般不在 servlet 中定义成员变量
,所以平常写 servlet 的时候不用去考虑线程安全问题
。
5. 开发架构
JavaBean 中 DAO 设计模式介绍
1. 信息系统的开发架构
客户层—显示层—业务层—数据层—数据库
- 客户层:客户层就是客户端,简单的来说就是
浏览器
。 - 显示层:
JSP/Servlet
,用于给浏览器显示。 - 业务层(service):对于
数据层的原子操作
进行整合
。 - 数据层(dao):对于
数据库
进行的原子操作
,增加、删除等;
2. DAO 设计模式介绍
DAO:Data Access Object 数据访问对象
DAO 应用在数据层,用于访问数据库,对数据库进行操作的类。
DAO 设计模式带来的好处:
-
透明化
商业对象可以在完全不知道
数据源如何具体实现
的情况下来使用数据源
。访问数据源是透明的,因为实现细节已经被隐藏进了 DAO。 -
迁移简单化
DAO 层的出现,使得应用程序向不同的数据库实现
进行迁移变的容易。商业对象可以对底层数据实现
一无所知。这样,迁移只涉及到了对 DAO 层的修改。另外,如果使用工厂策略,则使为每一种底层数据实现提供一个具体的工厂实现成为可能。在这种情况下, 迁移到一种不同的数据实现,其实就相当于为这个应用程序再提供一个新的工厂实现。 -
减少在商业对象中的编程难度
由于 DAO 管理着所有的
数据访问细节
,因而大大简化了在商业对象和其他使用 DAO 的数据客户端里的代码。所有的实现细节相关的代码比如(SQL 语句)都包含在 DAO 而不在商业对象中。这样使得代码变的更加健壮而且大大提高了开发效率。 -
将所有的数据访问都单独集中到一层中去
因为所有的
数据访问操作
现在都已经被DAO 所代理
,所以这个单独的数据访问层可以被看作可以是将数据访问实现和其余应用程序相互隔离的一层。这样的集中,使得应用程序可以更加容易的来维护和管理。(各层之间的耦合性(联系的紧密程度)降低了,那么当修改某一层时,对其它层的影响也就降低了,所以可以使编写的程序更易于维护)
3. DAO 设计模式的结构
DAO 设计模式一般分为几个类:
- DAO 接口:用于声明对于数据库的操作。
- DAOImpl:必须实现 DAO 接口,真实实现 DAO 接口的函数,但是不包括数据库的打开和关闭。
- DAOFactory:(可选)工厂类,创建 DAO 对象。
public interface BookDAO{
public boolean insert(Book book) throws Exception ; // 增加操作
public boolean update(Book book) throws Exception ; // 修改操作
public boolean delete(String id) throws Exception ; // 删除操作
public Book queryById(String id) throws Exception ; // 按 ID 查询操作
public List queryAll() throws Exception ; // 查询全部
public List queryByLike(String condition) throws Exception ;// 模糊查询
}
6.过滤器
1. 过滤器概述
Servlet 过滤是在 2001 年由 Servlet API2.3
版本中提出的。过滤是一种强大的技术,Servlet 开发人员可以用它来生成一系列的 java 类,以一定的顺序执行,响应客户端的请求。
开发人员从创建一个或多个
实现 javax.servlet.Filter 接口的 java 类开始,这些类可以在 Servlet 请求处理之前采取一些动作,换句话说,它先于与之相关的 servlet 和 JSP 页面运行在服务器上
。在请求发送到其目标之前创建一系列的动作(包括完全阻塞请求
)。
Filter 也称之为过滤器,对 web 服务器管理的所有 web 资源
(如 Jsp,Servlet,静态图片文件或静态 html 文件等)进行拦截,从而实现一些特殊的功能
。如实现 URL 级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。通过 Filter 技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。
过滤通俗的说:就是把要的东西留下,把不要的东西过滤掉,挡住。
2. 过滤器的基本原理
过滤器可以对客户的请求进行处理。处理完成后,它会交给下一个过滤器处理,这样,客户的请求在过滤链里逐个处理,直到请求发送到目标(Servlet
或者 JSP 页面
或者 HTML 页面
等)为止。
Filter 接口中有一个 doFilter 方法,当编写好 Filter,并配置对哪个 web 资源进行拦截后,WEB 服务器每次在调用 web资源的 service 方法之前
,都会先调用一下 filter 的 doFilter
方法,因此,在该方法内编写代码可达到如下目的:
- 调用目标资源之前,让一段代码执行。
- 是否调用目标资源(即是否让用户访问 web 资源)。
- 调用目标资源之后,让一段代码执行。
web 服务器在调用 doFilter
方法时,会传递一个 filterChain 对象进来,filterChain 对象是 filter 接口中最重要的一个对象,它也提供了一个 doFilter 方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则 web 服务器就会调用 web资源的 service 方法
,即 web 资源就会被访问,否则 web 资源不会被访问。
Filter 链
在一个 web 应用中,可以开发编写多个 Filter,这些 Filter 组合起来称之为一个 Filter 链。
web 服务器根据 Filter 在 web.xml 文件中的注册顺序,决定先调用哪个 Filter,当第一个 Filter 的 doFilter 方法被调用时,web服务器会创建一个代表 Filter 链的 FilterChain 对象传递给该方法。在 doFilter 方法中,开发人员如果调用了 FilterChain 对象的doFilter 方法,则 web 服务器会检查 FilterChain 对象中是否还有 filter,如果有,则调用第 2 个 filter,如果没有,则调用目标资源
3. Filter 的生命周期
Filter 的初始化方法 init 和销毁方法 destroy 在 Filter 的生命周期中仅执行一次
-
Filter 的创建
Filter 的创建和销毁由WEB 服务器
负责。web 应用程序启动时,web 服务器将创建 Filter 的实例对象,并调用其 init 方法,完成对象的初始化功能
,从而为后续的用户请求作好拦截的准备工作,filter 对象只会创建一次,init 方法也只会执行一次。通过 init 方法的参数,可获得代表当前 filter 配置信息的 FilterConfig 对象。FilterConfig 对象
用户在配置 filter 时,可以使用
<init-param>
为 filter 配置一些初始化参数
,当 web 容器实例化 Filter 对象,调用其 init 方法时,会把封装了 filter 初始化参数的 filterConfig 对象传递进来。因此在编写 filter 时,通过 filterConfig 对象的方法,就可获得配置的初始参数:String getFilterName(); // 得到 filter 的名称 String getInitParameter(String name); // 返回指定名称的初始化参数的值。如果不存在返回null Enumeration getInitParameterNames(); // 返回过滤器的所有初始化参数的名字的枚举集合 ServletContext getServletContext(); // 返回 Servlet 上下文对象的引用
-
Filter 的销毁
Web 容器调用 destroy 方法销毁 Filter。destroy 方法在 Filter 的生命周期中仅执行一次。在 destroy 方法中,可以释放过滤器使用的资源。
3. Filter 开发步骤
-
编写 java 类实现
Filter 接口
,并覆盖其doFilter
方法。public class FilterDemo implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("----过滤器初始化----"); String filterName = filterConfig.getFilterName(); //得到过滤器的名字 String initParam1 = filterConfig.getInitParameter("name");//得到初始化参数 String initParam2 = filterConfig.getInitParameter("like"); //返回过滤器的所有初始化参数的名字的枚举集合。 Enumeration<String>initParameterNames=filterConfig.getInitParameterNames(); while (initParameterNames.hasMoreElements()) { String paramName = (String) initParameterNames.nextElement(); System.out.println(paramName); } } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("UTF-8"); // 设置request和response的字符集 response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=UTF-8"); // 设置响应格式 System.out.println("FilterDemo01 执行前!!!"); chain.doFilter(request, response); //让目标资源执行,放行 System.out.println("FilterDemo01 执行后!!!"); } @Override public void destroy() { System.out.println("----过滤器销毁----"); } }
-
在 web.xml 文件中使用
<filter>
和<filter-mapping>
元素对编写的 filter 类进行注册,并设置它所能拦截的资源<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <filter><!-- 对过滤器进行注册 --> <filter-name>FilterDemo</filter-name> <filter-class>me.gacl.web.filter.FilterDemo</filter-class> <init-param> <description>配置 FilterDemo 过滤器的初始化参数</description> <param-name>name</param-name> <param-value>gacl</param-value> </init-param> <init-param> <param-name>like</param-name> <param-value>java</param-value> </init-param> </filter> <filter-mapping> <filter-name>FilterDemo</filter-name> <url-pattern>/*</url-pattern> <!-- “/*”表示拦截所有的请求 --> <dispatcher>request</dispatcher><!-- 拦截直接访问页面请求 --> <dispatcher>forward</dispatcher><!-- 拦截转发请求 --> </filter-mapping> </web-app>
Filter 拦截请求方式
-
拦截请求路径:
<url-pattern> 设置 filter 所拦截的请求路径(过滤器关联的 URL 样式)
-
拦截 Servlet 名称
<servlet-name> 指定过滤器所拦截的 Servlet 名称
-
拦截资源被调用的方式
<dispatcher> 指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是request,include,forward,error, 默认request。 用户可以设置多个 <dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截
<dispatcher>
子元素可以设置的值及其意义:- request:当用户直接访问页面时,web 容器将会调用过滤器。如果目标资源是通过requestdispatcher 的 include()或 forward()方法访问时,那么该过滤器就不会被调用。
- include:如果目标资源是通过 **requestdispatcher 的 include()**方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
- forward:如果目标资源是通过 **requestdispatcher 的 forward()**方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
- error:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用
-
7. 监听器
1. 监听器概述
监听器:监视,窃听,监听某一个事件的发生,就会触发某一个功能或者方法执行某个操作。
Servlet 监听器用于监听一些重要事件的发生,监听器对象可以在事件发生前、发生后做一些必要的处理,激发一些操作,如监听在线用户数量
,当增加一个 HttpSession,给在线人数增加 1
监听器实现步骤:
- 编写监听器需要实现相应的接口。
- 监听器需要在
web.xml
中配置。 - 在不修改现有系统的基础上,增加 web 应用程序生命周期事件的跟踪
2. 监听器类别
Servlet API2.3 以后提供了以下监听器接口:
-
ServletContextListener:
应用上下文生命周期
监听器作用域 application,对应
内置对象 application
,它监听着 ServletContext 这个对象的发生,比如说它创建了,销毁了等 -
ServletContextAttributeListener:
应用上下文属性事件
监听器(用这个对象的 setAttribute 往里面存属性时就会触发这个监听器) -
ServletResquestListener:
请求生命周期
监听器 -
ServletResquestAttributeListener:
请求属性事件
监听器 -
HttpSessionListener:
会话生命周期
监听器 -
HttpSessionActivationListener:
会话激活
(类似于线程的唤醒)和钝化
(类似于线程的休眠)事件监听器 -
HttpSessionAttributeListener:
会话属性事件
监听器 -
HttpSessionBindingListener:
会话值绑定事件
监听器
常用的监听器的接口
-
HttpSessionListener:监听 HttpSession 的操作(看 javaee 手册)
重要方法:
void sessionCreated(HttpSessionEvent se); // 当创建一个 session 时 void sessionDestroyed(HttpSessionEvent se); // 当销毁一个 session 时
-
HttpSessionAttributeListener:监听 HttpSession 中的属性的操作
重要方法:
void attributeAdded(HttpSessionBindingEvent se); // 当在 session 中增加一个属性时 void attributeRemoved(HttpSessionBindingEvent se); // 当在 session 中删除一个属性时 void attributeReplaced(HttpSessionBindingEvent se); // 当在 session 属性被重新设置时
3. 监听网站的在线人数
-
创建 Servlet 监听器 OnlineListener,用于监听网站的在线人数
public class OnlineListener implements HttpSessionListener{ private int onlineCount; //定义一个代表在线人数的变量 public OnlineListener(){ onlineCount = 0; } public void sessionCreated(HttpSessionEvent se) { //会话创建时触发 onlineCount++; //int 自动转化 Integer se.getSession().getServletContext().setAttribute("online", onlineCount); } public void sessionDestroyed(HttpSessionEvent se) { //会话销毁时触发 onlineCount--; se.getSession().getServletContext().setAttribute("online", onlineCount); } }
-
在 web.xml 中配置 OnlineListener 监听器
<listener> <listener-class>cn.com.bochy.listener.OnlineListener</listener-class> </listener>
-
创建 online.jsp 页面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>使用监听器监听在线人数</title> </head> <body> <h2>当前在线人数:<%=application.getAttribute("online") %></h2> </body> </html>
8. 文件上传
1. MIME
MIME(Multipurpose Internet Mail Extensions):多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型
,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开
。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
它是一个互联网标准,扩展了电子邮件标准,使其能够支持:
- 非 ASCII 字符文本;
- 非文本格式
附件
(二进制、声音、图像等); - 由
多部分(multiple parts
)组成的消息体; - 包含非 ASCII 字符的
头信息(Header information)
。
Tomcat 的安装目录\conf\web.xml
中就定义了大量 MIME 类型 ,可以参考。
MIME 类型 :文本 text/javascript、json 数据、application/xml、xml 数据
2. servlet 中设定 MIME
response.setContentType 设置响应的内容类型
response.setContentType("text/html; charset=utf-8"); // 设置发送到客户端的响应的内容类型
html.setContentType("text/plain; charset=utf-8");
response.setContentType(MIME):作用是使客户端浏览器,区分不同种类的数据
,并根据不同的 MIME 调用浏览器内不同的程序嵌入模块
来处理相应的数据。例如 web 浏览器就是通过 MIME 类型来判断文件还是 GIF 图片。通过 MIME 类型来处理 json 字符串。
response.setContentType(MIME):设置发送到客户端的响应的内容类型,此时响应还没有提交。给出的内容类型可以包括字符编码说明,例如:text/html;charset=UTF-8.
- 如果该方法在 getWriter()方法被调用之前设置,那么响应的字符编码将仅从给出的内容类型中设置。
- 如果该方法在 getWriter()方法被调用之后或者在被提交之后设置,将不会设置响应的字符编码,在使用 http 协议的情况中,该方法设置 Content-type 实体报头
一般在 Servlet 中,习惯性的会首先设置请求和响应的编码方式
以及响应的内容类型
:
response.setContentType("text/html;charset=UTF-8");
request.setCharacterEncoding("UTF-8");
3. commons-fileupload 组件
commons-fileupload 组件:它是使用最广泛的 java 文件上传组件
,大名鼎鼎的 Struts 采用这个包来处理文件上传。
使用时注意:
-
上传文件的表单的请求方式一定要是 post(post 安全,没有大小限制,通过流的方式进行传输)
-
请求的 MIME(多用途的网际邮件扩充协议)类型:
enctype="multipart/form-data"
(
multipart/form-data
多部件表单数据,之前我们一直在表单里面传的是字符串,现在传的是文件)
核心代码
在 fileupload 组件里面,用 FileItem 去封装每一个数据类型,在这个组件里面会用到这几个类:
- DiskFileItemFactory(磁盘文件项工厂),用来创建文件项,创建之后,需要把这个
文件项
交给解析请求数据
的ServletFileUpload 对象; - 通过 ServletFileUpload 对象去解析请求数据
- upload.parseRequest(request);返回文件项列表
DiskFileItemFactory factory = new DiskFileItemFactory(); // 创建文件项工厂
ServletFileUpload upload =
new ServletFileUpload(factory); // 创建解析请求数据的 ServletFileUpload 对象
List<FileItem> list = upload.parseRequest(request); // 解析请求数据,返回 FileItem 列表
FileItem item = list.get(i); // 获取每一个 FileItem 对象
item.isFormField(); // 用于判断 FileItem 类对象封装的数据是属于一个普通表单字段,
// 还是属于一个文件表单字段,如果是普通表单字段则返回 true,否则返回 false。
4. JavaWeb 开发实战
JSP、EL、JSTL、servlet、DAO 的综合应用
1. 分页查询
-
定义保存分页信息的实体类
public class Page<T> { public static final int DEFAULT_SIZE = 5; private int all; //总记录数 private int pageSize; //页大小 private int curPage; //当前页 private int totalPage; //总页数 private int prevPage; //前一页 private int nextPage; //后一页 private int startIndex; //起始下标 private int endIndex; //结束下标 private List<T> list; //当前页中的记录列表 public Page() { super(); } public Page(int all, int pageSize) { this(all, pageSize, 1); } public Page(int all, int pageSize, int curPage) { super(); this.all = all; this.pageSize = pageSize; this.curPage = curPage; this.totalPage = (this.all+(this.pageSize-1)) / this.pageSize; this.prevPage = this.curPage >1 ? this.curPage -1:1; this.nextPage = this.curPage < this.totalPage ? this.curPage +1 : this.totalPage; this.startIndex = (this.curPage-1) * this.pageSize; this.endIndex = this.startIndex + this.pageSize; } //getter/setter }
-
Dao 中编写执行分页查询的方法
public Page<Stu> queryByLikePage(String condition, int pageSize, int pageNo) throws Exception { String sql1 = "select count(*) from stu where sname like ?"; String sql = "select * from stu where sname like ? "; Object[] params = new Object[]{'%'+condition+'%'}; // 1.查出总记录数 Long all = (Long) JDBCUtil.executeQuerySingle(sql1, params); Page page = new Page(all.intValue(), pageSize, pageNo); // 增加分页条件 sql = sql + String.format("limit %d, %d", page.getStartIndex(), page.getPageSize()); List<Stu> list = new ArrayList<Stu>(); // 2.执行查询并将返回的结果放入到 list 中 ResultSet rs = JDBCUtil.executeQuery(sql, params); while(rs.next()){ Stu stu = new Stu(); stu.setSid(rs.getInt("sid")); //.... list.add(stu); } JDBCUtil.closeAll(); page.setList(list); // 3.将结果集合放入 page 对象中 return page; }
-
Servlet 中获取查询条件,当前页,页大小等信息 ,执行查询
String queryName = request.getParameter("queryName"); // 获取页面提交的参数 String queryValue = request.getParameter("queryValue"); if("get".equalsIgnoreCase(request.getMethod())){ //get 方式的请求,通常是点击浏览器的链接 if(queryValue != null){ //链接上参数的编码是 ISO8859-1 queryValue = new String(queryValue.getBytes("ISO8859-1"),"UTF-8"); } } String pageSize = request.getParameter("pageSize"); // 页大小 if(pageSize == null){ pageSize = String.valueOf(Page.DEFAULT_SIZE); } String pageNo = request.getParameter("pageNo"); // 当前页 if(pageNo == null){ pageNo = "1"; } //List<Stu> list = new ArrayList<Stu>(); Page page = null; if("c_name".equals(queryName)){ try { page=dao.queryByLikePage(queryValue, Integer.parseInt(pageSize),Integer.parseInt(pageNo)); } catch (Exception e) { e.printStackTrace(); } } if(queryValue != null){ //将经过处理的参数放入 request 中供 jsp 页面读取 request.setAttribute("queryValue", queryValue); } request.setAttribute("page", page); request.getRequestDispatcher("/list.jsp").forward(request, response);//转发到 jsp
-
jsp 页面中显示查询结果集合
<c:forEach var="stu" items="${requestScope.page.list}" varStatus="st"> <tr> <td>${st.count}</td> </tr> </c:forEach>
-
jsp 页面中显示首页、上一页、下一页、尾页的等链接
<a href="${pageContext.request.contextPath}/servlet/SearchStuServlet? queryName=${param.queryName}&queryValue=${requestScope.queryValue} &pageSize=${page.pageSize}&pageNo=1">首页</a> <a href="${pageContext.request.contextPath}/servlet/SearchStuServlet? queryName=${param.queryName}&queryValue=${requestScope.queryValue} &pageSize=${page.pageSize}&pageNo=${page.prevPage}">上一页</a> <a href="${pageContext.request.contextPath}/servlet/SearchStuServlet? queryName=${param.queryName}&queryValue=${requestScope.queryValue} &pageSize=${page.pageSize}&pageNo=${page.nextPage}">下一页</a> <a href="${pageContext.request.contextPath}/servlet/SearchStuServlet? queryName=${param.queryName}&queryValue=${requestScope.queryValue} &pageSize=${page.pageSize}&pageNo=${page.totalPage}">尾页</a>
ll){
//链接上参数的编码是 ISO8859-1
queryValue = new String(queryValue.getBytes(“ISO8859-1”),“UTF-8”);
}
}
String pageSize = request.getParameter(“pageSize”); // 页大小
if(pageSize == null){
pageSize = String.valueOf(Page.DEFAULT_SIZE);
}
String pageNo = request.getParameter(“pageNo”); // 当前页
if(pageNo == null){
pageNo = “1”;
}
//List list = new ArrayList();
Page page = null;
if(“c_name”.equals(queryName)){
try {
page=dao.queryByLikePage(queryValue,
Integer.parseInt(pageSize),Integer.parseInt(pageNo));
} catch (Exception e) {
e.printStackTrace();
}
}
if(queryValue != null){
//将经过处理的参数放入 request 中供 jsp 页面读取
request.setAttribute(“queryValue”, queryValue);
}
request.setAttribute(“page”, page);
request.getRequestDispatcher("/list.jsp").forward(request, response);//转发到 jsp
4. **jsp** 页面中显示查询结果集合
~~~jsp
<c:forEach var="stu" items="${requestScope.page.list}" varStatus="st">
<tr>
<td>${st.count}</td>
</tr>
</c:forEach>
-
jsp 页面中显示首页、上一页、下一页、尾页的等链接
<a href="${pageContext.request.contextPath}/servlet/SearchStuServlet? queryName=${param.queryName}&queryValue=${requestScope.queryValue} &pageSize=${page.pageSize}&pageNo=1">首页</a> <a href="${pageContext.request.contextPath}/servlet/SearchStuServlet? queryName=${param.queryName}&queryValue=${requestScope.queryValue} &pageSize=${page.pageSize}&pageNo=${page.prevPage}">上一页</a> <a href="${pageContext.request.contextPath}/servlet/SearchStuServlet? queryName=${param.queryName}&queryValue=${requestScope.queryValue} &pageSize=${page.pageSize}&pageNo=${page.nextPage}">下一页</a> <a href="${pageContext.request.contextPath}/servlet/SearchStuServlet? queryName=${param.queryName}&queryValue=${requestScope.queryValue} &pageSize=${page.pageSize}&pageNo=${page.totalPage}">尾页</a>