JavaWeb
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">
</web-app>
JSP头部编码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
概念
概述
Web开发:
- web,网页
- 静态web
- html,css
- 提供的数据始终不会发生变化
- 动态web
- 淘宝,不一样的人不一样的时间不一样的数据
- 技术栈:Servlet/JSP,ASP.PHP
在Java中,动态web资源开发技术统称JavaWeb
web应用程序
可以提供浏览器访问的程序;
统一的web资放在同一个文件夹下,web应用程序->Tomcat:服务器;
一个web应用多多部分组成(静态web,动态web),html,css,js,jsp,servlet,java程序,jar包,配置文件properties;
web应用程序编写完毕,若想提供给外界访问:需要一个服务器来统一管理。
静态web
![image-20210518175547642](https://i-blog.csdnimg.cn/blog_migrate/086fcc820948c61fc3176098b6472238.png)
![image-20210518175735365](https://i-blog.csdnimg.cn/blog_migrate/11b31c0b060897cd40804e2a40efdd02.png)
动态web
![image-20210518180101537](https://i-blog.csdnimg.cn/blog_migrate/080de4a3bba68bd564e38c7c3a024e82.png)
缺点:
- 假如服务器动态web资源出现了错误,我们需要重新编写我们的后台程序,重新发布;
优点:
- 动态更新,用户交互(数据持久化:注册,商品信息,用户信息…)
Web服务器
3个技术
![image-20210518180628653](https://i-blog.csdnimg.cn/blog_migrate/9ac235f3cf1fac24d3eb4de918c442ac.png)
![image-20210518180832445](https://i-blog.csdnimg.cn/blog_migrate/e1df8ff3c4de14989faf415ed4c84f23.png)
服务器
服务器是一种被动操作,用来处理用户的请求和返回响应信息。
Tomcat,IIS…
![image-20210518181258425](https://i-blog.csdnimg.cn/blog_migrate/a193b5bc5a42d695e2a955400ef76969.png)
Tomcat
![image-20210518182418762](https://i-blog.csdnimg.cn/blog_migrate/606bd896d02efa453ee548e577511b06.png)
)
![image-20210518182816715](https://i-blog.csdnimg.cn/blog_migrate/5ed647cdaeeeccf4e63471f3dd90cb99.png)
可以配置启动的端口号;
可以配置主机域名;
修改主机域名
![image-20210518183429364](https://i-blog.csdnimg.cn/blog_migrate/d3b0e85b0fec6705735c3d66125f1aa9.png)
面试题:
![image-20210518184103713](https://i-blog.csdnimg.cn/blog_migrate/f3b4a0a66c21c052d9278959cf2720cc.png)
web网站
- 将写的网站放到服务器的指定文件夹webapps中
-- webapps: Tomcat服务器的web目录
--ROOT
--mafu:网站的目录名
--WEB-INF
--classes:java程序
--lib:web应用程序所依赖的jar包
--web.xml:网站的配置文件
--index.html默认的首页
--static 资源文件
--css
--js
--img
HTTP
简述
超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容则具有一个类似MIME的格式。这个简单模型是早期Web成功的有功之臣,因为它使开发和部署非常地直截了当。
- http1.0:HTTP/1.0 一个连接只能获得一个web资源,断开连接
- http2.0:HTTP/1.1 一个连接只能获得多个web资源
HTTP请求
HTTP响应
![image-20210518200514117](https://i-blog.csdnimg.cn/blog_migrate/6ea60fe7245035323b02a0a022656635.png)
Maven
Maven核心思想:约定大于配置。
- 下载
![image-20210518201404094](https://i-blog.csdnimg.cn/blog_migrate/e8385f21c32ce302ba983f44a3ddc9e6.png)
- 环境变量
![image-20210518201956263](https://i-blog.csdnimg.cn/blog_migrate/f3be169ad94f3bb28782248245f67706.png)
- 修改镜像
- 本地仓库,远程仓库
在IDEA中使用Maven
![image-20210518203225295](https://i-blog.csdnimg.cn/blog_migrate/e710082f8ad8ac9cea125244934c6b80.png)
![image-20210518203404461](https://i-blog.csdnimg.cn/blog_migrate/5bbb8fe1e7869bb1a47829ad565bb64e.png)
IDEA中设置Maven
![image-20210518203702073](https://i-blog.csdnimg.cn/blog_migrate/371a91e318f8bd04a4e3bb3cbeeeff67.png)
![image-20210518205316323](https://i-blog.csdnimg.cn/blog_migrate/37046173af9af02be140dc52359c0f17.png)
Maven创建成功后注意看下IDEA默认设置。
![image-20210518210442733](https://i-blog.csdnimg.cn/blog_migrate/6d874fcbb5d3404bfcbd26c463660e35.png)
上面是用Maven模板的项目。
现在创建普通Maven项目-----一个干净的Maven项目。
![image-20210518204916915](https://i-blog.csdnimg.cn/blog_migrate/accb4e9c5fccb112a894745bf9a4cc22.png)
注意Maven项目的资源过滤问题:
![image-20210518211403458](https://i-blog.csdnimg.cn/blog_migrate/2903b664686c1ffe6fbc7e5859593ca1.png)
设置默认:
![image-20210518212420868](https://i-blog.csdnimg.cn/blog_migrate/8818f34decad1e2be33a5bb0835598c7.png)
Maven默认web项目中的web.xml版本问题:
采用Tomcat默认4.0版本。
Servlet
测试
学习路线:
![image-20210518215601024](https://i-blog.csdnimg.cn/blog_migrate/b83dbfe014c29f7de8f03b27df25bccd.png)
概述:
![image-20210518215738587](https://i-blog.csdnimg.cn/blog_migrate/47682b1c4ad791b37b45af029631f3ba.png)
源码分析
![image-20210518230345787](https://i-blog.csdnimg.cn/blog_migrate/effd7f1c9fde7ad4536460d46e5f1ca6.png)
测试:
public class Test01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
final PrintWriter writer = resp.getWriter();
writer.println("servletTest01");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
编写映射:
java程序通过浏览器访问,需要给浏览器提供访问的路径,而浏览器需要连接web服务器拿到资源,所以需要在web服务器中注册servlet。
<servlet>
<servlet-name>Test01</servlet-name>
<servlet-class>com.mf.study01.Test01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Test01</servlet-name>
<url-pattern>/Test01</url-pattern>
</servlet-mapping>
原理
![image-20210519081548463](https://i-blog.csdnimg.cn/blog_migrate/279f6f4344bcc8d6aea06a2cf41ba9ce.png)
Mapping
测试
- 一个servlet指定一个路径
<servlet>
<servlet-name>Test01</servlet-name>
<servlet-class>com.mf.study01.Test01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Test01</servlet-name>
<url-pattern>/Test01</url-pattern>
</servlet-mapping>
- 一个servlet指定多个路径
<servlet>
<servlet-name>Test01</servlet-name>
<servlet-class>com.mf.study01.Test01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Test01</servlet-name>
<url-pattern>/Test01</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Test01</servlet-name>
<url-pattern>/Test02</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Test01</servlet-name>
<url-pattern>/Test03</url-pattern>
</servlet-mapping>
- 一个servlet可以指定通用映射路径
<servlet-mapping>
<servlet-name>Test01</servlet-name>
<url-pattern>/Test03/*</url-pattern>
</servlet-mapping>
/*直接跳转到首页。
- 前缀
![image-20210519093433991](https://i-blog.csdnimg.cn/blog_migrate/3ff5c05ab16991ceded4ffe7af338387.png)
404处理
public class error extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf8");
final PrintWriter writer = resp.getWriter();
writer.println("<h2>页面走丢了!</h2>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.mf.study01.error</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
遇到问题
部署war包成功,war exploded失败的问题
![image-20210519091108792](https://i-blog.csdnimg.cn/blog_migrate/ef53ea9dc742c0c1649d833c10fe00cf.png)
重建webapp模块发现结构差异:
![image-20210519091031621](https://i-blog.csdnimg.cn/blog_migrate/2832fd1e0040d82ac6c6f2d5d693cfc9.png)
问题:
![image-20210519092558628](https://i-blog.csdnimg.cn/blog_migrate/4d301ed8d3e2b6d1b3056705ed57159f.png)
综上问题:后缀路写错了
![image-20210519093456190](https://i-blog.csdnimg.cn/blog_migrate/3ff5c05ab16991ceded4ffe7af338387.png)
ServletContext
web容器启动的时候,它会为每个web程序创建一个对应的ServletContext对象,代表了当前的web应用。
共享数据
之后会用session和request替换ServletContext。
![image-20210519102604480](https://i-blog.csdnimg.cn/blog_migrate/72e0872756ebe61610f04f8e6f63492f.png)
Test01
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context= this.getServletContext();
String username = "上官婉儿";
context.setAttribute("username",username);
}
Test02
@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().println("<h2>"+username+"</h2>");
}
![image-20210519104735369](https://i-blog.csdnimg.cn/blog_migrate/b1e68f5d35809f40572b48d2786ac92f.png)
获得初始化参数
几乎不用。
web.xml
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kKEXyWyL-1622023696504)(https://i-blog.csdnimg.cn/blog_migrate/19ac9f2f0fe13a09ccf2988b1bfd6b5e.png)]
![image-20210519105859655](https://i-blog.csdnimg.cn/blog_migrate/19ac9f2f0fe13a09ccf2988b1bfd6b5e.png)
转发
用request替换。
转发的时候url路径不会改变:
context.getRequestDispatcher("/test03").forward(req,resp);//调用forward进行转发
![image-20210519111146671](https://i-blog.csdnimg.cn/blog_migrate/175a688cd4581edf1d871c11813315d0.png)
获取资源文件
也可以用类加载反射获取。
Properties
![image-20210519112009732](https://i-blog.csdnimg.cn/blog_migrate/a7672a8d61b381ee7203c6b8aabb711d.png)
![image-20210519112343095](https://i-blog.csdnimg.cn/blog_migrate/a4f2154087475e904c10004aa44fb51e.png)
相对web应用拿到资源。
![image-20210519112920853](https://i-blog.csdnimg.cn/blog_migrate/c8a7a29a9c36cba6817ee7776a2f4461.png)
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
Properties properties = new Properties();
properties.load(is);
resp.getWriter().println("username:"+properties.getProperty("username"));
resp.getWriter().println("password:"+properties.getProperty("password"));
}
Response
HttpServletResponse
web服务器收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest的对象和一个代表响应的HttpServletResponse的对象。
![image-20210519113724044](https://i-blog.csdnimg.cn/blog_migrate/310766111561bfc2821142b8f4a04c5b.png)
- 输出消息
- 下载文件
下载文件
![image-20210519123947663](https://i-blog.csdnimg.cn/blog_migrate/df24775e62aa339d2ca570a69b88679c.png)
测试:
![image-20210519161610423](https://i-blog.csdnimg.cn/blog_migrate/939792022bc0981bc7b5354fb5765a29.png)
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取文件下载的路径
String realPath = "D:\\IDEA\\MyProject\\KuangStudy\\JavaWeb\\Servlet03\\target\\classes\\成徐.jpeg";
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 in = new FileInputStream(realPath);
//5.创建缓冲区
byte[] buffer = new byte[1024];
int len=0;
//6.获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
//7.将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
while ((len=in.read(buffer))!=-1){
out.write(buffer,0,len);
}
in.close();
out.close();
}
各种静态资源导出问题
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
<include>**/*.jpeg</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
![image-20210519160603116](https://i-blog.csdnimg.cn/blog_migrate/35c3f0e7a38cf39966f92d0d817e3a14.png)
验证码
- 前端实现
- 后端实现:需要用到Java的图片类,生成图片。
![image-20210519165601483](https://i-blog.csdnimg.cn/blog_migrate/4c8c3f4bc943425d54f3c6f44e16d3c7.png)
//生成随机数
private String makeRandom(){
Random random = new Random();
String num= random.nextInt(99999999)+"";
StringBuffer buffer = new StringBuffer();
for (int i = 0; i <8- num.length(); i++) {
buffer.append("0");
}
num=num+buffer.toString();
return num;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器1s刷新一次
resp.setHeader("refresh","1");
//在内存中创建图片
BufferedImage image = new BufferedImage(300, 80, BufferedImage.TYPE_INT_RGB);
//得到图片
Graphics2D graphics =(Graphics2D) image.getGraphics();//一只2D的笔
graphics.setBackground(Color.white);
graphics.fillRect(0,0,300,80);
//给图片写数据
graphics.setColor(Color.yellow);
graphics.setFont(new Font(null,Font.BOLD,60));
graphics.drawString(makeRandom(),10,60);
//告诉浏览器这个请求用图片方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//把图片写给图片
ImageIO.write(image,"jpeg",resp.getOutputStream());
}
重定向
(重点) 地址会发生该改变,一个web资源B受到客户端A请求后通知客户端A去访问另外一个web资源C,这个过程叫重定向。
常见场景:登录页面…
void sendRedirect(String var1) throws IOException;
重定向问题:添加项目名
resp.sendRedirect(req.getContextPath()+"/Image");
或
/* resp.setHeader("Location","/Servlet03_war/Image");
resp.setStatus(HttpServletResponse.SC_FOUND);//302*/
//需要添加项目名字
resp.sendRedirect("/Servlet03_war/Image");
![image-20210519171214705](https://i-blog.csdnimg.cn/blog_migrate/13537e39ce7d04906505abdbd26aa677.png)
面试题:转发和重定向区别
相同:页面都会实现跳转
不同:转发时url不改变 307,重定向url改变 302。
提交表单跳转路径问题:
-- 代表当前项目
${pageContext.request.contextPath}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<form action="${pageContext.request.contextPath}/Request" method="get">
<label>用户名:</label>
<input type="text" name="username">
<br>
<label>密码:</label>
<input type="password" name="password">
<br>
<input type="submit" value="提交">
</form>
</body>
</html>
Request
HttpServletRequest
![image-20210519174101336](https://i-blog.csdnimg.cn/blog_migrate/5678fa2245e11020da1c711c9a24658a.png)
获取参数
![image-20210519174440326](https://i-blog.csdnimg.cn/blog_migrate/596b2c145d79a43d7e9798399de7f3be.png)
转发
public class Login extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobby = req.getParameterValues("hobby");
System.out.println("==========================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobby));
System.out.println("==========================");
//通过请求转发
req.getRequestDispatcher("/header.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
![image-20210519184947450](https://i-blog.csdnimg.cn/blog_migrate/e098f6dcfdfe50f25f75236f50df99d3.png)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<title>登录</title>
<style>
h2{
margin-top: 61px;
color: #b9ee84;
position: relative;
left: 10%;
}
div{
width: 400px;
height: 110px;
background: #ec9cad;
opacity: 0.4;
border-radius: 10px;
text-align: center;
position: relative;
left: 10%;
}
</style>
<body>
<h2>登录</h2>
<div>
<form action="${pageContext.request.contextPath}/login" method="post">
<label>用户名:</label>
<input type="text" name="username">
<br>
<label>密码:</label>
<input type="password" name="password">
<br>
<label>技能:</label>
<input type="checkbox" name="hobby" value="九阳神功">九阳神功
<input type="checkbox" name="hobby" value="幽冥神掌">幽冥神掌
<input type="checkbox" name="hobby" value="大挪移">大挪移
<br>
<input type="submit" value="提交">
</form>
</div>
</body>
</html>
Cookie
会话:用户打开浏览器点开超链接访问web资源,然后关闭浏览器,这个过程称为会话。
有状态会话:
![image-20210519190219731](https://i-blog.csdnimg.cn/blog_migrate/bd45688e82cdc52cafb64dd5e88bc213.png)
保存会话的两种技术:
cookie:客户端技术(响应,请求)。
seesion:服务器技术,保存用户会话信息。
常见场景:网站二次登陆记住密码
输出乱码问题:
也就是说Demo2中的response.setCharacterEncoding隐藏在JSP页面中了。然后根据转换后的Servlet可以看出response.setContentType(“text/html;charset=utf-8”);才能达到应有的效果,在使用http协议的情况中,该方法设置 Content-type实体报头,response.setContentType()的作用是使客户端浏览器,区分不同种类的数据,并根据不,同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。
resp.setContentType("text/html;charset=utf-8");
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//服务器,告诉你,你来的时间,把这个时间封装成一个信件,你下次带着来,就知道你来了
//解决中文乱码
req.setCharacterEncoding("UTF-8");
//注意响应如果不走jsp的话还要加
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
//服务器从客户端获取Cookie
Cookie[] cookies = req.getCookies();
//判断Cookie是否存在
if(cookies!=null){
out.println("欢迎回来!");
out.print("你上一次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
if(cookie.getName().equals("lastLoginTime")){
long lastLoginTime = Long.parseLong(cookie.getValue());
out.println(new Date(lastLoginTime).toLocaleString());
}
}
}else {
out.println("欢迎注册!");
}
//服务端可客户端响应一个Cookie
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+"");
resp.addCookie(cookie);
}
![image-20210519204506360](https://i-blog.csdnimg.cn/blog_migrate/edc0a6c0abba08dcc30988867f001307.png)
1621428588129
1621428588129
//cookie有效期为一天
cookie.setMaxAge(24*60*60);
关闭浏览器重新登陆
![image-20210519205219564](https://i-blog.csdnimg.cn/blog_migrate/4ed3a4a7a0a1362584d0c0760178a893.png)
总结:
- 从请求中拿到cookie信息
- 服务器形影cookie给客户端
Cookie[] cookies = req.getCookies();
cookie.getName().equals("lastLoginTime")
cookie.getValue()
new Cookie("lastLoginTime", System.currentTimeMillis()+"");
cookie.setMaxAge(24*60*60);
resp.addCookie(cookie);
![image-20210519210037418](https://i-blog.csdnimg.cn/blog_migrate/9dbbbfec50dca9e35bd2ea5304837e4f.png)
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建一个cookie,名字和需要删除的cookie名字一样
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+"");
cookie.setMaxAge(0);
resp.addCookie(cookie);
}
传输中文乱码最快解决方法
out.println(URLDecoder.decode(cookie.getValue(),"utf-8"));
Cookie cookie = new Cookie("name", URLEncoder.encode("韩信","utf-8"));
Session
(重点)保存会话信息一般用Session不用Cookie,是用得最多的。
- 服务器会给每一个用户或浏览器创建一个session对象
- 一个session独占浏览器,只要浏览器没关闭,session就存在
- 用户登录之后,整个网站它都可以访问—》保存用户,购物车信息
Cookie和Session区别
![image-20210519215422550](https://i-blog.csdnimg.cn/blog_migrate/a0e8f1ecd9257464b96b41651a45cabe.png)
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//得到session
HttpSession session = req.getSession();
//给session存东西
session.setAttribute("name","刘邦");
//获取session的id
String id = session.getId();
//判断是否新创建session
PrintWriter writer = resp.getWriter();
if(session.isNew()){
writer.println("欢迎注册,ID为:"+id);
}else {
writer.println("欢迎登录,ID为:"+id);
}
}
![image-20210519213355707](https://i-blog.csdnimg.cn/blog_migrate/b9083e3808eb131547db45c1ffcc988b.png)
session本质还是放在cookie中:
//session创建的时候做了什么事情
Cookie cookie = new Cookie("JSESSIONID", id;
resp.addCookie(cookie);
存对象
session.setAttribute("name",new Person("刘稚",18,"女"));
writer.println(session.getAttribute("name"));
注销session
手动注销
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//得到session
HttpSession session = req.getSession();
session.removeAttribute("name");
session.invalidate();
}
web.xml设置超时时间
<session-config>
<!-- 15分钟后session注销-->
<session-timeout>15</session-timeout>
</session-config>
![image-20210519215734556](https://i-blog.csdnimg.cn/blog_migrate/6bd8f7e0949ba1bb044d3f75bf949934.png)
JSP
java服务器页面
原理
![image-20210519220352259](https://i-blog.csdnimg.cn/blog_migrate/dc5e25f9b80a543c0284222533003b72.png)
表示自己的目录优点怪:
![image-20210519234721011](https://i-blog.csdnimg.cn/blog_migrate/e5b5e570ff725de60efe2c5c49e6878a.png)
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet
![image-20210519232656160](https://i-blog.csdnimg.cn/blog_migrate/65f3acda8f1d3cdb81efd2b4513cf006.png)
![image-20210519233325785](https://i-blog.csdnimg.cn/blog_migrate/18d573a27654067888ad97d14cb1336d.png)
![image-20210519233510874](https://i-blog.csdnimg.cn/blog_migrate/d73eb6bc8f72075e19cc6cb085beea5d.png)
原理流程图
![image-20210519233910792](https://i-blog.csdnimg.cn/blog_migrate/46b241d98b348cac8e5d7d4efacda9c3.png)
在JSP页面中:
只要是JAVA代码就会原封不动的输出;
如果是HTML代码,就会被转换为这样的格式输出到前端:
out.write("<html>\r\n");
out.write("<head>\r\n");
![image-20210520120226052](https://i-blog.csdnimg.cn/blog_migrate/85bd5ea47c562e24f103ca3a0c3e555e.png)
JSTL
导入JSTL和标签库依赖
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
语法
<%%>
<%=%>
<%!%>
<%----%>
JSP作为java技术的一种应用,了解知道即可。Java所有语法JSP都支持。
JSP表达式:用来将程序输出到客户端
<%=
变量或者表达式
%>
例:
<%=
new Date().toLocaleString()
%>
也可以直接用$取出元素值:EL表达式
${new Date().toLocaleString()}
JSP脚本片段
<%
java程序
%>
例:
<%
int sum=0;
for (int i = 0; i < 100; i++) {
sum+=i;
}
out.println("<h2>sum="+sum+"</h2>");
%>
嵌入HTML元素
<%--嵌入HTML元素--%>
<%
int index=0;
for (index = 0; index < 5; index++) {
}
%>
<div class="div-1">
<div class="div-2">
<h3>现在是第: <%= index%> 进行笔试</h3>
</div>
</div>
<%
%>
嵌套后:
<style>
.div-1{
width: 500px;
height: 304px;
position: relative;
top: 3%;
left: 15%;
background: #0affa5;
opacity: 0.3;
}
.div-2 {
width: 400px;
height: 40px;
text-align: center;
position: relative;
left: 10%;
background: #ff4848;
top: 5%;
}
h3{
font-size: 18px;
line-height: 40px;
font-family: 华文宋体, serif;
}
</style>
<hr>
<%--嵌入HTML元素--%>
<div class="div-1">
<%
int index=0;
for (index = 0; index < 5; index++) {
%>
<div class="div-2">
<h3>现在是第: <%= index%> 进行笔试</h3>
</div>
<%
}
%>
</div>
![image-20210520115541579](https://i-blog.csdnimg.cn/blog_migrate/1bc4a92989bf43e0ffa460bc0c483eff.png)
以上代码都是在_jspService方法中的,所有变量都是方法内的局部变量。
全局变量
JSP声明:会被编译到JS生成的Java类中。其他的则会生成到_jspService方法中。
<%!
全局变量
%>
<%!
static {
System.out.println("正在加载servlet");
}
private int globalVar=0;
public void jspMafu(){
System.out.println("进入了Mafu方法");
}
%>
![image-20210520121018794](https://i-blog.csdnimg.cn/blog_migrate/7912054da2e85d728c41fbd410973eee.png)
指令
<%@page args....%>
errorPage
<%@ page errorPage="error/500.jsp" %>
<img src="${pageContext.request.contextPath}/image/500.jpeg" alt="500错误">
方式二:
web.xml中修改
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>
公共文件包含
<%@ include file="common/header.jsp"%>
<body>
<%@ include file="common/header.jsp"%>
<h1>项目主体1</h1>
<%@ include file="common/footer.jsp"%>
<hr>
<jsp:include page="/common/header.jsp"/>
<h1>项目主2</h1>
<jsp:include page="/common/footer.jsp"/>
</body>
一般使用第二种jsp标签方式包含公共页面。
区别:
第一种方式会将二者合二为一;
![image-20210520161623617](https://i-blog.csdnimg.cn/blog_migrate/0750c2cbfd694c7d6377f2b24ed4fb4d.png)
第二种是将两者通过引用拼接。
![image-20210520161550423](https://i-blog.csdnimg.cn/blog_migrate/b3438a7bf2709ab3bbaa6a8e2b7ca91b.png)
还有一个区别方式一的同名变量会发生冲突。
注:WEB-INF文件夹下不放资源,用户无法访问。
内置对象
9个
- PageContext
- Request
- Response
- Session
- Application [ServletContext]
- config [ServletConfig]
- out
- page
- excetion
<body>
<%-- 内置对象--%>
<%
pageContext.setAttribute("name1","刘邦1");//pageContext在当前页面有效
request.setAttribute("name2","刘邦2");//只在一次请求中有效哦,请求转发携带
session.setAttribute("name3","刘邦3");//一次会话会话中有效
application.setAttribute("name4","刘邦4");//在服务器中有效
%>
<%-- 脚本中代码原封不动放到java中 --%>
<%
//通过pageContext来取值
//从底层到高层(作用域):
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
%>
<%--使用el表达式输出--%>
<h2>取出的值为:</h2>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<%--
${name5}输出为空被过滤掉
<%=name5%>输出null 不希望看到
--%>
</body>
![image-20210521211322314](https://i-blog.csdnimg.cn/blog_migrate/909652ce3660a8be8dfc4c2a9cc6516b.png)
观察作用域:
![image-20210521211402019](https://i-blog.csdnimg.cn/blog_migrate/52c3135b5b9ed68c134c4fc855f37c83.png)
结果:
![image-20210521211454261](https://i-blog.csdnimg.cn/blog_migrate/a0a7155d201db61bd1402583fef1fd5f.png)
从底层到高层(作用域):pageContext–>request–>session–>application
JVM:双亲委派机制;
![image-20210521211819147](https://i-blog.csdnimg.cn/blog_migrate/13758f90c766227738a1796d59aa06dc.png)
转发
<%
pageContext.forward("/pageContext02.jsp");//页面中写法
request.getRequestDispatcher("/pageContext02.jsp").forward(request,response);//后台写法
%>
![image-20210521212910382](https://i-blog.csdnimg.cn/blog_migrate/6102b84896ef58777d3569087a6ff0ac.png)
JSP标签
都需要到包:jstl,standard
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
3个JS标签
<body>
<h2>jspTag01页面</h2>
<% request.setCharacterEncoding("utf8"); %>
<% response.setContentType("text/html;charset=UTF-8");%>
<%-- <jsp:include page="pageContext02.jsp"/>--%>
<jsp:forward page="jspTag02.jsp">
<jsp:param name="value1" value="值1"/>
<jsp:param name="value2" value="值2"/>
</jsp:forward>
</body>
JSTL标签
JSTL标签的使用就是为了弥补HTML标签的不足;JSTL功能标签和JAVA代码一样。
核心标签(掌握部分)
导入核心标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
![image-20210521215226556](https://i-blog.csdnimg.cn/blog_migrate/2ad08eb65e1a09fbf61681c9748d2437.png)
导包后标签出错可以检查Tomcat是否有包。
c:if
<body>
<h3>if测试</h3>
<form action="coreIf.jsp" method="get">
<%-- 获取表单中的数据:${param.参数名}--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="提交">
</form>
<%--判断如果提交的用户名是管理员则登录成功--%>
<%-- var是返回值--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="欢迎Admin登录!"/>
</c:if>
<c:out value="${isAdmin}"/>
</body>
c:chose c:when
<body>
<%--set用来设置变量值--%>
<c:set var="score" value="88"/>
<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>
c:foreach
<body>
<%
ArrayList<String> people= new ArrayList<>();
people.add("Mafu1");
people.add("Mafu2");
people.add("Mafu3");
people.add("Mafu4");
request.setAttribute("list",people);
%>
<c:forEach var="people" items="${list}">
<c:out value="${people}"/>
<br>
</c:forEach>
<%--
var:每次遍历出来的变量
items:要遍历的集合
begin:开始
end:结束
step:步长
--%>
<hr>
<c:forEach begin="1" end="3" step="2" var="people" items="${list}">
<c:out value="${people}"/>
<br>
</c:forEach>
</body>
格式化标签 SQL标签 XML 了解
EL表达式
- 获取数据
- 执行运算
- 获取web开发的常用对象
Bean
JavaBean实体类有特定的写法:
- 必须要有一个无参构造
- 属性必须私有化
- 必须有对应的get/set方法
- 一般用来和数据库的字段做映射 ORM(对象关系映射)
- 表–》类
- 字段–》属性
- 行记录–》对象
<body>
<%--
以前要使用一个类是这样new对象的 new Sc();
set方法相当于
--%>
<jsp:useBean id="sc" class="com.mf.pojo.Sc" scope="page"/>
<jsp:setProperty name="sc" property="cno" value="222"/>
<jsp:setProperty name="sc" property="sno" value="2"/>
<jsp:setProperty name="sc" property="grade" value="92"/>
<jsp:setProperty name="sc" property="newGrade" value="A"/>
课程号: <jsp:getProperty name="sc" property="cno"/>
学号: <jsp:getProperty name="sc" property="sno"/>
成绩: <jsp:getProperty name="sc" property="grade"/>
等级: <jsp:getProperty name="sc" property="newGrade"/>
</body>
- 过滤器
- 文件上传
- 邮件发送
- JDBC使用,CRUD,JDBC事务
MVC
模型、视图、控制器。
早期架构
用户直接访问控制层,控制层直接操作数据库;
servlet–>CRUD–>数据库
弊端:程序臃肿不利维护
servlet代码:处理请求,响应、视图跳转,处理JDBC、业务逻辑代码;
![image-20210522094557895](https://i-blog.csdnimg.cn/blog_migrate/76a8ee4073cf7268eb7f0bcd49e4b531.png)
解决:没有啥是加一层解决不了的。
![image-20210522094926349](https://i-blog.csdnimg.cn/blog_migrate/e8f2072ce1e45a47e522fd3bfdfaf72c.png)
MVC架构
![image-20210522095259924](https://i-blog.csdnimg.cn/blog_migrate/9c7af9016b114b7e8b4a1f7f1a616161.png)
Model:
- Service:业务逻辑
- Dao:数据持久层CRUD
View:
- 展示数据
- 提供链接发起servlet请求(a,form,img…)
Controller:
-
接收用户请求:(req:请求参数,Session信息…)
-
交给业务层处理对应的代码
-
控制视图的跳转
登录--》接收用户登陆请求--》处理用户请求获取参数--》交给业务层处理业务(判断,事务)--》Dao层查询事务正确--》数据库
Filter
重点
过滤器,过滤网站数据:
- 处理中文代码(在每次调用servlet之前就将乱码处理好)
- 登录验证
![image-20210522100128259](https://i-blog.csdnimg.cn/blog_migrate/d997b1557a5a62142b1a3034c4e976be.png)
Filter和servlet一样,都是直接实现相对应接口即可:
![image-20210522101002834](https://i-blog.csdnimg.cn/blog_migrate/2303adf5bcf5aaa77fe5efa59e0f6859.png)
随服务器一起启动:
![image-20210522103014661](https://i-blog.csdnimg.cn/blog_migrate/944a5943577bd8cc70515c4bc78de2df.png)
ShowServlet
resp.getWriter().write("你最近怎么样了?");
CharacterEncodingFilter:实现Filter接口,重写方法即可。
public class CharacterEncodingFilter implements Filter {
//初始化
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterEncodingFilter正在初始化....");
}
/*
Chain:链
1.过滤器中的所有代码,在过滤特定请求的时候都会执行
2,必须要让过滤器继续通行filterChain.doFilter
* */
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=UTF-8");
System.out.println("过滤器执行前....");
filterChain.doFilter(servletRequest,servletResponse);//多个过滤器,让请求继续走,不写则停止请求程序
System.out.println("过滤器执行后...");
}
//销毁
@Override
public void destroy() {
System.out.println("CharacterEncodingFilter正在销毁.....");
}
}
web.xml
<servlet>
<servlet-name>ShowServlet</servlet-name>
<servlet-class>com.mf.servlet.ShowServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ShowServlet</servlet-name>
<url-pattern>/show</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ShowServlet</servlet-name>
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.mf.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!-- 过滤/servlet后的所有请求-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
权限拦截
用户登录进入主页,用户注销无法登录。
出错:重定向路径问题
![image-20210522122148730](https://i-blog.csdnimg.cn/blog_migrate/16f1cb48fdfbde76f83c31bbde2d0452.png)
首先设置session常量:
public class Constant {
public static String USER_SESSION="USER_SESSION";
}
login.jsp
<body>
<form action="${pageContext.request.contextPath}/servlet/login" method="get">
<span>用户名:</span> <input type="text" name="username">
<br>
<span>密码:</span> <input type="password" name="password">
<br>
<input type="submit" value="登录">
</form>
</body>
LoginServlet:session常量第一次设置
public class LoginServlet 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);
System.out.println(password);
if(username.equals("admin")&&password.equals("123456")){
System.out.println("进入正确页面");
req.getSession().setAttribute(Constant.USER_SESSION,req.getSession().getId());
resp.sendRedirect(req.getContextPath()+"/sys/main.jsp");
}else {
System.out.println("进入错误页面");
resp.sendRedirect(req.getContextPath()+"/error.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
不在首页拦截main.jsp
<body>
<%-- <%--%>
<%-- Object userSession = request.getSession().getAttribute(Constant.USER_SESSION);--%>
<%-- if(userSession==null) response.sendRedirect(request.getContextPath()+"/login.jsp");--%>
<%-- %>--%>
<h2>主页</h2>
<div>
<a href="${pageContext.request.contextPath}/servlet/logout">注销</a>
</div>
</body>
SysFilter过滤器
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 {
//因为要用到session一些参数所以需要强转一下
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
if (req.getSession().getAttribute(Constant.USER_SESSION)==null) {
resp.sendRedirect(req.getContextPath()+"/error.jsp");
}
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
注册过滤器:
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.mf.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!-- 过滤/servlet后的所有请求-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>SysFilter</filter-name>
<filter-class>com.mf.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SysFilter</filter-name>
<url-pattern>/sys/*</url-pattern>
</filter-mapping>
应用:权限管理
- 用户登录后像session中放入用户的数据
- 进入主页的时候要判断用户是否已经登录,要求:在过滤器中实现。
![image-20210522180232721](https://i-blog.csdnimg.cn/blog_migrate/4a75302a8de304c2bfab7df513c1084f.png)
Listener
监听器。
实现监听器接口:有N种,,
OnlineCountListener
//统计在线人数
public class OnlineCountListener implements HttpSessionListener{
//创建session的监听
//一旦创建一个session就会触发
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
ServletContext context = httpSessionEvent.getSession().getServletContext();
System.out.println(httpSessionEvent.getSession().getId());
Integer onlineCount = (Integer) context.getAttribute("OnlineCount");
if (onlineCount==null){
onlineCount= 1;
}
else{
int count= onlineCount;
onlineCount= count + 1;
}
System.out.println(onlineCount);
context.setAttribute("onlineCount",onlineCount);
}
//销毁session的监听
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
ServletContext context = httpSessionEvent.getSession().getServletContext();
httpSessionEvent.getSession().invalidate();
Integer onlineCount = (Integer) context.getAttribute("OnlineCount");
if (onlineCount==null) onlineCount=0;
else onlineCount-=1;
context.setAttribute("onlineCount",onlineCount);
}
/*
session销毁:
1.手动:过期时间
2.重启服务器
* */
}
<!-- 注册监听器-->
<listener>
<listener-class>com.mf.Listener.OnlineCountListener</listener-class>
</listener>
<!-- 1分钟过期-->
<session-config>
<session-timeout>1</session-timeout>
</session-config>
index.jsp
<body>
<h2>
当前在线人数:
<span><%= this.getServletConfig().getServletContext().getAttribute("onlineCount")%></span>
</h2>
</body>
应用
关闭窗口
public class TestPanel {
public static void main(String[] args) {
Frame frame = new Frame("监听器GUI");
Panel panel = new Panel();
frame.setLayout(null);
frame.setBounds(300,300,600,400);
frame.setBackground(Color.red);
panel.setBounds(50,50,200,300);
panel.setBackground(Color.gray);
frame.add(panel);
frame.setVisible(true);
//关闭事件,加监听事件
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
}
JDBC
![image-20210522181139904](https://i-blog.csdnimg.cn/blog_migrate/18b61dcac608f20130c526f626ea1d7a.png)
预编译
时间设置:
事务
ACID保证数据安全。
![image-20210522182300620](https://i-blog.csdnimg.cn/blog_migrate/687f223105b75166d7f16f7135edc0a7.png)
![image-20210522182751086](https://i-blog.csdnimg.cn/blog_migrate/56ed594750ac23fd444955157cd99be4.png)
超市订单管理系统
SMBMS
登录页:
![image-20210522183255084](https://i-blog.csdnimg.cn/blog_migrate/5ff57b1d98f11a3d5557ef3fe805ec33.png)
首页:
![image-20210522183335632](https://i-blog.csdnimg.cn/blog_migrate/029199fa79f1f2110654af1c45fc8838.png)
模块分析:
![image-20210522183541069](https://i-blog.csdnimg.cn/blog_migrate/13573a851828085d28e124226f83795d.png)
数据库架构表:
![image-20210522183936834](https://i-blog.csdnimg.cn/blog_migrate/f5b9e929cb41348d2e9842c14a678683.png)
项目搭建准备工作
- 创建maven项目,测试。
- 导包
mysql-connector-java
servlet-api
jsp-api
jstl
standard
lombok
junit
- 创建项目包结构,创建实体类:ORM映射
/
-
基础公共类
-
数据库配置文件采用dbcp
-
过滤器
-
5.导入静态资源
![image-20210522210938016](https://i-blog.csdnimg.cn/blog_migrate/c509947511689bf039008b821eac4609.png)
接下来是每个功能模块的具体实现。
登录
![image-20210522211354152](https://i-blog.csdnimg.cn/blog_migrate/66ee30afb5a90c61369c1ed41ed4bb71.png)
-
编写前端页面
-
设置首页
-
编写dao层
-
编写service层,测试
-
编写servlet,测试
退出
移除session
req.getSession().removeAttribute(Constant.USER_SESSION);
登录权限拦截
退出后无法直接通过url访问主页。
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resq=(HttpServletResponse) servletResponse;
if(req.getSession().getAttribute(Constant.USER_SESSION)==null)
resq.sendRedirect(req.getContextPath()+"/error.jsp");
filterChain.doFilter(servletRequest,servletResponse);
}
密码修改
项目从底层向上写。
![image-20210523202518184](https://i-blog.csdnimg.cn/blog_migrate/cc790b0688edaf78e85f5afdc47f4cc9.png)
-
导入前端jsp
-
编写dao层
public int updatePwd(Connection conn, int id, int password) throws SQLException { String sql="update smbms_user set userPassword=? where id=?"; if (conn==null) return -1; PreparedStatement st = conn.prepareStatement(sql); Object[] params={password,id}; int i = UtilsJDBC_DBCP.executeUpdate(conn, sql, params); UtilsJDBC_DBCP.releaseConnection(null,st,null); return i; }
-
service层
@Override public boolean updatePwd(int id, int password) throws SQLException { Connection conn = UtilsJDBC_DBCP.getConnection(); boolean flag=false; if(userDao.updatePwd(conn,id,password)>0) flag=true; UtilsJDBC_DBCP.releaseConnection(conn,null,null); return flag; }
-
servlet 提取方法实现复用
public class UserServlet extends HttpServlet { //实现servlet复用 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getParameter("method"); if(method!=null&&method.equals("savepwd")) updatePwd(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } public void updatePwd(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("UserServlet...start...."); //从session拿id Object user = req.getSession().getAttribute(Constant.USER_SESSION); String newpassword = req.getParameter("newpassword"); System.out.println("newpassword" + newpassword); if(user!=null&& !StringUtils.isNullOrEmpty(newpassword)){ UserService userService = new UserServiceImpl(); boolean b = false; try { b = userService.updatePwd(((User) user).getId(), newpassword); } catch (SQLException e) { e.printStackTrace(); } if(b) { System.out.println("修改成功"); req.setAttribute(Constant.MESSAGE, "修改成功!请重新登陆!"); //移除session req.getSession().removeAttribute(Constant.USER_SESSION); } else req.setAttribute(Constant.MESSAGE,"修改失败!"); }else { req.setAttribute(Constant.MESSAGE,"新密码无效!"); } System.out.println("转发页面"); req.getRequestDispatcher("pwdmodify.jsp").forward(req,resp); } }
AJAX验证旧密码
导入json依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.50</version>
</dependency>
pwdmodify.js
oldpassword.on("blur", function () {
$.ajax({
type: "GET",
url: path + "/jsp/user.do",
data: {method: "pwdmodify", oldpassword: oldpassword.val()}, //ajax传递的参数
dataType: "json",
success: function (data) {
if (data.result == "true") {//旧密码正确
validateTip(oldpassword.next(), {"color": "green"}, imgYes, true);
} else if (data.result == "false") {//旧密码输入不正确
validateTip(oldpassword.next(), {"color": "red"}, imgNo + " 原密码输入不正确", false);
} else if (data.result == "sessionerror") {//当前用户session过期,请重新登录
validateTip(oldpassword.next(), {"color": "red"}, imgNo + " 当前用户session过期,请重新登录", false);
} else if (data.result == "error") {//旧密码输入为空
validateTip(oldpassword.next(), {"color": "red"}, imgNo + " 请输入旧密码", false);
}
},
error: function (data) {
//请求出错
validateTip(oldpassword.next(), {"color": "red"}, imgNo + " 请求错误", false);
}
});
}).on("focus", function () {
validateTip(oldpassword.next(), {"color": "#666666"}, "* 请输入原密码", false);
});
UserServlet
//验证旧密码,session中有用户密码
public void pwdModify(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Object user = req.getSession().getAttribute(Constant.USER_SESSION);
String oldpassword = req.getParameter("oldpassword");
//万能的Map
Map<String, String> resultMap = new HashMap<>();
if(user==null){//session过期
resultMap.put("result","sessionerror");
}else if(StringUtils.isNullOrEmpty(oldpassword)){
resultMap.put("result","error");
}else {
String userPassword = ((User) user).getUserPassword();
if(oldpassword.equals(userPassword)){
resultMap.put("result","true");
}else {
resultMap.put("result","false");
}
}
resp.setContentType("application/json");
PrintWriter writer = resp.getWriter();
//JSONArray阿里巴巴的json工具类转换格式
/*
resultMap=["result","sessionerror","result","error"]
json格式={k,v}
* */
writer.write(JSONArray.toJSONString(resultMap));
//由于是流所以需要刷新和关闭
writer.flush();
writer.close();
}
发起异步请求:没有刷新的情况下实现交互
![image-20210524094040867](https://i-blog.csdnimg.cn/blog_migrate/f983eb01d3d6175522602ac181e1bc50.png)
问题:转发
过滤器的问题:过滤页面和过滤请求不一样
![image-20210524101628482](https://i-blog.csdnimg.cn/blog_migrate/6155026ea038ba5954aa97fae56d2c05.png)
![image-20210524105001842](https://i-blog.csdnimg.cn/blog_migrate/97a0c5129ef798c3f9190e4dd7c06485.png)
用户管理
采用两个Tomcat进行测试。
流程设计
![image-20210524111224318](https://i-blog.csdnimg.cn/blog_migrate/efabddaf029234658984653223aadc10.png)
![image-20210524110947664](https://i-blog.csdnimg.cn/blog_migrate/a5e9b8ffc2b8db605393ce786d9280d2.png)
-
导入分页的工具类
-
用户列表导入
-
PageSupport工具类
后端实现
用户总数
dao
//根据用户名或者角色,查询用户总数
public int getUserCount(Connection conn,String userName,int userRole) throws SQLException;
//项目难点
@Override
public int getUserCount(Connection conn, String userName, int userRole) throws SQLException {
StringBuffer sql=new StringBuffer();
sql.append("select count(1) as count from smbms_user u,smbms_role r where u.userCode=r.id");
if (conn==null) return -1;
//list存放参数
List<Object> lists = new ArrayList<Object>();
if(!StringUtils.isNullOrEmpty(userName)){//拼接查询
sql.append(" and u.userName like ?");
lists.add("%"+userName+"%");//通配符
}
if (userRole>0){
sql.append("and u.userCode =?");
lists.add(userRole);
}
//如何把list转换成数组
Object[] params = lists.toArray();
System.out.println("UserServiceImpl:getUserCount-->"+sql.toString());//输出最后完整的sql
ResultSet res = UtilsJDBC_DBCP.executeQuery(conn, sql.toString(), params);
int count =0;
if(res.next()){
count = res.getInt("count");
}
UtilsJDBC_DBCP.releaseConnection(null,null,res);//conn在业务层关闭
return count;
}
service
//查询记录数
public int getUserCount(String userName, int userRole) throws SQLException;
@Override
public int getUserCount(String userName, int userRole) throws SQLException {
Connection conn = UtilsJDBC_DBCP.getConnection();
int count = userDao.getUserCount(conn, userName, userRole);
UtilsJDBC_DBCP.releaseConnection(conn,null,null);
return count;
}
测试
@Test
public void test() throws SQLException {
UserServiceImpl userService = new UserServiceImpl();
// User login = userService.login("admin", "1234567");
// System.out.println(login);
System.out.println(userService.getUserCount(null, 0));
}
用户列表
//h获取用户列表
List<User> getUserList(Connection conn, String userName, int userRole, int currentPageNo, int pageSize) throws Exception;
@Override
public List<User> getUserList(Connection conn, String userName, int userRole, int currentPageNo, int pageSize) throws Exception {
List<User> userList = new ArrayList<>();
StringBuffer sql=new StringBuffer();
sql.append("select u.*,r.roleName as userRoleName\n" +
"from smbms_user u,smbms_role r where\n" +
"u.userRole=r.id\n");
if (conn==null) return null;
//list存放参数
List<Object> lists = new ArrayList<Object>();
if(!StringUtils.isNullOrEmpty(userName)){//拼接查询
sql.append("and u.userName like ?\n");
lists.add("%"+userName+"%");//通配符
}
if (userRole>0){
sql.append("and u.userRole =?\n");
lists.add(userRole);
}
//在mysql数据库中,分页使用 limit startIndex,pageSize ; 总数
sql.append("order by creationDate desc \n" +
"limit ?,?\n");
currentPageNo=(currentPageNo-1)*pageSize;
lists.add(currentPageNo);
lists.add(pageSize);
//如何把list转换成数组
Object[] params = lists.toArray();
System.out.println(Arrays.toString(params));
System.out.println("UserServiceImpl:getUserList-->"+sql.toString());//输出最后完整的sql
ResultSet res = UtilsJDBC_DBCP.executeQuery(conn, sql.toString(), params);
while (res.next()){
User user = new User();
user.setId(res.getInt("id"));
user.setUserCode(res.getString("userCode"));
user.setUserName(res.getString("userName"));
user.setGender(res.getInt("gender"));
user.setBirthday(res.getDate("birthday"));
user.setPhone(res.getString("phone"));
user.setUserRole(res.getInt("userRole"));
user.setUserRoleName(res.getString("userRoleName"));
userList.add(user);
// System.out.println("UserDaoImpl:getUserList-->"+user);
}
UtilsJDBC_DBCP.releaseConnection(null,null,res);//conn在业务层关闭
return userList;
}
//查询用户列表
public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize) throws Exception;
@Override
public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize) throws Exception {
Connection conn = UtilsJDBC_DBCP.getConnection();
List<User> userList = userDao.getUserList(conn, queryUserName, queryUserRole, currentPageNo, pageSize);
UtilsJDBC_DBCP.releaseConnection(conn,null,null);
return userList;
}
@Test
public void test() throws Exception {
UserServiceImpl userService = new UserServiceImpl();
List<User> userList = userService.getUserList(null, 0, 3, 3);
for (User user : userList) {
System.out.println(user);
}
}
角色列表
![image-20210524190950758](https://i-blog.csdnimg.cn/blog_migrate/a4a395d7174deb58a2ad6138e2873091.png)
至此,三条线后台代码完毕:
![image-20210524191245176](https://i-blog.csdnimg.cn/blog_migrate/9c2087919847c99aaf52b14c341541b4.png)
接下来编写前端和控制请求:
![image-20210524191328546](https://i-blog.csdnimg.cn/blog_migrate/dd904b438116335bea939269e20347e9.png)
用户显示servlet
![image-20210524211607903](https://i-blog.csdnimg.cn/blog_migrate/a02938bb1c680575904f5cb1ebaea5d5.png)
//重点+难点
public void query(HttpServletRequest req, HttpServletResponse resp) throws Exception {
//查询用户列表
//从前端获取数据
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);
if(pageIndex!=null) currentPageNo = Integer.parseInt(pageIndex);
//获取用户总数:分页,上一页,下一页
int totalCount = userService.getUserCount(queryUserName, queryUserRole);
//总页数工具类
PageSupport pageSupport = new PageSupport();
pageSupport.setCurrentPageNo(currentPageNo);
pageSupport.setPageSize(pageSize);
pageSupport.setTotalPageCount(totalCount);
int totalPageCount =-1;
if(totalCount%pageSize==0 ) totalPageCount =totalCount/pageSize;
else totalPageCount =totalCount/pageSize+1;
//控制首页和尾页
if(currentPageNo<1) currentPageNo=1;
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);
//返回前端
req.getRequestDispatcher("userlist.jsp").forward(req,resp);
}
架构分析
![image-20210524213424301](https://i-blog.csdnimg.cn/blog_migrate/acdca5290cb9587c05a758e6fecaf42f.png)
文件上传
导入依赖
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
![image-20210525080356187](https://i-blog.csdnimg.cn/blog_migrate/4bc6d369ef873737bbf9780f3cf69a0c.png)
FileItem类
<title>文件上传</title>
<body>
<%-- 通过表单上传文件
get:大小有限制 post无限制
--%>
<form action="" enctype="multipart/form-data" method="post">
<p>上传用户:<input type="text" name="username"></p>
<P><input type="file" name="file1"></P>
<P><input type="file" name="file2"></P>
<P><input type="submit" value="上传"> | <input type="reset" value="重置"></P>
</form>
</body>
![image-20210525082855083](https://i-blog.csdnimg.cn/blog_migrate/eb637e084d6f6c320b3368475190d5ae.png)
![image-20210525083025950](https://i-blog.csdnimg.cn/blog_migrate/d1288909b7f8633cf26fc54db4491783.png)
原生态的文件上传流获取通常比较麻烦:处理上传的文件,一般需要通过流获取,我们可以使用request.getInputStream()。
建议使用Apache文件上传组件实现,common-fileupload,依赖commons-io组件。
![image-20210525092733012](https://i-blog.csdnimg.cn/blog_migrate/e9f8677af488896a2a95a047727638e2.png)
![image-20210525092811267](https://i-blog.csdnimg.cn/blog_migrate/f7182a2a27885f91a6a26433f85a1de6.png)
public class UploadServlet extends HttpServlet {
@SneakyThrows
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//判断上传的表单是普通表单还是带文件的表单
if (!ServletFileUpload.isMultipartContent(req)) return;
//创建保存路径,建议在web-inf目录下安全用户无法直接访问
String upLoadRealPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File upLoadFile = new File(upLoadRealPath);
if(!upLoadFile.exists()) upLoadFile.mkdir();
//缓存,临时文件
//加入文件超过预期大小我们将他放临时文件中过几天自动删除会提醒用户转永
//创建保存路径,建议在web-inf目录下安全用户无法直接访问
String tempRealPath = this.getServletContext().getRealPath("/WEB-INF/temp");
File tempFile = new File(tempRealPath);
if(!tempFile.exists()) tempFile.mkdir();
//1.创建DiskFileItemFactory对象,处理文件上传路径或者掉限制:
DiskFileItemFactory factory = getDiskFileItemFactory(tempFile);
//2.获取servletFileUpload
ServletFileUpload upload = getServletUpload(factory);
//3.处理上传的文件
String msg= upLoadParseRequest(upload,req,upLoadRealPath);
//servlet请求转发消息
req.setAttribute("msg",msg);
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
第一步:创建DiskFileItemFactory对象
public static DiskFileItemFactory getDiskFileItemFactory(File file){
DiskFileItemFactory factory = new DiskFileItemFactory();
//通过这个工厂设置缓冲区,当文件大于缓冲区的时候将他放到临时文件
factory.setSizeThreshold(1024*1024);//1M
factory.setRepository(file);
return factory;
}
第二步:获取servletFileUpload
public static ServletFileUpload getServletUpload(DiskFileItemFactory factory){
ServletFileUpload upload = new ServletFileUpload(factory);
//监听文件上传进度
upload.setProgressListener(new ProgressListener() {
@Override
public void update(long l, long l1, int i) {
System.out.println("总大小:"+l1+" 已上传:"+l);
}
});
//处理乱码
upload.setHeaderEncoding("UTF-8");
//设置单个文件最大值
upload.setFileSizeMax(1024*1024*10);
//设置总共能够上传文件的大小
upload.setSizeMax(1024*1024*10);
return upload;
}
第三步:上传文件
public static String upLoadParseRequest(ServletFileUpload upload,HttpServletRequest req,String upLoadRealPath) throws FileUploadException, IOException {
//将前端请求解析封装成一个FileItem对象,需要从servletUpload获取
List<FileItem> fileItems = upload.parseRequest(req);
String msg=null;
for (FileItem fileItem : fileItems) {
//判断上传的文件是普通的表单还是带文件的表单
if (fileItem.isFormField()){
String fieldName = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");
System.out.println(fieldName + ": " + value);
}else {
//==============处理文件=======================
String upLoadFieldName = fileItem.getName();
System.out.println("upLoadFieldName=>"+upLoadFieldName);
//文件名合法?
if (upLoadFieldName==null||upLoadFieldName.trim().equals("")) continue;
//获取文件名和后缀民
String fileName = upLoadFieldName.substring(upLoadFieldName.lastIndexOf("/") + 1);
String fileExtName = upLoadFieldName.substring(upLoadFieldName.lastIndexOf(".") + 1);
/*网络传输中东西都需要序列化,POJO实体类要在多个电脑运行也需要继承序列化标记接口*/
//UUID.randomUUID()生成唯一通用码
String uuidPath = UUID.randomUUID().toString();
//==============存放地址=======================
String realPath = upLoadRealPath + "/" + uuidPath;
//给每个文件创建一个文件夹
File realFilePath = new File(realPath);
if (!realFilePath.exists()) realFilePath.mkdir();
//==============文件传输=======================
InputStream in = fileItem.getInputStream();
FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);
byte[] buffer = new byte[1024 * 1024];
int len=0;
while ((len=in.read(buffer))>0){
fos.write(buffer,0,len);
}
fos.close();
in.close();
msg="上传成功";
fileItem.delete();//清除临时文件
}
}
return msg;
}
出错点:
String fieldName = fileItem.getFieldName();
区别
String upLoadFieldName = fileItem.getName();
上传后的文件应该在打包目录下。
war模式:将WEB工程以包的形式上传到服务器 ;
war exploded模式:将WEB工程以当前文件夹的位置关系上传到服务器;//支持热部署
(1)war模式这种可以称之为是发布模式,看名字也知道,这是先打成war包,再发布;
(2)war exploded模式是直接把文件夹、jsp页面 、classes等等移到Tomcat 部署文件夹里面,进行加载部署。因此这种方式支持热部署,一般在开发的时候也是用这种方式。
(3)在平时开发的时候,使用热部署的话,应该对Tomcat进行相应的设置,这样的话修改的jsp界面什么的东西才可以及时的显示出来。
邮件发送
![image-20210525121201940](https://i-blog.csdnimg.cn/blog_migrate/087760b72b2950f0d4d89cb4169abc19.png)
收发结构图:
![image-20210525121710345](https://i-blog.csdnimg.cn/blog_migrate/659c72de05482b7aa0149169d3807996.png)
发送邮件:SMTP协议;
接收邮件:POP3协议。
![image-20210525122124642](https://i-blog.csdnimg.cn/blog_migrate/7e8e26ef79db68c7ee47fd1c2b5e9bbb.png)
简单邮件
使用Java发送邮件
![image-20210525122317528](https://i-blog.csdnimg.cn/blog_migrate/4db567f7490df429df6a670b09677298.png)
包不要导错:
![image-20210525153154450](https://i-blog.csdnimg.cn/blog_migrate/8eafc9e6fa9f7a3fceecf8a076d4b990.png)
![image-20210525122643329](https://i-blog.csdnimg.cn/blog_migrate/87462d93cdc315248bb202c78a03b660.png)
Java邮件处理程序主要有四个核心的类:
![image-20210525122732678](https://i-blog.csdnimg.cn/blog_migrate/d391615168f6511278a65a9e712c10c8.png)
使用JavaMail发送邮件5个步骤:
- 创建session
- 通过session得到transport对象
- 使用邮箱用户名和授权码连上服务器
- 创建邮件
- 发送邮件
//发送一封简单邮件
public class MailTest01 {
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
//设置邮件服务器
prop.setProperty("mail.host","smtp.qq.com");
//邮件发送协议
prop.setProperty("mail.transport.protocol","smtp");
//验证用户密码
prop.setProperty("mail.smtp.auth","true");
//若是qq邮箱还需下面几步设置ssl加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable","true");
prop.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件5步骤
//1.创建定义整个应用程序所需的环境信息的session对象
//这段代码qq才有
Session session = Session.getDefaultInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("3460942872@qq.com", "nrsifyrlsqsudbjb");
}
});
//开启session的debug模式,可以查看程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com","3460942872@qq.com","nrsifyrlsqsudbjb");
//4.创建邮件
//创建邮件对象
MimeMessage message = new MimeMessage(session);
//指明邮件发送人
message.setFrom(new InternetAddress("3460942872@qq.com"));
//指明邮件的收件人,自己给自己发
message.setRecipient(Message.RecipientType.TO,new InternetAddress("1106486773@qq.com"));
//邮件的标题
message.setSubject("只包含文本的简单邮件");
//邮件内容
message.setContent("你好,邮件小程序","text/html;charset=UTF-8");
//5.发送邮件
ts.sendMessage(message,message.getAllRecipients());
ts.close();
}
}
![image-20210525161145051](https://i-blog.csdnimg.cn/blog_migrate/4b317ba17e944c4e36e3becf0404b5ef.png)
附件邮件
![image-20210525161525516](https://i-blog.csdnimg.cn/blog_migrate/a588739e833f53573d3542be5e8dc0de.png)
![image-20210525161842353](https://i-blog.csdnimg.cn/blog_migrate/75109a39575edfa55c37eb3c4a9df9da.png)
带图片邮件
//发送一封简单邮件
public class MailTest02 {
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
//设置邮件服务器
prop.setProperty("mail.host","smtp.qq.com");
//邮件发送协议
prop.setProperty("mail.transport.protocol","smtp");
//验证用户密码
prop.setProperty("mail.smtp.auth","true");
//若是qq邮箱还需下面几步设置ssl加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable","true");
prop.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件5步骤
//1.创建定义整个应用程序所需的环境信息的session对象
//这段代码qq才有
Session session = Session.getDefaultInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("3460942872@qq.com", "nrsifyrlsqsudbjb");
}
});
//开启session的debug模式,可以查看程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com","3460942872@qq.com","nrsifyrlsqsudbjb");
//4.创建邮件
//创建邮件对象
MimeMessage message = new MimeMessage(session);
//指明邮件发送人
message.setFrom(new InternetAddress("3460942872@qq.com"));
//指明邮件的收件人,自己给自己发
message.setRecipient(Message.RecipientType.TO,new InternetAddress("1106486773@qq.com"));
//邮件的标题
message.setSubject("带图片的邮件");
//==================================复杂邮件内容=======================
//准备图片数据
MimeBodyPart image = new MimeBodyPart();
DataHandler dh = new DataHandler(new FileDataSource("D:\\IDEA\\MyProject\\KuangStudy\\JavaWeb\\Email\\target\\classes\\雨.png"));
image.setDataHandler(dh);
image.setContentID(URLEncoder.encode("雨.png","UTF-8"));
//准备正文数据
MimeBodyPart text = new MimeBodyPart();
text.setContent("邮件带有<img src='cid:"+URLEncoder.encode("雨.png","UTF-8")+"'>的图片哦!","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());
ts.close();
}
}
![image-20210525165519931](https://i-blog.csdnimg.cn/blog_migrate/0a1e41d5e7c7c2bc001e6c2c42374ad2.png)
带附件邮件
//发送一封带附件邮件
public class MailTest03 {
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
//设置邮件服务器
prop.setProperty("mail.host","smtp.qq.com");
//邮件发送协议
prop.setProperty("mail.transport.protocol","smtp");
//验证用户密码
prop.setProperty("mail.smtp.auth","true");
//若是qq邮箱还需下面几步设置ssl加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable","true");
prop.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件5步骤
//1.创建定义整个应用程序所需的环境信息的session对象
//这段代码qq才有
Session session = Session.getDefaultInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("3460942872@qq.com", "nrsifyrlsqsudbjb");
}
});
//开启session的debug模式,可以查看程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮件服务器
ts.connect("smtp.qq.com","3460942872@qq.com","nrsifyrlsqsudbjb");
//4.创建邮件
//创建邮件对象
MimeMessage message = imageMail(session);
//==================================复杂邮件内容=======================
//5.发送邮件
ts.sendMessage(message,message.getAllRecipients());
ts.close();
}
public static MimeMessage imageMail(Session session) throws Exception {
//消息的固定信息
MimeMessage message = new MimeMessage(session);
//指明邮件发送人
message.setFrom(new InternetAddress("3460942872@qq.com"));
//指明邮件的收件人
message.setRecipient(Message.RecipientType.TO,new InternetAddress("1106486773@qq.com"));
//邮件的标题
message.setSubject("带附件的邮件");
//==================================复杂邮件内容=======================
//邮件内容:1.图片 2.附件 3.文本
//准备图片数据
MimeBodyPart image = new MimeBodyPart();
DataHandler dh = new DataHandler(new FileDataSource("D:\\IDEA\\MyProject\\KuangStudy\\JavaWeb\\Email\\target\\classes\\雨.png"));
image.setDataHandler(dh);
image.setContentID(URLEncoder.encode("雨.png","UTF-8"));
//准备正文数据
MimeBodyPart text = new MimeBodyPart();
text.setContent("邮件带有<img src='cid:"+URLEncoder.encode("雨.png","UTF-8")+"'>的图片哦!","text/html;charset=UTF-8");
//准备附件,
MimeBodyPart attachment = new MimeBodyPart();
FileDataSource fileDataSource = new FileDataSource("D:\\IDEA\\MyProject\\KuangStudy\\JavaWeb\\Email\\src\\main\\resources\\ccf.pdf");
attachment.setDataHandler(new DataHandler(fileDataSource));
attachment.setFileName(MimeUtility.encodeText(fileDataSource.getName()));
//描述数据关系
MimeMultipart mm = new MimeMultipart();
mm.addBodyPart(text);
mm.addBodyPart(image);
mm.setSubType("related");
MimeBodyPart contentText = new MimeBodyPart();
contentText.setContent(mm);
MimeMultipart mm2 = new MimeMultipart();
mm2.addBodyPart(attachment);
mm2.addBodyPart(contentText);
mm2.setSubType("mixed");
//设置到消息中,保存修改
message.setContent(mm2);
message.saveChanges();
return message;
}
}
![image-20210525174044394](https://i-blog.csdnimg.cn/blog_migrate/8a0855bb5f57eeb5a12afa3ccb63f9b9.png)
中文乱码命名:
![image-20210525174223158](https://i-blog.csdnimg.cn/blog_migrate/67f3d7fffb0ef988b4beff2798bb6d83.png)
网站注册发送邮件
![image-20210525174346880](https://i-blog.csdnimg.cn/blog_migrate/45f5dc8f9720fde81c31248bd0f117c0.png)
线程工具类
//网站3秒原则:多线程实现用户体验,异步
public class SendMail extends Thread{
private String from="3460942872@qq.com";
private String username="大公司";
private String password="nrsifyrlsqsudbjb";
private String host="smtp.qq.com";
private User user;
public SendMail(User user) {
this.user = user;
}
@SneakyThrows
@Override
public void run() {
Properties prop = new Properties();
//设置邮件服务器
prop.setProperty("mail.host","smtp.qq.com");
//邮件发送协议
prop.setProperty("mail.transport.protocol","smtp");
//验证用户密码
prop.setProperty("mail.smtp.auth","true");
//若是qq邮箱还需下面几步设置ssl加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable","true");
prop.put("mail.smtp.ssl.socketFactory",sf);
//使用JavaMail发送邮件5步骤
//1.创建定义整个应用程序所需的环境信息的session对象
//这段代码qq才有
Session session = Session.getDefaultInstance(prop, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//发件人邮件用户名、授权码
return new PasswordAuthentication("3460942872@qq.com", "nrsifyrlsqsudbjb");
}
});
//开启session的debug模式,可以查看程序发送Email的运行状态
session.setDebug(true);
//2.通过session得到transport对象
Transport ts = session.getTransport();
//3.使用邮箱的用户名和授权码连上邮件服务器
ts.connect(host,username,password);
//4.创建邮件
//创建邮件对象
MimeMessage message = new MimeMessage(session);
//指明邮件发送人
message.setFrom(new InternetAddress(from));
//指明邮件的收件人,自己给自己发
message.setRecipient(Message.RecipientType.TO,new InternetAddress(user.getEmail()));
//邮件的标题
message.setSubject("欢迎注册!");
String info="注册成功,您的用户名为:"+user.getUsername()+"\n密码为为:"+user.getPassword()+"\n祝您使用愉快";
message.setContent(info,"text/html;charset=UTF-8");
message.saveChanges();
//5.发送邮件
ts.sendMessage(message,message.getAllRecipients());
ts.close();
}
}
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(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);
send.start();
req.setAttribute("message","注册成功,查看邮箱接收信息,若网络不稳定可能过会才收到!");
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
![image-20210525202732150](https://i-blog.csdnimg.cn/blog_migrate/03cf60d27daf838e22b056d9d12ee552.png)
![image-20210525202705048](https://i-blog.csdnimg.cn/blog_migrate/61dc284207ad2d510ab1d3abee432c76.png)
后序学习
思想的学习:
![image-20210525204357028](https://i-blog.csdnimg.cn/blog_migrate/d7871320e069b4950cc4bbdca18ce3d7.png)
![image-20210525204540250](https://i-blog.csdnimg.cn/blog_migrate/e4aed66f05dab93a8ec34851e6e6c2d0.png)
![image-20210525204824138](https://i-blog.csdnimg.cn/blog_migrate/046dc8bb57d739a6978bdfdde9f48325.png)
![image-20210525205138118](https://i-blog.csdnimg.cn/blog_migrate/45bec2c70702a0d38acf533e571ea314.png)
![image-20210525205215764](https://i-blog.csdnimg.cn/blog_migrate/f64cb026493197dd7ae0e56a25868f11.png)
![image-20210525205405325](https://i-blog.csdnimg.cn/blog_migrate/50ef094737b1fc7f5c0a53b03f1b9713.png)
补充:
![image-20210525205513830](https://i-blog.csdnimg.cn/blog_migrate/b1b771ab8f40433986acf1abb59a0b0b.png)