Tomcat
Web 应⽤服务器:Tomcat、Jboos、Weblogic、Jetty
- 安装 Tomcat
1、官⽹下载压缩⽂件。https://tomcat.apache.org/download-90.cgi
2、解压缩。
bin
:存放各个平台下启动和停⽌ Tomcat 服务的脚本⽂件。
conf
:存放各种 Tomcat 服务器的配置⽂件。
lib
:存放 Tomcat 服务器所需要的 jar。
logs
:存放 Tomcar 服务运⾏的⽇志。
temp
:Tomcat 运⾏时的临时⽂件。
webapps
:存放允许客户端访问的资源(Java 程序)。
work
:存放 Tomcat 将 JSP 转换之后的 Servlet ⽂件。
IDEA 集成 Tomcat
1、创建 Java Web ⼯程。
Servlet
- 什么是 Servlet?
Servlet 是 Java Web 开发的基⽯,与平台⽆关的服务器组件,它是运⾏在 Servlet 容器/Web 应⽤服务
器/Tomcat,负责与客户端进⾏通信。
Servlet 的功能:
1、创建并返回基于客户请求的动态 HTML ⻚⾯。
2、与数据库进⾏通信。 - 如何使⽤ Servlet?
Servlet 本身是⼀组接⼝,⾃定义⼀个类,并且实现 Servlet 接⼝,这个类就具备了接受客户端请求以及
做出响应的功能。
package com.southwind.servlet;
import javax.servlet.*;
import java.io.IOException;
public class MyServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse
servletResponse) throws ServletException, IOException {
String id = servletRequest.getParameter("id");
System.out.println("我是Servlet,我已经接收到了客户端发来的请求,参数是"+id);
servletResponse.setContentType("text/html;charset=UTF-8");
servletResponse.getWriter().write("客户端你好,我已接收到你的请求");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
浏览器不能直接访问 Servlet ⽂件,只能通过映射的⽅式来间接访问 Servlet,映射需要开发者⼿动配
置,有两种配置⽅式。
- 基于 XML ⽂件的配置⽅式。
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.southwind.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/demo2</url-pattern>
</servlet-mapping>
- 基于注解的⽅式。
@WebServlet("/demo2")
public class HelloServlet implements Servlet {
}
上述两种配置⽅式结果完全⼀致,将 demo2 与 HelloServlet 进⾏映射,即在浏览器地址栏中直接访问
demo 就可以映射到 HelloServlet。
Servlet 的⽣命周期
1、当浏览器访问 Servlet 的时候,Tomcat 会查询当前 Servlet 的实例化对象是否存在,如果不存在,
则通过反射机制动态创建对象,如果存在,直接执⾏第 3 步。
2、调⽤ init ⽅法完成初始化操作。
3、调⽤ service ⽅法完成业务逻辑操作。
4、关闭 Tomcat 时,会调⽤ destory ⽅法,释放当前对象所占⽤的资源。
反射机制:(如下图)
Servlet 的⽣命周期⽅法:⽆参构造函数、init、service、destory
1、⽆参构造函数只调⽤⼀次,创建对象。
2、init 只调⽤⼀次,初始化对象。
3、service 调⽤ N 次,执⾏业务⽅法。
4、destory 只调⽤⼀次,卸载对象。
ServletConfig
该接⼝是⽤来描述 Servlet 的基本信息的。
getServletName()
返回 Servlet 的名称,全类名(带着包名的类名)
getInitParameter(String key)
获取 init 参数的值(web.xml)
getInitParameterNames()
返回所有的 initParamter 的 name 值,⼀般⽤作遍历初始化参数
getServletContext()
返回 ServletContext 对象,它是 Servlet 的上下⽂,整个 Servlet 的管理者。
ServletConfig 和 ServletContext 的区别:
ServletConfig
作⽤于某个 Servlet 实例,每个 Servlet 都有对应的 ServletConfig,ServletContext
作⽤
于整个 Web 应⽤,⼀个 Web 应⽤对应⼀个 ServletContext,多个 Servlet 实例对应⼀个
ServletContext。
⼀个是局部对象,⼀个是全局对象。
Servlet 的层次结构
Servlet ====> GenericServlet ====> HttpServlet
HTTP 请求有很多种类型,常⽤的有四种:
GET 读取
POST 保存
PUT 修改
DELETE 删除
GenericServlet 实现 Servlet 接⼝,同时为它的⼦类屏蔽了不常⽤的⽅法,⼦类只需要重写 service ⽅
法即可。
HttpServlet 继承 GenericServlet,根据请求类型进⾏分发处理,GET 进⼊ doGET ⽅法,POST 进⼊
doPOST ⽅法。
开发者⾃定义的 Servlet 类只需要继承 HttpServlet 即可,重新 doGET 和 doPOST。
package com.southwind.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("GET");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("POST");
}
}
JSP
JSP 本质上就是⼀个 Servlet,JSP 主要负责与⽤户交互,将最终的界⾯呈现给⽤户,
HTML+JS+CSS+Java 的混合⽂件。
当服务器接收到⼀个后缀是 jsp 的请求时,将该请求交给 JSP 引擎去处理,每⼀个 JSP ⻚⾯第⼀次被访
问的时候,JSP 引擎会将它翻译成⼀个 Servlet ⽂件,再由 Web 容器调⽤ Servlet 完成响应。
单纯从开发的⻆度看,JSP 就是在 HTML 中嵌⼊ Java 程序。
具体的嵌⼊⽅式有 3 种:
1、JSP 脚本,执⾏ Java 逻辑代码
<% Java代码 %>
2、JSP 声明:定义 Java ⽅法
<%!
声明 Java ⽅法
%>
3、JSP 表达式:把 Java 对象直接输出到 HTML ⻚⾯中
<%=Java变量 %>
<%!
public String test(){
return "HelloWorld";
}
%>
<%
String str = test();
%>
<%=str%>
JSP内置对象 9 个
1、request
:表示⼀次请求,HttpServletRequest。
2、response
:表示⼀次响应,HttpServletResponse。
3、pageContext
:⻚⾯上下⽂,获取⻚⾯信息,PageContext。
4、session
:表示⼀次会话,保存⽤户信息,HttpSession。
5、application
:表示当前 Web 应⽤,全局对象,保存所有⽤户共享信息,ServletContext。
6、config
:当前 JSP 对应的 Servlet 的 ServletConfig 对象,获取当前 Servlet 的信息。
7、out
:向浏览器输出数据,JspWriter。
8、page
:当前 JSP 对应的 Servlet 对象,Servlet。
9、exception
:表示 JSP ⻚⾯发⽣的异常,Exception。
常⽤的是 request、response、session、application、pageContext
request 常⽤⽅法:
1、String getParameter(String key)
获取客户端传来的参数。
2、void setAttribute(String key,Object value)
通过键值对的形式保存数据。(服务端内部传递参数)
3、Object getAttribute(String key)
通过 key 取出 value。
4、RequestDispatcher getRequestDispatcher(String path)
返回⼀个 RequestDispatcher 对象,该对象的 forward ⽅法⽤于请求转发。
5、String[] getParameterValues()
获取客户端传来的多个同名参数。
6、void setCharacterEncoding(String charset)
指定每个请求的编码。
HTTP 请求状态码
200:正常
404:资源找不到
400:请求类型不匹配
500:Java 程序抛出异常
response 常⽤⽅法:
1、sendRedirect(String path)
重定向,⻚⾯之间的跳转。
转发 getRequestDispatcher 和重定向 sendRedirect 的区别:
转发是将同⼀个请求传给下⼀个⻚⾯,重定向是创建⼀个新的请求传给下⼀个⻚⾯,之前的请求结束⽣
命周期。
转发
:同⼀个请求在服务器之间传递,地址栏不变,也叫服务器跳转。
重定向
:由客户端发送⼀次新的请求来访问跳转后的⽬标资源,地址栏改变,也叫客户端跳转。
如果两个⻚⾯之间需要通过 request 来传值,则必须使⽤转发,不能使⽤重定向。
⽤户登录,如果⽤户名和密码正确,则跳转到⾸⻚(转发),并且展示⽤户名,否则重新回到登陆⻚⾯
(重定向)。
Session
⽤户会话
服务器⽆法识别每⼀次 HTTP 请求的出处(不知道来⾃于哪个终端),它只会接受到⼀个请求信号,所
以就存在⼀个问题:将⽤户的响应发送给其他⼈,必须有⼀种技术来让服务器知道请求来⾃哪,这就是
会话技术。
会话:就是客户端和服务器之间发⽣的⼀系列连续的请求和响应的过程,打开浏览器进⾏操作到关闭浏
览器的过程。
会话状态:指服务器和浏览器在会话过程中产⽣的状态信息,借助于会话状态,服务器能够把属于同⼀次会话的⼀系列请求和响应关联起来。
实现会话有两种⽅式:
- session
- cookie
属于同⼀次会话的请求都有⼀个相同的标识符,sessionID
session 常⽤的⽅法:
String getId()
获取 sessionID
void setMaxInactiveInterval(int interval)
设置 session 的失效时间,单位为秒
int getMaxInactiveInterval()
获取当前 session 的失效时间
void invalidate()
设置 session ⽴即失效
void setAttribute(String key,Object value)
通过键值对的形式来存储数据
Object getAttribute(String key)
通过键获取对应的数据
void removeAttribute(String key)
通过键删除对应的数据
Cookie
Cookie 是服务端在 HTTP 响应中附带传给浏览器的⼀个⼩⽂本⽂件,⼀旦浏览器保存了某个 Cookie,
在之后的请求和响应过程中,会将此 Cookie 来回传递,这样就可以通过 Cookie 这个载体完成客户端
和服务端的数据交互。
- 创建 Cookie
Cookie cookie = new Cookie("name","tom");
response.addCookie(cookie);
- 读取 Cookie
Cookie[] cookies = request.getCookies();
for (Cookie cookie:cookies){
out.write(cookie.getName()+":"+cookie.getValue()+"<br/>");
}
Cookie 常⽤的⽅法
void setMaxAge(int age)
设置 Cookie 的有效时间,单位为秒
int getMaxAge()
获取 Cookie 的有效时间
String getName()
获取 Cookie 的 name
String getValue()
获取 Cookie 的 value
Session 和 Cookie 的区别
session:保存在服务器
保存的数据是 Object
会随着会话的结束⽽销毁
保存重要信息
cookie:保存在浏览器
保存的数据是 String
可以⻓期保存在浏览器中,⽆会话⽆关
保存不重要信息
存储⽤户信息:
session:
setAttribute("name","admin") //存
getAttribute("name") //取
⽣命周期:服务端:只要 WEB 应⽤重启就销毁,客户端:只要浏览器关闭就销毁。
退出登录:session.invalidate()
cookie:
response.addCookie(new Cookie(name,"admin")) //存
Cookie[] cookies = request.getCookies(); //取
for (Cookie cookie:cookies){
if(cookie.getName().equals("name")){
out.write("欢迎回来"+cookie.getValue());
}
}
⽣命周期:不随服务端的重启⽽销毁,客户端:默认是只要关闭浏览器就销毁,我们通过 setMaxAge()
⽅法设置有效期,⼀旦设置了有效期,则不随浏览器的关闭⽽销毁,⽽是由设置的时间来决定。
退出登录:setMaxAge(0)
JSP 内置对象作⽤域
4个
page、request、session、application
setAttribute、getAttribute
page
作⽤域:对应的内置对象是 pageContext。
request
作⽤域:对应的内置对象是 request。
session
作⽤域:对应的内置对象是 session。
application
作⽤域:对应的内置对象是 application。
page < request < session < application
page 只在当前⻚⾯有效。
request 在⼀次请求内有效。
session 在⼀次会话内有效。
application 对应整个 WEB 应⽤的。
- ⽹站访问量统计
<%
Integer count = (Integer) application.getAttribute("count");
if(count == null){
count = 1;
application.setAttribute("count",count);
}else{
count++;
application.setAttribute("count",count);
}
%>
您是当前的第<%=count%>位访客
EL 表达式
Expression Language 表达式语⾔,替代 JSP ⻚⾯中数据访问时的复杂编码,可以⾮常便捷地取出域对
象(pageContext、request、session、application)中保存的数据,前提是⼀定要先 setAttribute,
EL 就相当于在简化 getAttribute
${变量名}
变量名就是 setAttribute 对应的 key 值。
1、EL 对于 4 种域对象的默认查找顺序:
pageContext ====> request ====> session ====> application
按照上述的顺序进⾏查找,找到⽴即返回,在 application 中也⽆法找到,则返回 null
2、指定作⽤域进⾏查找
pageContext:${pageScope.name}
request:${requestScope.name}
session:${sessionScope.name}
application:${applicationScope.name}
数据级联:
<%
// pageContext.setAttribute("name","page");
// request.setAttribute("name","request");
// session.setAttribute("name","session");
// application.setAttribute("name","application");
User user = new User(1,"张三",86.5,new Address(1,"⼩寨"));
System.out.println(user.toString());
pageContext.setAttribute("user",user);
%>
<table>
<tr>
<th>编号</th>
<th>姓名</th>
<th>成绩</th>
<th>地址</th>
</tr>
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.score}</td>
<td>${user.address}</td>
</tr>
</table>
${user[“id”]}
EL 执⾏表达式:
${num1&&num2}
&& || ! < > <= <= ==
&& and
|| or
! not
== eq
!= ne
< lt
> gt
<= le
>= ge
empty //变量为 null,⻓度为0的String,size为0的集合
JSTL
JSP Standard Tag Library JSP 标准标签库,JSP 为开发者提供的⼀系列的标签,使⽤这些标签可以完成
⼀些逻辑处理,⽐如循环遍历集合,让代码更加简洁,不再出现 JSP 脚本穿插的情况。
实际开发中 EL 和 JSTL 结合起来使⽤,JSTL 侧重于逻辑处理,EL 负责展示数据。
JSTL 的使⽤
1、需要导⼊ jar 包(两个 jstl.jar
standard.jar
)存放的位置 web/WEB-INF
2、在 JSP ⻚⾯开始的地⽅导⼊ JSTL 标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
3、在需要的地⽅使⽤
<c:forEach items="${list}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.score}</td>
<td>${user.address.value}</td>
</tr>
</c:forEach>
JSTL 优点:
1、提供了统⼀的标签
2、可以⽤于编写各种动态功能
- 核⼼标签库常⽤标签:
set、out、remove、catch
set:向域对象中添加数据
<%
requset.setAttribute(key,value)
%>
<c:set var="name" value="tom" scope="request"></c:set>
${requestScope.name}
<%
User user = new User(1,"张三",66.6,new Address(1,"科技路"));
request.setAttribute("user",user);
%>
${user.name}
<hr/>
<c:set target="${user}" property="name" value="李四"></c:set>
${user.name}
out:输出域对象中的数据
<c:set var="name" value="tom"></c:set>
<c:out value="${name}" default="未定义"></c:out>
remove:删除域对象中的数据
<c:remove var="name" scope="page"></c:remove>
<c:out value="${name}" default="未定义"></c:out>
catch:捕获异常
<c:catch var="error">
<%
int a = 10/0;
%>
</c:catch>
${error}
- 条件标签:if choose
<c:set var="num1" value="1"></c:set>
<c:set var="num2" value="2"></c:set>
<c:if test="${num1>num2}">ok</c:if>
<c:if test="${num1<num2}">fail</c:if>
<hr/>
<c:choose>
<c:when test="${num1>num2}">ok</c:when>
<c:otherwise>fail</c:otherwise>
</c:choose>
- 迭代标签:forEach
<c:forEach items="${list}" var="str" begin="2" end="3" step="2"
varStatus="sta">
${sta.count}、${str}<br/>
</c:forEach>
格式化标签库常⽤的标签:
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%
request.setAttribute("date",new Date());
%>
<fmt:formatDate value="${date}" pattern="yyyy-MM-dd HH:mm:ss">
</fmt:formatDate><br/>
<fmt:formatNumber value="32145.23434" maxIntegerDigits="2"
maxFractionDigits="3"></fmt:formatNumber>
函数标签库常⽤的标签:
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%
request.setAttribute("info","Java,C");
%>
${fn:contains(info,"Python")}<br/>
${fn:startsWith(info, "Java")}<br/>
${fn:endsWith(info, "C")}<br/>
${fn:indexOf(info, "va")}<br/>
${fn:replace(info, "C","Python")}<br/>
${fn:substring(info, 2, 3)}<br/>
${fn:split(info, ",")[0]}-${fn:split(info, ",")[1]}
过滤器 Filter
功能:
1、⽤来拦截传⼊的请求和传出的响应。
2、修改或以某种⽅式处理正在客户端和服务端之间交换的数据流。
如何使⽤?
与使⽤ Servlet 类似,Filter 是 Java WEB 提供的⼀个接⼝,开发者只需要⾃定义⼀个类并且实现该接⼝
即可。
package com.southwind.filter;
import javax.servlet.*;
import java.io.IOException;
public class CharacterFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest,
seservletResponse, FilterChain filterChain) throws IOException, ServletException
{
servletRequest.setCharacterEncoding("UTF-8");
filterChain.doFilter(servletRequest,servletResponse);
}
}
web.xml 中配置 Filter
<filter>
<filter-name>charcater</filter-name>
<filter-class>com.southwind.filter.CharacterFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>charcater</filter-name>
<url-pattern>/login</url-pattern>
<url-pattern>/test</url-pattern>
</filter-mapping>
【注意】:doFilter ⽅法中处理完业务逻辑之后,必须添加filterChain.doFilter(servletRequest,servletResponse);
否则请求/响应⽆法向后传递,⼀直停留在过滤器中。
Filter 的⽣命周期
当 Tomcat 启动时,通过反射机制调⽤ Filter 的⽆参构造函数创建实例化对象,同时调⽤ init ⽅法实现
初始化,doFilter ⽅法调⽤多次,当 Tomcat 服务关闭的时候,调⽤ destory 来销毁 Filter 对象。
⽆参构造函数
:只调⽤⼀次,当 Tomcat 启动时调⽤(Filter ⼀定要进⾏配置)
init ⽅法
:只调⽤⼀次,当 Filter 的实例化对象创建完成之后调⽤
doFilter
:调⽤多次,访问 Filter 的业务逻辑都写在 Filter 中
destory
:只调⽤⼀次,Tomcat 关闭时调⽤。
同时配置多个 Filter,Filter 的调⽤顺序是由 web.xml 中的配置顺序来决定的,写在上⾯的配置先调
⽤,因为 web.xml 是从上到下顺序读取的。
<filter>
<filter-name>my</filter-name>
<filter-class>com.southwind.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>my</filter-name>
<url-pattern>/login</url-pattern>
</filter-mapping>
<filter>
<filter-name>charcater</filter-name>
<filter-class>com.southwind.filter.CharacterFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>charcater</filter-name>
<url-pattern>/login</url-pattern>
<url-pattern>/test</url-pattern>
</filter-mapping>
1、MyFilter
2、CharacterFilter
也可以通过注解的⽅式来简化 web.xml 中的配置
<filter>
<filter-name>my</filter-name>
<filter-class>com.southwind.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>my</filter-name>
<url-pattern>/login</url-pattern>
</filter-mapping>
等于
@WebFilter("/login")
public class MyFilter implements Filter {
}
Filter 的使⽤场景
实际开发中 Filter 的使⽤场景:
1、统⼀处理中⽂乱码。
2、屏蔽敏感词。
package com.southwind.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/test")
public class WordFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse
servletResponse, FilterChain filterChain) throws IOException, ServletException
{
servletRequest.setCharacterEncoding("UTF-8");
//将"敏感词"替换成"***"
String name = servletRequest.getParameter("name");
name = name.replaceAll("敏感词","***");
servletRequest.setAttribute("name",name);
filterChain.doFilter(servletRequest,servletResponse);
}
}
package com.southwind.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String name = (String) req.getAttribute("name");
System.out.println("servlet:"+name);
}
}
3、控制资源的访问权限。
package com.southwind.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter("/download.jsp")
public class DownloadFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse
servletResponse, FilterChain filterChain) throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpSession session = request.getSession();
String name = (String) session.getAttribute("name");
if(name == null){
//不是登录状态
response.sendRedirect("/login.jsp");
}else{
filterChain.doFilter(servletRequest,servletResponse);
}
}
}
⽂件上传下载
- JSP
1、input 的 type 设置为 file
2、form 表单的 method 设置post
,get 请求会将⽂件名传给服务端,⽽不是⽂件本身
3、form 表单的enctype
设置multipart/form-data
,以⼆进制的形式传输数据
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form enctype="multipart/form-data" action="/upload" method="post">
<input name="desc" type="text"/><br/>
<input name="text" type="file"/><br/>
<input type="submit" value="上传"/>
</form>
</body>
</html>
- Servlet
fileupload 组件
可以将所有的请求信息都解析成FileIteam
对象,可以通过对 FileItem 对象的操作完成上传,⾯向对象的思想。
package com.southwind.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// //通过输⼊流获取客户端传来的数据流
// InputStream inputStream = req.getInputStream();
// Reader reader = new InputStreamReader(inputStream);
// BufferedReader bufferedReader = new BufferedReader(reader);
// //通过输出流将数据流输出到本地硬盘
// //获取⽂件夹的绝对路径
// String path = req.getServletContext().getRealPath("file/copy.txt");
// OutputStream outputStream = new FileOutputStream(path);
// Writer writer = new OutputStreamWriter(outputStream);
// BufferedWriter bufferedWriter = new BufferedWriter(writer);
// String str = "";
// while((str = bufferedReader.readLine())!=null){
// System.out.println(str);
// bufferedWriter.write(str);
// }
// bufferedWriter.close();
// writer.close();
// outputStream.close();
// bufferedReader.close();
// reader.close();
// inputStream.close();
try {
DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
List<FileItem> list = servletFileUpload.parseRequest(req);
for(FileItem fileItem : list){
if(fileItem.isFormField()){
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");
System.out.println(name+":"+value);
}else{
String fileName = fileItem.getName();
long size = fileItem.getSize();
System.out.println(fileName+":"+size+"Byte");
InputStream inputStream = fileItem.getInputStream();
// Reader reader = new InputStreamReader(inputStream);
// BufferedReader bufferedReader = new BufferedReader(reader);
String path = req.getServletContext().getRealPath("file/"+fileName);
OutputStream outputStream = new FileOutputStream(path);
// Writer writer = new OutputStreamWriter(outputStream);
// BufferedWriter bufferedWriter = new BufferedWriter(writer);
int temp = 0;
while((temp = inputStream.read())!=-1){
outputStream.write(temp);
}
// bufferedWriter.close();
// writer.close();
outputStream.close();
// bufferedReader.close();
// reader.close();
inputStream.close();
System.out.println("上传成功");
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
⽂件下载
package com.southwind.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@WebServlet("/download")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String type = req.getParameter("type");
String fileName = "";
switch (type){
case "png":
fileName = "1.png";
break;
case "txt":
fileName = "test.txt";
break;
}
//设置响应⽅式
resp.setContentType("application/x-msdownload");
//设置下载之后的⽂件名
resp.setHeader("Content-Disposition","attachment;filename="+fileName);
//获取输出流
OutputStream outputStream = resp.getOutputStream();
String path = req.getServletContext().getRealPath("file/"+fileName);
InputStream inputStream = new FileInputStream(path);
int temp = 0;
while((temp=inputStream.read())!=-1){
outputStream.write(temp);
}
inputStream.close();
outputStream.close();
}
}
Ajax
Asynchronous JavaScript And XML:异步的 JavaScript 和 XML
AJAX 不是新的编程,指的是⼀种交互⽅式,异步加载,客户端和服务器的数据交互更新在局部⻚⾯的
技术,不需要刷新整个⻚⾯(局部刷新)
优点:
1、局部刷新,效率更⾼
2、⽤户体验更好
【注】:同步:(打电话);异步:(发微信)
基于 jQuery 的 A JAX:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function(){
var btn = $("#btn");
btn.click(function(){
$.ajax({
url:'/test',
type:'post',
data:'id=1',
dataType:'text',
success:function(data){
var text = $("#text");
text.before("<span>"+data+"</span><br/>");
}
});
});
})
</script>
</head>
<body>
<input id="text" type="text"/><br/>
<input id="btn" type="button" value="提交"/>
</body>
</html>
不能⽤表单提交请求,改⽤ jQuery ⽅式动态绑定事件来提交。
Servlet 不能跳转到 JSP,只能将数据返回
package com.southwind.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String id = req.getParameter("id");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String str = "Hello World";
resp.getWriter().write(str);
}
}
传统的 WEB 数据交互 VS AJAX 数据交互
- 客户端请求的⽅式不同:
传统:浏览器发送同步请求 (form、a)
AJAX:异步引擎对象发送异步请求 - 服务器响应的⽅式不同:
传统:响应⼀个完整 JSP ⻚⾯(视图)
AJAX:响应需要的数据 - 客户端处理⽅式不同:
传统:需要等待服务器完成响应并且重新加载整个⻚⾯之后,⽤户才能进⾏后续的操作
AJAX:动态更新⻚⾯中的局部内容,不影响⽤户的其他操作
AJAX 原理
基于 jQuery 的 AJAX 语法
$.ajax({属性})
常⽤的属性参数:
url
:请求的后端服务地址
type
:请求⽅式,默认 get
data
:请求参数
dataType
:服务器返回的数据类型,text/json
success
:请求成功的回调函数
error
:请求失败的回调函数
complete
:请求完成的回调函数(⽆论成功或者失败,都会调⽤)
JSON
JavaScript Object Notation,⼀种轻量级数据交互格式,完成 js 与 Java 等后端开发语⾔对象数据之间
的转换。
客户端和服务器之间传递对象数据,需要⽤ JSON 格式。
package com.southwind.entity;
public class User {
private Integer id;
private String name;
private Double score;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
public User(Integer id, String name, Double score) {
this.id = id;
this.name = name;
this.score = score;
}
}
User user = new User(1,"张三",96.5)
var user = {
id:1,
name:"张三",
score:96.5
}
package com.southwind.servlet;
import com.southwind.entity.User;
import net.sf.json.JSONObject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
User user = new User(1,"张三",96.5);
//将 Java 对象转为 JSON 格式
resp.setCharacterEncoding("UTF-8");
JSONObject jsonObject = JSONObject.fromObject(user);
resp.getWriter().write(jsonObject.toString());
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function(){
var btn = $("#btn");
btn.click(function(){
$.ajax({
url:'/test',
type:'post',
dataType:'json',
success:function(data){
$("#id").val(data.id);
$("#name").val(data.name);
$("#score").val(data.score);
}
});
});
})
</script>
</head>
<body>
编号:<input id="id" type="text"/><br/>
姓名:<input id="name" type="text"/><br/>
成绩:<input id="score" type="text"/><br/>
<input id="btn" type="button" value="提交"/>
</body>
</html>
AJAX的简单应用
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function(){
//修改省份
$("#province").change(function () {
var id = $(this).val();
$.ajax({
url:"/location",
type:"POST",
data:"id="+id+"&type=province",
dataType:"JSON",
success:function(data){
var content = "";
var cities = data.cities;
for(var i=0;i<cities.length;i++){
content += "<option>"+cities[i]+"</option>";
}
$("#city").html(content);
content = "";
var areas = data.areas;
for(var i=0;i<areas.length;i++){
content += "<option>"+areas[i]+"</option>";
}
$("#area").html(content);
}
});
});
//修改城市
$("#city").change(function(){
var id = $(this).val();
$.ajax({
url:"/location",
type:"POST",
data:"id="+id+"&type=city",
dataType:"JSON",
success:function(data){
var content = "";
for(var i=0;i<data.length;i++){
content += "<option>"+data[i]+"</option>";
}
$("#area").html(content);
}
});
});
});
</script>
</head>
<body>
省:<select id="province">
<option value="陕⻄省">陕⻄省</option>
<option value="河南省">河南省</option>
<option value="江苏省">江苏省</option>
</select>
市:<select id="city">
<option value="⻄安市">⻄安市</option>
<option value="宝鸡市">宝鸡市</option>
<option value="渭南市">渭南市</option>
</select>
区:<select id="area">
<option>雁塔区</option>
<option>莲湖区</option>
<option>新城区</option>
</select>
</body>
</html>
package com.southwind.entity;
import java.util.List;
public class Location {
private List<String> cities;
private List<String> areas;
public List<String> getCities() {
return cities;
}
public void setCities(List<String> cities) {
this.cities = cities;
}
public List<String> getAreas() {
return areas;
}
public void setAreas(List<String> areas) {
this.areas = areas;
}
}
package com.southwind.servlet;
import com.southwind.entity.Location;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@WebServlet("/location")
public class LocationServlet extends HttpServlet {
private static Map<String, List<String>> cityMap;
private static Map<String,List<String>> provinceMap;
static {
cityMap = new HashMap<>();
List<String> areas = new ArrayList<>();
//⻄安
areas.add("雁塔区");
areas.add("莲湖区");
areas.add("新城区");
cityMap.put("⻄安市",areas);
//宝鸡
areas = new ArrayList<>();
areas.add("陈仓区");
areas.add("渭宾区");
areas.add("新城区");
cityMap.put("宝鸡市",areas);
//渭南
areas = new ArrayList<>();
areas.add("临渭区");
areas.add("⾼新区");
cityMap.put("渭南市",areas);
//郑州
areas = new ArrayList<>();
areas.add("郑州A区");
areas.add("郑州B区");
cityMap.put("郑州市",areas);
//洛阳
areas = new ArrayList<>();
areas.add("洛阳A区");
areas.add("洛阳B区");
cityMap.put("洛阳市",areas);
provinceMap = new HashMap<>();
List<String> cities = new ArrayList<>();
cities.add("⻄安市");
cities.add("宝鸡市");
cities.add("渭南市");
provinceMap.put("陕⻄省",cities);
cities = new ArrayList<>();
cities.add("郑州市");
cities.add("洛阳市");
cities.add("开封市");
provinceMap.put("河南省",cities);
cities = new ArrayList<>();
cities.add("南京市");
cities.add("苏州市");
cities.add("南通市");
provinceMap.put("江苏省",cities);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String type = req.getParameter("type");
resp.setCharacterEncoding("UTF-8");
String id = req.getParameter("id");
switch (type){
case "city":
List<String> areas = cityMap.get(id);
JSONArray jsonArray = JSONArray.fromObject(areas);
resp.getWriter().write(jsonArray.toString());
break;
case "province":
List<String> cities = provinceMap.get(id);
String city = cities.get(0);
List<String> cityAreas = cityMap.get(city);
Location location = new Location();
location.setCities(cities);
location.setAreas(cityAreas);
JSONObject jsonObject = JSONObject.fromObject(location);
resp.getWriter().write(jsonObject.toString());
break;
}
}
}
JDBC
Java DataBase Connectivity 是⼀个独⽴于特定数据库的管理系统,通⽤的 SQL 数据库存取和操作的公
共接⼝。
定义了⼀组标准,为访问不同数据库提供了统⼀的途径。
JDBC 体系结构
JDBC 接⼝包括两个层⾯:
- ⾯向应⽤的 API,供程序员调⽤
- ⾯向数据库的 API,供⼚商开发数据库的驱动程序
JDBC API
提供者:Java 官⽅
内容:供开发者调⽤的接⼝
java.sql 和 javax.sql
DriverManager
类Connection
接⼝Statement
接⼝ResultSet
接⼝
DriverManager
提供者:Java 官⽅
作⽤:管理不同的 JDBC 驱动
JDBC 驱动
提供者:数据库⼚商
作⽤:负责连接不同的数据库
JDBC 的使⽤
1、加载数据库驱动,Java 程序和数据库之间的桥梁。
2、获取 Connection,Java 程序与数据库的⼀次连接。
3、创建 Statement 对象,由 Connection 产⽣,执⾏ SQL 语句。
4、如果需要接收返回值,创建 ResultSet 对象,保存 Statement 执⾏之后所查询到的结果。
package com.southwind.test;
import java.sql.*;
import java.util.Date;
public class Test {
public static void main(String[] args) {
try {
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8";
String user = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url,user,password);
// String sql = "insert into student(name,score,birthday) values('李四',78,'2019-01-01')";
// String sql = "update student set name = '李四'";
// String sql = "delete from student";
// Statement statement = connection.createStatement();
// int result = statement.executeUpdate(sql);
String sql = "select * from student";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()){
Integer id = resultSet.getInt("id");
String name = resultSet.getString(2);
Double score = resultSet.getDouble(3);
Date date = resultSet.getDate(4);
System.out.println(id+"-"+name+"-"+score+"-"+date);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e){
e.printStackTrace();
}
}
}
PreparedStatement
Statement 的⼦类,提供了 SQL 占位符的功能
使⽤ Statement 进⾏开发有两个问题:
1、需要频繁拼接 String 字符串,出错率较⾼。
2、存在 SQL 注⼊的⻛险。
SQL 注⼊:利⽤某些系统没有对⽤户输⼊的信息进⾏充分检测,在⽤户输⼊的数据中注⼊⾮法的 SQL
语句,从⽽利⽤系统的 SQL 引擎完成恶意⾏为的做法。
String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8";
String user = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url,user,password);
String username = "lisi";
String mypassword = "000";
String sql = "select * from t_user where username = ? and password = ?";
System.out.println(sql);
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,username);
preparedStatement.setString(2,mypassword);
ResultSet resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e){
e.printStackTrace();
}
数据库连接池
JDBC 开发流程
- 加载驱动(只需要加载⼀次)
- 建⽴数据库连接(Connection)
- 执⾏ SQL 语句(Statement)
- ResultSet 接收结果集(查询)
- 断开连接,释放资源
数据库连接对象是通过 DriverManager 来获取的,每次获取都需要向数据库申请获取连接,验证⽤户
名和密码,执⾏完 SQL 语句后断开连接,这样的⽅式会造成资源的浪费,数据连接资源没有得到很好的重复利⽤。(栗子:就像是你每次打一个电话都要买一个手机,打完一个电话就把手机扔了,很浪费,为什么我们不一直使用第一次买的手机一直打电话呢?)
可以使⽤数据库连接池解决这⼀问题。
数据库连接池的基本思想就是为数据库建⽴⼀个缓冲池,预先向缓冲池中放⼊⼀定数量的连接对象,当需要获取数据库连接的时候,只需要从缓冲池中取出⼀个对象,⽤完之后再放回到缓冲池中,供下⼀次请求使⽤,做到了资源的重复利⽤,允许程序重复使⽤⼀个现有的数据库连接对象,⽽不需要重新创
建。
当数据库连接池中没有空闲的连接时,新的请求就会进⼊等待队列,等待其他线程释放连接。
数据库连接池实现
JDBC 的数据库连接池使⽤ javax.sql.DataSource
接⼝来完成的,DataSource 是 Java 官⽅提供的接
⼝,使⽤的时候开发者并不需要⾃⼰来实现该接⼝,可以使⽤第三⽅的⼯具,C3P0 是⼀个常⽤的第三
⽅实现,实际开发中直接使⽤ C3P0 即可完成数据库连接池的操作。
1、导⼊ jar 包
传统⽅式拿到的 Connection:com.mysql.cj.jdbc.ConnectionImpl@557caf28
C3P0 拿到的 Connection:com.mchange.v2.c3p0.impl.NewProxyConnection@4988d8b8
2、代码实现
package com.southwind.test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
public class DataSourceTest {
public static void main(String[] args) {
try {
//创建C3P0
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8");
dataSource.setUser("root");
dataSource.setPassword("root");
Connection connection = dataSource.getConnection();
System.out.println(connection);
//还回到数据库连接池中
connection.close();
} catch (PropertyVetoException e) {
e.printStackTrace();
} catch (SQLException e){
e.printStackTrace();
}
}
}
实际开发,将 C3P0 的配置信息定义在 xml ⽂件中,Java 程序只需要加载配置⽂件即可完成数据库连接
池的初始化操作。
1、配置⽂件的名字必须是 c3p0-config.xml
2、初始化 ComboPooledDataSource 时,传⼊的参数必须是 c3p0-config.xml 中 named-config 标签的 name 属性值。
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<named-config name="testc3p0">
<!-- 指定连接数据源的基本属性 -->
<property name="user">root</property>
<property name="password">root</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=UTF-8</property>
<!-- 若数据库中连接数不⾜时, ⼀次向数据库服务器申请多少个连接 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">20</property>
<!-- 数据库连接池中的最⼩的数据库连接数 -->
<property name="minPoolSize">2</property>
<!-- 数据库连接池中的最⼤的数据库连接数 -->
<property name="maxPoolSize">40</property>
</named-config>
</c3p0-config>
package com.southwind.test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
public class DataSourceTest {
public static void main(String[] args) {
try {
//创建C3P0
ComboPooledDataSource dataSource = new ComboPooledDataSource("testc3p0");
Connection connection = dataSource.getConnection();
System.out.println(connection);
//还回到数据库连接池中
connection.close();
} catch (SQLException e){
e.printStackTrace();
}
}
}
DBUtils
DBUtils 可以帮助开发者完成数据的封装(结果集到 Java 对象的映射)
1、导⼊ jar 包
ResultHandler 接⼝是⽤来处理结果集,可以将查询到的结果集转换成 Java 对象,提供了 4 种实现类。
BeanHandler
将结果集映射成 Java 对象 StudentBeanListHandler
将结果集映射成 List 集合List <Student >
MapHandler
将结果集映射成 Map 对象MapListHandler
将结果集映射成 MapList 结合
【注】:根据返回值决定以上handler。
public static Student findByDBUtils(Integer id){
Connection connection = null;
Student student = null;
try {
connection = dataSource.getConnection();
String sql = "select * from student";
//String sql = "select * from student where id=?";
QueryRunner queryRunner = new QueryRunner();
List<Map<String,Object>> list = queryRunner.query(connection,sql,new MapListHandler());
//List<Map<String,Object>> list = queryRunner.query(connection,sql,new BeanHandler<>(Student.class),id);
for (Map<String,Object> map:list){
System.out.println(map);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return student;
}
【代码来源】参考楠哥教学视频:https://www.bilibili.com/video/BV1BJ411L7NR
【JavaWeb实战代码及所需jar包】:https://download.csdn.net/download/hhhmonkey/21516713