JavaWeb

JDBC

J: Java

DB: DataBase

C: Connectivity

作用:使用java操作数据库,Java操作数据库的规范,定义了所有操作数据库的一组接口,这组接口就是JDBC;java.sql

注:所有从外面引入的jar包都放入lib文件夹里面

导入驱动jar包

1.新建lib文件夹

2.将驱动包(mysql-connector-java-5.1.6-bin.jar)放入lib文件夹

3.选中驱动文件,右键buildpath–add to path

Statement

具体步骤如下例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");

//2.与数据库建立间接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root","123456");

//3.获取sql的执行平台 执行sql语句
Statement cs = conn.createStatement();

//4.sql语句
String sql = "select * from userdata";

//5.执行sql语句,获取sql结果
ResultSet rs = cs.executeQuery(sql);
while(rs.next()) {
System.out.println(rs.getInt(1)+"\t
"+rs.getString(2)+"\t"+rs.getString(3));
}
//6.关闭连接,释放资源
rs.close();
cs.close();
conn.close();

PreparedStatement

允许占位符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.建立连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc?userUnicode=true&characterEncoding=utf8", "root", "123456");
//3.准备sql语句
String sql = "update userdata userdata set uname=? where uid=1";
//4.获取预编译对象 使用?代表需要填充的数据
PreparedStatement ps = connection.prepareStatement(sql);
//5.填充数据
ps.setString(1,"测试");//也可以用"字段名" 指定
//6.执行更新操作
ps.executeUpdate();//执行查询操作的自查API
//7.关闭资源
ps.close();
connection.close();

Properties文件的获取

1
2
3
4
5
properties文件下的的内容:
classDriver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc? userUnicode=true&characterEncoding=utf8
user=root
password=123456
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//1.得到properties文件路径
String path=JdbcDruid.class.getResource("/").getPath()+"druid.properties";
//注意,上面getResource("/")得到的是bin的根目录下,也就是src的根目录下(src会在bin目录下复制一份)
//使用getResource("")得到的是程序所在包目录
//2.创建properties对象
Properties prop = new Properties();
try {
//3.加载输入流
prop.load(new FileInputStream(path));
//4.得到配置文件信息
JdbcDruid.classDriver=prop.getProperty("classDriver");
JdbcDruid.url = prop.getProperty("url");
JdbcDruid.user = prop.getProperty("user");
JdbcDruid.pass = prop.getProperty("password");
System.out.println("初始化配置文件成功");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

连接池

常用连接池

​ 德鲁伊 druid 阿里

​ dbcp apach

​ c3p0

德鲁伊的使用步骤如下:

1.导入druid-1.0.12.jar到lib文件夹下

2.选中文件,右键buildpath–add to path

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3.如下
//声明连接池对象
private static DruidDataSource dds = null;
static {
//初始化配置文件
JdbcDruid.initProperties();
//创建连接池对象
dds = new DruidDataSource();
//初始化驱动
dds.setDriverClassName(classDriver);
//设置 url username password
dds.setUrl(url);
dds.setUsername(user);
dds.setPassword(pass);
//设置连接池的连接数量
dds.setInitialSize(8);
}

另外两个创建对象的类

​ dbcp BasicDataSource()

​ c3p0 CombopooledDataSource()

开启连接池及归还连接代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package com.woniu.jdbc.util;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
import com.alibaba.druid.pool.DruidDataSource;
/**
* @ClassName
* @Description
* @author Jungle
* @date 2019年9月17日 下午2:51:37
*/
public class JdbcDruid {
private static String classDriver = null;
private static String url = null;
private static String user = null;
private static String pass = null;
//声明连接池对象
private static DruidDataSource dds = null;
static {
//初始化配置文件
JdbcDruid.initProperties();
//创建连接池对象
dds = new DruidDataSource();
//初始化驱动
dds.setDriverClassName(classDriver);
//设置 url username password
dds.setUrl(url);
dds.setUsername(user);
dds.setPassword(pass);
//设置连接池的连接数量
dds.setInitialSize(8);
}
/**
*
* @Title: getConnection
* @Description: 获取连接
* @param @return
* @return Connection
* @throws
*/
public static Connection getConnection() {
Connection connection = null;
//获取连接
try {
connection = DriverManager.getConnection(JdbcDruid.url, JdbcDruid.user, JdbcDruid.pass);
} catch (SQLException e) {
System.out.println("获取连接失败");
e.printStackTrace();
}
System.out.println("获取连接成功");
return connection;
}
/**
*
* @Title: closeConnection
* @Description: 关闭连接
* @param @param connection
* @return void
* @throws
*/
public static void closeConnection(Connection connection) {
try {
connection.close();

} catch (SQLException e) {
System.out.println("连接归还失败");
e.printStackTrace();
}
System.out.println("连接归还成功");
}
/**
*
* @Title: initProperties
* @Description: 初始化properties文件
* @param
* @return void
* @throws
*/
private static void initProperties(){
//得到properties文件路径
String path = JdbcDruid.class.getResource("/").getPath()+"druid.properties";
//创建properties对象
Properties prop = new Properties();
try {
//加载输入流
prop.load(new FileInputStream(path));
//得到配置文件信息
JdbcDruid.classDriver = prop.getProperty("classDriver");
JdbcDruid.url = prop.getProperty("url");
JdbcDruid.user = prop.getProperty("user");
JdbcDruid.pass = prop.getProperty("password");
System.out.println("初始化配置文件成功");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

MVC分层:model view control

JavaEE分三层:

​ 视图(view):

​ 服务层(service):逻辑判断,流程处理

​ 持久层(dao):所有操作数据库

​ dao层里面存对数据库操作的接口类,在接口里面规定所有对该表的查询方法,查询完成之后,将结果返回给service层,由service层进行判断处理

JavaBean

​ 也就是模型类(实体类)的升级版

​ 1.类必须使用public修饰

​ 2.所有属性私有化,提供get set方法

​ 3.必须提供一个无参构造

​ 4.最好实现序列化接口

增删改查工具类

1.导入commons-dbutils-1.6.jar

2.选中文件,右键buildpath–add to path

使用方式如下:

创建:

​ QueryRunner qr = new QueryRunner();

查询单个对象:

​ User user =

​ qr.query(connection,sql,new BeanHandler (user.class),account,password);

查询所有:

​ List userBoxList =

​ qr.query(connection,sql,new BeanListHandler(User.class));

新增 修改 删除

​ String sql = “insert into t_user values(?,?)”;

​ Object[] obj ={“root”,”123456”};

​ int result = qr.uptate(connection,sql,obj);

注:

​ 1. sql为自定义sql语句字符串,注意两种查询的返回类型不一样

​ 2. ?参数可以使用Object[] obj = {account,password}写入,作 为一个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public List<StudentData> getAllFromDataBase() {
//建立连接
Connection connection = JdbcDruid.getConnection();
//准备sql语句
String sql = "select sid,sname,ssex,sbir,sphonenum,ssubject from stu";
//准备增删改查对象
QueryRunner qr = new QueryRunner();
//执行查询
List<StudentData> studentBox=null;
try {
studentBox = qr.query(connection,sql,new BeanListHandler<StudentData>(StudentData.class));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return studentBox;
}

事务

开启事务:

​ 连接名.setAutoCommit(false);//默认为true false为手动事务

​ 例:connection.setAutoCommint(false);

提交事务

​ 连接名.commit();

​ 例:connection.commit();

HTTP协议

位于TCP/IP模型的应用层,主要用来规范前后端数据交互格式

请求:request 客户端发送数据给服务端

响应:response 服务端将处理结果返回给客户端

请求方式

GET:数据会在地址栏,发送文本数据

POST:数据不会出现在地址栏,可以发送任意类型数据

HEAD PUT DELETE OPTION

请求格式

响应格式

状态码

  • 1xx:请求发送成功,服务器在处理

  • 2xx:正常

  • 3xx:重定向

  • 4xx:客户端错误 404:请求的资源不存在

  • 5xx:服务器内部错误

特点

无连接:一次请求,一次连接,请求完成,连接断开

无状态:下一次请求不会去记录上一次请求的数据

服务器

应用程序:根据用户的实时请求找到对应的类进行处理的一个容器使用代码编写的一个应用程序

常见服务器:tomcat:开源,免费 apach ngnix

服务器目录

bin:可执行文件

conf:配置信息

lib:类库

logs:运行日志

temp:临时文件

webapps:项目运行文件

work:jsp文件

例:tomcat目录D:\tomcat\apache-tomcat-8.0.42-windows-x86\apache- tomcat-8.0.42\conf 下的server.xml就可以修改端口

Servlet

实现了Servlet接口的类,用来处理用户请求

生命周期

创建:

​ 请求->映射为Servlet->判断是否实例化(是继续执行,否跳到执行service())

​ ->执行无参构造->执行init()->执行service()->执行destroy()

销毁:

​ 当 Web 容器被关闭,或该应用被关闭,则调用执行 destroy()方法,销毁 Servlet 实例

接收请求的三种方式

Service doGet doPost

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//可以接收GET 和POST 请求
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//只能接受Get请求
}
@Override
protected void doPost(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException {
//只能接收POST
}

}

request对象

会将请求头,请求行,请求数据全部封装到request对象中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Test extends HttpServlet{
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException {
String method = req.getMethod();
// GET

String scheme = req.getScheme();
// http

StringBuffer requestURL = req.getRequestURL();
// http://192.168.80.15:8080/myFist/login

String requestURI = req.getRequestURI();
// /myFirst/login

req.getContextPath();
// /myFirst

String header = req.getHeader("网页中,请求行的键名");
//得到相应的值

String parameter = req.getParameter("用户发送请求时提交 的 name名");
//得到相应的value值
}
}

中文乱码问题

  • 出现原因:
    • 当用户通过浏览器提交一个包含 UTF-8 编码格式的两个字的中文请求时,浏览器会将这两个中文字符变为六个字节(一般一个 UTF-8 汉字占用三个字节),即形成六个类似%8E 的字节表示形式,并将这六个字节上传至Tomcat 服务器。Tomcat 服务器在接收到这六个字节后,并不知道它们原始采用的是什么字符编码。而Tomcat默认的编码格式为ISO-8859-1。所以会将这六个字节按照ISO-8859-1的格式进行编码,编码后在控制台显示,所以在控制台会显示乱码。

解决方式如下:

1
2
3
4
5
//放在service方法的最开始
//设置请求编码
req.setCharacterEncoding("utf-8");
//设置响应编码
resp.setContentType("text/html;charset=utf-8");

response对象

用来响应数据到浏览器

设置编码及响应内容:

​ resp.setContentType(“text/html;charset=utf-8”);

设置响应头信息:

​ resp.addHeader(“键”,”值”);//键可重复

​ resp.setHeader(“键”,值);//键不可重复

设置响应编码

​ resp.sendError(404,”你访问的页面不存在”);

响应数据

​ resp.getWriter().Write(“内容”);

转发

req.getRequestDispatcher(“/index”).forward(req, resp);

特点:

​ 1.对于浏览器,只发送了一次请求

​ 2.转发发生在服务端

​ 3.浏览器地址栏不会发生改变

​ 4.会造成表单重复提交

重定向

resp.sendRedirect(“index”);

特点:

​ 1.对于浏览器,发送了两次请求

​ 2.跳转发生在浏览器端

​ 2.地址会改变

注意

转发:web应用内部转发,web应用来说,/根目录代表:localhost:8080/项目名/

重定向:是以服务器的根目录:localhost:8080/

转发与重定向区别

1.转发发生在服务端,重定向在浏览器端

2.转发只发送一次请求,地址栏不会改变,重定向发送了两次请求,地址栏会改变

3.转发效率高,重定向效率低

4.转发会造成表单的重复提交

5.转发只能进行内部跳转,重定向可以跳转到外部应用

6.转发可以访问WEB-INF文件夹里面的资源,重定向不可以

会话跟踪

作用:

​ 解决多次请求需要发送相同的数据

特点:

​ 1.cookie对象在服务端设置

​ 2.cookie是保存在浏览器端

​ 3.cookie的生命周期默认为浏览器关闭失效(保存在浏览器缓存中)

​ 4.cookie保存的数据不能超过4kb

​ 5.一般不要超过20个cookie(不同的网站,不同的cookie)

​ 6.cookie值不要为中文

设置cookie

如下所示:

1
2
3
4
5
6
7
8
9
protected void service(HttpServletRequest req, 						HttpServletResponse resp) throws ServletException, 			IOException {
//创建cookie对象
Cookie cookie = new Cookie("name","zhangsan");
//设置失效时间
cookie.setMaxAge(60);//以秒为单位,设置后cookie保存
//在硬盘,从添加到响应开始计时
//相应到浏览器端
resp.addCookie(cookie);
}

获取cookie

1
2
3
4
5
6
7
8
9
10
11
12
protected void service(HttpServletRequest req, 					HttpServletResponse resp) throws ServletException, 			IOException {
//获取用户的cookie
Cookie[] cookies = req.getCookies();
if(cookies!=null) {
for(Cookie cookie:cookies) {
//获取键
String name = cookie.getName();
//获取值
String value = cookie.getValue();
}
}
}

3天免登录

  • 登录成功后添加cookie,cookie里面保存用户id

  • 访问页面时先获取cookie,拿到用户id,经过service层和dao层,判断是否存在,存在则免登陆

session

作用: 共享一个用户的数据

特点: 保存在服务器

创建方式:

1
2
3
4
5
protected void service(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException {
//获取session对象
HttpSession session = req.getSession();
String id = session.getId();
}

注:getSession():如果没有,就会创建;如果有,直接返回session对象,会将

​ sesssinid添加到JSSIONID cookie里面去,并且返回到浏览器端

生命周期

默认值为最后一次访问后的30分钟

修改方式 : session.setMaxInactiveInterval(60);//以秒为范围,不建议修改

强制销毁 : session.invalidate();//可以实现安全退出

1
2
3
4
5
6
7
8
9
session 是一个专门用于存放数据的集合,我们一般称这个用于存放数据的内存空间为域属性空间,简称域。HttpSession 中具有三个方法,是专门用于对该域属性空间中数据进行写、读操作的。
public void setAttribute(String name, Object value)
该方法用于向 Session 的域属性空间中放入指定名称、指定值的域属性。
public Object getAttribute(String name)
该方法用于从 Session 的域属性空间中读取指定名称为域属性值。
public void removeAttribute(String name)
该方法用于从 Session 的域属性空间中删除指定名称的域属性。
//以上都是使用session调用方法
//例:req.getSession().setAttribute("键","值");

ServletContext对象

全局上下文对象,存放的数据对所有客户端共享,服务器启动的时候创建,关闭的 时候销毁

获取Servlet对象方式:

1
2
3
4
5
6
7
8
9
10
11
protected void service(HttpServletRequest req, 					HttpServletResponse resp) throws ServletException, 			IOException {
//1.通过Servlet对象获取
ServletContext servletContext = this.getServletContext();
//2.通过session对象获取
HttpSession session = req.getSession();
ServletContext sc = session.getServletContext();
//3.通过servletConfig对象获取
ServletConfig servletConfig = this.getServletConfig();
ServletContext sc3 = servletConfig.getServletContext();
//三种方式获得的对象是同一个对象
}

作用

1.全局数据共享

2.获取初始化内容

​ web.xml里面配置的内容(getInitParameter)

1
2
3
4
5
6
7
8
9
10
11
<context-param>配置是是一组键值对,比如:

<context-param>
<param-name>home-page</param-name>
<param-value>home.jsp</param-value>
</context-param>
param-name是键,相当于就是参数名,param-value是值,相当于参数值
当服务器启动时,服务器会读取web.xml配置,当读到<listener></listener><context-param></context-param>这两个节点的时候,容器会将这两个节点set到ServletContext(上下文对象)中,这样我们在程序中就能通过这个上下文对象去取得我们这个配置值。
具体代码实现:
String sHomePage=getServletContext().getInitParameter("home-page");

3.获取webContent文件的绝对路径 getRealPath();

4.获取webContent文件的流对象 getResourceAsStream(“test.txt”);

ServletConfig对象

​ 主要用来获取web.xml里面需要对当前的servlet进行单独配置

WEB配置如下

1
2
3
4
5
6
7
8
9
10
11
12
<servlet>
<servlet-name>config</servlet-name>
<servlet-class>包名.类名</servlet-class>
<init-param>
<param-name>code</param-name>
<param-value>gbk</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>config</servlet-name>
<url-pattern>/config</url-pattern>
</servlet-mapping>

java端controler类端

1
2
3
4
5
6
7
8
9
10
public class Test extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.通过Servlet对象获取
ServletContext sc = this.getServletContext();
String code = sc.getInitParameter("code");
//设置请求编码
req.setCharacterEncoding(code);
}
}

字符串转JSON

使用方法:

​ 1.放jar包 将 json需用包下全部 放入lib文件夹下

​ 2.JSONObject jUser = JSONObject.fromObject(user)

​ //user对象重写了toString方法,满足JSON格式

ajax

  • Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)
  • 当使用结合了这些技术的AJAX模型以后, 网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面。这使得程序能够更快地回应用户的操作。

原生HTML实现

web端设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<input type="text" id="account" placeholder="请输入账号"/>
<script type="text/javascript">
var account = document.getElementById("account");
account.onblur = getMess;
function getMess(){
//发送Ajax请求
//1.获取输入框内容
var message = account.value;
//2.创建xmlHttpRequest对象
var xhr = new XMLHttpRequest();
//3.准备数据,及发送的地址 true为异步请求
xhr.open("GET","\Ajax?username="+message,true);
//4.发送
xhr.send();
//5.获取发送状态
xhr.onreadystatechange = function(){
console.log("发送状态:"+xhr.readyState);
if(xhr.readyState == 4){ //0-4分别对应5种状态
//获取后端传过来的数据
var data = xhr.responseText;
console.log("data:"+data);
if(data == "1"){
getA.innerHTML="用户名可用";
getA.style.color="red";
}else if(data == "0" ){
getA.innerHTML="用户名不可用";
}
}
}
}
</script>

java端设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@WebServlet("/Ajax")
public class AccountAjax extends HttpServlet{
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException {
//设置请求编码及响应编码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//取得ajax获得的值
String username = req.getParameter("username");
//将账号传递给服务层
//将用户名和密码传给服务层
DetermineTeacher dt = new DetermineTeacher();
TeacherData teacherData = dt.determineAccountAndPassword(username);
//判断
if(teacherData == null) {
resp.getWriter().write("0");
}else {
resp.getWriter().write("1");
}
}
}

JQuery实现

1.$.ajax()

post请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$.ajax({
//请求方式
type:'POST',
//发送请求的地址
url:'fzz.php',
//服务器返回的数据类型
dataType:'json',
//发送到服务器的数据,对象必须为key/value的格式,jquery会自动转换为字符串格式
data:{name:xxx,age:xxx},
success:function(data){
//请求成功函数内容
},
error:function(jqXHR){
//请求失败函数内容
}
});

get请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$.ajax({
//请求方式
type:'GET',
//发送请求的地址以及传输的数据
url:'fzz.php?number'+=xxx,
//服务器返回的数据类型
dataType:'json',
success:function(data){
//请求成功函数内容
},
error:function(jqXHR){
//请求失败函数内容
}
});
2.$.get()
1
2
3
4
5
6
$.get('fzz.php',{
//发送至服务器的数据,格式为key/value
number:xxx
},function(data){
//请求成功时的callback
},'json');//json为返回内容的格式
3.$.post()
1
2
3
4
5
6
$.post('fzz.php',{
//发送至服务器的数据,格式为key/value
number:xxx
},function(data){
//请求成功时的callback
},'json');//json为返回内容的格式
4.实例
  • input.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--实现效果:不刷新页面查询账号是否存在-->
<input type="text" class="account" placeholder="请输入账号"/><font class="show"></font>

<script type="text/javascript">
$(".account").blur(function(){
var account = $(".account").val();
if(""==account){
alert("账户名为空");
}
$.ajax({
url:"AjaxTest",
type:"post",
dataType:"json",
data:{"account":account},
"success":function(responseObject){
if(responseObject.success){
$(".show").text(responseObject.content);
}
}
});
});
  • AjaxTest
1
2
3
4
5
6
7
8
9
10
11
12
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("application/json;charaset=utf-8");
String account = request.getParameter("account");
if("root".equals(account)) {
response.getWriter().append("{\"success\":true,\"content\":\"V\"}");
}else {
response.getWriter().append("{\"success\":true,\"content\":\"X\"}");
}

doGet(request, response);
}

调用函数的两种方式

1.配置web.xml,通自定义url名跳转

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>自定义名1</servlet-name>
<servlet-class>包名.类名</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>自定义名1</servlet-name>
<url-pattern>/自定义url名</url-pattern>
</servlet-mapping>

2.类名上加注解,加上后就可以通过转发(有/)或重定向跳转(无/),

​ 注意转发和重定向的目录起始点不一样

​ 例:@WebServlet(“/Ajax”)

小结

前言

JavaEE java企业级版本

  • 基础技术

    • 已学
    • Servlet
    • JDBC
    • Cookie
    • Session
  • 未学

    • JSP (Java Server Page) 动态页面组件
    • EL
    • JSTL
    • Filter 过滤器
    • Listener 监听器
  • 自定义Web框架

    • 知识准备
    • 泛型
    • 反射
    • 注解
  • 框架

    • MVC框架
    • ORM框架
  • 开发项目


JSP

  • 动态页面/静态页面
    • 动态页面是指:内容会根据不同的数据发生改变的页面
    • 实现方式一 : 前端实现 AJAX+JSON+JS
    • 实现方式二 : 后端实现 servlet=>out.write(““)
    • 实现方式三 : 后端实现 JSP java(动态性)+html(编写静态的HTML代码)

组成部分

  • 指令元素<%@>
    • page => 整个页面的配置
      • import
      • contentType:text/html;charset=utf-8
      • language
      • pageEncoding 字符编码集
      • isErrorPage
    • include => 在页面中包含另一个jsp页面
    • taglib => 引用标签库(JSTL)
  • html元素HTML标签
  • 脚本元素 <% %> java代码
  • 表达式元素 <%= %> 在页面上输出数据 等价于 out.write(数据) 结尾不能有分号
  • 声明元素 <%! %> 声明java方法或属性
  • JSP注释元素 <%– 注释内容 –%>

传值

Servlet -> jsp

  • 跳转的两种方式

    • 转发 : 服务端转发,共享请求对象
    • 重定向
  • JSP也可以处理网络请求,Servlet可以将请求转发给JSP

  • request

    • setAttribute

    • getAttribute

翻译规则

  • JSP是后端的页面技术

  • JSP本质上是Servlet

  • JSP翻译过后的类 extends HttpJspBase extends HttpServlet

  • 运行过程 : JSP==tomcat翻译==>Java==javac编译==>Class==>JVM运行

  • 翻译 tomcat实现

  • 翻译的规则

    • html 原样输出
    • 表达式元素 out.print(内容);
    • 脚本元素 原样转移到生成类中
    • 声明元素 翻译到类体中,作为独立的方法和属性存在
    • 查看翻译结果:使用<%=request.getServletContext().getRealPath(“/“)%>在网页上输出路径,再根据路径找到翻译后的内容,例:D:\tomcat\apache-tomcat-8.0.42-windows-x86\apache-tomcat-8.0.42\work\Catalina\localhost\11Week01Day\org\apache\jsp下的文件就是翻译之后的文件

隐式对象

  • pageContext

  • request

  • session HttpSession

  • application ServletContext

  • response

  • out JspWriter

  • page object

  • config ServletConfig

  • exception Throwable

  • 在JSP中无需创建就可以使用的9个对象,它们是:
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    l out(JspWriter):等同与response.getWriter(),用来向客户端发送文本数据;

    l config(ServletConfig):对应“真身”中的ServletConfig;

    l page(当前JSP的真身类型):当前JSP页面的“this”,即当前对象;

    l pageContext(PageContext):页面上下文对象;

    l exception(Throwable):只有在错误页面中可以使用这个对象;

    l request(HttpServletRequest):即HttpServletRequest类的对象;

    l response(HttpServletResponse):即HttpServletResponse类的对象;

    l application(ServletContext):即ServletContext类的对象;

    l session(HttpSession):即HttpSession类的对象,不是每个JSP页面中都可以使用,如果在某个JSP页面中设置<%@page session=”false”%>,说明这个页面不能使用session。
  • JSP中除了声明元素,其他都翻译到了service中

  • 内置对象,是jspService方法中的参数或者预先声明赋值的局部变量

四大作用域

pageContext==>request==>session==>application
都有 setAttribute/getAttribute/removeAttribute
可以存取数据,但仅在一定范围内生效,把生效范围称为作用域

  • pageContext 一个JSP页面 当前页面有效
  • request 同一个请求 同一次请求有效
  • session 同一个会话 同一次会话有效
  • application 同一个应用 : 服务器启动时创建、服务器关闭时销毁(全局有效)
    原则 : 满足需求的情况下,尽量选择小的作用域范围
    例:
    ​```java
    pageContext作用域:
    this.getServletContext().setAttribute(arg0, arg1);
    session作用域:
    Object sessionToken = req.getSession().getAttribute(“_anti_duplicate_token”);
    1
    2
    3
    4
    5
    6
    7
    8
    9

    ### EL

    作用:替代表达式元素,在页面上取值

    语法:${变量名},如果有嵌套则使用`.`调用属性

    ```jsp
    ${user} ${user.address} ${user.address.city}

取值规则:从四大作用域,从小到大自动获取

补充

  • 后端模板引擎 : FreeMarker\velocity\Thymeleaf\Mustache

例:

Java代码(这个例子未用EL)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@WebServlet("/Test")
public class Test extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
*/
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求编码
req.setCharacterEncoding("utf-8");
//设置响应编码
resp.setContentType("text/html;charset=utf-8");
//从服务层获得数据
GetAllStuMessa getAllStuMessa = new GetAllStuMessa();
List<StudentData> allStu = getAllStuMessa.getAllStu();
//将数据传递,转发
req.setAttribute("stu", allStu);
req.getRequestDispatcher("/index.jsp").forward(req, resp);
}
}

index.jsp文件内部分代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<%--得到数据 --%>
<table>
<tr><td>编号</td><td>姓名</td><td>电话</td><td>性别</td><td>科目</td><td>生日</td></tr>
<% List<StudentData> allStu = (List<StudentData>)request.getAttribute("stu");
for(StudentData stu : allStu){
int id = stu.getSid();
String name = stu.getSname();
String phone = stu.getSphonenum();
String sex = stu.getSsex();
String subject = stu.getSsubject();
int bir = stu.getSbir();
out.write("<tr><td>"+id+"</td><td>"+name+"</td><td>"+phone+"</td><td>"+sex+"</td><td>"+subject+"</td><td>"+bir+"</td></tr> ");
}
%>
</table>
//注:eclipse更改xml文件编码:Window--->Preference--->Web--->点击JSP Files
下面的jsp代码耦合度低一些
<%
List<User> list = (List)request.getAttribute("users");
for(int i=0;i<list.size();i++){
User u = list.get(i);
%>
<tr>
<td><%=u.getUsername() %></td>
<td><%=u.getPassword() %></td>
<td><%=u.getAge() %></td>
<td><%=u.getSex() %></td>
<td><%=u.getAddress() %></td>
</tr>
<%
}
%>

JSTL

Java Standard Taglib java 标准标签库

JSP,引入各种元素 <%> <%= %>

简化页面动态逻辑的编写(干掉<%)

提供一系列的标签库,实现循环、判断、取值等页面逻辑

使用方式

  • 导包:standard.jar jstl.jar

  • 引用核心标签库:
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    JSTL格式化标签用来格式化并输出文本、日期、时间、数字(根据需要)
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32

    #### set out remove

    - set
    - <c:set var="name" value="${'gareen'}" scope="request" />
    - 相当于:<%request.setAttribute("name","gareen")%>
    - out
    - <c:out value="${name}" />
    - 相当于: <%=request.getAttribute("name")%>
    - remove
    - <c:remove var="name" scope="request" />
    - 相当于:<c:remove var="name" scope="request" />
    - 注:作用域可以是pageContext, request, session, application

    #### if else

    - JSTL通过<c:if test=""> 进行条件判断,但是JSTL没有<c:else,所以常用的办法是在<c:if的条件里取反
    - 配合if使用的还有通过empty进行为空判断
    empty可以判断对象是否为null,字符串长度是否为0,集合长度是否为0

    例:

    ```xml
    <c:if test="${hp<5}">
    <p>这个英雄要挂了</p>
    </c:if>
    <c:if test="${!(hp<5)}">
    <p>这个英雄觉得自己还可以再抢救抢救</p>
    </c:if>
    <c:if test="${empty weapon}">
    <p>没有装备武器</p>
    </c:if>

choose

虽然JSTL没有提供else标签,但是提供了一个else功能的标签

1
2
3
4
5
6
7
8
<c:choose>
<c:when test="${hp<5}">

</c:when>
<c:otherwise>

</c:otherwise>
</c:choose>

forEach

可以在JSP中使用for循环,但是其可读性很差。 借助JSTL的c:forEach标签,可以改善可读性

1
2
3
4
5
6
7
8
9
10
11
12
13
//items="${stu }" 表示遍历的集合
//var="u" 表示把中途元素放在u上
//varStatus="vs" 表示遍历的状态 vs.count从1开始计数 vs.index从0
<c:forEach items="${stu }" var="u" varStatus="vs">
<tr>
<td>${vs.count }</td>
<td>${u.sname }</td>
<td>${u.ssex }</td>
<td>${u.sbir }</td>
<td>${u.sphonenum }</td>
<td>${u.ssubject }</td>
</tr>
</c:forEach>

fmt:formatDate 格式化日期

  • <fmt:formatDate value=”${now}” pattern=”yyyy-MM-dd HH:mm:ss”/>

  • 例:

    1
    2
    <% Date now = new Date(); pageContext.setAttribute("now",now); %>
    <td><mt:formatDate value="${now }" pattern="yyyyMMdd"/></td>

过滤器Filter

针对于具备多个servlet共有的系统功能提供的过滤机制

使用方法

  • 实现接口javax.servlet.Filter

  • 实现doFilter方法,最后写filterChain.doFilter(req.resp);

  • 添加配置

    • web.xml
      • filter
        • filter-name
        • filter-class
      • filter-mapping
        • filter-name
        • url-pattern
    • 注解(@WebFilter(“*”)或)@WebFilter(urlPatterns={“”,””})
  • mark

  •     public void doFilter(ServletRequest sreq, ServletResponse sresp, FilterChain chain)
                  throws IOException, ServletException {
              HttpServletRequest req = (HttpServletRequest)sreq;
              HttpServletResponse resp = (HttpServletResponse)sresp;
              chain.doFilter(sreq, sresp);
              System.out.println("已进入过滤器......");
          }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    ```xml
    <filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>com.woniu.test.类名</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>*</url-pattern>
    </filter-mapping>

doFilter方法的request和response可以向下转型

  • HttpServletRequest req = (HttpServletRequest)sreq;
    HttpServletResponse resp =(HttpServletResponse)sresp;
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33


    ### 设置编码

    - ![mark](http://pybd8px2l.bkt.clouddn.com/blog/20190924/RbFIamVrqu52.png?imageslim)

    - ![mark](http://pybd8px2l.bkt.clouddn.com/blog/20190924/CQsf4G3KVCMj.png?imageslim)
    - ![mark](http://pybd8px2l.bkt.clouddn.com/blog/20190924/JoCQ5xhVJaoH.png?imageslim)

    ### 获取用户输入参数

    - ```java
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    //设置编码
    request.setCharacterEncoding("utf-8");
    response.setCharacterEncoding("utf-8");
    response.setContentType("text/html;utf-8");
    //获取数据
    StringBuilder sb = new StringBuilder();
    Map<String, String[]> parameterMap = request.getParameterMap();
    for(Map.Entry<String, String[]> entry:parameterMap.entrySet()) {
    sb.append(entry.getKey());
    sb.append(":");
    if(entry.getValue()!=null&&entry.getValue().length==1) {
    sb.append(entry.getValue()[0]);
    }else {
    sb.append(Arrays.toString(entry.getValue()));
    }
    }
    String uri = ((HttpServletRequest)request).getRequestURI();
    System.out.println("请求"+uri+"的参数为:"+sb.toString());
    chain.doFilter(request, response);
    }

防重复提交

  • 访问login.jsp,先经过过滤器,生成一个随机数,放入jsp的表单中

  • jsp响应返回到浏览器

  • 先经过过滤器,过滤器验证提交的随机数是否为之前生成的token,一致就放行,并且清空

  • 用户重复提交,先经过过滤器,过滤器验证提交随机数不符合,不放行,跳转到提示页面

    • 
      
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16



    - ```xml
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <form action="MainUI" method="post">
    <input type="hidden" name="_anti_duplicate_token" value="${_anti_duplicate_token }"/>
    请输入账号:<input type="text" name="name">
    请输入密码:<input type="password" name="password">
    <input type="submit" value="提交">
    </form>
    </body>
    </html>
    • mark

    •     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                //设置编码
                request.setCharacterEncoding("utf-8");
                response.setCharacterEncoding("utf-8");
                response.setContentType("text/html;utf-8");
                
                HttpServletRequest req = (HttpServletRequest)request;
                HttpServletResponse resp = (HttpServletResponse)response;
                //获取前端传来的的参数(之前保存)
                String parameterToken = request.getParameter("_anti_duplicate_token");
                //判断是否为第一次进入login.jsp
                if(parameterToken!=null) {
                    //获取用户现在进入时的参数
                    Object sessionToken = req.getSession().getAttribute("_anti_duplicate_token");
                    //不是第一次,则判断网页保存的参数和用户进入时的参数是否相等
                    if(parameterToken.equals(sessionToken)) {
                        System.out.println("两值相等");
                        //移除用户的参数
                        req.getSession().removeAttribute("_anti_duplicate_token");
                        chain.doFilter(request, response);
                        return;
                    }else {
                        System.out.println("两值不相等");
                        //重复提交,跳转到页面上去
                        req.getRequestDispatcher("/repeateLogin").forward(request, response);
                    }
                }else {
                    System.out.println("parameterToken == null");
                    //是第一次进入网页login.jsp,生成token
                    String token = UUID.randomUUID().toString();
                    req.getSession().setAttribute("_anti_duplicate_token", token);
                    chain.doFilter(request, response);
                }
            }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43

      注:火狐浏览器会自动防止用户重复提交,意思就是,服务器未响应之前,用户的点击操作,不会进入过滤器(大坑)

      ### 登录验证

      - 登录成功后,放入session标识

      - 登录过滤器

      - ```java
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      HttpServletRequest req = (HttpServletRequest)request;
      HttpServletResponse resp = (HttpServletResponse)response;
      //得到用户输入地址
      String requestURI = req.getRequestURI();
      //当用户输入界面为登录界面的时候,直接通过此滤器,不进行验证
      //当进行登录操作时,也直接通过过滤器 if(requestURI.endsWith("index.jsp")||requestURI.endsWith("loginServlet")) {
      System.out.println("requestURI.endsWith(\"index.jsp\")");
      chain.doFilter(request, response);
      return;
      }
      System.out.println(requestURI);
      //判断用户登录状态
      //得到用户登录状态
      String attribute = (String)req.getSession().getAttribute("LoginFlag");
      if(null == attribute) {
      System.out.println("null == attribute");
      //当过滤器发现没有登录过时,直接跳转到登录后的界面
      req.getRequestDispatcher("index.jsp").forward(request, response);
      return;
      }
      System.out.println("到这里,说明我该睡");
      //到这里,说明我该睡了
      chain.doFilter(request, response);
      }

      //注可以定义:
      private static final List<String> EXCLUDE_URL_LIST =
      Arrays.asList("index.jsp","login"."register.jsp");
      if(EXCLUDE_URL_LIST.contains(path)){
      //使用Arrays.asList将多个String转化成List集合
      //这样就可以统一判断
      }
  • 上面的将资源文件也拦截了,应该将对应资源文件也放行

监听器 Listener

  • javaEE中,用于处理特定事件的组件,在特定的系统事件发生时会调用相应的监听来处理
  • 事件有:应用启动、销毁、请求创建、销毁等等
  • 事件列表
    • ServletContext相关
      • ServletContextListener => 生命周期监听、创建、销毁
      • ServletContextAttributeListener => 属性监听 属性创建、销毁、修改
    • HttpSession相关
      • HttpSessionListener =>生命周期 生命周期监听、创建、销毁
      • HttpSessionAttributeListener => 属性监听 属性创建、销毁、修改
      • HttpSessionActivationListener => 会话活化监听器
      • HttpSessionBindingListener => 会话绑定监听器
    • ServletRequest相关
      • ServletRequestListener => 生命周期监听、创建、销毁
      • ServletRequestAttributeListener => 属性监听 属性创建、销毁、修改
    • 内存=>硬盘(钝化) 硬盘=>内存(活化)
    • ServletContext生命周期时间:ServletContextEvent

更加合理的方式:

1
2
<base href="http://localhost:8080/emall/" />可以修改默认相对的路径
product/add => http://localhost:8080/emall/product/add

实例

  • 未用注解

    • xml文件里

      1
      2
      3
      <listener>
      <listner-class>包名.类名</listner-class>
      </listner>
  • 使用注解

    • @WebListener
      public class AppLifecycleListener implements ServletContextListener{
          @Override
          public void contextDestroyed(ServletContextEvent arg0) {
              System.out.println("应用关闭");
          }
          @Override
          public void contextInitialized(ServletContextEvent arg0) {
              System.out.println("应用启动");
          }
      }
      
      1
      2
      3
      4
      5
      6

      ### 获得访问次数及在线人数

      - ```xml
      总访问次数:<a>${inline_num }</a><br/>
      当前在线人数:<a>${online_num }</a>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//以下是实现统计在线人数的主要代码,有30分钟延迟销毁在线人数bug
//对于此类统计,重点在于使用监听器的不同,例如访问次数用ServletRequestListner来监听
//用户的访问请求即可 实现服务器的重启数据保存,可以使用读取文件或者properties文件的方式
@WebListener
public class OnlineNumberListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent hse) {
System.out.println("创建了一个session");
//每创建一个session就更新一次
ServletContext sc = hse.getSession().getServletContext();
Object attribute = sc.getAttribute("online_num");
if(null == attribute) {
attribute=0;
sc.setAttribute("online_num", attribute);
}else {
//使session值加1
int num = (int)attribute;
num++;
sc.setAttribute("online_num", num);
}
}
public void sessionDestroyed(HttpSessionEvent hse) {
//销毁session即使在线人数-1
ServletContext sc = hse.getSession().getServletContext();
Object attribute = sc.getAttribute("online_num");
//使session值加-1
int num = (int)attribute;
num--;
sc.setAttribute("online_num", num);
}
}
  • 数据库的下划线问题解决

    • 具体出现原因:数据库的字段名使用下划线,造成beanListHandler无法成功读取,可以通过加参数的方式解决,如下所示

    • RowProcessor pro = new BasicRowProcessor(new GenerousBeanProcessor());
      BeanListHandler<Product> beanListHandler = new BeanListHandler<product>(Product.class,pro);