- Java Web基础
- Servlet的执行流程及生命周期
- 请求与响应的结构
- 请求转发与响应重定向
- Session的原理
- JSP九大内置对象
- 数据处理
- JDBC的使用步骤
- Statement和PreparedStatement的区别
Java Web基础
一、Servlet的执行流程及生命周期
执行流程
- 服务器解析程序的
web.xml
文件,找到与请求匹配的url
和servlet-name
,根据servlet-name
找到对应的servlet-class
,然后对这个类进行实例化和初始化 -
tomcat应用服务器执行Servlet实例的
service()
方法(doGet/doPost),通过响应将数据返回给浏览器 -
Servlet默认情况下是在第一次访问时实例化,也可以通过
web.xml
配置load-on-startup
,使其在服务启动的时候实例化 -
对于Serlvet来说,在tomcat中有且仅有一个唯一的对象,tomcat不会创建Serlvet的多个实例
-
Servlet在并发环境下是如何处理的?
Servlet是基于单例多线程处理并发情况,利用多线程技术提供web服务 -
多线程处理的情况下,如何解决线程安全问题?
所有的线程,都共享一个Servlet实例。在使用Servlet时,不允许在Servlet内创建存在状态的变量或对象,避免在并发访问时产生无法预期的结果
生命周期
- 装载:Java应用程序启动的时候,tomcat会扫描
web.xml
文件,得知当前应用有哪些Servlet,装载时并不会实例化Servlet(java层面的对象创建) - 创建:当
url
第一次访问Servlet地址的时候进行创建,同时执行构造函数 - 初始化:Servlet在创建对象以后,会马上执行
init()
初始化函数,对Servlet进行初始化(Servlet自身专门用于初始化Servlet执行资源的方法) -
提供服务:
service()
方法servlce()
方法:对于发来的请求(无论是post/get),一律使用servlet方法接收处理。如果将请求细化,service()方法下还可以细化为doGet()/doPost()方法doGet()
:只处理get请求doPost()
:只处理post请求
- 销毁:在web应用重启或关闭时使用
destory()
方法将Servlet的资源彻底销毁
二、请求与响应的结构
请求 request
- 请求行:说明了发送的方式(
get/post
),发送的地址,HTTP协议的版本号 -
请求头:说明了从浏览器到服务器发送到辅助信息
Accept-Language: zh-CN
:说明浏览器优先使用中文User-Agent
:代表了用户的使用环境。帮助判断用户使用的是手机还会电脑进行的访问,然后根据浏览器的规格不同进行不同的展现Content-Type
:说明了提交的表单的格式(字符串或二进制流)
- 请求体:由浏览器向服务器发送的真实数据,请求体中,数据使用键值对的形式,“键”和“值”之间使用“=”连接,多个键值对之间使用“&”进行分隔。只有在post请求中才会存在,get请求中没有请求体这一项,请求体会被附加在url后面发送到服务器
响应 response
- 响应行:包含http版本、状态码、状态码的英文描述
- HTTP状态码
- 200:表示访问成功
- 404:表示资源未找到
- 500:代表的是服务器的内部错误。
- 响应头:表述了返回数据的一些辅助信息
Server
:表示使用了哪种web服务器Content-Type
:表示数据返回给浏览器以后,浏览器采用什么样的方式进行处理呢。(text/html表示把返回的数据解释成html进行显示)Date
:响应数据产生的时间
- 响应体:服务器向浏览器返回的真实数据(html片段、二进制内容、xml)
三、请求转发与响应重定向
请求转发
- 请求转发是服务器跳转,只会产生一次请求(会将请求原封不动的转发给下一个请求)
-
请求转发语句:
request.getRequestDispatcher().forward();
响应重定向
- 响应重定向是浏览器端的跳转,会产生两次请求
-
响应重定向语句:
response.sendRedirect();
四、Session的原理
Session
又被称为用户会话,是指与客户端浏览器窗口绑定的,且存储在服务器内部的用户数据
Session的工作原理:客户端登录完成之后,服务器会创建对应的Session,Session创建完之后,会把 Session的id发送给客户端,客户端再存储到浏览器的Cookie中。只要当前浏览器没关闭,这个Cookie是一直存在的。这样客户端每次访问服务器时,都会带着SessionId,服务器拿到SessionId之后,在内存找到与之对应的 Session,这样就可以正常工作了
五、JSP(Java Server Pages)九大内置对象
- reqeust 获取用户参数,服务器通过request得到客户的数据
- response 服务器返回给客户端的数据
- session 用户会话
- application 可以存放全局的变量,可以实现用户之间的数据共享。生命周期同服务器生命周期一致
- out 用于在页面中输出
- page 当前的JSP页面
- pageContext 页面上下文对象,很少使用
- config web.xml中配置的全局参数或者Servlet参数,都可以使用config进行获取
- exception 异常对象,将其他JSP对象产生的异常在当前页面进行输出和控制(使用前,需要在配置指令中,将isError配置为’true”)
数据处理
六、JDBC的使用步骤
- 加载JDBC驱动
- 创建数据库连接Connection
- 创建命令Statement
- 处理结果ResultSet
- 关闭连接
// 标准JDBC操作五步骤
public class StandardJDBCSample {
public static void main(String[] args) {
Connection conn = null;
try {
// 1.加载并注册JDBC驱动
// 如果使用JDBC操作数据库,需要先准备好其驱动包
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.创建数据库连接
// url连接字符串格式:jdbc.mysql://[主机ip][:端口]/数据库名?参数列表。如果主机ip不写,默认为127.0.0.1,端口不写默认为3306,一般来说都要写
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/databasename?useSSL=false&useUnicode=true&characterEncodeing=UTF-8&serverTimezone=Asia/Shanghai",
"root", "password");
// 3.创建Statement对象
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from tablename");
// 4.遍历查询结果
while (rs.next()) {
Integer eno = rs.getInt(1); //通过字段位置获取
String ename = rs.getString("ename");//通过字段名获取
Float salary = rs.getFloat("salary");
String dname = rs.getString("dname");
System.out.println(dname + "-" + eno + "-" + ename + "-" + salary);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 5.关闭连接,释放资源
try {
if (conn != null && conn.isClosed() == false) {
conn.close();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
}
七、Statement和PreparedStatement的区别
PreparedStatement
是预编译的SQL语句,效率高于Statement
,尤其执行复杂SQL语句或频繁执行某一SQL时,执行效率优势更明显PreparedStatement
支持参数化操作(?操作符),相比Statement
使用字符串连接组织SQL语句更加灵活,可读性更好PreparedStatement
可以防止SQL注入,安全性高于Statement
-
Statement
在运行时要经过两个步骤:
i. Java会把SQL字符串进行解析,区分哪里是字段、条件、表名等
ii. 根据解析的SQL语句,执行SQL
这就意味着,如果我们执行了10万次,就要解析SQL语句10万次 -
PreparedStatement
中,如果需要执行10万遍SQL,只需要解析一次SQL语句就可以了;而且会将解析的sql语句保存到数据库底层的内存中,当这条SQL再次执行时,只要把对应的数据传递到解析好的结果中就可以 -
SQL注入风险
:SQL注入是专门针对Statement
设计的,因为其是靠字符串拼接创建SQL语句的,所以对特殊字符没有做任何处理;而PrepareStatement
则会将敏感字符处理掉,将其整体作为人名进行查询
例子
数据库中数据
使用Statement
// 使用`Statement`
Class.forName(driverName);
Connection conn = DriverManager.getConnection(URL, username, password);
Statement statement = conn.createStatement();
String sql = "SELECT * FROM emp where ename = '" + ename + "' ";
System.out.println(sql);
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString("ename") + "," +
rs.getString("job") + "," +
rs.getFloat("sal"));
}
// ename传入"SMITH"执行结果
SELECT * FROM emp where ename = 'SMITH'
SMITH,CLERK,800.0
// ename传入"SMITH' or 1=1 or '"执行结果
SELECT * FROM emp where ename = 'SMITH' or 1=1 or ''
SMITH,CLERK,800.0
ALLEN,SALESMAN,1600.0
WARD,SALESMAN,1250.0
JONES,MANAGER,2975.0
MARTIN,SALESMAN,1250.0
BLAKE,MANAGER,2850.0
CLARK,MANAGER,2450.0
KING,PRESIDENT,5000.0
TURNER,SALESMAN,1500.0
ADAMS,CLERK,1100.0
JAMES,CLERK,1200.0
FORD,ANALYST,3000.0
MILLER,CLERK,1300.0
使用预编译PreparedStatement
// 使用预编译PreparedStatement
Class.forName(driverName);
Connection conn = DriverManager.getConnection(URL, username, password);
String sql = "SELECT * FROM emp where ename = ?";
System.out.println(sql);
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString(1, ename);
ResultSet rs = preparedStatement.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("ename") + "," +
rs.getString("job") + "," +
rs.getFloat("sal"));
}
// ename传入"SMITH"执行结果
SELECT * FROM emp where ename = ?
SMITH,CLERK,800.0
// ename传入"SMITH' or 1=1 or '"执行结果
SELECT * FROM emp where ename = ?
发表回复