JavaWeb

这篇博客详细介绍了JavaWeb开发中的核心技术,包括Servlet的工作原理,如Servlet生命周期、ServletContext和请求转发。接着讲解了HttpServletResponse、HttpServletRequest的使用,以及cookie和session的逻辑和源码分析。还涵盖了JSP的特点、基础语法和内置对象,以及JSTL和EL表达式的应用。此外,还讨论了JDBC的基础、文件上传、邮件发送的实现,以及安全和权限访问的话题。
摘要由CSDN通过智能技术生成

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信息和映射
//访问下载
//成功!

验证码功能

验证怎么来的?

  1. 前端实现

  2. 后端实现

    这种方法以后基本碰不到,了解一下代码格式流程,大概意思就好

    第一步:需要用到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 标签

核心标签 (掌握部分)

img

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 支持的数据库

img

使用 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

流程图

img

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协议去邮件服务器中取出邮件】

img

当然,有时我们也会使用163的账号,向126的账号发送邮件。这时邮件是发送到126的邮件服务器,而对于163的邮件服务器是不会存储这封邮件的。

【不同一个邮件服务器的情况下,发送方的邮件服务器不存储邮件信息,发送邮件时会通过smtp协议将邮件发送到收件方邮件服务器,收件方存储邮件信息,等收件方通过pop3协议去邮件服务器中取出邮件】

javamail

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协议去邮件服务器中取出邮件】

javamail

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)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值