简介
web开发:
- web,网页的意思,www.baidu.com·
- 静态web
- html,sss
- 提供给所有人看的数据始终不会发生厘革!
- 动态web
- 淘宝,险些是所有的网站;
- 提供给所有人看的数据始终会发生厘革,每个人在差别的时间,差别的所在看到的信息各不相同!
- 技能栈:Servlet/JSP,ASP,PHP
web应用步伐web应用步伐:
可以提供欣赏器访问的步伐;
- a.html、b.html.….多个web资源,这些web资源可以被外界访问,对外界提供服务;
- 你们能访问到的任何一个页面大概资源,都存在于这个世界的某一个角落的盘算机上。
- URL
- 这个统一的web资源会被放在同一个文件夹下,web应用步伐>Tomcat:服务器
- 一个web应用由多部门组成(静态web,动态web)
- html,css,js
- jsp,servlet
- Java步伐
- jar包
- 设置文件(Properties)
Web酸用步伐编写完毕后,若想提供给外界访问;需要一个服务蔬来统一管理
静态web
- *.htm, *.html这些都是网员的后坠 、如果服务器上一直存在这些东四,我们就可以直接举行读取、需要网络;
- 静态web存在的缺点
- Web页面无法动态更新,所有用户看到都是同一个页面
- 轮播图,点击特效:伪动态
- JavaScript[实际开发中,它用的最多]
- VBScript
- 它无法和数据库交互(数据无法恒久化,用户无法交互)
动态web
页面会动态展示,“web页面的展示效果因人而异”
缺点:
- 加入服务器的动态web资源出现了错误,我们需要重新编写我们的背景步伐,重新发布;
优点:
- Web页面可以动态更新,所有用户看到都不是同一个页面
- 它可以与数据库交互(数据恒久化:注册,商品信息,用户信息………)
web服务器
技能讲授
ASP:
- 微软:国内最早盛行的就是ASP;
- ·在HTML中嵌入了VB的脚本,ASP+COM;
- ·在ASP开发中,根本一个页面都有几干行的业务代码,页面极其换乱
- ·维护本钱高!
- C#
- IIS
php:
- PHP开发速度很快,功能很强大,跨平台,代码很简单(70%,WP)
- ·无法承载大访问量的情况(局限性)
jSP/Servlet:
B/S;欣赏和服务器C/S:客户端和服务器
- sun公司主推的B/S架构
- 基于Java语言的(所有的大公司,大概一些开源的组件,都是用Java写的)
- 可以承载三高问题带来的影响;(高并发,高可用,高性能)
- 语法像ASP,ASP->JSP,增强市场强度;
web服务器
服务器是一种被动的操纵,用来处理处罚用户的一些请求和给用户一些响应信息;
lIS
微软的;ASP.,Windows中自带的
Tomcat
面向百度编程:
Tomcat是Apache 软件基金会(Apache Software Foundation)的jakarta项目中的一个核心项目,最新的Servlet 和ISP 规范总是能在Tomcat中得到体现,因为Tomcat 技能先进、性能稳定,而且免费,因而深受lava爱好者的喜爱并得到了部门软件开发商的认可,成为现在比力盛行的Web应用服务器。
Tomcat 服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是许多的场合下被普遍使用,是开发和调试JSP步伐的首选。对于一个Java初学web的人来说,它是最佳的选择
Tomcat 实际上运行JSP页面和Serlet。Tomcat最新版易9.0
Tomcat
安装tomcat tomcat
官网:http://tomcat.apache.org/
Tomcat启动和设置
文件夹作用:
访问测试:http://localhost:8080/
大概遇到的问题:
- Java情况变量没有设置
- 闪退问题:需要设置兼容性
- 乱码问题:设置文件中设置
可以修改 conf/logging.properties 中的 java.util.logging.ConsoleHandler.encoding = GBK 办理乱码问题
设置
可以设置启动的端口号
- tomcat的默认端口号为:8080
- mysql:3306
- http:80
- https:443
- [/code] 可以设置主机的名称
-
- [list]
- [*]默认的主机名为:localhost->127.0.0.1
- [*]默认网站应用存放的位置为:webapps
- [/list] [code]
复制代码 高难度口试题:
请你谈谈网站是如何举行访问的!
- 输入一个域名;回车
- 查抄本机的C:\Windows\System32\drivers\etc\hosts设置文件下有没有这个域名映射;
- 有:直接返回对应的ip所在,这个所在中,有我们需要访问的web步伐,可以直接访问
- 127.0.0.1 www.qinjiang.com
复制代码
- 没有:去DNS服务器找,找到的话就返回,找不到就返回找不到;
4.可以设置一下情况变量(可选性)
csdn免登岸复制技巧
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-4KQqNSEo-1609316574047)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201228001630186.png)]
发布一个web网站
不会就先模仿
- 将自己写的网站,放到服务器(Tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了
网站布局
- --webapps :Tomcat服务器的web目次 -ROOT -kuangstudy :网站的目次名 - WEB-INF -classes : java步伐 -lib:web应用所依赖的jar包 -web.xml :网站设置文件 - index.html 默认的首页 - static -css -style.css -js -img -.....
复制代码 HTTP协议:口试
Maven:构建工具
Servlet入门
- HelloWorld!
- Servlet设置 ·原理
Http
什么是HTTP
(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上。
- 文本:html,字符串, …
- 超文本:图片,音乐,视频,定位,舆图.……
- 端口:80
Https:安全的
两个时代
- http1.0
- HTTP/1.0:客户端可以与web服务器毗连后,只能得到一个web资源,断开毗连
- http2.0
- HTTP/1.1:客户端可以与web服务器毗连后,可以得到多个web资源。
Http请求
百度:
- Request URL:https://www.baidu.com/ 请求所在Request Method:GET get方法/post方法Status Code:200 OK 状态码:200Remote(远程) Address:14.215.177.39:443Accept:text/html Accept-Encoding:gzip, deflate, brAccept-Language:zh-CN,zh;q=0.9 语言Cache-Control:max-age=0Connection:keep-alive
复制代码 请求行
- 请求行中的请求方式:GET
- 请求方式:Get,Post,HEAD,DELETE,PUT,TRACT.…
- get:请求可以大概携带的参数比力少,大小有限制,会在欣赏器的URL所在栏显示数据内容,不安全,但高效
- post:请求可以大概携带的参数没有限制,大小没有限制,不会在欣赏器的URL所在栏显示数据内容,安全,但不高效。
消息头
- Accept:告诉欣赏器,它所支持的数据范例Accept-Encoding:支持哪种编码格式 GBK UTF-8 GB2312 ISO8859-1Accept-Language:告诉欣赏器,它的语言情况Cache-Control:缓存控制Connection:告诉欣赏器,请求完成是断开还是保持毗连HOST:主机..../.
复制代码 Http响应
百度:
- Cache-Control:private 缓存控制Connection:Keep-Alive 毗连Content-Encoding:gzip 编码Content-Type:text/html 范例
复制代码 响应体
- Accept:告诉欣赏器,它所支持的数据范例Accept-Encoding:支持哪种编码格式 GBK UTF-8 GB2312 ISO8859-1Accept-Language:告诉欣赏器,它的语言情况Cache-Control:缓存控制Connection:告诉欣赏器,请求完成是断开还是保持毗连HOST:主机..../.Refresh:告诉客户端,多久刷新一次;Location:让网页重新定位;
复制代码 响应状态码
200:请求响应乐成200
3xx:请求重定向·重定向:你重新到我给你新位置去;
4xx:找不到资源404·资源不存在;
5xx:服务器代码错误 500 502:网关错误
常见口试题:
当你的欣赏器中所在栏输入所在并回车的一瞬间到页面可以大概展示回来,经历了什么?
Maven
为什么要学习这个技能?
- 在Javaweb开发中,需要使用大量的jar包,我们手动去导入;
- 如何可以大概让一个东西自动帮我导入和设置这个jar包。
由此,Maven诞生了!
Maven项目架构管理工具
我们现在用来就是方便导入jar包的!
Maven的核心思想:约定大于设置
Maven会规定好你该如何去编写我们Java代码,必须要按照这个规范来;
下载安装Maven
官网:https://maven.apache.org/
下载完成后,解压即可;
电脑上的所有情况都放在一个文件夹下,方便管理;
设置情况变量
在我们的系统情况变量中设置如下设置:
- M2_HOME maven目次下的bin目次
- MAVEN_HOME maven的目次
- 在系统的path中设置%MAVEN_HOME%\bin
测试Maven是否安装乐成,包管必须设置完毕!
阿里云镜像
- 镜像:mirrors
- 作用:加速我们的下载
- 国内发起使用阿里云的镜像
- nexus-aliyun *,!jeecg,!jeecg-snapshots Nexus aliyun http://maven.aliyun.com/nexus/content/groups/public
复制代码 D:Enmvironment\apache-maven-3.6.2\conf\ettings.xml
(狂神老师设置源和仓库的文件位置)
当地仓库
在当地的仓库,远程仓库; 创建一个当地仓库:localRepository
- D:\Environment\apache-maven-3.6.2\maven-repo
复制代码 在IDEA中使用Maven
创建一个MavenWeb项目
- 等待项目初始化完毕
- 观察maven仓库中多了什么东西?
- IDEA中的Maven设置
注意:IDEA项目创建乐成后,看一眼Maven的设置
- 到这里,Maven在IDEA中的设置和使用就OK了!
创建一个普通的Maven项目
这个只有在Web应用下才会有!
标记文件夹功能
设置Tomcat
办理告诫问题
必须要的设置:为什么会有这个问题:我们访问一个网站,需要指定一个文件夹名字;
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-T4fcvZ8N-1609316574059)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201228122448987.png)]
pom文件
pom.xml 是Maven的核心设置文件
- 4.0.0 com.kuang javaweb-01-maven 1.0-SNAPSHOT war UTF-8 1.8 1.8 junit junit 4.11 javaweb-01-maven maven-clean-plugin 3.1.0 maven-resources-plugin 3.0.2 maven-compiler-plugin 3.8.0 maven-surefire-plugin 2.22.1 maven-war-plugin 3.2.2 maven-install-plugin 2.5.2 maven-deploy-plugin 2.8.2
复制代码
maven约定大于设置
我们之后可以能遇到我们写的设置文件,无法被导出大概生效的问题,办理方案:
- src/main/resources **/*.properties **/*.xml true src/main/java **/*.properties **/*.xml true
复制代码 IDEA操纵
办理遇到的问题
- Maven 3.6.2
办理方法:降级为3.6.1
- Tomcat闪退
- IDEA中每次都要重复设置Maven
在IDEA中的全局默认设置中去设置
- Maven项目中Tomcat无法设置
- maven默认web项目中的web.xml版本问题
- 替换为webapp4.0版本和tomcat一致
- [/code]
- [*] Maven仓库的使用
- 所在:https://mvnrepository.com/
- [align=center][img]https://img-blog.csdnimg.cn/20200517152227350.png[/img][/align]
- [align=center][img]https://img-blog.csdnimg.cn/20200517152245364.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjM5MDQzMw==,size_16,color_FFFFFF,t_70[/img][/align]
- [align=center][img]https://img-blog.csdnimg.cn/20200517152310219.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjM5MDQzMw==,size_16,color_FFFFFF,t_70[/img][/align]
- [align=center][img]https://img-blog.csdnimg.cn/20200517152329226.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjM5MDQzMw==,size_16,color_FFFFFF,t_70[/img][/align]
- [/list] [size=6]设置响应编码[/size]
- [外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-rcrIQ7YZ-1609316574062)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201228135608735.png)]
- [size=6]Servlet[/size]
- Servlet简介
-
- [list]
- [*]Servlet就是sun公司开发动态web的一门技能
- [*]Sun在这些APi中提供一个接口叫做:Servlet,如果你想开发一个Servlet步伐,只需要完成两个小步调:
- [list]
- [*]编写一个类,实现Serlet接口
- [*]把开发好java类摆设到web服务器中。
- [/list]
- [/list] [b]把实现了Servlet接口的Java步伐叫做,Servlet[/b]
- HelloServlet
- Serlvet接口Sun公司有两个默认的实现类:HttpServlet,GenericServled
- HelloServlet
- [list=1]
- [*]构建一个普通的Maven项目,删掉内里的sc目次,以后我们的学习就在这个项目内里创建Moudel;这个空的工程就是Maven主工程;
- [*]关于Maven父子工程的明确;
- 父项目中会有
- [/list] [code] servlet-01
复制代码 子项目会有
- javaweb-02-servlet com.kuang 1.0-SNAPSHOT
复制代码 父项目中的java子项目可以直接使用
- Maven情况优化
- 修改web.xml为最新的
- 将maven的布局搭建完整
- [/code] 2.编写一个Servlet步伐
- [list=1]
- [*]编写一个普通类
- [*]实现Servlet接口,这里我们直接继承HttpServlet
- [/list] [code] 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,Serlvet"); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
复制代码 3.编写Servlet的映射
为什么需要映射:我们写的是JAVA步伐,但是要通过欣赏器访问,而欣赏器需要毗连web服务器,所以我们需
要再web服务中注册我们写的Servlet,还需给他一个欣赏器可以大概访问的路径;
- hello com.kuang.servlet.HelloServlet hello /hello
复制代码
- 设置Tomcat
注意:设置项目发布的路径就可以了
- 启动测试,OK!
项目标一些描述信息
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-OY7ikb0B-1609316574076)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201228144307374.png)]
Servlet原理
Servlet是由Web服务器调用,web服务器在收到欣赏器请求之后,会:
Mapping问题
一个Servlet可以指定一个映射路径
.
一个servlet可以指定多个映射路径
- hello /hello hello /hello2 hello /hello3 hello /hello4 hello /hello5
复制代码 一个servlet可以指定通用映射路径
默认请求路径
会把首页干掉
指定一些后缀大概前缀等等…
优先级问题
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理处罚请求;
- error com.kuang.servlet.ErrorServlet error /*
复制代码 ServletContext
web容器在启动的时候,它会为每个web步伐都创建一个对应的ServletContext对象,它代表了当前的web应用;
共享数据
我在这个Servlet中生存的数据,可以在别的一个servlet中拿到;
- 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 }}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 { doGet(req, resp); }} hello com.kuang.servlet.HelloServlet hello /hello getc com.kuang.servlet.GetServlet getc /getc
复制代码 测试访问效果;
获取初始化参数
- url jdbc:mysql://localhost:3306/mybatis protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); String url = context.getInitParameter("url"); resp.getWriter().print(url);}
复制代码 请求转发
- @Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext context = this.getServletContext(); System.out.println("进入了ServletDemo04"); //RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); //转发的请求路径 //requestDispatcher.forward(req,resp); //调用forward实现请求转发; context.getRequestDispatcher("/gp").forward(req,resp);}
复制代码
读取资源文件classpath
Properties
- 在java目次下新建properties
- 在resources目次下新建properties
发现:都被打包到了同一个路径下:classes,我们俗称这个路径为classpath:
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-U40JTNEB-1609316574078)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201228164917411.png)]
思路:需要一个文件流
- username=root12312password=zxczxczxc
复制代码
- public class ServletDemo05 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/kuang/servlet/aa.properties"); Properties prop = new Properties(); prop.load(is); String user = prop.getProperty("username"); String pwd = prop.getProperty("password"); resp.getWriter().print(user+":"+pwd); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); }}
复制代码 访问测试即可ok;
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 varl,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 varl,int var2);
复制代码 响应的状态码
下载文件
- 向欣赏器输出消息
- 下载文件
- 要获取下载文件的路径
- 下载的文件名是啥?
- 设置想办法让欣赏器可以大概支持下载我们需要的东西URLEncoder.encode(fileName,“UTF-8”)
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到bufer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端!
- @Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 要获取下载文件的路径 String realPath = "F:\\班级管理\\西开【19525】\\2、代码\\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 in = new FileInputStream(realPath); // 5. 创建缓冲区 int len = 0; byte[] buffer = new byte[1024]; // 6. 获取OutputStream对象 ServletOutputStream out = resp.getOutputStream(); // 7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端! while ((len=in.read(buffer))>0){ out.write(buffer,0,len); } in.close(); out.close();}
复制代码 验证码功能
验证怎么来的?
- 前端实现
- 后端实现,需要用到Java的图片类,生产一个图片
- package com.kuang.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); }} ImageServlet com.kuang.servlet.ImageServlet Imageservlet /img
复制代码 重定向
常见场景:
- void sendRedirect(String var1) throws IOException;
复制代码 测试:
- @overrideprotected void doGet(HttpservletRequest req, HttpservletResponse resp) throws ServletException, IOException { resp. sendRedirect("/r/img");//重定向 /* resp. setHeader("Location","/r/img"); resp. setstatus (302); *}
复制代码
index.jsp
- [size=5]Hel1o World![/size]
- 用户名:
- 暗码:
-
复制代码 RequestTest.java
- 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("/r/success.jsp");}}
复制代码 重定向页面success.jsp
- Title [size=6]success[/size]
-
复制代码 web.xml设置
- requset com. kuang. servlet. RequestTest requset /login
复制代码 导入依赖的jar包
- javax.servlet javax. servlet-api 4.0.1 javax.servlet.jsp javax. servlet.jsp-api 2.3.3
复制代码 HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器, HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,得到客户端的所有信息;
获取参数,请求转发
自己创建类,且需要继承HttpServlet类
- @Overrideprotected 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[] hobbys = req.getParameterValues("hobbys"); System.out.println("=========="); //背景接收中文乱码问题 System. out.println(username); System. out.println(password); System. out.println(Arrays.tostring(hobbys)); System. out.println("============"); System. out.println(req.getContextPath()); //通过请求转发 //这里的/代表当前的web应用 req.getRequestDispatcher("/success.jsp").forward(req,resp);}
复制代码
Cookie、Session
会话
会话:用户打开一个欣赏器,点击了许多超链接,访问多个web资源,关闭欣赏器,这个过程可以称之为会话;
有状态会话:一个同学来过课堂,下次再来课堂,我们会知道这个同学,曾经来过,称之为有状态会话;
你能怎么证明你是西开的学生?
你 西开
一个网站,怎么证明你来过?
客户端 服务端
- 服务端给客户端一个 信件,客户端下次访问服务端带上信件就可以了; cookie
- 服务器登记你来过了,下次你来的时候我来匹配你; seesion
生存会话的两种技能
cookie
session
- 服务器技能,使用这个技能,可以生存用户的会话信息? 我们可以把信息大概数据放在Session中!
常见常见:网站登录之后,你下次不消再登录了,第二次访问直接就上去了!
Cookie
- 从请求中拿到cookie信息
- 服务器响应给客户端cookie
- Cookie[] cookies = req.getCookies(); //得到Cookiecookie.getName(); //得到cookie中的keycookie.getValue(); //得到cookie中的vlauenew Cookie("lastLoginTime", System.currentTimeMillis()+""); //新建一个cookiecookie.setMaxAge(24*60*60); //设置cookie的有效期resp.addCookie(cookie); //响应给客户端一个cookie
复制代码 cookie:一般会生存在当地的 用户目次下 appdata;
一个网站cookie是否存在上限!聊聊细节问题
- 一个Cookie只能生存一个信息;
- 一个web站点可以给欣赏器发送多个cookie,最多存放20个cookie;
- Cookie大小有限制4kb;
- 300个cookie欣赏器上限
删除Cookie;
- 不设置有效期,关闭欣赏器, 自动失效;
- 设置有效期时间为 0 ;
编码解码:
- URLEncoder.encode("秦疆","utf-8")URLDecoder.decode(cookie.getValue(),"UTF-8")
复制代码- package com.kuang.servlet;import javax.servlet.ServletException;import javax.servlet.http.Cookie;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.Date;import java.util.concurrent.CopyOnWriteArrayList;public class CookieDemo01 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); PrintWriter out = resp.getWriter(); Cookie[] cookies = req.getCookies(); if(cookies!=null){ out.write("nishangyici"); for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; if(cookie.getName().equals("lastLoginTime")){ long lastLoginTime = Long.parseLong(cookie.getValue()); Date date = new Date(lastLoginTime); out.write(date.toLocaleString()); } } }else{ out.write("zheshinidiyici"); } Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + ""); resp.addCookie(cookie); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); }}
复制代码 Session(重点)
什么是Session:
- 服务器会给每一个用户(欣赏器)创建一个Seesion对象;
- 一个Seesion独占一个欣赏器,只要欣赏器没有关闭,这个Session就存在;
- 用户登录之后,整个网站它都可以访问!–> 生存用户的信息;生存购物车的信息……
…
…
…
Session和cookie的区别:
- Cookie是把用户的数据写给用户的欣赏器,欣赏器生存 (可以生存多个)
- Session把用户的数据写到用户独占Session中,服务器端生存 (生存重要的信息,减少服务器资源的浪费)
- Session对象由服务器创建;
使用场景:
- 生存一个登录用户的信息;
- 购物车信息;
- 在整个网站中常常会使用的数据,我们将它生存在Session中;
使用Session:
- package com.kuang.servlet;import com.kuang.pojo.Person;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); }}//得到SessionHttpSession session = req.getSession();Person person = (Person) session.getAttribute("name");System.out.println(person.toString());HttpSession session = req.getSession();session.removeAttribute("name");//手动注销Sessionsession.invalidate();
复制代码 会话自动逾期:web.xml设置
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-4J5UtIGc-1609316574080)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201228235433351.png)]
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-10fKYsof-1609316574081)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201228235656922.png)]
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-8Jx56kug-1609316574081)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201228235902022.png)]
JSP原理
思路:JSP到底怎么执行的!
- 代码层面没有任何问题
- 服务器内部工作
tomcat中有一个work目次;
IDEA中使用Tomcat的会在IDEA的tomcat中生产一个work目次
我电脑的所在:
C:\Users\Administrator.IntelliJIdea2018.1\system\tomcat\Unnamed_javaweb-session-cookie\work\Catalina\localhost\ROOT\org\apache\jsp
发现页面转酿成了Java步伐!
欣赏器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet!
JSP最终也会被转换成为一个Java类!
全部导入jar
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-DU0SNvFS-1609316574085)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229001204098.png)]
JSP 本质上就是一个Servlet
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-0tXkc1m6-1609316574087)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229002724413.png)]
- //初始化 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; //sessionfinal javax.servlet.ServletContext application; //applicationContextfinal javax.servlet.ServletConfig config; //configjavax.servlet.jsp.JspWriter out = null; //outfinal java.lang.Object page = this; //page:当前HttpServletRequest request //请求HttpServletResponse response //响应12345678
复制代码 - 输出页眼前增加的代码
- response.setContentType("text/html"); //设置响应的页面范例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;123456789
复制代码 - 以上的这些个对象我们可以在JSP页面中直接使用!
在JSP页面中;
只要是 JAVA代码就会原封不动的输出;
如果是HTML代码,就会被转换为:
这样的格式,输出到前端!
JSP基础语法
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-G4py4yRQ-1609316574089)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229003705147.png)]
任何语言都有自己的语法,JAVA中有,。 JSP 作为java技能的一种应用,它拥有一些自己扩充的语法(相识,知道即可!),Java所有语法都支持!
- 4.0.0 org.example TEST02 1.0-SNAPSHOT war TEST02 Maven Webapp http://www.example.com UTF-8 1.7 1.7 junit junit 4.11 test javax.servlet javax.servlet-api 4.0.1 javax.servlet.jsp javax.servlet.jsp-api 2.3.3 javax.servlet.jsp.jstl jstl-api 1.2 taglibs standard 1.1.2 TEST02 maven-clean-plugin 3.1.0 maven-resources-plugin 3.0.2 maven-compiler-plugin 3.8.0 maven-surefire-plugin 2.22.1 maven-war-plugin 3.2.2 maven-install-plugin 2.5.2 maven-deploy-plugin 2.8.2
复制代码 [外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-4rVwFF33-1609316574092)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229004718836.png)]
JSP表达式
jsp脚本片段
脚本片段的再实现
- 这是一个JSP文档
- [size=6]Hello,World [/size]
-
复制代码 JSP声明
JSP声明:会被编译到JSP生成Java的类中!其他的,就会被生成到_jspService方法中!
在JSP,嵌入Java代码即可!
- [/code] JSP的注释,不会在客户端显示,HTML就会!
- [size=6]JSP指令[/size]
- [code][size=6]网页主体[/size]
- [size=6]网页主体[/size]
复制代码 [外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-0UOaBRqZ-1609316574095)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229102752359.png)]
9大内置对象
- PageContext 存东西
- Request 存东西
- Response
- Session 存东西
- Application 【SerlvetContext】 存东西
- config 【SerlvetConfig】
- out
- page ,不消相识
- exception
- pageContext.setAttribute("name1","秦疆1号"); //生存的数据只在一个页面中有效request.setAttribute("name2","秦疆2号"); //生存的数据只在一次请求中有效,请求转发会携带这个数据session.setAttribute("name3","秦疆3号"); //生存的数据只在一次会话中有效,从打开欣赏器到关闭欣赏器application.setAttribute("name4","秦疆4号"); //生存的数据只在服务器中有效,从打开服务器到关闭服务器
复制代码
request:客户端向服务器发送请求,产生的数据,用户看完就没用了,好比:新闻,用户看完没用的!
session:客户端向服务器发送请求,产生的数据,用户用完一会尚有用,好比:购物车;
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还大概使用,好比:谈天数据;
JSP标签、JSTL标签、EL表达式
- javax.servlet.jsp.jstl jstl-api 1.2 taglibs standard 1.1.2
复制代码 EL表达式: ${ }
JSP标签
JSTL表达式
JSTL标签库的使用就是为了增补HTML标签的不敷;它自定义许多标签,可以供我们使用,标签的功能和Java代码一样!
格式化标签
SQL标签
XML 标签
核心标签 (掌握部门)
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-f58b9NIx-1609316574097)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229112309307.png)]
JSTL标签库使用步调
- 引入对应的 taglib
- 使用此中的方法
- 在Tomcat 也需要引入 jstl的包,否则会报错:JSTL分析错误
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-Ntzkg4IC-1609316574099)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229122605140.png)]
c:if
c:choose c:when
- 你的结果为优秀 你的结果为一般 你的结果为良好 你的结果为不合格
复制代码 c:forEach
JavaBean
实体类
JavaBean有特定的写法:
- 必须要有一个无参构造
- 属性必须私有化
- 必须有对应的get/set方法;
一般用来和数据库的字段做映射 ORM;
ORM :对象关系映射
people表
idnameageaddress1秦疆1号3西安2秦疆2号18西安3秦疆3号100西安- class People{ private int id; private String name; private int id; private String address;}class A{ new People(1,"秦疆1号",3,"西安"); new People(2,"秦疆2号",3,"西安"); new People(3,"秦疆3号",3,"西安");}
复制代码
- 过滤器
- 文件上传
- 邮件发送
- JDBC 复习 : 如何使用JDBC , JDBC crud, jdbc 事务
MVC三层架构
- 什么是MVC: Model view Controller 模子、视图、控制器
以前的架构
用户直接访问控制层,控制层就可以直接操纵数据库;
- servlet--CRUD-->数据库毛病:步伐十分臃肿,倒霉于维护 servlet的代码中:处理处罚请求、响应、视图跳转、处理处罚JDBC、处理处罚业务代码、处理处罚逻辑代码架构:没有什么是加一层办理不了的!步伐猿调用↑JDBC (实现该接口)↑Mysql Oracle SqlServer ....(差别厂商)
复制代码 MVC三层架构
Model
- 业务处理处罚 :业务逻辑(Service)
- 数据恒久层:CRUD (Dao - 数据恒久化对象)
View
- 展示数据
- 提供链接发起Servlet请求 (a,form,img…)
Controller (Servlet)
- 接收用户的请求 :(req:请求参数、Session信息….)
- 交给业务层处理处罚对应的代码
- 控制视图的跳转
- 登录--->接收用户的登录请求--->处理处罚用户的请求(获取用户登录的参数,username,password)---->交给业务层处理处罚登录业务(判断用户名暗码是否正确:事务)--->Dao层查询用户名和暗码是否正确-->数据库
复制代码 Filter (重点)
好比 Shiro安全框架技能就是用Filter来实现的
Filter:过滤器 ,用来过滤网站的数据;
(好比用来过滤网上骂人的话,我***我自己 0-0)
Filter开发步调:
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-BKFVbqYl-1609316574102)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229132428680.png)]
- javax.servlet servlet-api 2.5 javax.servlet.jsp javax.servlet.jsp-api 2.3.3 javax.servlet.jsp.jstl jstl-api 1.2 taglibs standard 1.1.2 mysql mysql-connector-java 5.1.47
复制代码 2.编写过滤器
实现Filter接口,重写对应的方法即可
- public class CharacterEncodingFilter implements Filter { //初始化:web服务器启动,就以及初始化了,随时等待过滤对象出现! public void init(FilterConfig filterConfig) throws ServletException { System.out.println("CharacterEncodingFilter初始化"); } //Chain : 链 /* 1. 过滤中的所有代码,在过滤特定请求的时候都会执行 2. 必须要让过滤器继承同行 chain.doFilter(request,response); */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=UTF-8"); System.out.println("CharacterEncodingFilter执行前...."); chain.doFilter(request,response); //让我们的请求继承走,如果不写,步伐到这里就被拦截停止! System.out.println("CharacterEncodingFilter执行后...."); } //销毁:web服务器关闭的时候,过滤器会销毁 public void destroy() { System.out.println("CharacterEncodingFilter销毁"); } }
复制代码- 1. 在web.xml中设置 Filter CharacterEncodingFilter com.kuang.filter.CharacterEncodingFilter CharacterEncodingFilter /servlet/*
复制代码 监听器
实现一个监听器的接口;(有n种监听器)
让开始的时候人数为1
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-VUr3baNY-1609316574106)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229141531472.png)]
- 编写一个监听器
实现监听器的接口…
依赖的jar包
- //统计网站在线人数 : 统计sessionpublic class OnlineCountListener implements HttpSessionListener { //创建session监听: 看你的一举一动 //一旦创建Session就会触发一次这个事件! public void sessionCreated(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(1); }else { int count = onlineCount.intValue(); onlineCount = new Integer(count+1); } ctx.setAttribute("OnlineCount",onlineCount); } //销毁session监听 //一旦销毁Session就会触发一次这个事件! public void sessionDestroyed(HttpSessionEvent se) { ServletContext ctx = se.getSession().getServletContext(); 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中注册监听器
- com.kuang.listener.OnlineCountListener
复制代码 - 看情况是否使用!
过滤器、监听器常见应用
监听器:GUI编程中常常使用;
- public class TestPanel { public static void main(String[] args) { Frame frame = new Frame("中秋节快乐"); //新建一个窗体 Panel panel = new Panel(null); //面板 frame.setLayout(null); //设置窗体的布局 frame.setBounds(300,300,500,500); frame.setBackground(new Color(0,0,255)); //设置配景颜色 panel.setBounds(50,50,300,300); panel.setBackground(new Color(0,255,0)); //设置配景颜色 frame.add(panel); frame.setVisible(true); //监听事件,监听关闭事件 frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { super.windowClosing(e); } }); }}
复制代码 用户登录之后才华进入主页!用户注销后就不能进入主页了!
- 用户登录之后,向Sesison中放入用户的数据
- 进入主页的时候要判断用户是否已经登录;要求:在过滤器中实现!
- HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) resp;if (request.getSession().getAttribute(Constant.USER_SESSION)==null){ response.sendRedirect("/error.jsp");}chain.doFilter(request,response);
复制代码 [外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-5RPdJatm-1609316574109)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229151643266.png)]
JDBC
什么是JDBC : Java毗连数据库!
需要jar包的支持:
- java.sql
- javax.sql
- mysql-conneter-java… 毗连驱动(必须要导入)
实验情况搭建
- CREATE TABLE users( id INT PRIMARY KEY, `name` VARCHAR(40), `password` VARCHAR(40), email VARCHAR(60), birthday DATE);INSERT INTO users(id,`name`,`password`,email,birthday)VALUES(1,'张三','123456','zs@qq.com','2000-01-01');INSERT INTO users(id,`name`,`password`,email,birthday)VALUES(2,'李四','123456','ls@qq.com','2000-01-01');INSERT INTO users(id,`name`,`password`,email,birthday)VALUES(3,'王五','123456','ww@qq.com','2000-01-01');SELECT * FROM users;
复制代码 导入数据库依赖
- mysql mysql-connector-java 5.1.47
复制代码 IDEA中毗连数据库:
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-ir0qBVC9-1609316574113)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229152523631.png)]
JDBC 固定步调:
- 加载驱动
- 毗连数据库,代表数据库
- 向数据库发送SQL的对象Statement : CRUD
- 编写SQL (根据业务,差别的SQL)
- 执行SQL
- 关闭毗连(先开的后关)
- public class TestJdbc { public static void main(String[] args) throws ClassNotFoundException, SQLException { //设置信息 //useUnicode=true&characterEncoding=utf-8 办理中文乱码 String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "123456"; //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2.毗连数据库,代表数据库 Connection connection = DriverManager.getConnection(url, username, password); //3.向数据库发送SQL的对象Statement,PreparedStatement : CRUD Statement statement = connection.createStatement(); //4.编写SQL String sql = "select * from users"; //5.执行查询SQL,返回一个 ResultSet : 效果集 ResultSet rs = statement.executeQuery(sql); while (rs.next()){ System.out.println("id="+rs.getObject("id")); System.out.println("name="+rs.getObject("name")); System.out.println("password="+rs.getObject("password")); System.out.println("email="+rs.getObject("email")); System.out.println("birthday="+rs.getObject("birthday")); } //6.关闭毗连,释放资源(一定要做) 先开后关 rs.close(); statement.close(); connection.close(); }}
复制代码 预编译SQL
- public class TestJDBC2 { public static void main(String[] args) throws Exception { //设置信息 //useUnicode=true&characterEncoding=utf-8 办理中文乱码 String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "123456"; //1.加载驱动 Class.forName("com.mysql.jdbc.Driver"); //2.毗连数据库,代表数据库 Connection connection = DriverManager.getConnection(url, username, password); //3.编写SQL String sql = "insert into users(id, name, password, email, birthday) values (?,?,?,?,?);"; //4.预编译 PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setInt(1,2);//给第一个占位符? 的值赋值为1; preparedStatement.setString(2,"狂神说Java");//给第二个占位符? 的值赋值为狂神说Java; preparedStatement.setString(3,"123456");//给第三个占位符? 的值赋值为123456; preparedStatement.setString(4,"24736743@qq.com");//给第四个占位符? 的值赋值为1; preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));//给第五个占位符? 的值赋值为new Date(new java.util.Date().getTime()); //5.执行SQL int i = preparedStatement.executeUpdate(); if (i>0){ System.out.println("插入乐成@"); } //6.关闭毗连,释放资源(一定要做) 先开后关 preparedStatement.close(); connection.close(); }}
复制代码 事务
要么都乐成,要么都失败!
ACID原则:包管数据的安全。
- 开启事务事务提交 commit()事务回滚 rollback()关闭事务转账:A:1000B:1000 A(900) --100--> B(1100)
复制代码 Junit单元测试
依赖
简单使用
@Test注解只有在方法上有效,只要加了这个注解的方法,就可以直接运行!
- @Testpublic void test(){ System.out.println("Hello");}
复制代码
失败的时候是赤色:
搭建一个情况
- CREATE TABLE account( id INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(40), money FLOAT);INSERT INTO account(`name`,money) VALUES('A',1000);INSERT INTO account(`name`,money) VALUES('B',1000);INSERT INTO account(`name`,money) VALUES('C',1000); @Test public void test() { //设置信息 //useUnicode=true&characterEncoding=utf-8 办理中文乱码 String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "123456"; Connection connection = null; //1.加载驱动 try { Class.forName("com.mysql.jdbc.Driver"); //2.毗连数据库,代表数据库 connection = DriverManager.getConnection(url, username, password); //3.通知数据库开启事务,false 开启 connection.setAutoCommit(false); String sql = "update account set money = money-100 where name = 'A'"; connection.prepareStatement(sql).executeUpdate(); //制造错误 //int i = 1/0; String sql2 = "update account set money = money+100 where name = 'B'"; connection.prepareStatement(sql2).executeUpdate(); connection.commit();//以上两条SQL都执行乐成了,就提交事务! System.out.println("success"); } catch (Exception e) { try { //如果出现异常,就通知数据库回滚事务 connection.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } e.printStackTrace(); }finally { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } }
复制代码 SMBMS(超市管理项目)
SMBMS(超市管理项目)
数据库:
项目如何搭建?
思量是不是用maven? jar包,依赖
搭建项目准备工作
- 搭建一个maven web 项目
- 设置Tomcat
- 测试项目是否可以大概跑起来
- 导入项目中需要的jar包;
jsp,Servlet,mysql驱动jstl,stand…
- 构建项目包布局
- 编写实体类
ROM映射:表-类映射
- 编写基础公共类
1、数据库设置文件(mysql5.xx和8.xx的编写有差别)
- driver=com.mysql.jdbc.Driver#在和mysql通报数据的过程中,使用unicode编码格式,而且字符集设置为utf-8url=jdbc:mysql://127.0.0.1:3306/smbms?useSSL=false&useUnicode=true&characterEncoding=utf-8user=rootpassword=root
复制代码 2、编写数据库的公共类
- package dao;import java.io.IOException;import java.io.InputStream;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.util.Properties;/** * 操纵数据库的基类--静态类 * @author Administrator * */public class BaseDao { static{//静态代码块,在类加载的时候执行 init(); } private static String driver; private static String url; private static String user; private static String password; //初始化毗连参数,从设置文件里得到 public static void init(){ Properties params=new Properties(); String configFile = "database.properties"; InputStream is=BaseDao.class.getClassLoader().getResourceAsStream(configFile); try { params.load(is); } catch (IOException e) { e.printStackTrace(); } driver=params.getProperty("driver"); url=params.getProperty("url"); user=params.getProperty("user"); password=params.getProperty("password"); } /** * 获取数据库毗连 * @return */ public static Connection getConnection(){ Connection connection = null; try { Class.forName(driver); connection = DriverManager.getConnection(url, user, password); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return connection; } /** * 查询操纵 * @param connection * @param pstm * @param rs * @param sql * @param params * @return */ public static ResultSet execute(Connection connection,PreparedStatement pstm,ResultSet rs, String sql,Object[] params) throws Exception{ pstm = connection.prepareStatement(sql); for(int i = 0; i < params.length; i++){ pstm.setObject(i+1, params[i]); } rs = pstm.executeQuery(); return rs; } /** * 更新操纵 * @param connection * @param pstm * @param sql * @param params * @return * @throws Exception */ public static int execute(Connection connection,PreparedStatement pstm, String sql,Object[] params) throws Exception{ int updateRows = 0; pstm = connection.prepareStatement(sql); for(int i = 0; i < params.length; i++){ pstm.setObject(i+1, params[i]); } updateRows = pstm.executeUpdate(); return updateRows; } /** * 释放资源 * @param connection * @param pstm * @param rs * @return */ public static boolean closeResource(Connection connection,PreparedStatement pstm,ResultSet rs){ boolean flag = true; if(rs != null){ try { rs.close(); rs = null;//GC接纳 } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); flag = false; } } if(pstm != null){ try { pstm.close(); pstm = null;//GC接纳 } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); flag = false; } } if(connection != null){ try { connection.close(); connection = null;//GC接纳 } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); flag = false; } } return flag; }}
复制代码 . 3、编写字符编码过滤器
登录功能实现
- public User getLoginUser(Connection connection, String userCode) throws Exception;
复制代码- import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import dao.BaseDao;import pojo.User;public class UserDaoImpl implements UserDao{ //恒久层只做查询数据库的内容 public User getLoginUser(Connection connection, String userCode) throws Exception{ //准备三个对象 PreparedStatement pstm = null; ResultSet rs = null; User user = null; //判断是否毗连乐成 if(null != connection){ String sql = "select * from smbms_user where userCode=?"; Object[] params = {userCode}; rs = BaseDao.execute(connection, pstm, rs, sql, params); if(rs.next()){ user = new User(); user.setId(rs.getInt("id")); user.setUserCode(rs.getString("userCode")); user.setUserName(rs.getString("userName")); user.setUserPassword(rs.getString("userPassword")); user.setGender(rs.getInt("gender")); user.setBirthday(rs.getDate("birthday")); user.setPhone(rs.getString("phone")); user.setAddress(rs.getString("address")); user.setUserRole(rs.getInt("userRole")); user.setCreatedBy(rs.getInt("createdBy")); user.setCreationDate(rs.getTimestamp("creationDate")); user.setModifyBy(rs.getInt("modifyBy")); user.setModifyDate(rs.getTimestamp("modifyDate")); } BaseDao.closeResource(null, pstm, rs); } return user; } }
复制代码- //用户登录public User login(String userCode, String userPassword);
复制代码- import java.sql.Connection;//import org.junit.Test;import dao.BaseDao;import dao.user.UserDao;import dao.user.UserDaoImpl;import pojo.User;public class UserServiceImpl implements UserService{ //业务层都会调用dao层.所以我们要引入Dao层(重点) //只处理处罚对应业务 private UserDao userDao; public UserServiceImpl(){ userDao = new UserDaoImpl(); } public User login(String userCode,String userPassword) { // TODO Auto-generated method stub Connection connection = null; //通过业务层调用对应的具体数据库操纵 User user = null; try { connection = BaseDao.getConnection(); user = userDao.getLoginUser(connection, userCode); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ BaseDao.closeResource(connection, null, null); } return user; } /*@Test public void test() { UserServiceImpl userService = new UserServiceImpl(); String userCode = "admin"; String userPassword = "12345678"; User admin = userService.login(userCode, userPassword); System.out.println(admin.getUserPassword()); } */}
复制代码- import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import pojo.User;import util.Constants;import service.user.UserService;import service.user.UserServiceImpl;@SuppressWarnings("serial")public class LoginServlet extends HttpServlet{ //接受用户参数、调用业务层、转发视图 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO 自动生成的方法存根 System.out.println("login ============ " ); //获取用户名和暗码 String userCode = req.getParameter("userCode"); String userPassword = req.getParameter("userPassword"); //调用service方法,举行用户匹配 UserService userService = new UserServiceImpl(); User user = userService.login(userCode,userPassword); if(null != user){//登录乐成 //放入session req.getSession().setAttribute(Constants.USER_SESSION,user); //页面跳转(frame.jsp) resp.sendRedirect("jsp/frame.jsp"); }else{ //页面跳转(login.jsp)带出提示信息--转发 req.setAttribute("error", "用户名或暗码不正确"); req.getRequestDispatcher("login.jsp").forward(req,resp); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO 自动生成的方法存根 doGet(req, resp); }}
复制代码- LoginServlet com.kuang.servlet.user.LoginServlet LoginServlet /login.do
复制代码 登录功能优化
注销功能
思路:移除session,返回登录页面
- public class LogoutServlet extends HttpServlet { public void doPOST(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //清除session request.getSession().removeAttribute(Constants.USER_SESSION); response.sendRedirect(request.getContextPath()+"/login.jsp");//返回登录页面 }}
复制代码 注册xml
- LogoutServlet servlet.user.LogoutServlet LogoutServlet /jsp/logout.do
复制代码 登录拦截优化
编写一个过滤器,并注册
- import java.io.IOException;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import pojo.User;import util.Constants;public class SysFilter implements Filter{ public void init(FilterConfig filterConfig) throws ServletException{ } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // TODO 自动生成的方法存根 HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)resp; //过滤器,从session中获取用户 User user = (User)request.getSession().getAttribute(Constants.USER_SESSION); if(user == null){//已经被移除大概注销了,大概未登录 response.sendRedirect("/smbms/error.jsp"); }else { chain.doFilter(req, resp); } } @Override public void destroy() { // TODO 自动生成的方法存根 }}
复制代码 注册xml
- SysFilter filter.SysFilter SysFilter /jsp/*
复制代码 测试,登录,注销,权限,都要包管OK
js乱码办理
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-6aB8YiOC-1609316574114)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201229213417430.png)]
暗码修改
- [url=https://www.jianchenwangluo.com/${pageContext.request.contextPath }/jsp/pwdmodify.jsp]暗码修改[/url]
复制代码- //修改当前用户暗码 public int updatePwd(Connection connection,int id,int password)throws SQLException, Exception;
复制代码- @Override//修改当前暗码 public int updatePwd(Connection connection, int id, String password) throws Exception { // TODO 自动生成的方法存根 PreparedStatement pstm = null; int execute =0; if(connection!=null) { String sql = "update smbms_user set userPassword = ? where id = ?"; Object[] params = {password,id}; execute = BaseDao.execute(connection, pstm, sql, params); BaseDao.closeResource(null, pstm, null); } return execute; }
复制代码- public boolean updatePwd(int id,String password)throws SQLException, Exception;
复制代码- public boolean updatePwd(int id, String password) throws SQLException, Exception { // TODO 自动生成的方法存根 Connection connection = null; boolean flag = false; //修改暗码 try { connection = BaseDao.getConnection(); if(userDao.updatePwd(connection, id, password)>0) { flag = true; } } catch (SQLException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } finally { BaseDao.closeResource(connection, null, null); } return flag; }
复制代码
- servlet记得实现复用,要提取出方法!
在 dao层 和 service层 自己写映射类和实现类
下面是 servlet层 的主体
- public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO 自动生成的方法存根 String method = req.getParameter("method"); if (method != "savepwd" && method != null) { this.updatePwd(req, resp); } //实现复用~~~~~~ // 想添加新的增删改查,直接用if(method != "savepwd" && method != null); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO 自动生成的方法存根 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"); boolean flag = false; if (o != null && newpassword != null) { UserService userService = new UserServiceImpl(); try { flag = userService.updatePwd(((User) o).getId(), newpassword); } catch (SQLException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (Exception e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } if (flag) { req.setAttribute("message", "暗码修改乐成,请退出,使用新暗码登录"); // 暗码修改乐成,移除session(移除后不能再次修改暗码,发起不移除) req.getSession().removeAttribute(Constants.USER_SESSION); } else { // 暗码修改失败 req.setAttribute("message", "暗码修改失败"); } } else { // 暗码修改有问题 req.setAttribute("message", "新暗码有问题"); } try { req.getRequestDispatcher("/jsp/pwdmodify.jsp").forward(req, resp); } catch (ServletException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } }}
复制代码 注册xml
- UserServlet servlet.user.UserServlet UserServlet /jsp/user.do
复制代码 优化暗码修改使用Ajax
- com.alibaba fastjson 1.2.68
复制代码 导入阿里的包
- com.alibaba fastjson 1.2.68package com.kuang.servlet.user;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.kuang.pojo.User;import com.kuang.service.user.UserService;import com.kuang.service.user.UserServiceImpl;import com.kuang.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.sql.SQLException;import java.util.HashMap;import java.util.Map;@SuppressWarnings("serial")public class UserServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO 自动生成的方法存根 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); } // 想添加新的增删改查,直接用if(method != "savepwd" && method != null); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO 自动生成的方法存根 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"); boolean flag = false; if (o != null && newpassword != null) { UserService userService = new UserServiceImpl(); try { flag = userService.updatePwd(((User) o).getId(), newpassword); } catch (SQLException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (Exception e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } if (flag) { req.setAttribute("message", "暗码修改乐成,请退出,使用新暗码登录"); // 暗码修改乐成,移除session(移除后不能再次修改暗码,发起不移除) req.getSession().removeAttribute(Constants.USER_SESSION); } else { // 暗码修改失败 req.setAttribute("message", "暗码修改失败"); } } else { // 暗码修改有问题 req.setAttribute("message", "新暗码有问题"); } try { req.getRequestDispatcher("/jsp/pwdmodify.jsp").forward(req, resp); } catch (ServletException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } public void pwdmodify(HttpServletRequest req, HttpServletResponse resp) { // 通过session得到用户id Object o = req.getSession().getAttribute(Constants.USER_SESSION); String oldpassword = req.getParameter("oldpassword"); Map resultMap = new HashMap(); if(o==null) {//session失效,session逾期了 resultMap.put("result","seesionerror"); }else if(StringUtils.isNullOrEmpty(oldpassword)){//输入暗码为空 resultMap.put("result","error"); }else {// String userPassword = ((User)o).getUserPassword();//seesion中的用户暗码 if(oldpassword.equals(userPassword)) { resultMap.put("result","true"); }else { resultMap.put("result","false"); } } try { resp.setContentType("application/josn"); PrintWriter writer = resp.getWriter(); /* * resultMap = ["result","sessionerror","result",error] * josn格式={key,value */ //writer.write(JSONArray.toJSONString(resultMap)); writer.write(JSONArray.toJSONString(resultMap)); writer.flush(); writer.close(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } }}
复制代码 用户管理实现
- 导入分页的工具类-PageSupport
- 用户列表页面导入-userlist.jsp
1、获取用户数量
- //根据用户名大概脚色查询用户总数public int getUserCount(Connection connection,String username ,int userRole)throws SQLException, Exception;
复制代码- @Override public int getUserCount(Connection connection, String userName, int userRole) throws Exception { // TODO Auto-generated method stub PreparedStatement pstm = null; ResultSet rs = 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"); List list = new ArrayList(); if(!StringUtils.isNullOrEmpty(userName)){ sql.append(" and u.userName like ?"); list.add("%"+userName+"%"); } if(userRole > 0){ sql.append(" and u.userRole = ?"); list.add(userRole); } Object[] params = list.toArray(); System.out.println("sql ----> " + sql.toString()); rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params); if(rs.next()){ count = rs.getInt("count"); } BaseDao.closeResource(null, pstm, rs); } return count; }
复制代码- //查询记载数 public int getUserCount(String username, int userRole);
复制代码- //查询记载数 @Override public int getUserCount(String queryUserName, int queryUserRole) { // TODO Auto-generated method stub Connection connection = null; int count = 0; System.out.println("queryUserName ---- > " + queryUserName); System.out.println("queryUserRole ---- > " + queryUserRole); try { connection = BaseDao.getConnection(); count = userDao.getUserCount(connection, queryUserName,queryUserRole); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ BaseDao.closeResource(connection, null, null); } //System.out.println("count"+count); return count; }
复制代码 2、获取用户列表
1.UserDao
- //通过条件查询-userList public List getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize)throws Exception;
复制代码- @Override public List getUserList(Connection connection, String userName,int userRole,int currentPageNo, int pageSize) throws Exception { // TODO Auto-generated method stub PreparedStatement pstm = null; ResultSet rs = null; List userList = new ArrayList(); 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 list = new ArrayList(); 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;总数 //当前页 (当前页-1)*页面大小 //0,5 1,0 01234 //5,5 5,0 56789 //10,5 10,0 10~ sql.append(" order by creationDate DESC limit ?,?"); currentPageNo = (currentPageNo-1)*pageSize; list.add(currentPageNo); list.add(pageSize); Object[] params = list.toArray(); System.out.println("sql ----> " + sql.toString()); rs = BaseDao.execute(connection, pstm, rs, sql.toString(), params); while(rs.next()){ User _user = new User(); _user.setId(rs.getInt("id")); _user.setUserCode(rs.getString("userCode")); _user.setUserName(rs.getString("userName")); _user.setGender(rs.getInt("gender")); _user.setBirthday(rs.getDate("birthday")); _user.setPhone(rs.getString("phone")); _user.setUserRole(rs.getInt("userRole")); _user.setUserRoleName(rs.getString("userRoleName")); userList.add(_user); } BaseDao.closeResource(null, pstm, rs); } return userList; }
复制代码- //根据条件查询用户列表 public List getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize);
复制代码- @Override public List getUserList(String queryUserName,int queryUserRole,int currentPageNo, int pageSize) { // TODO Auto-generated method stub Connection connection = null; List userList = null; System.out.println("queryUserName ---- > " + queryUserName); System.out.println("queryUserRole ---- > " + queryUserRole); System.out.println("currentPageNo ---- > " + currentPageNo); System.out.println("pageSize ---- > " + pageSize); try { connection = BaseDao.getConnection(); userList = userDao.getUserList(connection, queryUserName,queryUserRole,currentPageNo,pageSize); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ BaseDao.closeResource(connection, null, null); } return userList; }
复制代码 3、获取脚色操纵
为了我们的职责统一,我们可以把脚色的操纵单独放在一个包中,和pojo类对应。。。
- //获取脚色列表 public List getRoleList(Connection connection)throws Exception;
复制代码- public class RoleDaoImpl implements RoleDao { @Override public List getRoleList(Connection connection) throws Exception { PreparedStatement pstm = null; ResultSet rs = null; List roleList = new ArrayList(); if (connection != null) { String sql = "select * from smbms_role"; Object[] params = {}; rs = BaseDao.execute(connection, pstm, rs, sql, params); while (rs.next()) { Role _role = new Role(); _role.setId(rs.getInt("id")); _role.setRoleCode(rs.getString("roleCode")); _role.setRoleName(rs.getString("roleName")); roleList.add(_role); } BaseDao.closeResource(null, pstm, rs); } return roleList; }}
复制代码 [外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-kceXSlcC-1609316574117)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201230094842460.png)]
- public interface RoleService { //脚色列表查询 public List getRoleList(); }
复制代码- public class RoleServiceImpl implements RoleService{ private RoleDao roleDao; public RoleServiceImpl(){ roleDao = new RoleDaoImpl(); } @Override public List getRoleList() { Connection connection = null; List roleList = null; try { connection = BaseDao.getConnection(); roleList = roleDao.getRoleList(connection); } catch (Exception e) { e.printStackTrace(); }finally{ BaseDao.closeResource(connection, null, null); } return roleList; } }
复制代码 4、用户显示的Servlet
- 获取用户前端的数据(查询)
- 判断请求是否需要执行,看参数的值判断
- 为了实现分页,需要盘算出当前页面和总页面,页面大小…
- 用户列表展示
- 返回前端
- //重点、难点 private void query(HttpServletRequest req, HttpServletResponse resp) { // TODO 自动生成的方法存根 //查询用户列表 //从前端获取数据 //查询用户列表 String queryUserName = req.getParameter("queryname"); String temp = req.getParameter("queryUserRole"); String pageIndex = req.getParameter("pageIndex"); int queryUserRole = 0; //获取用户列表 UserServiceImpl userService = new UserServiceImpl(); List 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.setTotalCount(totalCount); int totalPageCount =pageSupport.getTotalPageCount();//总共有几页 //(totalCount+pageSize-1/pageSize)取整 // pageSupport.getTotalCount() //System.out.println("totalCount ="+totalCount); //System.out.println("pageSize ="+pageSize); //System.out.println("totalPageCount ="+totalPageCount); //控制首页和尾页 //如果页面小于 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 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) { // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } }
复制代码 小黄鸭调试法:自言自语
项目原理流程图:
上传文件和导包
文件上传注意事项
- 1.为包管服务器安全,上传文件应该放在外界无法直接访问你得目次下,好比放在WEB-INF目次下
- 2.为防止文件覆盖现象的发生,要为文件产生一个唯一的文件名 (添加时间戳 大概 uuid 大概MD5 大概位运算)
- 3.要限制上传文件的大小
- 4.可以限制上传文件的范例,在收到上传文件名时,要判断后缀名是否合格。
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-wsBoF7TL-1609316574123)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201230111632383.png)]
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-cC5HaVaX-1609316574126)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201230111841125.png)]
需要用到的类详解
- ServletFileUpload 负责处理处罚上传的文件数据,并将表单中每个输入项封装成一个FileItem对象,在使用ServletFileUpload对象分析请求时,需要DiskFileItemFactory对象。所以,我们需要在举行分析工作前构造号DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法,或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性。
- Title 上传用户:
- 上传文件1:
- 上传文件2:
- |
-
复制代码- package edu.cqupt.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.*;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 tempPath = this.getServletContext().getRealPath("/WEB-INF/temp"); File file = new File(tempPath); if(!file.exists()){ file.mkdir(); } String msg = null; try { //1.创建 DiskFileItemFactory DiskFileItemFactory factory = getDiskFileItemFactory(file); //2.获取ServletFileUpload ServletFileUpload upload = getServletFileUpload(factory); // 3.处理处罚上传文件 msg = uploadParseRequest(upload,req,uploadPath); } catch (FileUploadException e) { e.printStackTrace(); } // 请求转发消息 req.setAttribute("msg",msg); req.getRequestDispatcher("info.jsp").forward(req,resp); } public static DiskFileItemFactory getDiskFileItemFactory(File file){ DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setSizeThreshold(1024*1024); // 缓存区大小为1M factory.setRepository(file); // 暂时目次的生存目次,需要一个file return factory; } public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){ ServletFileUpload upload = new ServletFileUpload(factory); upload.setProgressListener(new ProgressListener() { @Override public void update(long pBytesRead, long pContenLength, int pItems) { System.out.println("总大小:" + pContenLength + "已上传:" + pBytesRead ); } }); 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 uploadPath) throws FileUploadException, IOException { String message = null; List fileItems = upload.parseRequest(req); // 把前端请求分析,封装成一个FileItem对象 for (FileItem fileItem : fileItems) { if (fileItem.isFormField()) { // 普通表单 String name = fileItem.getName(); String value = fileItem.getString("utf-8"); System.out.println(name + ":" + value); } else { //判断是文件表单 String uploadFileName = fileItem.getName(); // ===== 处理处罚文件 ============= if (uploadFileName.trim().equals("") || uploadFileName == null) { continue; } String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1); String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1); // UUID.randomUUID() 随机生成一个唯一识别的通用码 // 网络中传输东西,都需要序列化 // POJO, 实体类, 如果想要生成在多个电脑上运行, 传输-->需要把对象都序列化 // JNI java native Interface // implements Serializable :标记接口 ,JVM --> Java栈 当地方法栈 native --> c++ String uuidPath = UUID.randomUUID().toString();// 可以 使用UUID(唯一识别的通用码),包管文件唯一 String realPath = uploadPath + "/" + uuidPath; // ========= 存放所在 ======== File realPathFile = new File(realPath); if (!realPathFile.exists()) { realPathFile.mkdir(); } InputStream is = fileItem.getInputStream(); // ========= 文件传输 ======== FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName); byte[] buffer = new byte[1024]; int len = 0; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } fos.close(); is.close(); message = "文件上传乐成"; fileItem.delete(); //上传乐成,清除暂时文件 } } return message; }}
复制代码 发送邮件
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-IejcoJ5V-1609316574127)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201230142142319.png)]
加载包pom.xml
vgzetyyijkuo****ddfa
- javax.mail mail 1.4.7 javax.activation activation 1.1.1
复制代码 注册页面 index.jsp
注册乐成跳转页面:info.jsp
用户实体类User.java
- package edu.cqupt.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 + '\'' + '}'; }}
复制代码 RegisterServlet.java
- package edu.cqupt.servlet;import edu.cqupt.pojo.User;import edu.cqupt.utils.SendEmail;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 doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username"); String password = req.getParameter("password"); String email = req.getParameter("email"); System.out.println(username); System.out.println(password); System.out.println(email); User user = new User(username, password, email); SendEmail send = new SendEmail(user); send.start(); // 使用线程,加速邮件发送 req.setAttribute("message","注册乐成,请查收邮件"); req.getRequestDispatcher("info.jsp").forward(req,resp); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); }}
复制代码 SendEmail.java
- package edu.cqupt.utils;import com.sun.mail.util.MailSSLSocketFactory;import edu.cqupt.pojo.User;import javax.mail.*;import javax.mail.internet.InternetAddress;import javax.mail.internet.MimeMessage;import java.util.Properties;public class SendEmail extends Thread{ private String from = "发送方@qq.com"; private String username = "发送方@qq.com"; private String password = "邮箱授权码"; private String host = "smtp.qq.com"; private User user; public SendEmail(User user) { this.user = user; } @Override public void run() { try { 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.socketFactory", sf); // 使用JavaMail发送邮件的5个步调 // 1、创建session Session session = Session.getInstance(prop, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, password); } }); // 2. 开启Session的debug模式:true session.setDebug(false); // 3.通过session得到transport对象 Transport ts = session.getTransport(); ts.connect(host, username, password); // 4. 创建邮件 Message message = new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail())); message.setSubject("注册邮件"); String info = "Yours username:" + user.getUsername() + "password:" + user.getPassword(); message.setContent(info, "text/html;chartset=UTF-8"); // 5.发送邮件 ts.sendMessage(message, message.getAllRecipients()); // 6.关闭毗连 ts.close(); }catch (Exception e){ throw new RuntimeException(e); } }}
复制代码
邮件发送
2.jar包的支持
activation-1.1.1.jar
mail-1.4.7.jar
3.授权码的获取
4.浅易文本邮件发送的实现
由上图我们可以确定几个必须步调
1.创建session对象
2.创建Transport对象
3.使用邮箱的用户名和授权码连上邮件服务器
4.创建一个Message对象(需要通报session)
5.发送邮件
6.关闭毗连
- 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 prop=new Properties(); prop.setProperty("mail.host","smtp.qq.com");///设置QQ邮件服务器 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对象 Session session=Session.getDefaultInstance(prop, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("XXXX@qq.com","授权码"); } }); //开启session的debug模式,这样可以查察到步伐发送Email的运行状态 session.setDebug(true); //2.通过session得到transport对象 Transport ts=session.getTransport(); //3.使用邮箱的用户名和授权码连上邮件服务器 ts.connect("smtp.qq.com","XXXX@qq.com","授权码"); //4.创建邮件:写文件 //注意需要通报session MimeMessage message=new MimeMessage(session); //指明邮件的发件人 message.setFrom(new InternetAddress("XXXX@qq.com")); //指明邮件的收件人 message.setRecipient(Message.RecipientType.TO,new InternetAddress("XXXX@qq.com")); //邮件标题 message.setSubject("发送的标题"); //邮件的文本内容 message.setContent("内容","text/html;charset=UTF-8"); //5.发送邮件 ts.sendMessage(message,message.getAllRecipients()); //6.关闭毗连 ts.close(); }}
复制代码 5.复杂文件内容的发送
5.1文件构成分析

除了邮件内容部门,其他的部门代码往往是相同的,除了需要根据差别的邮箱运营商编写差别的设置代码外。邮件内容也被分为许多个部门,由文件、图片、附件等构成,编写邮件内容的过程,类似于积木的拼接,别的值得注意的是文本内容一般为HTML的格式发送。
每一个文本、图片、附件可以分为一个MimeBodyPart,由MimeMultipart完成组装
5.2包罗图片的发送
- 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 prop=new Properties(); prop.setProperty("mail.host","smtp.qq.com");///设置QQ邮件服务器 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对象 Session session=Session.getDefaultInstance(prop, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("11927XXX@qq.com","授权码"); } }); //开启session的debug模式,这样可以查察到步伐发送Email的运行状态 session.setDebug(true); //2.通过session得到transport对象 Transport ts=session.getTransport(); //3.使用邮箱的用户名和授权码连上邮件服务器 ts.connect("smtp.qq.com","11927XXX@qq.com","授权码"); //4.创建邮件:写文件 //注意需要通报session MimeMessage message=new MimeMessage(session); //指明邮件的发件人 message.setFrom(new InternetAddress("11927XXX@qq.com")); //指明邮件的收件人 message.setRecipient(Message.RecipientType.TO,new InternetAddress("11927XXX@qq.com")); //邮件标题 message.setSubject("java发出"); //邮件的文本内容 //=================================准备图片数据======================================= MimeBodyPart image=new MimeBodyPart(); //图片需要颠末数据化的处理处罚 DataHandler dh=new DataHandler(new FileDataSource("D:\\Bert\\1594126632(1).jpg")); //在part中放入这个处理处罚过图片的数据 image.setDataHandler(dh); //给这个part设置一个ID名字 image.setContentID("bz.jpg"); //准备正文的数据 MimeBodyPart text=new MimeBodyPart(); text.setContent("这是一张正文[align=center][img]https://www.jianchenwangluo.com/'cid:bz.jpg'[/img][/align]","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(); }}
复制代码 5.3包罗附件的发送
- import com.sun.mail.util.MailSSLSocketFactory;import javax.activation.DataHandler;import javax.activation.FileDataSource;import javax.mail.*;import javax.mail.internet.*;import java.util.Properties;public class MailDemo03 { public static void main(String[] args) throws Exception { Properties prop=new Properties(); prop.setProperty("mail.host","smtp.qq.com");///设置QQ邮件服务器 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对象 Session session=Session.getDefaultInstance(prop, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("1192XXXX@qq.com","授权码"); } }); //开启session的debug模式,这样可以查察到步伐发送Email的运行状态 session.setDebug(true); //2.通过session得到transport对象 Transport ts=session.getTransport(); //3.使用邮箱的用户名和授权码连上邮件服务器 ts.connect("smtp.qq.com","1192XXXX@qq.com","授权码"); //4.创建邮件:写文件 //注意需要通报session MimeMessage message=new MimeMessage(session); //指明邮件的发件人 message.setFrom(new InternetAddress("1192XXXX@qq.com")); //指明邮件的收件人 message.setRecipient(Message.RecipientType.TO,new InternetAddress("1192XXXX@qq.com")); //邮件标题 message.setSubject("java发出"); //邮件的文本内容 //=================================准备图片数据 MimeBodyPart image=new MimeBodyPart(); //图片需要颠末数据化的处理处罚 DataHandler dh=new DataHandler(new FileDataSource("D:\\Bert\\1594126632(1).jpg")); //在part中放入这个处理处罚过图片的数据 image.setDataHandler(dh); //给这个part设置一个ID名字 image.setContentID("bz.jpg"); //=================================准备正文数据 MimeBodyPart text=new MimeBodyPart(); text.setContent("这是一张正文[align=center][img]https://www.jianchenwangluo.com/'cid:bz.jpg'[/img][/align]","text/html;charset=UTF-8"); //=================================准备附件数据 MimeBodyPart body1= new MimeBodyPart(); body1.setDataHandler(new DataHandler(new FileDataSource("D:\\Bert\\cmd.txt"))); body1.setFileName("1.txt"); //描述数据关系 MimeMultipart mm=new MimeMultipart(); mm.addBodyPart(body1); mm.addBodyPart(text); mm.addBodyPart(image); mm.setSubType("mixed"); //设置到消息中,生存修改 message.setContent(mm); message.saveChanges(); //5.发送邮件 ts.sendMessage(message,message.getAllRecipients()); //6.关闭毗连 ts.close(); }}
复制代码 实战:注册通知邮件
index.JSP
- [size=5]Hello World![/size]
- 用户名: 暗码: 邮箱:
复制代码 实体类
- public class User { 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 + '\'' + '}'; }}
复制代码 绑定路径
- RegisterServlet cn.csn.MailDemo01.servlet.RegisterServlet RegisterServlet /RegisterServlet.do
复制代码 servlet
- import cn.csn.MailDemo01.pojo.User;import cn.csn.MailDemo01.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 doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username=req.getParameter("username"); String password=req.getParameter("pwd"); String email=req.getParameter("email"); User user=new User(username,password,email); Sendmail send=new Sendmail(user); send.start(); System.out.println("success"); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); }}
复制代码 发送邮件核心类
- import cn.csn.MailDemo01.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;public class Sendmail extends Thread { private String from="XXX@qq.com"; private String host="smtp.qq.com"; private User user; public Sendmail(User user){ this.user=user; } @Override public void run() { try { Properties prop=new Properties(); prop.setProperty("mail.host",host);///设置QQ邮件服务器 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对象 Session session= Session.getDefaultInstance(prop, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(from,"授权码"); } }); //开启session的debug模式,这样可以查察到步伐发送Email的运行状态 session.setDebug(true); //2.通过session得到transport对象 Transport ts=session.getTransport(); //3.使用邮箱的用户名和授权码连上邮件服务器 ts.connect(host,from,"授权码"); //4.创建邮件:写文件 //注意需要通报session MimeMessage message=new MimeMessage(session); //指明邮件的发件人 message.setFrom(new InternetAddress(from)); //指明邮件的收件人 message.setRecipient(Message.RecipientType.TO,new InternetAddress(user.getEmail())); //邮件标题 message.setSubject("注册通知"); //邮件的文本内容 message.setContent("恭喜你("+user.getUsername()+")乐成注册!"+"暗码:"+user.getPassword() ,"text/html;charset=UTF-8"); //5.发送邮件 ts.sendMessage(message,message.getAllRecipients()); //6.关闭毗连 ts.close(); }catch (Exception e){ System.out.println(e); } }}
复制代码 这里引入多线程的目标是为了提高用户的体验,防止因发送文件时间过长,导致前端响应过久,因此这里接纳异步响应。
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-IGkz32bF-1609316574129)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201230152905585.png)]
[外链图片转存失败,源站大概有防盗链机制,发起将图片生存下来直接上传(img-me0Qj3SH-1609316574130)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201230153222045.png)]
tomcat大概缺少包导致运行失败
来源:https://blog.csdn.net/qq_45783660/article/details/111993187
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
|