JavaWeb
1、基本概念
1.1、前言
web开发:
-
web,网页 www.baidu.com
-
静态web
- html,css
- 提供给所有人看的数据始终不会发生变化!
-
动态web
-
淘宝等……
-
提供给所有人看的数据会发生变化,每个人在不同的时间,不同的地点看到的信息各不相同!
-
技术栈:Servlet/JSP,ASP,PHP
-
在Java中,动态web资源开发的技术统称为JavaWeb;
1.2、web应用程序
web应用程序:可以提供浏览器访问的程序;
-
a.html、b.html……多个web资源,这些web资源可以被外界访问,对外界提供服务;
-
能访问到的任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上。
-
URL
-
这个统一的web资源会被放在同一个文件夹下,web应用程序–>Tomcat:服务器
-
一个web应用由多部份组成(静态web,动态web)
- html,css,js
- jsp,servlet
- Java程序
- jar包
- 配置文件(properties)
web应用程序编写完毕后,若想提供给外界访问,需要一个服务器来统一管理;
1.3、静态web
- 星.htm,星.html,这些都是网页的后缀,如果服务器上一直存在这些东西,我们就可以之际进行读取。
- 静态web存在的缺点:
- web页面无法动态更新,所有用户看到的都是同一个页面
- 轮播图,点击特效:微动态
- JavaScript(实际开发中,用得最多)
- VB script
- 它无法和数据库交互(数据无法持久化,用户无法交互)
- web页面无法动态更新,所有用户看到的都是同一个页面
1.4、动态web
页面会动态展示:“web页面展示的效果会因人而异”
缺点:
- 假如服务器的动态web资源出现了错误,需要重新编写后台程序,重新发布;
- 停机维护
优点:
- web页面可以动态更新,所有用户看到的都不是同一个页面
- 它可以和数据库交互(数据持久化:注册,商品信息,用户信息。。。)
分析原理,看源码
2、web服务器
2.1、技术讲解
ASP:
- 微软:国内最早流行的就是ASP
- 在HTML中嵌入了VB脚本,ASP+COM;
- 在ASP开发中,基本一个页面都有几千行的业务代码,页面极其混乱
- 维护成本高!
- C#
- IIS(Internet Information Service)
PHP
- PHP开发速度很快,功能很强大,跨平台,代码很简单 (WordPress)
- 无法承载大访问量的情况(局限性)
JSP/Servlet:
B/S:浏览器和服务器
C/S:客户端和服务器
- sun公司主推的B/S架构
- 基于Java语言的(所有的大公司,或者一些开源的组件,都是用Java写的)
- 可以承载三高问题带来的影响
- 语法像ASP,ASP–>JSP,加强市场强度
2.2、web服务器
服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息;
IIS
微软的;ASP…,windows中自带
Tomcat
面向百度编程;
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为比较流行的Web 应用服务器。
Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache 服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat是Apache 服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。
诀窍是,当配置正确时,Apache 为HTML页面服务,而Tomcat 实际上运行JSP 页面和Servlet。另外,Tomcat和IIS等Web服务器一样,具有处理HTML页面的功能,另外它还是一个Servlet和JSP容器,独立的Servlet容器是Tomcat的默认模式。不过,Tomcat处理静态HTML的能力不如Apache服务器。Tomcat最新版本为10.0.14。
3、Tomcat
3.1、安装tomcat
tomcat官网:Apache Tomcat® – Welcome!
3.2、Tomcat启动和配置
启动,关闭Tomcat
startup.bat
shutdown.bat
可能遇到的问题:
- Java环境变量没有配置 JAVA_HOME
- 闪退:需要配置兼容性
- 乱码:配置文件中设置
3.3、配置
conf目录下server.xml文件
可以配置启动的端口号 connector下
- tomcat的默认端口号为:8080
- mysql:3306
- http:80
- https:443
可以配置主机的名称 host下
windows中host配置,C:\\Windows\System32\drivers\etc
- 默认的主机名为:localhost->127.0.0.1
- 默认网站应用存放的位置为:webapps
高难度面试题:
请你谈谈网站是如何进行访问的!
-
输入一个域名;回车
-
检查本机C:\\Windows\System32\drivers\etc配置文件中有没有这个域名映射;
-
有:直接返回对应的ip地址 这个地址中,有我们需要访问的web程序,可以直接访问
127.0.0.1 localhost
-
没有:去DNS服务器找,找到就返回,找不到就返回找不到
-
3.4、发布一个web网站
先模仿写
- 将自己写的网站,放到服务器(Tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了
网站应该有的结构
--webapps : Tomcat服务器的web目录
-ROOT
-kazeStudy : 网站的目录名
-WEB-INF
-classes : Java程序
-lib : web应用所依赖的jar包
-web.xml : 网站配置文件
-index.html 默认的首页
-static
-css
-style.css
-js
-img
-。。。
4、Http
4.1、什么是HTTP
HTTP(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上。
- 文本:html,字符串。。。
- 超文本:图片,音乐,视频,定位,地图。。。
- 80
Https:安全的
- 443
4.2、两个时代
- http1.0
- HTTP/1.0:客户端与web服务器连接后,只能获得一个web资源,断开连接
- http2.0
- HTTP/1.1:客户端与web服务器连接后,可以获得多个web资源
4.3、Http请求
- 客户端—发请求(Request)—服务器
百度:
Request URL: https://www.baidu.com/ 请求地址
Request Method: GET get方法 post方法
Status Code: 200 OK 状态码:200
Remote Address: 39.156.66.14:443
Accept: text/html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cache-Control: max-age=0
Connection: keep-alive
1、请求行
- 请求行中的请求方式:GET
- 请求方式:Get/Post,HEAD,DELETE,PUT,TRACT…
- get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
- post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效
2、消息头
Accept: 告诉浏览器,它所支持的数据类型
Accept-Encoding: 支持哪种编码格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language: 告诉浏览器,它的语言环境
Cache-Control: 缓存控制
Connection: 告诉浏览器,请求完成是断开连接还是保持连接
HOST:主机.../.
4.4、Http响应
- 服务器—响应—客户端
百度:
Connection: keep-alive 连接
Content-Encoding: gzip 编码
Content-Type: text/html; charset=utf-8 类型
Date: Thu, 04 Aug 2022 15:13:51 GMT
Server: BWS/1.1
1、响应体
Accept: 告诉浏览器,它所支持的数据类型
Accept-Encoding: 支持哪种编码格式 GBK UTF-8 GB2312 ISO8859-1
Accept-Language: 告诉浏览器,它的语言环境
Cache-Control: 缓存控制
Connection: 告诉浏览器,请求完成是断开连接还是保持连接
HOST:主机.../.
Refresh:告诉客户端,多久刷新一次
Location:让网页重新定位
2、响应状态码
200:请求响应成功 200
3xx:请求重定向
- 重定向:你重新到我给你的新位置去
4xx:找不到资源 404
- 资源不存在
5xx:服务器代码错误 500 502:网关错误
常见面试题:
当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?
5、Maven
为什么学?
-
在JavaWeb开发中,需要使用大量的jar包,我们手动去导入;
-
如何能够自动导入和配置jar包
由此,Maven诞生了!
5.1、Maven项目架构管理工具
目前用来方便导入jar包
Maven的核心思想:约定大于配置
- 有约束,不要去违反。
Maven会规定好你该如何去编写Java代码,必须按照这个规范
5.2、下载安装Maven
官网:Maven – Welcome to Apache Maven
bin
5.3、配置环境变量
在系统环境变量中配置
- M2_HOME maven目录下的bin目录
- MAVEN_HOME maven的目录
- 在系统中的path配置 %MAVEN_HOME%\bin
cmd 控制台 mvn -verison查看maven版本,验证安装
5.4、阿里云镜像
-
镜像:mirrors
- 作用:加速下载
-
国内建议用阿里云镜像 仓库服务 (aliyun.com)
<mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>
5.5、本地仓库
在本地的仓库,远程仓库;
建立一个本地仓库:
<localRepository>D:\Environment\apache-maven-3.8.6\maven-repo</localRepository>
5.6、在idea中使用Maven
- 启动IDEA
- 创建一个Maven项目
创建maven
修改Maven home path、User setting file、Local repository
- 观察maven仓库中多了什么东西
- IDEA中的Maven设置,注意观察maven配置
5.7、标准普通Maven项目
没有webapp?
file-project structure-modules-项目名-web- +
或者创建maven archetype项目,archetype选择maven-archetype-webapp
5.8、标记文件夹功能
右键-Mark Directory as
或 project structure-modules
5.9、在idea中配置Tomcat
1.右上角Add Configuration,点+,找到Tomcat-local
2.解决警告问题,访问一个网站需要指定一个文件夹名字(未指定访问地址为localhost:8080,指定后为localhost:8080/指定,这个过程叫虚拟路径映射),配置方式:edit configurations-development
5.10、pom文件
pom.xml是Maven的核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--Maven版本和头文件-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--GAV-->
<groupId>com.kazesan</groupId>
<artifactId>javaweb-01-maven</artifactId>
<version>1.0-SNAPSHOT</version>
<!--打包方式
jar:Java应用
war:JavaWeb应用
-->
<packaging>war</packaging>
<properties>
<!--编码版本-->
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
依赖(jar包)
…
资源导出失败—>build中配置resources
resources目录默认导出,可不写
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
5.11、Maven 目录树
Maven中jar包的联系关联图
6、Servlet
6.1、Servlet简介
-
Servlet就是sun公司开发动态web的一门技术
-
Sun公司在这个api中提供了一个接口叫做:Servlet
-
开发Servlet程序步骤:
- 编写一个类,实现Servlet接口
- 把开发好的Java类部署到web服务器中
把实现了Servlet接口的Java程序叫做,Servlet
6.2、HelloServlet
Servlet接口Sun公司有两个默认的实现类:HttpServlet,GenericServlet
1.构建一个普通的Maven项目,删掉里面的src目录,之后学习自己建Modules;这个空的工程就是Maven主工程
2.关于Maven父子工程的理解
父项目中会有
<modules>
<module>servlet-01</module>
</modules>
子项目会有
<parent>
<artifactId>javaweb-02-servlet</artifactId>
<groupId>com.kazesan</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
父项目中的Java子项目可以直接使用
son extends father
导入依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
</dependencies>
3.Maven环境优化
- 修改web.xml为最新的
- 将maven结构搭建完整
4.编写一个Servlet程序
- 编写一个普通类
- 实现Servlet接口,选择直接继承HttpServlet
package com.kazesan.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloServlet extends HttpServlet {
//由于get或者post只是请求实现的不同的方式,可以相互调用,业务都逻辑都一样
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ServletOutputStream outputStream = resp.getOutputStream();
PrintWriter writer = resp.getWriter();//响应流
writer.print("Hello,Servlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
5.编写Servlet的映射
为什么需要映射:写的是Java程序,需要通过浏览器访问,而浏览器需要连接web服务器,所以需要在web服务中注册所写的Servlet,还需要一个浏览器能访问的路径
<!--注册Servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kazesan.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
6.配置Tomcat
注意:配置项目发布的路径就可以了
7.启动测试
注意:
- Tomcat版本最好与JDK版本对应
- web.xml可以在Tomcat下conf目录找到,复制里面的粘贴到idea中相应位置
6.3、Servlet原理
Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会
6.4、Mapping
1.一个Servlet可以指定一个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
2.一个Servlet可以指定多个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
3.一个Servlet可以指定通用映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
4.默认请求路径(少用,不会走首页)
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
5.指定一些后缀或者前缀等等…
<!--可以自定义后缀实现请求映射
注意点: *前面不能加项目映射的路径
hello/safafgaf.kazesan
-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.kazesan</url-pattern>
</servlet-mapping>
6.优先级问题
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求
6.5、ServletContext
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,这个对象全局唯一,它代表了当前的web应用;
1、共享数据
我在这个Servlet中保存的数据,可以在另外一个Servlet中拿到
存数据
package com.kazesan.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//this.getInitParameter() 初始化参数
//this.getServletConfig() Servlet配置
//this.getServletContext() Servlet上下文
ServletContext context = this.getServletContext();
String username = "大白很白";//数据
context.setAttribute("username",username);//将一个数据保存在ServletContext中,名字为username,值为username
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
读数据
package com.kazesan.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String)context.getAttribute("username");
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("名字"+username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
配置xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.kazesan.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>getC</servlet-name>
<servlet-class>com.kazesan.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>getC</servlet-name>
<url-pattern>/getC</url-pattern>
</servlet-mapping>
</web-app>
2、获取初始化参数getInitParameter(“s”)
<!--配置一些web应用初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
<servlet>
<servlet-name>gP</servlet-name>
<servlet-class>com.kazesan.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gP</servlet-name>
<url-pattern>/gP</url-pattern>
</servlet-mapping>
package com.kazesan.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}
}
3、请求转发getRequestDispatcher(“s”).forward(req,resp)
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("进入了Demo04");
ServletContext context = this.getServletContext();
//RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gP");//转发的请求路径
//requestDispatcher.forward(req,resp);//调用forward实现请求转发;
context.getRequestDispatcher("/gP").forward(req,resp);
}
4、读取资源文件
Properties
- 在Java目录下新建properties
- 在resources目录下新建properties
发现都被打包到了同一个路径下–> classes,称这个路径为classpath
思路:需要一个文件流(资源流) getResourceAsStream()
package com.kazesan.servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream inputStream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties properties = new Properties();
properties.load(inputStream);
String user = properties.getProperty("username");
String pwd = properties.getProperty("password");
resp.getWriter().print(user+":"+pwd);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
6.6、HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse对象;
- 如果要获取客户端请求过来的参数:找HttpServletRequest
- 如果要给客户端响应一些信息:找HttpServletResponse
1、简单分类
负责向浏览器发送数据的方法
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
响应的状态码
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
2、常见应用
- 向浏览器输出消息
- 下载文件
- 要获取下载文件的路径
- 下载的文件名是啥?
- 设置想办法让浏览器能够支持下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端
package com.kazesan.servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
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.net.URLEncoder;
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 要获取下载文件的路径
String realPath = "D:\\JavaStudy\\1.JAVA_plan\\CODE\\JavaWeb\\javaweb-02-servlet\\response\\target\\classes\\大白很白.png";
System.out.println("下载文件的路径:"+realPath);
// 2. 下载的文件名是啥?
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
// 3. 设置想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode()编码
resp.setHeader("Content-Disposition","attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));
// 4. 获取下载文件的输入流
FileInputStream fileInputStream = new FileInputStream(realPath);
// 5. 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
// 6. 获取OutputStream对象
ServletOutputStream outputStream = resp.getOutputStream();
// 7. 将FileOutputStream写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
while ((len=fileInputStream.read(buffer))!=-1){
outputStream.write(buffer,0,len);
}
outputStream.close();
fileInputStream.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
3、验证码功能
验证怎么来的?
- 前端实现
- 后端实现,需要用到Java的图片类,生产一个图片
package com.kazesan.servlet;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器3秒自动刷新一次
resp.setHeader("refresh","3");
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
//得到图片
Graphics2D g = (Graphics2D)image.getGraphics();//画笔
//设置图片的背景颜色
g.setColor(Color.white);
g.fillRect(0,0,80,20);
//给图片写数据
g.setColor(Color.BLUE);
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),0,20);
//告诉浏览器。这个请求用图片的方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//把图片写给缓存
ImageIO.write(image,"jpg",resp.getOutputStream());
}
//生成随机数
private String makeNum(){
Random random = new Random();
String num = random.nextInt(9999999) + "";
StringBuffer sb = new StringBuffer();
for (int i = 0; i <7-num.length() ; i++) {
sb.append("0");
}
num = sb.toString() + num;
return num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
4、实现重定向
B一个web资源收到客户端请求后,B它会通知A客户端去访问另外一个web资源C,这个过程叫做重定向。
常见场景:
- 用户登录
void sendRedirect(String var1) throws IOException;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
原理
resp.setHeader("Location","/response_war/img");
resp.setStatus(302);
int SC_MOVED_TEMPORARILY = 302
*/
resp.sendRedirect("/response_war/img");
}
面试题:请你聊聊重定向和转发的区别?
相同点
- 页面都会跳转
不同点
- 请求转发的时候,url不会产生变化
- 重定向的时候,url地址栏会发生变化
6.7、HttpServletRequest
初识
package com.kazesan.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RequestTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//处理请求
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username + ":" +password);
//重定向注意路径
resp.sendRedirect("/response_war/success.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息;
1、获取参数,请求转发
req.getParameter();
req.getParameterValues();
package com.kazesan.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobbies");
System.out.println("====================================");
//后台接收中文乱码问题
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbies));
System.out.println("====================================");
//通过请求转发
//这里的/代表当前的web应用
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
面试题:请你聊聊重定向和转发的区别?
相同点
- 页面都会跳转
不同点
- 请求转发的时候,url不会产生变化 307
- 重定向的时候,url地址栏会发生变化 302
7、Cookie、Session
7.1、会话
会话:用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话。
有状态会话:一个同学来过教室,下次再来教师,我们会知道这个同学曾经来过,称之为有状态会话
一个网站,怎么证明你来过?
客户端 服务端
- 服务端给客户端一个信件(cookie),客户端下次访问服务端带上信件就可以了;cookie
- 服务器登记你来过了,下次你来的时候我匹配你;session
7.2、保存会话的两种技术
cookie
- 客户端技术(响应,请求)
session
- 服务器技术,利用这个技术,可以保存用户的会话信息,我们可以把信息或者数据放在session中!
常见场景:网站登录之后,下次再访问,不用再次登录
7.3、Cookie
- 从请求中拿到cookie信息
- 服务器响应给客户端cookie
Cookie[] cookies = req.getCookies();//获得Cookie
cookie.getName();//获得cookie中的key
cookie.getValue();//获得cookie中的value
new Cookie("lastLoginTime", System.currentTimeMillis()+"");//新建一个cookie
cookie.setMaxAge(24*60*60);//设置cookie有效期
resp.addCookie(cookie);//将cookie响应给客户端
cookie:一般会保存在本地 的用户目录下appdata
一个网站cookie是否存在上限?细节问题
- 一个Cookie只能保存一个信息
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
- Cookie大小有限制4kb
- 300个cookie浏览器上限
删除Cookie:
- 不设置有效期,关闭浏览器,自动失效;
- 设置有效期时间为0;
乱码相关
//解决乱码问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");//这条必须写,可以不写charset
仅测试新版edge
编码与解码:
URLEncoder.encode("s","enc");
URLDecoder.decode( ,"enc");
7.4、Session(重点)
什么是Session:
- 服务器会给每一个用户(浏览器)创建一个Session对象;
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在;
- 用户登录之后,整个网站它都可以访问–>保存用户的信息;保存购物车的信息。。。
Session和Cookie的区别:
- Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)
- Session把用户的数据写到用户独占Session中,服务器端保存(保存重要的信息,减少服务器资源的浪费)
- Session对象由服务器创建
使用场景:
- 保存一个登录用户的信息;
- 购物车信息;
- 在整个网站中经常会使用的数据,将它们保存到Session中
package com.kazesan.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
public class SessionDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//得到Session
HttpSession session = req.getSession();
//给Session中添加数据
session.setAttribute("name",new Person("大白很白",1));
//获得Session的ID
String sessionId = session.getId();
//判断Session是不是新创建的
if (session.isNew()){
resp.getWriter().write("session创建成功,ID:"+sessionId);
}else {
resp.getWriter().write("session已经在服务器中存在了,ID:"+sessionId);
}
//Session创建的时候做了什么事情;
// Cookie cookie = new Cookie("JSESSIONID","sessionId");
// resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
Session放对象:
package com.kazesan.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class SessionDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//得到Session
HttpSession session = req.getSession();
Person person = (Person)session.getAttribute("name");
System.out.println(person.toString());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
手动注销Session:
package com.kazesan.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class SessionDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//得到Session
HttpSession session = req.getSession();
session.removeAttribute("name");
//手动注销Session
session.invalidate();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
会话自动过期:web.xml
<!--设置Session默认的失效时间-->
<session-config>
<!--15分钟后Session自动失效,以分钟为单位-->
<session-timeout>1</session-timeout>
</session-config>
作用域相关
ServletContext即ApplicationContext
8、JSP
8.1、什么是JSP
Java Server Pages:Java服务器端页面,也和Servlet一样,用于动态Web技术!
最大的特点:
- 写JSP就像在写HTML
- 区别:
- HTML只给用户提供静态的数据
- JSP页面中可以嵌入Java代码,为用户提供动态数据;
8.2、JSP原理
思路:JSP到底怎么执行的
-
代码层面没有任何问题
-
服务器内部工作
tomcat中有一个work目录;
IDEA中使用Tomcat会在IDEA的tomcat中生产一个work目录
地址:
C:\Users\trueh\AppData\Local\JetBrains\IntelliJIdea2022.1\tomcat\0e7a8baf-ef5d-4801-9ed4-9e4f38cb5cb1\work\Catalina\localhost\request_war\org\apache\jsp
发现页面转变成了Java程序
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet!
JSP最终也会被转换成一个Java类
看JSP转换成Java后的源码,发现:
再导入jasper-runtime依赖,查看HttpJspBase源码,发现:
得出结论:
JSP本质上是Servlet
//初始化
public void _jspInit() {
}
//销毁
public void _jspDestroy() {
}
//JSPService
public void _jspService(HttpServletRequest request, HttpServletResponse response){}
-
判断请求
-
内置一些对象
final javax.servlet.jsp.PageContext pageContext;//页面上下文 javax.servlet.http.HttpSession session = null;//session final javax.servlet.ServletContext application;//applicationContex final javax.servlet.ServletConfig config;//config javax.servlet.jsp.JspWriter out = null;//out final java.lang.Object page = this;//当前页 HttpServletRequest request;//请求 HttpServletResponse response;//响应
-
输出页面前增加的代码
response.setContentType("text/html;charset=UTF-8");//设置响应的页面类型 pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out;
-
以上的这些个对象我们可以在JSP页面中直接使用!
-
在JSP页面中:
只要是Java代码就会被原封不动的输出;
如果是HTML代码,就会被转换成:
这样的格式输出到前端
8.3、JSP基础语法
任何语言都有自己的语法,Java中有,JSP作为Java技术的一种应用,它拥有一些自己扩充的语法(了解,知道即可!),Java所有语法都支持!
JSP表达式
<%--JSP表达式
作用:用于将程序的输出,输出到客户端
<%= 变量或者表达式%>
--%>
jsp脚本片段
<%--jsp脚本片段--%>
<%
int sum = 0;
for (int i = 1; i <=100 ; i++) {
sum+=i;
}
out.println("<h1>Sum="+sum+"</h1>");
%>
脚本片段的再实现
<%
int x = 10;
out.println(x);
%>
<p>这是一个JSP文档</p>
<%
int y = 2;
out.println(y);
%>
<hr>
<%--在代码中嵌入HTML元素--%>
<%
for (int i = 0; i < 5; i++) {
%>
<h1>Helllo,World <%=i%> </h1>
<%
}
%>
JSP声明
<%!
static{
System.out.println("Loading Servlet!");
}
private int globalVar = 0;
public void kazesan(){
System.out.println("进入了方法kazesan!");
}
%>
JSP声明:会被编译到JSP生成的Java类中!其他的就会被生成到_jspService()方法中
在JSP中嵌入Java代码即可!
EL表达式
${}
jsp语法总结:
<%%>
<%=%>
<%!%>
<%--注释--%>
JSP的注释,不会在客户端显示,HTML的注释会在源代码中显示
8.4、JSP指令
<%@page args...%>
<%@include file=""%>
jsp3.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--%@include会将两个页面合二为一--%>
<%@include file="common/header.jsp"%>
<h1>网页主体</h1>
<%@include file="common/footer.jsp"%>
<hr>
<%--jsp标签
jsp:include:拼接页面,本质还是三个
--%>
<jsp:include page="/common/header.jsp"/>
<h1>网页主体</h1>
<jsp:include page="/common/footer.jsp"/>
</body>
</html>
header.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1>我是Header</h1>
footer.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1>我是Footer</h1>
8.5、9大内置对象
- PageContext 存东西
- Request 存东西
- Response
- Session 存东西
- Application 【ServletContext】 存东西
- config 【ServletConfig】
- out
- page 不用
- exception
pageContext.setAttribute("name1","大白1号");//保存的数据只在一个页面中有效
request.setAttribute("name2","大白2号");//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","大白3号");//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4","大白4号");//保存的数据只在服务器中有效,从打开服务器到关闭服务器
%>
request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻
session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数据
8.6、JSP标签、JSTL标签、EL表达式
EL表达式(Express Language):${}
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api -->
<!--JSTL表达式的依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
- 获取数据
- 执行运算
- 获取web开发的常用对象
调用Java方法
JSP标签
<%--jsp:include--%>
<%--
htttp://localhost:8080/jsptag.jsp?name=kazesan&age=11
--%>
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="kazesan"></jsp:param>
<jsp:param name="age" value="11"></jsp:param>
</jsp:forward>
JSTL表达式
JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签,可以供我们使用,标签的功能和Java代码一样!
格式化标签
SQL标签
XML标签
核心标签(掌握部分)
JSTL标签库使用步骤
-
引入对应的taglib
-
使用其中的方法
-
在Tomcat中也需要引入jstl.jar包到lib目录下,否则会报错,JSTL解析报错
这里注意,再次检查Maven依赖,两个包,特别注意groupId,如下:
<!--JSTL表达式的依赖--> <!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl --> <!--标签库--> <dependency> <groupId>org.apache.taglibs</groupId> <artifactId>taglibs-standard-impl</artifactId> <version>1.2.5</version> <scope>runtime</scope> </dependency>
使用其中的方法
<c:if>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%--引入JSTL核心标签库,我们才能使用JSTL标签 core+--%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Title</title> </head> <body> <h4>if测试</h4> <hr> <form action="coreif.jsp" method="get"> <%-- EL表达式获取表单中的数据 ${param.参数名} --%> <input type="text" name="username" value="${param.username}"> <input type="submit" value="登录"> </form> <%--判断如果提交的用户名是管理员,则登陆成功--%> <%--<% if (request.getParameter("username").equals("admin")){ out.println("登陆成功"); } %>--%> <c:if test="${param.username=='admin'}" var="isAdmin"> <c:out value="管理员欢迎您!"/> </c:if> <%--自闭合标签--%> <c:out value="${isAdmin}"/> </body> </html>
<c:when>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Title</title> </head> <body> <%--定义一个变量score,值为85--%> <c:set var="score" value="55"/> <c:choose> <c:when test="${score>=90}"> 你的成绩为优秀 </c:when> <c:when test="${score>=80}"> 你的成绩为良好 </c:when> <c:when test="${score>=70}"> 你的成绩为一般 </c:when> <c:when test="${score<=60}"> 你的成绩为不合格 </c:when> </c:choose> </body> </html>
<c:forEach>
<%@ page import="java.util.ArrayList" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>Title</title> </head> <body> <% ArrayList<String> people = new ArrayList<>(); people.add(0,"张三"); people.add(1,"李四"); people.add(2,"王五"); people.add(3,"老六"); people.add(4,"田七"); request.setAttribute("list",people); %> <%-- var ,每一次遍历出来的变量 items ,要遍历的对象 behin,哪里开始 end,到哪里 step 步长 --%> <c:forEach var="people" items="${list}"> <c:out value="${people}"/> <br> </c:forEach> <hr> <%--step="2" 步长为2,从1开始,走两步到3 --%> <c:forEach var="people" items="${list}" begin="1" end="3" step="2"> <c:out value="${people}"/> <br> </c:forEach> </body> </html>
9、JavaBean
实体类
javaBean有特定的写法:
- 必须要有一个无参构造
- 属性必须私有化
- 必须有对应的get/set方法;
一般用来和数据库的字段做映射 ORM
ORM(Object-Relational Mapping):对象关系映射
- 表—>类
- 字段–>属性
- 行记录–>对象
people表
id | name | age | address |
---|---|---|---|
1 | 大白1号 | 3 | 地球 |
2 | 大白2号 | 18 | 地球 |
3 | 大白3号 | 10 | 地球 |
class People{
private int id;
private String name;
private int age;
private String address;
}
class A{
new People(1,"大白1号",3,"地球");
new People(2,"大白2号",3,"地球");
new People(3,"大白3号",3,"地球");
}
10、MVC三层架构
什么是MVC:Model View Controller 模型、视图、控制器
10.1、早些年
没有第三层
用户直接访问控制层,控制层就可以直接操作数据库;
servlet--CRUD-->数据库
弊端:程序十分臃肿,不利于维护 servlet的代码中:处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码
架构:没有什么是加一层解决不了的!
程序员调用
|
JDBC
|
Mysql Oracle sqlServer...
10.2、MVC三层架构
Model
- 业务处理:业务逻辑(Service)
- 数据持久层:CRUD (Dao)
View
- 展示数据
- 提供链接发起Servlet请求(a,form,img。。。)
Controller(Servlet)
-
接受用户的请求:(req:请求参数、Session信息。。。)
-
交给业务层处理对应的代码
-
控制视图的跳转
登录--->接收用户的登录请求--->处理用户的请求(获取用户登录的参数,username,password)--->交给业务层处理登录业务(判断用户名密码是否正确 事务)--->Dao层查询用户名和密码是否正确--->数据库
11、Filter(重点)
Filter:过滤器,用来过滤网站的数据
- 处理中文乱码
- 登录验证…
Filter开发步骤:
- 导包
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!--JSTL表达式的依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--标签库-->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
</dependency>
<!--连接数据库-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
</dependencies>
-
编写过滤器
- Filter导包不要错,注意是 javax.servlet下的Filter
- 实现Filter接口,重写对应的方法即可
package com.kazesan.filter; import javax.servlet.*; import java.io.IOException; public class CharacterEncodingFilter implements Filter { //初始化:web服务器启动,就立即初始化了,随时等待过滤对象出现! @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("CharacterEncodingFilter初始化"); } //filterChain:过滤链 /* 1.过滤器中的所有代码,在过滤特定请求的时候都会执行 2.必须让过滤器继续通行 filterChain.doFilter(servletRequest,servletResponse); */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setCharacterEncoding("utf-8"); servletResponse.setCharacterEncoding("utf-8"); servletResponse.setContentType("text/html;charset=UTF-8"); System.out.println("CharacterEncodingFilter执行前..."); filterChain.doFilter(servletRequest,servletResponse);//放行,让我们的请求继续走,如果不写,程序到这里就被拦截停止 System.out.println("CharacterEncodingFilter执行后..."); } //销毁:web服务器关闭的时候,过滤器会销毁 @Override public void destroy() { System.out.println("CharacterEncodingFilter销毁"); } }
-
在web.xml中配置Filter
<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>com.kazesan.filter.CharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <!--只要是/servlet下的任何请求,都会经过这个过滤器--> <url-pattern>/servlet/*</url-pattern> <!--<url-pattern>/*</url-pattern>--> </filter-mapping>
12、监听器
实现一个监听器的接口:(有N种)
-
编写一个监听器
实现监听器的接口
package com.kazesan.listener; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; //统计网站在线人数:统计session public class OnlineCountListener implements HttpSessionListener { //创建session监听:看你的一举一动 //一旦创建Session就会触发一次这个事件 @Override public void sessionCreated(HttpSessionEvent se) { ServletContext ctx = se.getSession().getServletContext(); Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount"); if (onlineCount==null){ onlineCount = new Integer(1); }else { int count = onlineCount.intValue(); onlineCount = new Integer(count+1); } ctx.setAttribute("OnlineCount",onlineCount); } //销毁session监听 @Override public void sessionDestroyed(HttpSessionEvent se) { ServletContext ctx = se.getSession().getServletContext(); System.out.println(se.getSession().getId()); Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount"); if (onlineCount==null){ onlineCount = new Integer(0); }else { int count = onlineCount.intValue(); onlineCount = new Integer(count-1); } ctx.setAttribute("OnlineCount",onlineCount); } /* Session销毁 1. 手动销毁 getSession().invalidate(); 2. 自动销毁,在web.xml中配置session-config session-timeout */ }
-
web.xml中注册监听器
<!--注册监听器--> <listener> <listener-class>com.kazesan.listener.OnlineCountListener</listener-class> </listener>
-
看情况是否使用
13、过滤器、监听器常见应用
详见GUI
用户登录之后才能进入主页!用户注销后就不能进入主页了!
-
用户登录之后,向Session中放入用户的数据
-
进入主页的时候要判断用户是否已经登录,要求:在过滤器中实现!
package com.kazesan.filter; import com.kazesan.util.Constant; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class SysFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //ServletRequest HttpServletRequest HttpServletRequest request = (HttpServletRequest) servletRequest; //ServletResponse HttpServletResponse HttpServletResponse response = (HttpServletResponse) servletResponse; Object user_session = request.getSession().getAttribute(Constant.USER_SESSION); if (user_session==null){ response.sendRedirect("/error.jsp"); } filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { } }
14、JDBC复习
什么是JDBC:Java连接数据库!
没有什么是加一层解决不了的。
需要jar包的支持:
- java.sql
- javax.sql
- mysql-connector-java…连接驱动(必须要导入)
实验环境搭建
见《数据库——MySQL》-“JdbcFirstDemo.java”
- 准备一个数据库
- 导入数据库依赖(jdbc-Driver),IDEA会自动适配驱动(前提建的是maven项目)
- IDEA中连接数据库
JDBC固定步骤:
- 加载驱动
- 连接数据库,代表数据库
- 向数据库发送SQL的对象Statement:CRUD
- 编写SQL(根据业务,不同的SQL)
- 执行SQL,获得返回的结果集
- 释放连接
一般使用PreparedStatement对象
祥见《数据库——MySQL》-10.5 PreparedStatement对象
事务
详见《数据库——MySQL》-6 事务(ACID)和10.7 事务
拓展:
Junit单元测试(就不用每次都另写main了)
依赖
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--scope标签
在测试类时需删除作用域标签
-->
简单使用
@Test注解只在方法上有效,只要加了这个注解的方法,就可以直接运行
package com.kazesan.test;
import org.junit.Test;
public class TestJDBC3 {
@Test
public void test(){
System.out.println("hello");
}
}
成功为绿色,如下:
失败为红色,如下:
15、SMBMS(SupermarketBillManageSystem)
数据库:
项目如何搭建?
考虑是否使用maven?maven需要依赖,jar
15.1、项目搭建准备工作
- 搭建maven项目,两种方式(模板or add Framwork)
- 配置Tomcat
- 测试项目是否能运行
- 导入项目中会遇到的jar
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl -->
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>
-
创建项目包结构
还可能有listener之类的,依据情况创建
-
编写实体类
ORM映射:表–类映射
-
编写基础公共类
-
数据库配置文件
-
编写数据库的公共类
BaseDao.java
package com.kazesan.dao; import java.io.IOException; import java.io.InputStream; import java.sql.*; import java.util.Properties; //操作数据库的公共类 public class BaseDao { private static String driver; private static String url; private static String username; private static String password; //静态代码块,类加载的时候就是初始化了 static { Properties properties = new Properties(); //通过类加载器读取相应的资源 InputStream is = BaseDao.class.getClassLoader().getResourceAsStream("db.properties"); try { properties.load(is); } catch (IOException e) { throw new RuntimeException(e); } driver = properties.getProperty("driver"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); } //获取数据库的连接 public static Connection getConnection(){ Connection connection = null; try { Class.forName(driver); connection = DriverManager.getConnection(url, username, password); } catch (Exception e) { throw new RuntimeException(e); } return connection; } //编写查询公共类 public static ResultSet execute(Connection connection,PreparedStatement preparedStatement,ResultSet resultSet,String sql,Object[] params) throws SQLException { //prepareStatement预编译的sql,在后面直接执行就行了 preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < params.length ; i++) { //setObject,占位符从1开始,但是我们的数组是从0开始! preparedStatement.setObject(i+1,params[i]); } resultSet = preparedStatement.executeQuery(); return resultSet; } //编写增删改公共方法 public static int executeUpdate(Connection connection,PreparedStatement preparedStatement,String sql,Object[] params) throws SQLException { preparedStatement = connection.prepareStatement(sql); for (int i = 0; i < params.length ; i++) { //setObject,占位符从1开始,但是我们的数组是从0开始! preparedStatement.setObject(i+1,params[i]); } int updateRows = preparedStatement.executeUpdate(); return updateRows; } //释放资源 public static boolean closeResource(Connection connection,PreparedStatement preparedStatement,ResultSet resultSet){ boolean flag = true; if (resultSet!=null){ try { resultSet.close(); //GC回收 resultSet = null; } catch (SQLException e) { e.printStackTrace(); flag = false; } } if (preparedStatement!=null){ try { preparedStatement.close(); //GC回收 preparedStatement = null; } catch (SQLException e) { e.printStackTrace(); flag = false; } } if (connection!=null){ try { connection.close(); //GC回收 connection = null; } catch (SQLException e) { e.printStackTrace(); flag = false; } } return flag; } }
-
编写字符编码过滤器
CharacterEncodingFilter.java
package com.kazesan.filter; import javax.servlet.*; import java.io.IOException; public class CharacterEncodingFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { servletRequest.setCharacterEncoding("utf-8"); servletResponse.setCharacterEncoding("utf-8"); filterChain.doFilter(servletRequest,servletResponse); } public void destroy() { } }
-
-
导入静态资源
15.2、登录功能实现
-
编写前端页面
-
设置首页
<welcome-file-list> <welcome-file>login.jsp</welcome-file> </welcome-file-list>
-
编写dao层,得到用户登录的接口
UserDao.java
package com.kazesan.dao.user; import com.kazesan.pojo.User; import java.sql.Connection; import java.sql.SQLException; public interface UserDao { //得到要登录的用户 public User getLoginUser(Connection connection,String userCode) throws SQLException; }
-
编写dao接口的实现类
package com.kazesan.dao.user; import com.kazesan.dao.BaseDao; import com.kazesan.pojo.User; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class UserDaoImp implements UserDao{ //得到要登录的用户 public User getLoginUser(Connection connection, String userCode) throws SQLException { PreparedStatement preparedStatement = null; ResultSet resultSet = null; User user = null; if (connection!=null){ String sql = "select * from smbms_user where userCode=?"; Object[] params = {userCode}; resultSet = BaseDao.execute(connection, preparedStatement, resultSet, sql, params); if (resultSet.next()){ user = new User(); user.setId(resultSet.getInt("id")); user.setUserCode(resultSet.getString("userCode")); user.setUserName(resultSet.getString("userName")); user.setUserPassword(resultSet.getString("userPassword")); user.setGender(resultSet.getInt("gender")); user.setBirthday(resultSet.getDate("birthday")); user.setPhone(resultSet.getString("phone")); user.setAddress(resultSet.getString("address")); user.setUserRole(resultSet.getInt("userRole")); user.setCreatedBy(resultSet.getInt("createBy")); user.setCreationDate(resultSet.getTimestamp("creationDate")); user.setModifyBy(resultSet.getInt("modifyBy")); user.setModifyDate(resultSet.getTimestamp("modifyDate")); } BaseDao.closeResource(null,preparedStatement,resultSet); } return user; } }
-
业务层接口
UserService.java
package com.kazesan.service; import com.kazesan.pojo.User; public interface UserService { //用户登录 public User login(String userCode,String password); }
-
业务层实现类
UserServiceImpl.java
package com.kazesan.service; import com.kazesan.dao.BaseDao; import com.kazesan.dao.user.UserDao; import com.kazesan.dao.user.UserDaoImpl; import com.kazesan.pojo.User; import org.junit.Test; import java.sql.Connection; import java.sql.SQLException; public class UserServiceImpl implements UserService{ //业务层都会调用dao层,所以我们要引入Dao层; private UserDao userDao; public UserServiceImpl(){ userDao = new UserDaoImpl(); } public User login(String userCode, String password) { Connection connection = null; User user = null; try { connection = BaseDao.getConnection(); //通过业务层调用对应的具体的数据库操作 user = userDao.getLoginUser(connection, userCode); } catch (SQLException e) { throw new RuntimeException(e); }finally{ BaseDao.closeResource(connection,null,null); } return user; } @Test public void test(){ UserServiceImpl userService = new UserServiceImpl(); User admin = userService.login("admin", "1234567"); System.out.println(admin.getUserPassword()); } }
-
编写Servlet
LoginServlet.java
package com.kazesan.servlet.user; import com.kazesan.pojo.User; import com.kazesan.service.UserService; import com.kazesan.service.UserServiceImpl; import com.kazesan.util.Constants; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class LoginServlet extends HttpServlet { //Servlet:控制层,调用业务层代码 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("LoginServlet--start..."); //获取用户名和密码 String userCode = req.getParameter("userCode"); String userPassword = req.getParameter("userPassword"); //和数据库中的密码进行对比,调用业务层 UserService userService = new UserServiceImpl(); User user = userService.login(userCode, userPassword);//这里已经把登录的人给查出来了 if (user!=null&& userCode.equals(user.getUserCode())&& userPassword.equals(user.getUserPassword())){//查有此人,可以登录,登录id,密码验证,和数据库中数据对比,match成功才能登陆 //将用户的信息放到Session中; req.getSession().setAttribute(Constants.USER_SESSION,user); //跳转到主页 resp.sendRedirect("jsp/frame.jsp"); }else {//查无此人,无法登录 //转发回登录页面,顺带提示他,用户名或者密码错误 req.setAttribute("error","用户名或者密码不正确"); req.getRequestDispatcher("login.jsp").forward(req,resp); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
-
注册Servlet
<!--Servlet--> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.kazesan.servlet.user.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/login.do</url-pattern>
-
测试访问,确保以上功能成功
15.3、登录功能优化
注销功能:
思路:移除Session,返回登录页面
LogoutServlet.java
package com.kazesan.servlet.user;
import com.kazesan.util.Constants;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.getSession().removeAttribute(Constants.USER_SESSION);
resp.sendRedirect(req.getContextPath()+"/login.jsp");//返回登录页面
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
注册xml
<!--注销-->
<servlet>
<servlet-name>LogoutServlet</servlet-name>
<servlet-class>com.kazesan.servlet.user.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogoutServlet</servlet-name>
<url-pattern>/jsp/logout.do</url-pattern>
15.3.1、登录拦截优化
编写一个过滤器并注册
SysFilter.java
package com.kazesan.filter;
import com.kazesan.pojo.User;
import com.kazesan.util.Constants;
import javax.servlet.*;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SysFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//过滤器,从Session中获取用户
User user = (User) request.getSession().getAttribute(Constants.USER_SESSION);
if (user==null){//已经被移除或注销,或者未登录
response.sendRedirect("/error.jsp");
}else {
filterChain.doFilter(servletRequest,servletResponse);
}
}
public void destroy() {
}
}
注册xml
<!--用户登录过滤器-->
<filter>
<filter-name>SysFilter</filter-name>
<filter-class>com.kazesan.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SysFilter</filter-name>
<url-pattern>/jsp/*</url-pattern>
测试,登录,注销,权限,都OK
15.4、密码修改
-
导入前端素材
<li><a href="${pageContext.request.contextPath }/jsp/pwdmodify.jsp">密码修改</a></li>
-
写项目,建议从底层向上
-
UserDao接口
//修改当前用户密码 public int updatePwd(Connection connection,int id,int passsword) throws SQLException;
-
UserDao接口实现类
//修改当前用户密码 public int updatePwd(Connection connection, int id, int passsword) throws SQLException { PreparedStatement preparedStatement = null; int executeUpdate = 0; if (connection!=null){ String sql = "update smbms_user set userPassword = ? where id = ?"; Object params[] = {passsword,id}; //Object[] params = {passsword,id}; executeUpdate = BaseDao.executeUpdate(connection, preparedStatement, sql, params); BaseDao.closeResource(null,preparedStatement,null); } return executeUpdate; }
-
UserService层接口
//根据用户ID修改密码 public boolean updatePwd(int id, int passsword);
-
UserService实现类
public boolean updatePwd(int id, int passsword) { Connection connection = null; boolean flag = false; //修改密码 try { connection = BaseDao.getConnection(); if (userDao.updatePwd(connection,id,passsword)>0){ flag = true; } } catch (SQLException e) { throw new RuntimeException(e); }finally { BaseDao.closeResource(connection,null,null); } return flag; }
-
Servlet实现复用,需提取出方法,即封装
UserServlet.java
package com.kazesan.servlet.user; import com.kazesan.pojo.User; import com.kazesan.service.UserService; import com.kazesan.service.UserServiceImpl; import com.kazesan.util.Constants; import com.mysql.jdbc.StringUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; //实现Servlet复用 public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method"); if (method.equals("savepwd")&&method!=null){ this.updatePwd(req,resp); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } public void updatePwd(HttpServletRequest req, HttpServletResponse resp){ //从Session里面拿ID Object o = req.getSession().getAttribute(Constants.USER_SESSION); String newpassword = req.getParameter("newpassword"); System.out.println("UserServlet:"+newpassword); boolean flag = false; //这里调试可以输出值看是否进入了方法 System.out.println(o!=null); System.out.println(StringUtils.isNullOrEmpty(newpassword)); //建议不使用工具类,使用newpassword!==null && newpassword.length()!=0 if (o!=null && !StringUtils.isNullOrEmpty(newpassword)){//此方法源码中参数值为null或0返回为true,这里不为空返回为false,需取反 UserService userService = new UserServiceImpl(); flag = userService.updatePwd(((User) o).getId(), newpassword); if (flag){ req.setAttribute("message","修改密码成功,请退出,使用新密码登录"); //密码修改成功,移除当前Session req.getSession().removeAttribute(Constants.USER_SESSION); }else {//修改失败 req.setAttribute("message","密码修改失败"); } }else { req.setAttribute("message","新密码有问题"); } try { req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp); } catch (ServletException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } }
注册xml
<servlet> <servlet-name>UserServlet</servlet-name> <servlet-class>com.kazesan.servlet.user.UserServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>UserServlet</servlet-name> <url-pattern>/jsp/user.do</url-pattern> </servlet-mapping>
-
测试
15.4.1、优化密码修改使用Ajax
-
阿里巴巴的fastjson
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.12</version> </dependency>
-
后台代码修改
UserServlet.java
package com.kazesan.servlet.user; import com.alibaba.fastjson.JSONArray; import com.kazesan.pojo.User; import com.kazesan.service.UserService; import com.kazesan.service.UserServiceImpl; import com.kazesan.util.Constants; import com.mysql.jdbc.StringUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; //实现Servlet复用 public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method"); if (method.equals("savepwd")&&method!=null){ this.updatePwd(req,resp); }else if (method.equals("pwdmodify")&&method!=null){ this.pwdModify(req,resp); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } //修改密码 public void updatePwd(HttpServletRequest req, HttpServletResponse resp){ //从Session里面拿ID Object o = req.getSession().getAttribute(Constants.USER_SESSION); String newpassword = req.getParameter("newpassword"); System.out.println("UserServlet:"+newpassword); boolean flag = false; //这里调试可以输出值看是否进入了方法 System.out.println(o!=null); System.out.println(StringUtils.isNullOrEmpty(newpassword)); //建议不使用工具类,使用newpassword!==null && newpassword.length()!=0 if (o!=null && !StringUtils.isNullOrEmpty(newpassword)){//此方法源码中参数值为null或0返回为true,这里不为空返回为false,需取反 UserService userService = new UserServiceImpl(); flag = userService.updatePwd(((User) o).getId(), newpassword); if (flag){ req.setAttribute("message","修改密码成功,请退出,使用新密码登录"); //密码修改成功,移除当前Session req.getSession().removeAttribute(Constants.USER_SESSION); }else {//修改失败 req.setAttribute("message","密码修改失败"); } }else { req.setAttribute("message","新密码有问题"); } try { req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp); } catch (ServletException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } //验证旧密码,session中有用户的旧密码 public void pwdModify(HttpServletRequest req, HttpServletResponse resp){ //从Session里面拿ID Object o = req.getSession().getAttribute(Constants.USER_SESSION); String oldpassword = req.getParameter("oldpassword"); //万能的Map : 结果集 Map<String,String> resultNap = new HashMap<String,String>(); if (o==null){//Session失效了,Session过期了 resultNap.put("result","sessionerror"); } else if (StringUtils.isNullOrEmpty(oldpassword)) {//输入的密码为空 resultNap.put("result","error"); }else { String userPassword = ((User) o).getUserPassword();//Session中用户的密码 if (oldpassword.equals("userPassword")){ resultNap.put("result","true"); }else {//密码不为空,但是输错了 resultNap.put("result","false"); } } try { resp.setContentType("application/json"); PrintWriter writer = resp.getWriter(); //JSONArray 阿里巴巴的工具类,作用:转换格式 /* resultNap = ["result","sessionerror","result","error"] Json格式 = {key:value} */ writer.write(JSONArray.toJSONString(resultNap)); writer.flush(); writer.close(); } catch (IOException e) { throw new RuntimeException(e); } } }
-
测试
15.5、用户管理实现
思路:
-
导入分页的工具类
-
用户列表页面导入
userlist.jsp
15.5.1、获取用户数量
-
UseDao
//根据用户名或者角色查询用户总数 public int getUserCount(Connection connection,String username,int userRole) throws SQLException;
-
UserDaoImpl(最难理解的SQL)
//根据用户名或者角色查询用户总数 public int getUserCount(Connection connection, String username, int userRole) throws SQLException { PreparedStatement preparedStatement = null; ResultSet resultSet = null; int count = 0; if (connection!=null){ StringBuffer sql =new StringBuffer(); sql.append("select count(1) as count from smbms_user u,smbms_role r where u.userRole = r.id"); ArrayList<Object> list = new ArrayList<Object>(); if (!StringUtils.isNullOrEmpty(username)){ sql.append(" and u.userName like ?"); list.add("%"+username+"%");//index:0 } if (userRole>0){ sql.append(" and u.userRole = ?"); list.add(userRole);//index:1 } //怎么把list转换为数组 Object[] params = list.toArray(); System.out.println("UserDaoImpl->getUserCount:"+sql.toString());//输出最后完整的sql语句 resultSet = BaseDao.execute(connection,preparedStatement,resultSet,sql.toString(),params); if (resultSet.next()){ count = resultSet.getInt("count");//从结果集中获取最终的数量 } BaseDao.closeResource(null,preparedStatement,resultSet); } return count; }
-
UserService
//查询记录数 public int getUserCount(String username,int userRole);
-
UserServiceImpl
//查询记录数 public int getUserCount(String username, int userRole) { Connection connection = null; int count = 0; try { connection = BaseDao.getConnection(); count = userDao.getUserCount(connection, username, userRole); } catch (SQLException e) { throw new RuntimeException(e); }finally { BaseDao.closeResource(connection,null,null); } return count; }
15.5.2、获取用户列表
-
userDao
//通过条件查询-userList public List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo,int pageSize) throws SQLException;
-
userDaoImpl
//通过条件查询-userList public List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize) throws SQLException { PreparedStatement preparedStatement = null; ResultSet resultSet = null; List<User>userList = new ArrayList<User>(); if (connection!=null){ StringBuffer sql = new StringBuffer(); sql.append("select u.*,r.roleName as userRoleName from smbms_user u,smbms_role r where u.userRole = r.id"); List<Object> list = new ArrayList<Object>(); if (!StringUtils.isNullOrEmpty(userName)){ sql.append(" and u.username like ?"); list.add("%"+userName+"%"); } if (userRole>0){ sql.append(" and u.userRole = ?"); list.add(userRole); } //在数据库中,分页使用limit startIndex , pageSize 总数 //第一页 0,5 index->0 01234 //第二页 5,5 index->5 56789 //第三页 10,5 index->10 10 11 12 13 14 sql.append(" order by creationDate DESC limit ?,?"); currentPageNo = (currentPageNo-1)*pageSize;//开始的索引=(当前的页码-1)*每页显示的条数 list.add(currentPageNo); list.add(pageSize); Object[] params = list.toArray(); System.out.println("sql----->" + sql.toString()); resultSet = BaseDao.execute(connection, preparedStatement, resultSet, sql.toString(), params); while (resultSet.next()){ User _user = new User(); _user.setId(resultSet.getInt("id")); _user.setUserCode(resultSet.getString("userCode")); _user.setUserName(resultSet.getString("userName")); _user.setGender(resultSet.getInt("gender")); _user.setBirthday(resultSet.getDate("birthday")); _user.setPhone(resultSet.getString("phone")); _user.setUserRole(resultSet.getInt("userRole")); _user.setUserRoleName(resultSet.getString("userRoleName")); userList.add(_user); } BaseDao.closeResource(null,preparedStatement,resultSet); } return userList; }
-
UserService
//根据条件查询用户列表 public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize);
-
UserServiceImpl
//根据条件查询用户列表 public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize) { Connection connection = null; List<User> userList = null; System.out.println("queryUserName --->" + queryUserName); System.out.println("queryUserRole --->" + queryUserRole); System.out.println("currentPageNo --->" + currentPageNo); System.out.println("pageSize --->" + pageSize); connection = BaseDao.getConnection(); try { userList = userDao.getUserList(connection, queryUserName, queryUserRole, currentPageNo, pageSize); } catch (Exception e) { throw new RuntimeException(e); }finally { BaseDao.closeResource(connection,null,null); } return userList; }
15.5.3、获取角色操作
为了我们职责统一,可以把角色的操作单独放在一个包中,和POJO类对应
Dao层
RoleDao.java
package com.kazesan.dao.role;
import com.kazesan.pojo.Role;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public interface RoleDao {
//获取角色列表-RoleList
public List<Role> getRoleList(Connection connection) throws SQLException;
}
RoleDaoImpl.java
package com.kazesan.dao.role;
import com.kazesan.dao.BaseDao;
import com.kazesan.pojo.Role;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class RoleDaoImpl implements RoleDao{
//获取角色列表-RoleList
public List<Role> getRoleList(Connection connection) throws SQLException {
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
ArrayList<Role> roleList = new ArrayList<Role>();
if (connection!=null){
String sql = "select * from smbms_role";
Object[] params = {};
resultSet = BaseDao.execute(connection, preparedStatement, resultSet, sql, params);
while (resultSet.next()){
Role _role = new Role();
_role.setId(resultSet.getInt("id"));
_role.setRoleCode(resultSet.getString("roleCode"));
_role.setRoleName(resultSet.getString("roleName"));
roleList.add(_role);
}
BaseDao.closeResource(null,preparedStatement,resultSet);
}
return roleList;
}
}
Service层
RoleService.java
package com.kazesan.service.role;
import com.kazesan.pojo.Role;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public interface RoleService {
//获取角色列表-RoleList
public List<Role> getRoleList();
}
RoleServiceImpl.java
package com.kazesan.service.role;
import com.kazesan.dao.BaseDao;
import com.kazesan.dao.role.RoleDao;
import com.kazesan.dao.role.RoleDaoImpl;
import com.kazesan.pojo.Role;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
public class RoleServiceImpl implements RoleService{
//引入Dao
private RoleDao roleDao;
public RoleServiceImpl(){
roleDao = new RoleDaoImpl();
}
public List<Role> getRoleList() {
Connection connection = null;
List<Role> roleList = null;
try {
connection = BaseDao.getConnection();
roleList = roleDao.getRoleList(connection);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
BaseDao.closeResource(connection,null,null);
}
return roleList;
}
}
15.5.4、用户显示的Servlet
-
获取用户前端的数据(查询)
-
判断请求是否需要执行,看参数的值判断
-
为了实现分页,需要计算出当前页面和总页面,页面大小…
-
用户列表展示
-
返回前端
UserServlet
//query 重点,难点
public void query(HttpServletRequest req, HttpServletResponse resp){
//查询用户列表
//从前端获取数据
String queryUserName = req.getParameter("queryname");
String temp = req.getParameter("queryUserRole");//用户角色,前端有一个默认值
String pageIndex = req.getParameter("pageIndex");
int queryUserRole = 0;
//获取用户列表
UserServiceImpl userService = new UserServiceImpl();
List<User> userList = null;
//第一次走这个请求,一定是第一页,页面大小固定的
int pageSize = 5; //可以把这个写到配置文件中,方便后期修改;
int currentPageNo = 1;
if (queryUserName == null){
queryUserName = "";
}
if (temp!=null && !temp.equals("")){
queryUserRole = Integer.parseInt(temp);//给查询赋值 0 1 2 3
}
if (pageIndex!=null){
currentPageNo = Integer.parseInt(pageIndex);
}
//获取用户的总数(分页:上一页,下一页的情况)
int totalCount = userService.getUserCount(queryUserName, queryUserRole);
//总页数支持
PageSupport pageSupport = new PageSupport();
pageSupport.setCurrentPageNo(currentPageNo);
pageSupport.setPageSize(pageSize);
pageSupport.setTotalCount(totalCount);
int totalPageCount = pageSupport.getTotalPageCount();//总共页数,总数/ye'mian'd
//控制首页和尾页
//如果页面小于1,就强制显示第一页
if (currentPageNo<1){
currentPageNo = 1;
} else if (currentPageNo>totalPageCount) {//当前页面大于了最后一页;
currentPageNo = totalPageCount;
}
//获取用户列表展示
userList = userService.getUserList(queryUserName, queryUserRole, currentPageNo, pageSize);
req.setAttribute("userList",userList);
//获取角色列表
RoleServiceImpl roleService = new RoleServiceImpl();
List<Role> roleList = roleService.getRoleList();
req.setAttribute("roleList",roleList);
req.setAttribute("totalCount",totalCount);
req.setAttribute("currentPageNo",currentPageNo);
req.setAttribute("totalPageCount",totalPageCount);
req.setAttribute("queryUserName",queryUserName);
req.setAttribute("queryUserRole",queryUserRole);
//返回前端
try {
req.getRequestDispatcher("userlist.jsp").forward(req,resp);
} catch (ServletException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
小黄鸭调试法:自言自语
15.5.5、增删改用户
16、文件上传
1.准备工作
对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的。一般选择采用apache的开源工具common-fileupload这个文件上传组件。common-fileupload是依赖于common-io这个包的,所以还需要下载这个包。
下载最新的jar包
-
common-io Maven Repository: commons-io » commons-io (mvnrepository.com)
-
common-fileupload Maven Repository: commons-fileupload » commons-fileupload (mvnrepository.com)
2.使用类介绍
【文件上传的注意事项】–调优
- 为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
- 为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名 (方法:时间戳、uuid、md5、位运算算法)
- 要限制上传文件的最大值
- 可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
【需要用到的类详解】
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象, 在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象。所以,我们需要在进行解析工作前构造好DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性。
FileItem类
在HTML页面input必须有name
表单如果包含一个文件上传输入项的话,这个表单的enctype属性就必须设置为multipart/form-data
<%--
get:上传文件大小有限制
post:上传文件大小没有限制
--%>
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
上传用户:<input type="text" name="username"><br/>
上传文件1:<input type="file" name="file1"><br/>
上传文件2:<input type="file" name="file2"><br/>
<input type="sumbit" value="提交">
</form>
浏览器表单的类型如果为multipart/form-data,在服务端想获取数据就要通过流。
【常用方法介绍】
//isFormField方法用于判断FileItem类对象封装的数据是一个普通文本表单
//还是一个文件表单,如果是普通表单字段则返回true,否则返回false
boolean isFormField();
//getFieldName方法用于返回表单标签name属性的值。
String getFieldName();
//getString方法用于将FileItem对象中保存的数据流内容以一个字符串返回
String getString();
//getName方法用于获得文件上传字段中的文件名。
String getName();
//以流的形式返回上传文件的数据内容。
InputStream getInputStream();
//delete方法用来清空FileItem类对象中存放的主体内容
//如果主体内容被保存在临时文件中,delete方法将删除该临时文件。
void delete();`
ServletFileUpload类
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象中. 使用
其parseRequest(HttpServletRequest)方法可以将通过表单中每一个HTML 标签提交的数据封装成一
个Fileltem对象,然后以List列表的形式返回。使用该方法处理上传文件简单易用。
3.编写代码
注意:
- 在doPost()中书写代码,对应method=“post”
- 本地的Tomcat的lib目录也需要放common-io、common-fileupload两个jar包
pom.xml
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api -->
<!--JSTL表达式的依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
FileServlet.java
package com.kazesan.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
public class FileServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//判断上传的文件是普通表单还是带文件的表单
if (!ServletFileUpload.isMultipartContent(req)){
return;//终止方法运行,说明这是一个普通的表单,直接返回
}
//创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);//创建一个文件对象
if (!uploadFile.exists()){//如果上传文件不存在
uploadFile.mkdir();//创建这个目录
}
//缓存,临时文件
//临时路径,假如文件超过了预期的大小,我们就把它放到一个临时文件中,过几天自动删除,或者提醒用户转存为永久
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File file = new File(tmpPath);
if (!file.exists()){
file.mkdir();//创建这个临时目录
}
//处理上传的文件,一般都需要通过流来获取,我们可以使用req.getInputStream(),原生态的文件上传流获取,十分麻烦
//但是我们都建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于commons-io组件:
/*
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象,
在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象。
所以,我们需要在进行解析工作前构造好DiskFileItemFactory对象,
通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性。
*/
try {
//1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制的:
DiskFileItemFactory factory =getDiskFileItemFactory(file);
//2.获取ServletFileUpload
ServletFileUpload upload =getServletFileUpload(factory);
//3.处理上传的文件
String msg = uploadParseRequest(upload,req,uploadPath);
//Servlet请求转发消息
req.setAttribute("msg",msg);
req.getRequestDispatcher("info.jsp").forward(req,resp);
} catch (FileUploadException e) {
throw new RuntimeException(e);
}
}
//1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制的:
public static DiskFileItemFactory getDiskFileItemFactory(File tmpFile){
DiskFileItemFactory factory = new DiskFileItemFactory();//下面可不写,有默认值
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中
factory.setSizeThreshold(1024*1024);//缓存区大小为1M
factory.setRepository(tmpFile);//临时目录的保存目录,需要一个File
return factory;
}
//2.获取ServletFileUpload
public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){
ServletFileUpload upload = new ServletFileUpload(factory);
//监听文件上传进度:
upload.setProgressListener(new ProgressListener() {
@Override
//pBytesRead:已经读取到文件大小
//pContentLength:文件大小
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("总大小:"+pContentLength+"已上传:"+pBytesRead);
}
});
//处理乱码问题
upload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值
upload.setFileSizeMax(1024*1024*10);
//设置总共能够上传文件的大小
//1024=1kb*1024=1M*10=10M
upload.setFileSizeMax(1024*1024*10);
return upload;
}
//3.处理上传的文件
public static String uploadParseRequest(ServletFileUpload upload,HttpServletRequest req,String uploadPath)
throws FileUploadException,IOException{
String msg = "";
//把前端请求解析,封装成一个fileItem对象,需要从ServletFileLoad对象中获取
List<FileItem> fileItems = upload.parseRequest(req);
//fileItem 每一个表单对象
for (FileItem fileItem : fileItems) {
//判断上传的文件是普通的表单还是带文件的表单
if (fileItem.isFormField()){
//getFileName指的是前端表单控件的name
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");//处理乱码
System.out.println(name+":"+value);
}else {//文件表单情况下
//========================处理文件=====================//
//拿到文件名字
String uploadFileName = fileItem.getName();
System.out.println("上传的文件名:"+uploadFileName);
//可能存在文件名不合法的情况 trim()清除前后空格
if (uploadFileName.trim().equals("")||uploadFileName==null){
continue;
}
//获得上传的文件名 /images/girl/kazesan.png
String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
//获得文件的扩展名
String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
/*
如果后缀名fileExtName 不是我们所需要的
就直接return,不处理,告诉用户文件类型不对
*/
System.out.println("文件信息 [文件名:"+fileName+"----文件类型"+fileExtName+"]");
//可以使用UUID(唯一识别的通用码),保证文件名唯一:
//UUID.randomUUID(),随机生成一个唯一识别的通用码
//网络传输中的东西都需要序列化
//写实体类时(pojo),如果想要在多个电脑上运行,传输--->需要把对象序列化
//写pojo时,都实现一下这个标记接口 implements Serializable
String uuidPath = UUID.randomUUID().toString();
//========================存放地址=====================//
//存到哪儿?uploadPath
//文件真实存在的路径realPath
String realPath = uploadPath + "/" + uuidPath;
//给每个文件创建一个对应的文件夹
File realPathFile = new File(realPath);
if (!realPathFile.exists()){
realPathFile.mkdir();
}
//========================文件传输=====================//
//获得文件上传的流
InputStream inputStream = fileItem.getInputStream();
//创建一个文件输出流
//realPath = 真实的文件夹
//差了一个文件;加上输出文件的名字+”/“+uuidFileName
FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);
//创建一个缓冲区
byte[] buffer = new byte[1024*1024];
//判断是否读取完毕
int len=0;
//如果大于0说明还存在数据;
while ((len=inputStream.read(buffer))>0){
fos.write(buffer,0,len);
}
//关闭流
fos.close();
inputStream.close();
msg = "文件上传成功!";
fileItem.delete();//上传成功,清除临时文件
}
}
return msg;
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>FileServlet</servlet-name>
<servlet-class>com.kazesan.servlet.FileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FileServlet</servlet-name>
<url-pattern>/upload.do</url-pattern>
</servlet-mapping>
</web-app>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<%--通过表单上传文件
get:上传文件大小有限制
post:上传文件大小没有限制
--%>
<%--${pageContext.request.contextPath}获取服务器路径--%>
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
上传用户:<input type="text" name="username"><br/>
<p><input type="file" name="file1"></p>
<p><input type="file" name="file2"></p>
<p><input type="submit"> | <input type="reset"></p>
</form>
</body>
</html>
info.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>消息提示</title>
</head>
<body>
${msg}
</body>
</html>
17、邮件收发
电子邮件
要在网络上实现邮件功能,必须要有专门的邮件服务器。
这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中。
SMTP服务器地址:一般是 smt.xx.com,比如163邮箱是smtp.163.com, qq邮箱是smtp.qq.com。
电子邮箱(E-Mail地址)的获得需要在邮件服务器上进行申请。比如我们要使用QQ邮箱,就需要开通邮箱功能;
传输协议
SMTP协议
发送邮件:
我们通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器)。
POP3协议
接收邮件:
我们通常把处理用户pop3请求(邮件接收请求)的服务器称之为POP3服务器(右键接收服务器 )
邮件收发原理
- 张三通过smtp协议连接到smtp服务器,然后发送一封邮件给网易的邮件服务器
- 网易分析发现需要去QQ的邮件服务器,通过smtp协议将邮件转投给QQ的smtp服务器
- QQ将接收到的邮件存储在李四的QQ邮箱账号的空间中
- 李四通过pop3协议连接到pop3服务器收取邮件
- 从李四的QQ邮箱账号的空间中取出邮件
- pop3服务器将取出来的邮件送到李四的面前
【注意】有可能收件人地址,发件人地址等信息都正确了,控制台也打印了正确的信息,但是在收件箱就是收不到信息。这是因为可能收件服务器拒收了你发的邮件(比如认为你的邮件是广告),这时候可能在垃圾箱里能找到,可能找不到。解决办法是重复的邮件内容不要多次发送,或者更换收件箱试试。
1.概述
我们将用代码完成邮件的发送。这在实际项目中应用的非常广泛,比如注册需要发送邮件进行账号激活,再比如OA项目中利用邮件进行任务提醒等等。
使用Java发送E-mail十分简单,但是首先你应该准备JavaMail API和Java Activation Framework。
得到两个jar包:
- mail.jar
- activation.jar
JavaMail是sun公司(现已被甲骨文收购)为方便Java开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发包,它支持一些常用的邮件协议, 如前面所讲的SMTP,POP3, IMAP,还有MIME等。 我们在使用JavaMail API编写邮件时,无须考虑邮件的底层实现细节,只要调用JavaMail 开发包中相应的API类就可以了。
我们可以先尝试发送一封简单的邮件, 确保电脑可以连接网络。
- 创建包含邮件服务器的网络连接信息的Session对象
- 创建代表邮件内容的Message对象
- 创建Transport对象,连接服务器,发送Message,关闭连接
主要有四个核心类,我们在编写程序时,记住这四个核心类,就很容易编写出Java邮件处理程序。
2.纯本文邮件
导入两个jar包
QQ邮箱需要先获取授权码、开启SMTP和POP3
jdk1.8禁用了ssl,需将jdk安装目录下java.security文件中”SSLv3,TLSv1,TLSv1.1“删除,或者将SSL加密代码注释
参考我的目录D:\Environment\Java\jdk1.8\jre\lib\security,找到java.security文件
MailDemo01.java
package com.kazesan;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
//发送一封简单的邮件
public class MailDemo01 {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.setProperty("mail.host","smtp.qq.com");//设置QQ邮件服务器
properties.setProperty("mail.transport.protocol","smtp");//邮件发送协议
properties.setProperty("mail.smtp.auth","true");//需要验证用户名密码
//关于QQ邮箱,还要设置SSL加密,加上以下代码即可
//jdk1.8会报ssl错误,将此段代码注释可解决,或者修改java.security文件
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable","true");
properties.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件的5个步骤
//1.创建定义整个应用程序所需的环境信息的Session对象
//QQ邮箱才有的代码
Session session = Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("自己输@qq.com","授权码自己获取");
}
});
//开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com","自己输@qq.com","授权码自己获取");
//4.创建邮件:写邮件
//注意需要传递Session
MimeMessage message = new MimeMessage(session);
//指明邮件的发件人
message.setFrom(new InternetAddress("自己输@qq.com"));
//指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发
message.setRecipient(Message.RecipientType.TO, new InternetAddress("自己输@qq.com"));
//邮件的标题
message.setSubject("欢迎CV程序猿");
//邮件的文本内容
message.setContent("<h1 style='color:red'>又菜又爱玩</h1>","text/html;charset=utf-8");
//5.发送邮件
ts.sendMessage(message,message.getAllRecipients());
//6.关闭连接
ts.close();
}
}
3.带图片和附件的邮件
先认识两个类一个名词:
MIME(Multipurpose Internet Mail Extensions 多用途互联网邮件扩展类型)
MimeBodyPart类
javax.mail.internet.MimeBodyPart类表示的是一个MIME消息,它和MimeMessage类一样都是从Part接口继承过来。
MimeMultipart类
javax.mail.internet.MimeMultipart是抽象类Multipart的实现子类,它用来组合多个MIME消息。一个MimeMultipart对象可以包含多个代表MIME消息的MimeBodyPart对象。
MailDemo02.java 带图片
package com.kazesan;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Properties;
//发送一封简单的邮件
public class MailDemo02 {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.setProperty("mail.host","smtp.qq.com");//设置QQ邮件服务器
properties.setProperty("mail.transport.protocol","smtp");//邮件发送协议
properties.setProperty("mail.smtp.auth","true");//需要验证用户名密码
//关于QQ邮箱,还要设置SSL加密,加上以下代码即可
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable","true");
properties.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件的5个步骤
//1.创建定义整个应用程序所需的环境信息的Session对象
//QQ邮箱才有的代码
Session session = Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("1091780195@qq.com","wabilhfmkxopijha");
}
});
//开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com","1091780195@qq.com","wabilhfmkxopijha");
//4.创建邮件:写邮件
//注意需要传递Session
MimeMessage message = new MimeMessage(session);
//指明邮件的发件人
message.setFrom(new InternetAddress("1091780195@qq.com"));
//指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发
message.setRecipient(Message.RecipientType.TO, new InternetAddress("1091780195@qq.com"));
//邮件的标题
message.setSubject("欢迎CV程序猿");
//邮件的文本内容
//=============================================
//准备图片数据
MimeBodyPart image = new MimeBodyPart();
//图片需要经过数据处理, DataHandler:数据处理
DataHandler dh = new DataHandler(new FileDataSource("src/123.jpg"));
image.setDataHandler(dh);//在Body中放入这个处理的图片数据
image.setContentID("kaze.jpg");//给图片设置一个ID,我们在后见可以使用!
//准备正文数据
MimeBodyPart text = new MimeBodyPart();
text.setContent("这是一封邮件正文带图片<img src='cid:kaze.jpg'>的邮件","text/html;charset=UTF-8");
//描述数据关系
MimeMultipart mm = new MimeMultipart();
mm.addBodyPart(text);
mm.addBodyPart(image);
mm.setSubType("related");
//设置到消息中,保存修改
message.setContent(mm);//把最后编辑好的邮件放到消息中
message.saveChanges();//保存修改
//==============================================
//5.发送邮件
ts.sendMessage(message,message.getAllRecipients());
//6.关闭连接
ts.close();
}
}
带图片、附件
图片对比附件代码
拼接附件代码
4.JavaWeb发送邮件
现在很多的网站都提供有用户注册功能,通常我们注册成功之后就会收到一封来自注册网站的邮件。邮件里面的内容可能包含了我们的注册的用户名和密码以及一个激活账户的超链接等信息。今天我们也来实现一个这样的功能,用户注册成功之后,就将用户的注册信息以Email的形式发送到用户的注册邮箱当中,实现发送邮件功能就得借助于JavaMail了。
代码实现:
本地的Tomcat安装目录导入mai.jar和activation.jar两个包
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kazesan</groupId>
<artifactId>WebMail</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.mail/mail -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>RegisterServlet</servlet-name>
<servlet-class>com.kazesan.servlet.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegisterServlet</servlet-name>
<url-pattern>/RegisterServlet.do</url-pattern>
</servlet-mapping>
</web-app>
实体类pojo
User.java
package com.kazesan.pojo;
import java.io.Serializable;
public class User implements Serializable {
private String username;
private String password;
private String email;
public User() {
}
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
工具类utils
Sendmail.java
package com.kazesan.utils;
import com.kazesan.pojo.User;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
//网站3s原则:用户体验
//多线程提升用户体验 (异步处理)
public class Sendmail extends Thread{
//用于给用户发送邮件的邮箱
private String from = "1091780195@qq.com";
//邮箱的用户名
private String username = "1091780195@qq.com";
//邮箱的密码
private String password = "wabilhfmkxopijha";
//发送邮件的服务器地址
private String host = "smtp.qq.com";
//引入pojo
private User user;
public Sendmail(User user){
this.user = user;
}
//重写run方法的实现,在run方法中发送邮件给指定的用户
@Override
public void run() {
try {
Properties properties = new Properties();
properties.setProperty("mail.host",host);//设置QQ邮件服务器
properties.setProperty("mail.transport.protocol","smtp");//邮件发送协议
properties.setProperty("mail.smtp.auth","true");//需要验证用户名密码
//关于QQ邮箱,还要设置SSL加密,加上以下代码即可
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable","true");
properties.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件的5个步骤
//1.创建定义整个应用程序所需的环境信息的Session对象
//QQ邮箱才有的代码
Session session = Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("1091780195@qq.com","wabilhfmkxopijha");
}
});
//开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮件服务器
ts.connect(host,username,password);
//4.创建邮件:写邮件
//注意需要传递Session
MimeMessage message = new MimeMessage(session);
//指明邮件的发件人
message.setFrom(new InternetAddress(from));
//指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发
message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
//邮件的标题
message.setSubject("欢迎CV程序猿");
//邮件的文本内容
String info = "恭喜您注册成功,您的用户名:"+user.getUsername()+",您的密码:"+user.getPassword()+",请妥善保管,如有问题请自负";
message.setContent(info,"text/html;charset=utf-8");
message.saveChanges();
//5.发送邮件
ts.sendMessage(message,message.getAllRecipients());
//6.关闭连接
ts.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
servlet类
RegisterServlet.java
package com.kazesan.servlet;
import com.kazesan.pojo.User;
import com.kazesan.utils.Sendmail;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收用户请求,封装成对象
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
User user = new User(username, password, email);
//用户注册成功之后,给用户发送一封邮件
//我们使用线程来专门发送邮件,防止出现耗时,和网站注册人数过多的情况;
Sendmail send = new Sendmail(user);
//启动线程,线程启动之后就会执行run方法来发送邮件
send.start();
//注册用户
req.setAttribute("message","注册成功,请查看你的邮箱");
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
}
register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/RegisterServlet.do" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
邮箱:<input type="text" name="email"><br/>
<input type="submit" value="注册">
</form>
</body>
</html>
info.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>CV温馨提醒</h1>
${message}
</body>
</html>