JavaWeb
1、Servlet工作原理:
先 localhost:8080/javaweb-01/zhang 请求发过来
服务器接收到请求后,会解析请求的URL路径,访问Servlet的资源路径。
根据这个路径 localhost:8080 找到正在运行的tomcat容器↓
根据idea中tomcat给当前项目配置的虚拟目录 javaweb-01 找部署在tomcat中对应的项目↓
//下面需要打开Servlet项目的web.xml文件对比解读
如果没有虚拟路径或者默认为 / ,跳过上一步直接去找项目下的 web.xml 查找遍历是否有 /zhang 对应的<servlet-mapping>里的<url-pattern>配置,再根据这个配置的同级配置<servlet-name>去找映射的<servlet>配置,最后根据<servlet>下的<servlet-class>里的全类名对应的字节码文件加载到内存中: Class cls= Class.forName(全类名),再创建对象: HelloServlet hs= cls.newInstance(),最后调用service()方法:hs.service。 因为遵守了Servlet的规则,所以会自动调用service()方法↓
最终service方法执行,由开发人员事先实现的操作来处理请求消息和返回内容。
Servlet生命周期:servlet在用户第一次请求时创建,直到服务器关闭才销毁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xydfQXGF-1624634278975)(D:\有道\Text\weixinobU7VjuPiULJulDY7dzNgusLPRY8\4ad93a3487104ece83b8819a52de829f\servlet执行原理.png)]
得出的结论:
Servlet依赖于toncat容器,容器帮Servlet实现方法的调用
xml文件中的<servlet-mapping>中的路径优先级为 单指>多指>/*
ServletContext(上下文/环境共享数据)(后期使用session代替):
ServletContext(上下文),可以实现数据共享,一个Servlet通过创建实例存入一些数据或者节点信息之后,所有的Servlet都可以通过创建ServletContext的实例来拿到这些节点信息
注:存入数据以k-v的方式
ServletContext的方法:
首先 ://获取context对象 :ServletContext context = this.getServletContext();
获取web应用的初始化参数
String gp = context.getInitParameter("url");
//返回的结果:
"jdbc:mysql://localhost:3306:mybatis"
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306:mybatis</param-value>
</context-param>
请求转发(后期使用request代替)
//请求转发:传一个目标路径,然后 forward转发把request和response的参数传进去
//这里起到的只是将请求转发的作用
context.getRequestDispatcher("/contexTest").forward(req,resp);
<!--转发的url-->
<servlet>
<servlet-name>contexTest</servlet-name>
<servlet-class>com.zsh.servlet.ReaderContext</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>contexTest</servlet-name>
<url-pattern>/context</url-pattern>
</servlet-mapping>
解释一下转发和重定向的大概意思
假设有 A(客户端) B(1号服务器) C(2号服务器) 这样的关系
转发:
A想去拿一个资源,这个资源只有C有,但是A不知道有C的存在,也就是不知道C有资源和如何访问到C,只能面向B。
B接收到请求之后B做了一件事,把A需要的资源请求转发到C,C接收到请求之后返回资源给B。
B再将资源发送给A,A从头到尾都不知道C的存在
重定向:
A想去拿一个资源,这个资源只有C有,但是A不知道有C的存在,也就是不知道C有资源和如何访问到C,只能面向B。
B接收到请求之后B做了一件事,把A请求的目标变为C,去访问C可以拿到这个资源。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28IcfCMg-1624634278977)(C:\Users\Administrator\Pictures\QQ浏览器截图\请求和重定向.PNG)]
读取资源文件(Properties)(也不会常用这种方式,代替的方式有 类加载、反射机制等待其他技术)
1)、在java类路径下新建properties
2)、在resources目录下新建properties
发现:每次都被打包到了同一个路径下classes,这个路径称为类路径:classpath
思路:需要一个文件流去读取路径下的文件
//返回InputStream字节输入流
//参数为Resources下的资源路径 \代表当前target下的web应用
//给一个文件流
InputStream stream = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
//通过new Properties类来操作 Properties资源文件
Properties prop = new Properties();
prop.load(stream);
//getkey返回value
String s = prop.getProperty("username");
String s2 = prop.getProperty("password");
2、HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象
和一个代表响应的HttpServletResponse;
如果要获取客户端请求过来的参数:找HttpServletRequest
如果要给客户端响应一些信息:找HttpServletResponse
简单分类
负责向浏览器发送数据的方法
//实现父接口获得的方法
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
//实现父接口获得的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
//自己类中的方法
void setDateHeader(String var1, long var2);
void addDateHeader(String var1, long var2);
void setHeader(String var1, String var2);
void addHeader(String var1, String var2);
void setIntHeader(String var1, int var2);
void addIntHeader(String var1, int var2);
void setStatus(int var1);
响应的状态码
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
//200 = ok
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
//3xx = 请求重定向
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
//400 = 错误的请求
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
//404 = 未找到
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
//500 = 内部服务器错误
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
//502 = 网关错误
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
//504 = 网关超时
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
下载文件
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//- 获取下载文件的路径 直接使用绝对路径 写死了??
String realPath = "D:\\ideaProject\\az\\javaweb-02-servlet\\httpresponse\\target\\classes\\彭于晏.jpg";
System.out.println("----->"+realPath);
//- 定义下载的文件名 记住:一个资源路径的最后一位/后面的字符就是请求文件的文件名
//这行代码的意思是:截取获得的绝对路径最后的 / 后面的字符
String filename = realPath.substring(realPath.lastIndexOf("/") + 1);
//- 设置浏览器支持我下载需要的资源 修改字符编码,防止中文名文件下载时出现乱码 也不一定解决
resp.setHeader("Content-Disposition","attachment;filename=" + URLEncoder.encode(filename,"UTF-8"));
//- 获取下载文件的输入流 把文件加载到流中
FileInputStream in = new FileInputStream(realPath);
//- 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
//- 获取response的OutputStream对象
ServletOutputStream out = resp.getOutputStream();
//- 将FileOutputStream流写入到buffer缓冲区
//- 使用OutputStream将缓冲区的数据输出到客户端
while ((len = in.read(buffer))> 0){
out.write(buffer,0,len);
}
//- 关闭流
out.close();
in.close();
}
//再去web.xml文件补充servlet信息和映射
//访问下载
//成功!
验证码功能
验证怎么来的?
-
前端实现
-
后端实现
这种方法以后基本碰不到,了解一下代码格式流程,大概意思就好
第一步:需要用到java的图片类,产生一个图片
@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); //拿到这张图片,以2d的格式 Graphics2D graphics = (Graphics2D) image.getGraphics(); //设置这张图片填充的背景颜色 graphics.setBackground(Color.WHITE); //填充位置 参数:坐标 宽 高 graphics.setClip(0,0,80,20); //字体颜色 graphics.setColor(Color.red); //设置字体样式 第一个节点为null,字体类型,大小 graphics.setFont(new Font(null,Font.BOLD,20)); //给图片写入随机数,坐标 graphics.drawString(makeRandom(),0,20); //告诉浏览器,这个请求用图片打开 resp.setContentType("image/png"); //网站存在缓存,不让浏览器缓存 这些都是缓存策略 resp.setDateHeader("expires",-1); resp.setHeader("Cache-Control","no-cache"); resp.setHeader("Pragma","no-cache"); //把图片写到浏览器 ImageIO.write(image,"png",resp.getOutputStream()); }第二步:创建一个随机数类
//生成随机数作为验证码 private String makeRandom(){ //先获取随机数的对象 Random random = new Random(); //创建指定范围的随机数,加一个空串让这个随机数变为String类型 String num = random.nextInt(999999) + ""; //防止出现低于六位数的随机数,计算得到的随机数需要补几个 0 //i < 6-s.length() 意思是如果拿到的随机数不够六位的 也就是 > 6那就进入循环 sb添加进去一个零,与6相比随机数的length差n<=位就会进入n<6次循环,补的0就有n<=6个 StringBuffer sb = new StringBuffer(); for (int i = 0; i < 6-num.length() ; i++) { sb.append("0"); } num = num + sb.toString(); return num; }String random = drawRandomNum((Graphics2D) g,“l”);//生成纯字母的验证码
Response重定向
//需要使用的方法
void sendRedirect(String var1) throws IOException;
//如果这个servlet被访问就会重定向到 /yzm
//记得加上tomcat中设置的项目路径
resp.sendRedirect("/rp/yzm");
/*
剖析一下原理,其实就做了两步
*/
//跳转的路径
resp.setHeader("Location","/rp/yzm");
//状态码,3xx = 请求重定向
resp.setStatus(302);
面试题
请你聊聊重定向和请求转发的区别
相同点:都会实现页面跳转
不同点:
请求转发的时候,url不会发生变化 header状态码:307
重定向的时候,url地址栏会发生变化 header状态码:302
3、HttpServletRequest
HttpServletRequest代表用户端的请求,用户通过http协议访问服务器
转发不需要虚拟项目路径,重定向需要
获取前端请求参数,并请求转发
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求和响应的编码格式,保证不乱码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
//单个接收前端发送的请求
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbies = req.getParameterValues("hobbies");
//控制台打印
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbies));
//转发到成功页面
req.getRequestDispatcher("/success.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
4、cookie,session
逻辑和原理
由于HTTP是一种无状态协议,服务器没有办法单单从网络连接上面知道访问者的身份,为了解决这个问题,就诞生了Cookie
Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie
客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,
以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。
实际就是颁发一个通行证,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理
cookie 可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量,
而 Session 的出现正是为了解决这个问题。同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器的时候生成的, 而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 NANE 为JSESIONID 的一个 Cookie。
源码和工作原理剖析
cookie的内容主要包括name(名字)、value(值)、maxAge(失效时间)、path(路径),domain(域)和secure
name:cookie的名字,一旦创建,名称不可更改。
value:cookie的值,如果值为Unicode字符,需要为字符编码。如果为二进制数据,则需要使用BASE64编码.
maxAge:cookie失效时间,单位秒。如果为正数,则该cookie在maxAge后失效。如果为负数,该cookie为临时cookie,关闭浏览器即失效, 浏览器也不会以任何形式保存该cookie。如果为0,表示删除该cookie。默认为-1
path:该cookie的使用路径。如果设置为"/sessionWeb/",则只有ContextPath为“/sessionWeb/”的程序可以访问该cookie。如果设置为“/”,则本域名下ContextPath都可以访问该cookie。
domain:域.可以访问该Cookie的域名。第一个字符必须为".",如果设置为".google.com",则所有以"google.com结尾的域名都可以访问该cookie",如果不设置,则为所有域名
secure:该cookie是否仅被使用安全协议传输。
Session机制
Session机制是一种服务端的机制,服务器使用一种类似散列表的结构来保存信息。
当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端里的请求里是否已包含了一个session标识--sessionID,
如果已经包含一个sessionID,则说明以前已经为此客户端创建过session,服务器就按照sessionID把这个session检索出来使用
如果客户端请求不包含sessionID,则为此客户端创建一个session并且声称一个与此session相关联的sessionID,
sessionID的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串(服务器会自动创建),这个sessionID将被在本次响应中返回给客户端保存。
两种技术的优弊端or解决方案
使用cookie的缺点
如果浏览器使用的是cookie,那么所有的数据都保存在浏览器端,
cookie可以被用户禁止
cookie不安全(对于敏感数据,需要加密)
cookie只能保存少量的数据(大约是4k),cookie的数量也有限制(大约是几百个),不同浏览器设置不一样,反正都不多
cookie只能保存字符串
对服务器压力小
使用session的缺点
一般是寄生在Cookie下的,当Cookie被禁止,Session也被禁止
当然可以通过url重写来摆脱cookie
当用户访问量很大时,对服务器压力大
我们现在知道session是将用户信息储存在服务器上面,如果访问服务器的用户越来越多,那么服务器上面的session也越来越多, session会对服务器造成压力,影响服务器的负载.如果Session内容过于复杂,当大量客户访问服务器时还可能会导致内存溢出。
用户信息丢失, 或者说用户访问的不是这台服务器的情况下,就会出现数据库丢失.
session销毁
手动销毁
某个对象.getSession.invalidate();
自动销毁
<!--在xml配置session的销毁时间-->
<session-config>
<session-timeout>1</session-timeout> //分钟
</session-config>
cookie和session的区别
具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案。同时我们也看到, 由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的
cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session
session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie
单个cookie保存的数据不能超过4k,很多浏览器都限制一个站点最多保存20个cookie。
可以将登陆信息等重要信息存放为session。
cookie是把用户的数据写给用户浏览器,浏览器保存(可以多个)
session是把用户的数据写到用户独占的session中,服务端保存(只保存重要的信息,减少资源的浪费),且对象由服务器创建
session-config
session配置
<session-config>
<cookie-config></cookie-config> cookie配置
<session-timeout></session-timeout> session超时时间,多久之后过期,单位:分钟
<tracking-mode></tracking-mode>
</session-config>
5、JSP(Java Server Pages)
jsp : Java服务端页面,也和servlet一样,用于动态web技术!
最大的特点:
- 写JSP就像在写HTML
- 区别
- HTML只给用户提供静态的数据
- JSP中可以嵌入Java代码,为用户提供动态数据
浏览器向服务器发送请求,不管访问什么资源,其实都是在访问servlet
jsp最终也会被转换为一个java类
jsp本质上就是一个servlet
这是jsp.java中的方法
//初始化
public void _jspInit(){
}
//销毁
public void _jspDestroy(){
}
//JSPService
public void _jspService(.HttpServletRequest req,HttpServletResponse resp){
这个方法中先判断了请求是否错误,然后判断请求为post还是get
}
内置了一些对象
final javax.servlet.jsp.PageContext pageContext; //页面上下文
javax.servlet.http.HttpSession session = null; //会话
final javax.servlet.ServletContext application; //applicationContext
final javax.servlet.ServletConfig config; //配置
javax.servlet.jsp.JspWriter out = null; //写出
final java.lang.Object page = this; //page = this:代表当前页面
HttpServletRequest req //请求
HttpServletResponse resp //响应
//后面还有两个不需要了
输出页面前的代码
response.setContentType("text/html"); //定义响应的页面类型
pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext(); //application 本质上就是 ServletContext
config = pageContext.getServletConfig(); //获取配置对象
session = pageContext.getSession(); //会话对象
out = pageContext.getOut(); //写出对象
_jspx_out = out;
//以上的对象,在JSP中可以直接使用
访问JSP的整个过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b4FBiTIN-1624634278978)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210331084112199.png)]
JSP页面中:
只要是嵌入在<%这里的代码%>,直接原封不动的用out.write()写到页面
只要是HTML代码,就会转换为out.write(“字符串,这里会自动识别是否需要换行”)写到页面
JSP基础语法
先导入需要的依赖
<dependencies>
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!--jsp依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!--JSTL表达式依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!--jsp标签库-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
JSP注释:<%–在这写–%>
JSP表达式:<%= 变量或者表达式%>
<%里面写的Java代码可以分段插入html代码 比如:
for(int i = 0;i<100;i++){
out.write("111");
%>
<h1>
你好
</h1>
<%
out.write("2222");
}
%>
JSP声明:
<%!在这写%>
<%!
写函数的地方,这里写的代码会被编译到类中,其他的会被编译到_jspService方法中
作用域更大,可以写全局方法、变量和静态代码块
%>
EL表达式:
JSP的注释不会在客户端显示
JSP指令
<%注意%>
这个脚本文件里写的代码片段一定要是Java代码,注释也是,不能出现jsp,html的代码和注释。
因为这个代码片段会被编译成java代码,其他语言的代码在这个片段里被编译之后无法转换为java语法的代码,
定制错误跳转页面
<%--访问到这个页面的时候,错误代码为500时就会跳转到 "error/500.jsp"--%>
<%@ page errorPage="error/500.jsp" %>
<%--显示的声明这是一个错误页面%>
<%@ page isErrorPage="true" %>
xml文件中配置访问错误映射
<error-page>
<!--接收什么错误代码-->
<error-code>404</error-code>
<!--异常类型-->
<exception-type></exception-type>
<!--接收到错误之后跳转的位置-->
<location>/error/500.jsp</location>
</error-page>
9大内置对象
- PageContext 存东西
- Request 存东西
- Response
- Session 存东西
- Application 【ServletContext】 存东西
- Config 【ServletConfig】
- out
- Page 不用
- exception
内置对象的作用域及生命周期
<%--内置对象--%>
<%
pageContext.setAttribute("name1","小张一号");//保存的数据只在一个页面中有效
request.setAttribute("name2","小张二号");//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","小张三号");//保存的数据只在一次会话中有效,生命周期在打开浏览器到关闭浏览器
application.setAttribute("name4","小张四号");//保存的数据在服务器中有效,生命周期从打开服务器到关闭服务器
%>
<%
//从底层到高层(作用域)去查找:Page-->Request--->Session--->Application
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
String name5 = (String) pageContext.findAttribute("name5");//没有这个数据
%>
<%--使用EL表达式输出--%>
<h1>${name1}</h1>
<h1>${name2}</h1>
<h1>${name3}</h1>
<h1>${name4}</h1>
<%--这个不存的数据用两种方式输出--%>
<%--输出结果:什么也没有,空--%>
<h1>${name5}</h1>
<%--输出结果:NULL--%>
<h1><%=name5%></h1>
开启另一个会话再访问(启动另一个浏览器窗口)
<%
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
String name5 = (String) pageContext.findAttribute("name5");//没有这个数据
%>
<h1>${name1}</h1>
<h1>${name2}</h1>
<%--打开新的标签页,取出;打开新的浏览器窗口之后找不带该元素,结果为空--%>
<h1>${name3}</h1>
<%--取出--%>
<h1>${name4}</h1>
<%--空--%>
<h1>${name5}</h1>
<%--NULL--%>
<h1><%=name5%></h1>
结论
request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!
session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车;
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:评论;
重定向前后端方式
Servlet:
request.getRequestDispatcher("路径.jsp/.html/.htm").forward(request,response);
Jsp
pageContext.forward("路径");
JSP标签库(JSTL标签)、EL表达式
使用 JSTL标签 需要引入依赖
<!--JSTL表达式依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!--jsp标签库-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
EL表达式: ${ }
-
获取数据
-
执行运算
-
获取web开发常用对象
JSP标签:
JSP页面合并
<%--这个会将这个文件里的代码提取出来合并到一个页面,且原页面和引入页面不可定义同名变量--%>
<%@include file="500.jsp"%>
<%--这个jsp标签是以引入的方式拼接两个页面,灵活性更高,使用这个--%>
<%--在本页面定义一个和引入页面相同的变量不会产生冲突--%>
<jsp:include page="404.jsp">
JSP请求转发
<%--转发的页面--%>
<jsp:forward page="index.jsp">
<%--转发可以带多个参数,以k-v的方式 --%>
<jsp:param name="key1" value="value1"></jsp:param>
<jsp:param name="key2" value="value2"></jsp:param>
</jsp:forward>
<%--取出参数的方式,指的是取参数功能的对比--%>
<%--EL表达式只能获取到request发过来的参数,不能取到request对象,不建议使用--%>
${requestScope.get("key1")}
${requestScope.get("key2")}
-----------------------------------------------------------------------------
<%--jsp的方式取参数--%>
<%=request.getParameter("key1")%>
<%=request.getParameter("key2")%>
JSP使用bean对象
<%
//使用一个bean对象
//下面代码等同于People people = new People();
%>
<jsp:useBean id="people" class="com.zsh.pojo.People" scope="page"/>
<%--
给这个bean赋值 等同于 people.set
name:useBean的id
property:要赋值的字段名
value:值
--%>
<jsp:setProperty name="people" property="id" value="1"/>
<%--
获取这个bean的某个属性 等同于 people.get
--%>
<jsp:getProperty name="people" property="id" />
JSTL表达式:
JSTL表达式的使用是为路弥补HTML标签的不足;它自定义许多标签,可以提供我们使用,标签的功能和 Java 代码一样!
格式化标签
SQL标签
XML 标签
核心标签 (掌握部分)

JSTL标签库使用步骤
-
引入对应的 taglib
-
<%--需要在jsp中配置这行代码--%> 这个prefix="c" c代表本jsp中代表jstl语法出现的格式,如:c:if c:out c:when ..... <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-
-
使用其中的方法
-
在 Tomcat 也需要引入 JSTL 的包,否则会报错:JSTL 解析错误
找到这两个jar包的文件位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-weBGqYjC-1624634278981)(C:\Users\Administrator\Pictures\QQ浏览器截图\jar包 2021-04-07 152956.png)]
复制到tomcat的lib目录中
F:\JAVA\tomcat\apache-tomcat-9.0.41\lib
c:if
<%--表单提交本页面--%>
<form action="jstlOut.jsp" method="get">
<%--
使用EL表达式中的param对象拿到提交过来的参数,点后面跟参数名
${param.参数}
--%>
用户名:<input type="text" name="username" value="${param.username}">
<input type="submit" value="提交">
</form>
<%--判断传过来的参数是否等于 admin var接收返回的参数。返回一个布尔值--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="登录成功"/>
</c:if>
<c:out value="${isAdmin}">
c:choose c:when
<body>
<%--定义一个变量score,值为85--%>
<c:set var="score" value="55"/>
<c:choose>
<c:when test="${score>=90}">
你的成绩为优秀
</c:when>
<c:when test="${score>=80}">
你的成绩为一般
</c:when>
<c:when test="${score>=70}">
你的成绩为良好
</c:when>
<c:when test="${score<=60}">
你的成绩为不及格
</c:when>
</c:choose>
</body>
c:foreach
<%
//存入一些数据
ArrayList<String> list = new ArrayList<>();
list.add(0,"张三");
list.add(1,"张四");
list.add(2,"张五");
list.add(3,"张六");
//存到request中,也可以放到其他作用域中
request.setAttribute("list",list);
%>
<%--使用foreach遍历 var="list"的意思是接收的变量名 遍历集合/数组:items="${list}" --%>
<c:forEach var="list" items="${list}">
<%--输出--%>
<c:out value="${list}"/>
</c:forEach>
JavaBean
实体类
- 有无参构造方法
- 属性必须私有化
- 有对应的setter/getter
一般用于做数据库字段映射 英文缩写:ORM(一般指对象关系映射)
MVC三层架构
- M:model //也就是实体类
- 业务层 :Service
- 数据持久层 CRUD:Dao
- 是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
- V:view //前端页面;html、jsp
- 展示数据;提供链接;发起servlet请求
- C:Controller //Servlet?
- Controller层
- 专注于处理用户请求(req、session…)
- 交给业务层处理需要实现的操作
- 以及控制视图(jsp/html)跳转
Filter(过滤器)
实现Filter接口,不要点错了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mfHYT0bq-1624634278981)(C:\Users\Administrator\Pictures\QQ浏览器截图\Filter实现的接口.png)]
实现
//解决乱码问题的过滤器
public class CharacterEncodingFilter implements Filter {
//初始化
//web服务器启动时就已经初始化,随时等待过滤对象出现
//filterConfig这个参数可以拿到一些数据,比如上下文对象和过滤器名字、过滤器参数等等
//可以通过拿到上下文来操作一些信息,比如设置一些启动前就有的固有属性
//但是不建议这样做,太笨了
filterConfig.getServletContext();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("****CharacterEncodingFilter初始化****");
}
//FilterChain 过滤器 链
/*
1.过滤中的所有代码,在过滤特定请求时都会执行
2.必须让过滤器继续通信
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//设置文字编码过滤
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=UTF-8");
System.out.println("数据经过过滤器前");
//必须要写,固定代码
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("数据经过过滤器后");
}
//销毁
@Override
public void destroy() {
//可以在过滤器关闭的时候做一些操作
System.out.println("****CharacterEncodingFilter销毁****");
}
}
filter配置
<servlet>
<servlet-name>show</servlet-name>
<servlet-class>com.zsh.servlet.ShowServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>show</servlet-name>
<!--这里加一层请求(/servlet)为了方便请求servlet前能被filter也接收到这个请求-->
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<!--配置Filter请求-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.zsh.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!--过滤servlet请求-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
Listener(监听器)
JUI编程中经常使用
-
通过实现Listener相关的接口来创建一个监听器
-
重写对应的方法:新建监听、销毁监听
-
xml映射对应的class路径
-
<listener> <listener-class>com.zsh.listener.ListenerTest</listener-class> </listener>
-
过滤器、监听器常用应用
javaweb-jsp项目下的smbms项目实例
需求:登录之后跳转到主页,注销后不能进入主页
须知
//常量类
public class Constant {
public final static String USER_SESSION = "USER_SESSION";
}
<!--登录请求映射-->
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.zsh.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/servlet/login</url-pattern>
</servlet-mapping>
<!--注销请求映射-->
<servlet>
<servlet-name>destroy</servlet-name>
<servlet-class>com.zsh.servlet.Destroy</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>destroy</servlet-name>
<url-pattern>/servlet/destroy</url-pattern>
</servlet-mapping>
<!--过滤Sys请求-->
<filter>
<filter-name>sys</filter-name>
<filter-class>com.zsh.filter.SysFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sys</filter-name>
<url-pattern>/sys/*</url-pattern>
</filter-mapping>
用户提交后,向Session中存入用户的sessionId作为一个节点信息
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
if ("admin".equals(username)){
//登录成功
//存入一个标志到Session,方便多页面获取
req.getSession().setAttribute(Constant.USER_SESSION,req.getSession().getId());
resp.sendRedirect("/sys/corePage.jsp");
}else {
//登录失败,跳转到错误页面
resp.sendRedirect("/error.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
使用过滤器,防止未登录访问私密页面
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 {
//转换成与Servlet一致的请求和响应
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//如果Session里没有用户登录信息
if (request.getSession().getAttribute(Constant.USER_SESSION) == null){
//跳转到错误页面
response.sendRedirect("/error.jsp");
}
//固定代码,链接
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
处理注销请求
public class Destroy extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//点击注销之后跳转到登录页
Object user_session = req.getSession().getAttribute(Constant.USER_SESSION);
if (user_session!=null) {
req.getSession().removeAttribute(Constant.USER_SESSION);
resp.sendRedirect("/login.jsp");
}else {
resp.sendRedirect("/login.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
JDBC(初学)
jdbc:Java Database Connection (java数据库连接)
好处:
1) 程序员如果要开发访问数据库的程序,只需要会调用 JDBC 接口中的方法即可,不用关注类是如何实现的。
2) 使用同一套 Java 代码,进行少量的修改就可以访问其他 JDBC 支持的数据库

使用 JDBC 开发使用到的包:
| 会使用到的包 | 说明 |
|---|---|
| java.sql | 所有与 JDBC 访问数据库相关的接口和类 |
| javax.sql | 数据库扩展包,提供数据库额外的功能。如:连接池 |
| 数据库的驱动 | 由各大数据库厂商提供,需要额外去下载,是对 JDBC 接口实现的类 |
JDBC 的核心 API
| 接口或类 | 作用 |
|---|---|
| DriverManager 类 | 1) 管理和注册数据库驱动 2) 得到数据库连接对象 |
| Connection 接口 | 一个连接对象,可用于创建 Statement 和 PreparedStatement 对象 |
| Statement 接口 | 一个 SQL 语句对象,用于将 SQL 语句发送给数据库服务器 |
| PreparedStatemen 接口 | 一个 SQL 语句对象,是 Statement 的子接口 |
| ResultSet 接口 | 结果集 |
Statement 类不安全,传输sql语句时可能发生sql注入
安全的PreparedStatement对象,防止sql注入 使用这个 这个好
PreparedStatement statement1 = connection.prepareStatement("sql写在这里");
JDBC固定步骤
1.加载驱动
2.连接数据库,获得数据库连接对象
3.获取向数据库发送SQL的对象:Statement:CRUD
4.根据业务需求编写SQL
5.执行SQL
6.关闭连接
实例
搭建环境
1)数据库表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-41hUflgP-1624634278984)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210413092736540.png)]
2)导包
<dependencies>
<!--mysql的驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
</dependencies>
3)idea连接数据库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mcIn87BK-1624634278984)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210413092951772.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0cU5SUJ1-1624634278985)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210413093041543.png)]
代码实现
实体类
//实体类
public class Users {
private Integer id;
private String name;
private String password;
private String email;
private Date birthDay;
//重写的方法.....
}
普通编译sql
//测试jdbc连接
public class UsersJDBC {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//配置信息
//定义连接的url,方便后面多次使用
String url= "jdbc:mysql://localhost:3306/jdbc-test?useUnicode=true&characterEncoding=UTF-8";
String username="root";
String password="123456";
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库 CRUD
Connection connection = DriverManager.getConnection(url, username, password);
//向数据库发送sql语句的对象;不安全,可能发生sql注入
Statement statement = connection.createStatement();
//安全的PreparedStatement对象,防止sql注入 使用这个 这个好
PreparedStatement statement1 = connection.prepareStatement("sql写在这里");
//设计sql
String sql="select * from users";
//提交并执行sql语句,返回结果集
ResultSet set = statement.executeQuery(sql);
//String sql="delete from users where id = 4";
//增删改都是用statement.executeUpdate(sql)
//使用链表的方式遍历出所有数据
while (set.next()){
//set.getObject因为存在可能不知道表中数据类型的情况,使用Object来存储拿到的数据
System.out.println("id="+set.getObject("id"));
System.out.println("name="+set.getObject("name"));
System.out.println("password="+set.getObject("password"));
System.out.println("email="+set.getObject("email"));
System.out.println("birthDay="+set.getObject("birthDay"));
System.out.println("-------------------------------------------------");
}
//关闭资源连接,先开后关
set.close();
statement.close();
connection.close();
}
}
预编译sql(使用这个)
public class UsersJDBC2 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//配置信息
//定义连接的url,方便后面多次使用
String url= "jdbc:mysql://localhost:3306/jdbc-test?useUnicode=true&characterEncoding=UTF-8";
String username="root";
String password="123456";
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库 CRUD
Connection connection = DriverManager.getConnection(url, username, password);
//设计sql,使用占位符 后赋值的方式来插入数据
String sql="insert into users(id, name, password, email, birthDay) VALUES (?,?,?,?,?);";
//使用预编译的安全连接
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1,7);//给第一个占位符的字段添加一个数据 ‘6’
statement.setString(2,"五香蛋");
statement.setString(3,"123456");
statement.setString(4,"wxd@163.com");
statement.setDate(5,new java.sql.Date(new java.util.Date().getTime()));
//执行SQL,返回记录更新条数
int i = statement.executeUpdate();
if (i>0){
System.out.println("插入成功@" + i);
}else {
System.out.println("插入失败");
}
statement.close();
connection.close();
}
}
SMBMS项目搭建
1.准备工作
1.使用maven的方式创建项目,不使用maven的的话,需要手动导jar包
2.配置tomcat
3.测试tomcat是否搭建成功
4.导入依赖
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!--jsp的依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!--数据库连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
</dependency>
<!--jstl的依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--jsp标签库-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
5.创建项目包结构
6.ORM映射 (倒也不用这么多,建着玩)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FSFkzKWZ-1624634278986)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210415091358743.png)]
D:\ideaProject\az\smbms\src\main\java\com\zsh\pojo 目录下
7.编写基础公共类
1.数据库配置文件 在resources/db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/smbms?useSSL=false&useUnicode=true&characterEncoding=utf-8
user=root
password=123456
package com.zsh.dao;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
//操作数据库的公共类
public class BaseDao {
//配置信息映射的字段
private static String driver;
private static String url;
private static String name;
private static String password;
//在类加载的时候初始化
static {
//用于获取配置文件中的信息
Properties properties = new Properties();
//以流的方式拿到配置文件
InputStream resource = BaseDao.class.getClassLoader().getResourceAsStream("db.properties");
try {
//加载这个拿到的配置文件
properties.load(resource);
} catch (IOException e) {
e.printStackTrace();
}
//k-v
driver = properties.getProperty("driver");
url = properties.getProperty("url");
name = properties.getProperty("user");
password = properties.getProperty("password");
}
//获取数据库连接对象
public static Connection getConnection(){
Connection connection = null;
try {
Class.forName(driver);
connection = DriverManager.getConnection(url,name,password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
//查询方法
/*
参数解析
Connection connection:需要传过来一个数据库连接,里面有关于数据库的信息
String sql :徐娅执行的sql语句
Object[] params :sql的参数列表
下面的参数只是为了方便使用,而不是真正需要的参数
ResultSet set:返回的结果集
PreparedStatement preparedStatement:提交sql的对象
*/
public static ResultSet execute(Connection connection, String sql, Object[] params, ResultSet set, PreparedStatement preparedStatement) {
try {
//预编译之后赋值给 preparedStatement 后面直接执行
preparedStatement = connection.prepareStatement(sql);
//循环给参数赋值
for (int i = 0; i <params.length ; i++) {
preparedStatement.setObject((i+1),params[i]);
}
//不需要填写sql 因为已经预编译过了
set = preparedStatement.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return set;
}
//增删改方法
public static int update(Connection connection,String sql,Object[] params ,PreparedStatement preparedStatement){
int count = 0;
try {
preparedStatement =connection.prepareStatement(sql);
for (int i = 0; i < params.length ; i++) {
preparedStatement.setObject((i+1),params[i]);
}
count = preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
public static boolean resourceClose(Connection connection,PreparedStatement preparedStatement){
if (preparedStatement!= null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
if (connection!= null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
return true;
}
}
8.编写字符编码过滤器
package com.zsh.filter;
import javax.servlet.*;
import java.io.IOException;
public class CharacterEncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setCharacterEncoding("utf-8");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
再去xml文件注册
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.zsh.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/暂定</url-pattern>
</filter-mapping>
9.导入静态文件
文件目录:F:\JAVA\git\clone\smbms\src\main\webapp
拷贝到
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZrUfNidr-1624634278986)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210415194201840.png)]
2.登录功能的实现
1.编写前端页面(写得出来?)
2.设置首页(项目启动页)
<!--设置首页-->
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
3.编写登录功能的Dao接口
public interface UserDao {
//这里的connection会在实现类中传入 BaseDao的connection
//userCode是登录时传入的用户名
public User getLoginUser(Connection connection,String userCode);
}
4.具体实现这个Dao接口
package com.zsh.dao.user.imp;
import com.zsh.dao.BaseDao;
import com.zsh.dao.user.UserDao;
import com.zsh.pojo.User;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDaoImp implements UserDao {
@Override
public User getLoginUser(Connection connection, String userCode) {
//固定参数,作用于
PreparedStatement preparedStatement = null;
ResultSet set = null;
User user = new User();
if (connection!=null){
//定义sql语句
String sql = "SELECT * FROM smbms_user WHERE userCode = ?";
//占位符 参数
Object[] params = {userCode};
//调用查询方法返回结果集
set = BaseDao.execute(connection, sql, params, set, preparedStatement);
try {
//将拿到的结果集遍历赋值给User
if (set.next()) {
user = new User();
user.setId(set.getInt("Id"));
user.setUserCode(set.getString("userCode"));
user.setUserName(set.getString("username"));
user.setUserPassword(set.getString("UserPassword"));
user.setGender(set.getInt("gender"));
user.setBirthDay(set.getDate("birthday"));
user.setPhone(set.getString("phone"));
user.setAddress(set.getString("address"));
user.setUserRole(set.getInt("userRole"));
user.setCreateBy(set.getInt("createdBy"));
user.setCreationDate(set.getDate("creationDate"));
user.setModifyBy(set.getInt("modifyBy"));
user.setModifyDate(set.getDate("modifyDate"));
}
//关闭资源
//connection不需要在这里关闭,还有后面的功能需要使用到这个连接
//频繁创建新的连接会狼浪费大量资源
BaseDao.resourceClose(null,preparedStatement,set);
} catch (SQLException e) {
e.printStackTrace();
}
}
return user;
}
}
5.业务层接口
public interface UserService {
//用户登录业务
public User login(String userCode,String userPassword);
}
6.业务层实现类
package com.zsh.service.user.imp;
import com.zsh.dao.BaseDao;
import com.zsh.dao.user.UserDao;
import com.zsh.dao.user.imp.UserDaoImp;
import com.zsh.pojo.User;
import com.zsh.service.user.UserService;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
public class UserServiceImp implements UserService {
//业务层都会调用Dao层
private UserDao userDao;
//拿到UserDaoImp
public UserServiceImp(){
userDao = new UserDaoImp();
}
@Override
public User login(String userCode, String userPassword) {
//创建一个连接
Connection connection = null;
User user = null;
try {
//给这个连接赋值
connection = BaseDao.getConnection();
user = userDao.getLoginUser(connection, userCode);
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.resourceClose(connection,null,null);
}
//匹配密码
//根据用户名取到这个user之后,那这个user里的密码和传过来的密码匹配
if(null != user){
if(!user.getUserPassword().equals(userPassword))
user = null;
}
return user;
}
}
7.编写Servlet
package com.zsh.servlet.user;
import com.zsh.pojo.User;
import com.zsh.service.user.UserService;
import com.zsh.service.user.imp.UserServiceImp;
import com.zsh.util.Constants;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LoginServlet extends HttpServlet{
//控制层(controller??)调用Service层 先用new的方式
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//Service
UserService service = new UserServiceImp();
//拿到传过来的参数
//账户
String userCode = req.getParameter("userCode");
//密码
String userPassword = req.getParameter("userPassword");
User user = service.login(userCode, userPassword);
//如果不为null表示匹配成功并且拿到数据
if (user.getUserCode().equals(userCode) && user.getUserPassword().equals(userPassword)){
//将信息存入Session中,新页面无需登录 使用常量类
req.getSession().setAttribute(Constants.USER_SESSION,user);
//进入首页,请求转发,带上参数
resp.sendRedirect("jsp/frame.jsp");
}else{ //查无此人
//存入一个error信息
//前端页面会找到这个error并且执行对应的操作
req.setAttribute("error","用户名或者密码不正确");
//带上错误信息转发到登录页
req.getRequestDispatcher("login.jsp").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
8.注册Servlet
<!--登录Servlet-->
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.zsh.servlet.user.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login.do</url-pattern>
</servlet-mapping>
3.注销和权限访问
1.注销
无需写业务以及更底层的代码,因为只需要删除Session就可以实现注销
编写Servlet
package com.zsh.servlet.user;
import com.zsh.util.Constants;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//注销请求
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//删除这个user的Session
req.getSession().removeAttribute(Constants.USER_SESSION);
//重定向到登录页
resp.sendRedirect(req.getContextPath()+"/login.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
xml映射
<!--注销Servlet-->
<servlet>
<servlet-name>LogoutServlet</servlet-name>
<servlet-class>com.zsh.servlet.user.LogoutServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogoutServlet</servlet-name>
<url-pattern>/jsp/logout.do</url-pattern>
</servlet-mapping>
2.权限访问
防止未登录访问后台页面
使用过滤器实现
package com.zsh.filter;
import com.zsh.util.Constants;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class LimitsFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//把servletRequest、servletResponse转换成Http
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (request.getSession().getAttribute(Constants.USER_SESSION) == null){
//跳转到权限错误页面
response.sendRedirect(request.getContextPath()+"/error.jsp");
}
//提交形参,不是自己转换的参数
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
xml配置Filter
<!--请求权限过滤器-->
<filter>
<filter-name>LimitsFilter</filter-name>
<filter-class>com.zsh.filter.LimitsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LimitsFilter</filter-name>
<url-pattern>/jsp/*</url-pattern>
</filter-mapping>
测试以上功能是否成功
AJAX总结
使用Jquery就导入Jquery,Vue就导入Vue,两个都用就,自己原生态实现
1.编写对应的Controller,返回字符串或json格式的数据
2.编写ajax请求:
1.url :Controller请求
2.data :键值对(传入的数据)
3.success :函数执行成功,回调函数
4.密码修改
Dao层
接口方法
//修改当前用户密码
public int updatePwd(Connection connection,int id,int password) throws SQLException;
实现类方法
//修改当前用户密码
@Override
public int updatePwd(Connection connection, int id, int password) throws SQLException {
PreparedStatement preparedStatement = null;
int updateCount = 0;
User user = new User();
if(connection!=null){
//定义sql语句
String sql = "UPDATE smbms_user SET userPassword = ? WHERE id = ?";
//参数集
Object[] params = {password,id};
updateCount = BaseDao.update(connection, sql, params, preparedStatement);
BaseDao.resourceClose(null,preparedStatement,null);
}
return updateCount;
}
Service层
接口方法
//根据id修改当前用户密码
public boolean updatePwd(int id,String pwd);
实现类方法
//修改密码业务实现
@Override
public boolean updatePwd(int id, String pwd) {
//获取数据库连接
Connection connection = null;
boolean flag = false;
try {
connection = BaseDao.getConnection();
//如果Dao的方法返回条数大于0,表示修改成功
if(userDao.updatePwd(connection,id,pwd)>0){
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.resourceClose(connection,null,null);
}
return flag;
}
Servlet层
servlet实现复用,提高效率,结合前端食用
参照前端代码进行配置
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//根据前端参数判断需要操作的是哪个功能
//前端提交表单的隐藏域:<input type="hidden" name="method" value="?">
String method = req.getParameter("method");
//前端代码 <input type="hidden" name="method" value="savepwd">
if (method!=null && method.equals("savepwd")){
this.updatePwd(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
//每个功能对应的方法
public void updatePwd(HttpServletRequest req, HttpServletResponse resp){
UserService service = new UserServiceImp();
//获取提交过来的新密码
String newpassword = req.getParameter("newpassword");
//拿到User对象,先不强转过来,方便后面优化代码
Object o = req.getSession().getAttribute(Constants.USER_SESSION);
//判断Session中是否有元素,并且获取前端的参数不为空
if (o!= null && !StringUtils.isNullOrEmpty(newpassword)){
//确认里面有元素再强转
User user = (User)o;
//拿到当前用户id
Integer id = user.getId();
//调用service方法修改密码
boolean flag = service.updatePwd(id, newpassword);
if (flag){//修改成功
//存入一个节点信息
req.setAttribute("message","密码修改成功,请使用新密码登录。即将退出当前页面");
//移除旧的Session
req.removeAttribute(Constants.USER_SESSION);
/*
注意:这里不需要请求转发和重定向,因为过滤器一直在工作,只要这里删除掉Session之后
程序一定会执行到下面的转发,如果没有Session就会自动跳转到错误页面
*/
}else{
//存入一个节点信息
req.setAttribute("message","密码修改失败");
}
}else{
//存入一个节点信息
req.setAttribute("message","新密码定义错误");
}
//请求转发到
try {
req.getRequestDispatcher("modify.jsp").forward(req,resp);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
注册xml
<!--UserServlet-->
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.zsh.servlet.user.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/jsp/user.do</url-pattern>
</servlet-mapping>
旧密码验证
结合前端ajax实现
//更新后的doGet方法
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//根据前端参数判断需要操作的是哪个功能
//前端提交表单的隐藏域:<input type="hidden" name="method" value="?">
String method = req.getParameter("method");
//前端代码 <input type="hidden" name="method" value="savepwd">
if (method!=null && method.equals("savepwd")){
this.updatePwd(req,resp);
}else if(method!=null && method.equals("pwdmodify")){
this.pwdModify(req,resp);
}
}
//验证旧密码
public void pwdModify(HttpServletRequest req, HttpServletResponse resp){
//获取当前Session中的用户对象
Object o = req.getSession().getAttribute(Constants.USER_SESSION);
//获取用户输入的旧密码
String oldpassword = req.getParameter("oldpassword");
//使用Map来存储执行结果
Map<String, String> resultSet = new HashMap<>();
if (o==null){//Session过期
//参照前端规定参数写入信息 data.result
resultSet.put("result","sessionerror");
}else if(StringUtils.isNullOrEmpty(oldpassword)){//用户输入的旧密码为空
resultSet.put("result","error");
}else {
//拿到当前Session的密码
String password = ((User) o).getUserPassword();
//判断密码是否匹配
if (oldpassword.equals(password)){//密码正确
resultSet.put("result","true");
}else {
resultSet.put("result","false");
}
}
PrintWriter out = null;
try {
//修改返回结果的文件类型
resp.setContentType("application/json");
//获取流
out = resp.getWriter();
//以json数组的方式写给浏览器前端
out.write(JSONArray.toJSONString(resultSet));
out.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (out!=null){
out.close();
}
}
}
用户管理页面实现
准备工作
1.导入写好的分页工具类
package com.zsh.util;
public class PageSupport {
//当前页码-来自于用户输入
private int currentPageNo = 1;
//总数量(表)
private int totalCount = 0;
//页面容量
private int pageSize = 0;
//总页数-totalCount/pageSize(+1)
private int totalPageCount = 1;
public int getCurrentPageNo() {
return currentPageNo;
}
public void setCurrentPageNo(int currentPageNo) {
if(currentPageNo > 0){
this.currentPageNo = currentPageNo;
}
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
if(totalCount > 0){
this.totalCount = totalCount;
//设置总页数
this.setTotalPageCountByRs();
}
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
if(pageSize > 0){
this.pageSize = pageSize;
}
}
public int getTotalPageCount() {
return totalPageCount;
}
public void setTotalPageCount(int totalPageCount) {
this.totalPageCount = totalPageCount;
}
public void setTotalPageCountByRs(){
if(this.totalCount % this.pageSize == 0){
this.totalPageCount = this.totalCount / this.pageSize;
}else if(this.totalCount % this.pageSize > 0){
this.totalPageCount = this.totalCount / this.pageSize + 1;
}else{
this.totalPageCount = 0;
}
}
}
2.用户列表页面导入
文件名:userlist.jsp rollpage.jsp 两个页面
1、根据用户名或者角色名查询用户总数
DAO层
//根据用户名或者角色名查询用户总数
public int getUserCount(Connection connection,String userName,int userRole);
实现类
@Override
public int getUserCount(Connection connection,String userName,int userRole) {
//用来传给BaseDao操作数据库
PreparedStatement preparedStatement = null;
//接受结果集
ResultSet rs = null;
//接收数据条数
int count = 0;
if (connection!=null) {
ArrayList<Object> list = new ArrayList<>();//存放参数
//需要根据传过来的参数判断要执行的sql,使用拼接的方式
StringBuffer sql = new StringBuffer();
sql.append("SELECT COUNT(1) AS 'count' FROM smbms_user u,smbms_role r WHERE u.userRole = r.id ");
//如果名字不为空
if (!StringUtils.isNullOrEmpty(userName)){
sql.append("AND u.userName LIKE ? ");
//把需要的参数存入
list.add("%"+userName+"%");//index:0
}
//角色是否输入
if (userRole>0){
sql.append("AND u.userRole = ?");
list.add(userRole);
}
try {
//把list转换为需要的数组参数
Object[] params = list.toArray();
//调用BaseDao的查询方法
rs = BaseDao.execute(connection, sql.toString(), params, rs, preparedStatement);
if (rs.next()){
//通过取的别名拿到用户总数
count = rs.getInt("count");
System.out.println(count);
}
//关闭资源
BaseDao.resourceClose(null,preparedStatement,rs);
} catch (SQLException e) {
e.printStackTrace();
}
}
return count;
}
Service层
//查询用户总数
public int getUserCount(String userName,int userRole);
实现类
//查询用户总数实现
@Override
public int getUserCount(String userName, int userRole) {
Connection connection = BaseDao.getConnection();
//为啥不需要抛出异常????
int count = userDao.getUserCount(connection, userName, userRole);
BaseDao.resourceClose(connection,null,null);
return count;
}
2、获取用户列表信息
Dao层
//查询用户数据/分页
public List<User> getUserList(Connection connection, String userName, int userRole, int currentPageNo, int pageSize) throws SQLException;
Dao实现类
//查询用户数据
//currentPageNo:用户输入页数 pageSize 页面大小
@Override
public List<User> getUserList(Connection connection,String userName,int userRole,int currentPageNo,int pageSize) throws SQLException {
PreparedStatement pstm = null;
ResultSet rs = null;
//用于接收查出的每一个User
List<User> userList = new ArrayList<User>();
if(connection!=null){
//做字符串连接
StringBuffer sql = new StringBuffer();
sql.append("SELECT u.*,r.roleName as userRoleName FROM smbms_user u,smbms_role r where u.roleName = r.id ");
//存储参数
ArrayList<Object> list = new ArrayList<>();
if (!StringUtils.isNullOrEmpty(userName)){//用户输入的名字不为空
sql.append("AND u.userName LIKE ? ");
list.add("%"+userName+"%");
}
if (userRole>0){//输入的角色名不为空
sql.append("AND u.userRole = ? ");
}
//先倒序再分页
sql.append("order by createDate DESC LIMIT ?,?");
//分页算法:当前页 - 1 * 每页条数
currentPageNo = (currentPageNo -1) * pageSize;
//作为参数存储进去
list.add(currentPageNo);
list.add(pageSize);
//转成需要的Object数组作为参数列表
Object[] params = list.toArray();
//执行查询方法,传入对应的参数,得到结果集
rs = BaseDao.execute(connection, sql.toString(), params, rs, pstm);
//取出查询到的user
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.setPhone(rs.getString("phone"));
_user.setAddress(rs.getString("address"));
_user.setUserRole(rs.getInt("userRole"));
_user.setUserRoleName(rs.getString("userRoleName"));
userList.add(_user);
}
BaseDao.resourceClose(null,pstm,rs);
}
return userList;
}
Service层
//查询用户信息/分页
public List<User> getUserList(String queryUserName,int queryUserRole,int currentPageNo,int pageSize);
Service实现类
@Override
public List<User> getUserList(String queryUserName, int queryUserRole, int currentPageNo, int pageSize) {
Connection connection = null;
List<User> list = null;
try {
connection = BaseDao.getConnection();
list = userDao.getUserList(connection, queryUserName, queryUserRole, currentPageNo, pageSize);
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.resourceClose(connection,null,null);
}
return list;
}
3、获取角色列表
为了职责同意,可以把角色的操作单独放在一个包里,和pojo类对应,易维护
Dao层
//查询角色列表
public List<Role> getRoleList(Connection connection) throws SQLException;
实现类
@Override
public List<Role> getRoleList(Connection connection) throws SQLException {
//操作数据库对象
PreparedStatement preparedStatement = null;
//存储查询到的角色信息
List<Role> roleList = new ArrayList<>();
//虽然没有参数,但如果直接写null会报空指针
Object[] params = {};
ResultSet rs = null;
if (connection!=null){
String sql = "SELECT * FROM smbms_role";
rs = BaseDao.execute(connection, sql, params, rs, preparedStatement);
if (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.resourceClose(null,preparedStatement,rs);
}
return roleList;
}
Service层
//查询角色列表
public List<Role> getRoleList()throws SQLException;
实现类
private RoleDao roleDao;
public RoleServiceImp(){
roleDao = new RoleDaoImp();
}
//获取角色列表
@Override
public List<Role> getRoleList() throws SQLException {
//接收Role
List<Role> roleList = new ArrayList<>();
Connection connection = BaseDao.getConnection();
roleList = roleDao.getRoleList(connection);
BaseDao.resourceClose(connection,null,null);
return roleList;
}
分页操作;条件查询;用户
4、把数据发送到前端
Servlet
//先加上新的条件
else if (method.equals("query")){
this.getUserList(req, resp);
}
参考前端参数配置对应信息
public void getUserList(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//************************获取前端数据************************
String queryUserName = req.getParameter("queryname");
//查询到的角色名,临时变量?
String temp = req.getParameter("queryUserRole");
//当前页码
String pageIndex = req.getParameter("pageIndex");
/*
queryUserRole 查询到的角色名
因为拿到的是字符串,需要转换为int,所以上面的变量作为临时变量接收数据
使用下面这个变量作为最终传输的变量
*/
int queryUserRole = 0;
//Service层
UserService userServiceImp = new UserServiceImp();
RoleService roleServiceImp = new RoleServiceImp();
List<User> userList = null;
List<Role> roleList = null;
//当前页写死,默认为首页
int currentPageNo = 1;
//每页条数固定为5,实际项目中应该卸载配置文件中(Properties),方便维护
int pageSize = 5;
//************************因为上面这些数据不一定有用,所以需要判断是否为空************************
if (queryUserName==null){
//给个默认值让程序能够执行
queryUserName = "";
}
//temp不会为空,因为前端默认为0,且从0开始
if (temp!=null&&!temp.equals("")){
queryUserRole = Integer.parseInt(temp);
System.out.println("---->"+queryUserRole);
}
//当前页改变之后解析出来
if (pageIndex != null){
try {
currentPageNo = Integer.valueOf(pageIndex);
} catch (NumberFormatException e) {
try {
resp.sendRedirect("error.jsp");
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
//总人数;数据总条数
int totalCount = userServiceImp.getUserCount(queryUserName, queryUserRole);
//************************实现分页;获取工具类支持,来计算当前页面和总页面,页面大小。。。************************
PageSupport support = new PageSupport();
//设置当前页
support.setCurrentPageNo(currentPageNo);
//设置页码
support.setPageSize(pageSize);
//设置数据总条数
support.setTotalCount(totalCount);
//拿到用户列表
userList = userServiceImp.getUserList(queryUserName, queryUserRole, currentPageNo, pageSize);
int totalPageCount = support.getTotalPageCount();
//************************用户列表展示************************
//在请求中存入这些信息
try {
//角色列表
roleList = roleServiceImp.getRoleList();
req.setAttribute("roleList",roleList);
} catch (SQLException e) {
e.printStackTrace();
}
req.setAttribute("userList",userList);
req.setAttribute("queryUserName", queryUserName);
req.setAttribute("totalCount",totalCount);
req.setAttribute("queryUserRole", queryUserRole);
req.setAttribute("currentPageNo",currentPageNo);
req.setAttribute("totalPageCount", totalPageCount);
System.out.println(1);
//************************返回前端************************
req.getRequestDispatcher("userlist.jsp").forward(req,resp);
}
smbms未完待续
6、文件上传
1、准备工作
导入需要使用到的jar包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMPrWAXq-1624634278988)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210427081550269.png)]
复制到idea中的lib目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QdLsTLBn-1624634278988)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210427081717572.png)]
把这两个jar包加入当前项目的类库中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V7DStwyN-1624634278988)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210427081837776.png)]
添加进类库 点击fix 完成
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lxflsxdi-1624634278989)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210427092102617.png)]
2、使用类的介绍
【上传文件的注意事项】
1.为了服务器安全,上传文件应该放在外界无法直接访问的目录下,比如WEB-INF目录下
2.为了防止文件覆盖现象的发生,要为上传的每个文件产生一个唯一的名字
3.要限制文件的最大上传值
4.可以限制上传文件的类型,在收到上传文件名时,判断后缀是否合法
表单如果包含一个文件上传输入项的话,这个表单 必须设置enctype属性必须设置为multipart/form-data
流程图

3、常用的方法
//isFormFile方法用于判断FileItem类对象封装的数据是一个普通文本表单
//还是一个文件表单,如果时普通表单字段则返回true,否则返回false
boolean isField();
//getFieldName方法用于返回表单标签name属性的值
String getFieldName();
//getString方法用于将FileItem对象中保存的数据流内容以一个字符串返回
String getString();
//getName方法用于获得文件上传字段中的文件名。
String getName();
//以流的形式返回上传文件的数据内容。
InputStream getInputStream();
//delete方法用来清空FileItem类对象中存放的主体内容
//如果主体内容被保存在临时文件中,delete方法将删除该临时文件
void delete();
package com.zsh;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
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;
/**
* 文件上传
*/
public class FileServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException {
//准备工作
//过滤表单 判断请求中的是普通表单还是带文件的表单
if (!ServletFileUpload.isMultipartContent(request)) {
return;//能进来表示是一个普通表单,终止方法运行
}
//创建出上传文件的保存位置,建议在外界无法随意访问的目录,如WEB-INF/upload
//查找当前项目中的以下目录
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
//如果没找到这个目录,就自己创建出来
if (!uploadFile.exists()) {
uploadFile.mkdir();
}
//存放临时文件;当上传的文件过大时;需要先存为临时文件
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File tmpFile = new File(tmpPath);
//如果没找到这个目录,就自己创建出来
if (!tmpFile.exists()) {
tmpFile.mkdir();
}
try {
//1、创建DiskFileItemFactory对象,处理文件上传路径或限制文件大小
DiskFileItemFactory factory = gteDiskFileItemFactory(tmpFile);
//2、获取ServletFileUpload
ServletFileUpload upload = getServletFileUpload(factory);
//3、处理上传文件
String msg = uploadParseRequest(upload,request,uploadPath);
//Servlet请求转发消息
request.setAttribute("msg",msg);
request.getRequestDispatcher("/info.jsp").forward(request,response);
}catch (FileUploadException e){
e.printStackTrace();
}
}
public static DiskFileItemFactory gteDiskFileItemFactory(File file){
//1、创建DiskFileItemFactory对象,处理文件上传路径或限制文件大小
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
factory.setRepository(file);
return factory;
}
public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){
ServletFileUpload upload = new ServletFileUpload(factory);
//监听文件上传进度
upload.setProgressListener(new ProgressListener() {
public void update(long pBytesRead, long lpContentLenght, int i) {
//pBytesRead:已读取到的文件大小
//pContentLenght:文件大小
System.out.println("总大小:"+lpContentLenght+"已上传:"+pBytesRead);
}
});
//处理乱码问题
upload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值
upload.setFileSizeMax(1024 * 1024 * 10);
//设置总共能够上传文件的大小
//1024 = 1kb * 1024 = 1M * 10 = 10M
upload.setSizeMax(1024 * 1024 * 10);
return upload;
}
public static String uploadParseRequest(ServletFileUpload upload,HttpServletRequest request,String uploadPath) throws FileUploadException, IOException {
String msg = "";
//3.处理上传的文件
//把前端请求解析成一个FileItem对象
List<FileItem> items = upload.parseRequest(request);
for (FileItem fileItem : items) {
if (fileItem.isFormField()) {//说明是一个普通输入项
String fieldName = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");//处理乱码
System.out.println(fieldName+":"+value);
} else {//输入项是File
/*处理文件*/
//文件名
String name = fileItem.getName();
System.out.println("上传的文件名:"+name);
//如果文件名去掉前后空格还是为空,表示文件名不正确
if (name.trim().equals("")||name == null){
//直接判断下一个输入项
continue;
}
//获得上传的文件名
String fileName = name.substring(name.lastIndexOf("/") + 1);
//获取文件后缀
String fileExtName = name.substring(name.lastIndexOf(".") + 1);
System.out.println("文件信息【文件名:"+fileName+" 文件类型:"+fileExtName);
//使用UUID获取一个唯一识别的通用码,保证文件不重复
String uuid = UUID.randomUUID().toString();
//给每一个文件创建一个目录;存到哪?放到传过来的文件路径下给一个UUID命名的文件夹
String realPath = uploadPath + "/"+uuid;
File file = new File(realPath);
if (!file.exists()){
file.exists();
}
//输出流
FileOutputStream out = new FileOutputStream(realPath +"."+fileExtName);
//保存上传的文件文件
InputStream inp = fileItem.getInputStream();
//读取文件老三样
//缓冲区
byte[] buffer = new byte[1024*1024];
//读取表示
int count = 0;
while((count = inp.read(buffer))>0){
out.write(buffer);
}
out.close();
inp.close();
msg = "文件上传成功";
fileItem.delete();//上传成功,删除临时文件
}
}
return msg;
}
}
7、邮件发送
发邮件是从客户端把邮件发送到邮件服务器,收邮件是把邮件服务器的邮件下载到客户端。
1.邮件协议概述
与HTTP协议相同,收发邮件也是需要有传输协议的。
SMTP:(Simple Mail Transfer Protocol,简单邮件传输协议)发邮件协议;
POP3:(Post Office Protocol Version 3,邮局协议第3版)收邮件协议;
IMAP:(Internet Message Access Protocol,因特网消息访问协议)收发邮件协议,我们的课程不涉及该协议。
我们在163、126、QQ、sohu、sina等网站注册的Email账户,其实就是在邮件服务器中注册的。这些网站都有自己的邮件服务器。
2.理解邮件收发过程
其实你可以把邮件服务器理解为邮局!如果你需要给朋友寄一封信,那么你需要把信放到邮筒中,这样你的信会“自动”到达邮局,邮局会把信邮到另一个省市的邮局中。然后这封信会被送到收信人的邮箱中。最终收信人需要自己经常查看邮箱是否有新的信件。
其实每个邮件服务器都由SMTP服务器和POP3服务器构成,其中SMTP服务器负责发邮件的请求,而POP3负责收邮件的请求。‘
【同一个邮件服务器的情况下,发送邮件时会在邮件服务器会存储邮件信息,等收件方通过pop3协议去邮件服务器中取出邮件】

当然,有时我们也会使用163的账号,向126的账号发送邮件。这时邮件是发送到126的邮件服务器,而对于163的邮件服务器是不会存储这封邮件的。
【不同一个邮件服务器的情况下,发送方的邮件服务器不存储邮件信息,发送邮件时会通过smtp协议将邮件发送到收件方邮件服务器,收件方存储邮件信息,等收件方通过pop3协议去邮件服务器中取出邮件】

3.邮件服务器名称
smtp服务器的端口号为25,服务器名称为smtp.xxx.xxx。
pop3服务器的端口号为110,服务器名称为pop3.xxx.xxx。
例如:
163:smtp.163.com和pop3.163.com;
126:smtp.126.com和pop3.126.com;
qq:smtp.qq.com和pop3.qq.com;
代码实现
package com.zsh;
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 MailSendDemo1 {
public static void main(String[] args) throws Exception{
//把配置信息封装成一个Properties对象
Properties properties = new Properties();
properties.setProperty("mail.host","smtp.qq.com");//设置QQ邮箱服务器
properties.setProperty("mail.transport.protocol","smtp");//邮件发送协议
properties.setProperty("mail.smtp.auth","true");//设置需要验证用户名密码
//如果使用的是qq邮箱,需要定义以下配置,使用其他邮箱可不写这些配置
//设置SSL加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable","true");
properties.put("mail.smtp.ssl.socketFactory",sf);
//QQ邮箱才有,其他邮箱一般没有授权码的方式
Session session = Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//这里的授权码换成其他的邮箱服务器就是密码的意思
return new PasswordAuthentication("2899277505@qq.com","yzgbdiqghpahdcfa");
}
});
//开启Session的debug,可以查看邮件发送过程中的信息
session.setDebug(true);
//通过Session得到transPort对象
Transport ts = session.getTransport();
//使用邮箱账号和授权码连接到邮件服务器
ts.connect("smtp.qq.com","2899277505@qq.com","yzgbdiqghpahdcfa");
//创建邮件
MimeMessage message = new MimeMessage(session);
//发送人
message.setFrom(new InternetAddress("2899277505@qq.com"));
//指名收件人;给自己发
message.setRecipient(Message.RecipientType.TO,new InternetAddress("2899277505@qq.com"));
//设置邮件主题;标题;必须写
message.setSubject("你好");
//设置邮件内容
message.setContent("你好,这里是JavaMail","text/html;charset=UTF-8");
//发送邮件
ts.sendMessage(message,message.getAllRecipients());
ts.close();
}
}
邮件发送【带图片】
1.引入图片
其他代码不用修改,只需要修改邮件内容,存放的对象
一封邮件本质上是一个html文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cKmnf9O1-1624634278990)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210429144416991.png)]
2.设置邮件包含的数据限制【纯文本、图片;音频、文件】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PrNfN6iK-1624634278990)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210429145109185.png)]
3.把包装好的数据放进邮件的消息体中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0A8mnfaK-1624634278991)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210429144947528.png)]
4.发出邮件
邮件发送【带附件】
前面代码也不用修改,修改DataHandler中的文件和设置附件名字,与发送图片的区别就在这里
设置附件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7hcCqbpf-1624634278991)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210430203417545.png)]
拼装消息内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hfJ3E3B5-1624634278992)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210430203811478.png)]
当然,有时我们也会使用163的账号,向126的账号发送邮件。这时邮件是发送到126的邮件服务器,而对于163的邮件服务器是不会存储这封邮件的。
【不同一个邮件服务器的情况下,发送方的邮件服务器不存储邮件信息,发送邮件时会通过smtp协议将邮件发送到收件方邮件服务器,收件方存储邮件信息,等收件方通过pop3协议去邮件服务器中取出邮件】

3.邮件服务器名称
smtp服务器的端口号为25,服务器名称为smtp.xxx.xxx。
pop3服务器的端口号为110,服务器名称为pop3.xxx.xxx。
例如:
163:smtp.163.com和pop3.163.com;
126:smtp.126.com和pop3.126.com;
qq:smtp.qq.com和pop3.qq.com;
代码实现
package com.zsh;
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 MailSendDemo1 {
public static void main(String[] args) throws Exception{
//把配置信息封装成一个Properties对象
Properties properties = new Properties();
properties.setProperty("mail.host","smtp.qq.com");//设置QQ邮箱服务器
properties.setProperty("mail.transport.protocol","smtp");//邮件发送协议
properties.setProperty("mail.smtp.auth","true");//设置需要验证用户名密码
//如果使用的是qq邮箱,需要定义以下配置,使用其他邮箱可不写这些配置
//设置SSL加密
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable","true");
properties.put("mail.smtp.ssl.socketFactory",sf);
//QQ邮箱才有,其他邮箱一般没有授权码的方式
Session session = Session.getDefaultInstance(properties, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
//这里的授权码换成其他的邮箱服务器就是密码的意思
return new PasswordAuthentication("2899277505@qq.com","yzgbdiqghpahdcfa");
}
});
//开启Session的debug,可以查看邮件发送过程中的信息
session.setDebug(true);
//通过Session得到transPort对象
Transport ts = session.getTransport();
//使用邮箱账号和授权码连接到邮件服务器
ts.connect("smtp.qq.com","2899277505@qq.com","yzgbdiqghpahdcfa");
//创建邮件
MimeMessage message = new MimeMessage(session);
//发送人
message.setFrom(new InternetAddress("2899277505@qq.com"));
//指名收件人;给自己发
message.setRecipient(Message.RecipientType.TO,new InternetAddress("2899277505@qq.com"));
//设置邮件主题;标题;必须写
message.setSubject("你好");
//设置邮件内容
message.setContent("你好,这里是JavaMail","text/html;charset=UTF-8");
//发送邮件
ts.sendMessage(message,message.getAllRecipients());
ts.close();
}
}
邮件发送【带图片】
1.引入图片
其他代码不用修改,只需要修改邮件内容,存放的对象
一封邮件本质上是一个html文件
[外链图片转存中…(img-cKmnf9O1-1624634278990)]
2.设置邮件包含的数据限制【纯文本、图片;音频、文件】
[外链图片转存中…(img-PrNfN6iK-1624634278990)]
3.把包装好的数据放进邮件的消息体中
[外链图片转存中…(img-0A8mnfaK-1624634278991)]
4.发出邮件
邮件发送【带附件】
前面代码也不用修改,修改DataHandler中的文件和设置附件名字,与发送图片的区别就在这里
设置附件
[外链图片转存中…(img-7hcCqbpf-1624634278991)]
拼装消息内容
[外链图片转存中…(img-hfJ3E3B5-1624634278992)]
这篇博客详细介绍了JavaWeb开发中的核心技术,包括Servlet的工作原理,如Servlet生命周期、ServletContext和请求转发。接着讲解了HttpServletResponse、HttpServletRequest的使用,以及cookie和session的逻辑和源码分析。还涵盖了JSP的特点、基础语法和内置对象,以及JSTL和EL表达式的应用。此外,还讨论了JDBC的基础、文件上传、邮件发送的实现,以及安全和权限访问的话题。

5725

被折叠的 条评论
为什么被折叠?



