• JavaWeb项目(登录注册页面)全过程详细总结


    文章目录

    JavaWeb项目(登录注册页面)全过程总结

    一、环境准备与开发工具
    二、创建 JavaWeb 项目
        2.1 新建Dynamic Web Project项目
        2.2 创建前端页面
            2.2.1 登录页面
                1.login.jsp
                2. login.js
                3. time.js
                4. focus.js + animate.js
            2.2.2 注册页面(register.jsp)
            2.2.3 修改密码的页面(change_pwd.jsp)
            2.2.4 登录成功的页面(success.jsp)
        2.3 创建相关配置类
            2.3.1 DAO类(DAO.java)
            2.3.2 JDBCUtil类(JDBCUtil.java)
            2.3.3 JsonResult类(JsonResult.java)
            2.3.4 MailUtil类(MailUtil.java)
            2.3.5 User类(User.java)
        2.4 创建 Servlet
            2.4.1 登录界面的Servlet(LoginServlet)
            2.3.2 注册界面的Servlet(RegisterServlet)
            2.3.3 修改密码的Servlet(HandlePwdServlet)
            2.3.4 发送邮件的Servlet(EmailServlet)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    项目需求:
    实现登录页面:
    顶部有logo栏目
    左侧提供轮播图
    提供两种登录方式:①账号密码登录 ②邮箱+验证码登录
    登录成功后跳到显示“登录成功”四字的页面(简单设计)
    有修改密码的功能
    注册页面
    数据统一存储在数据库

    一、环境准备与开发工具

    此次项目用到的工具是:

    ① 前端:HTML + CSS + JS

    ② 后端:Tomcat 9 + Servlet

    ③ 项目开发工具 :Eclipse(Java EE IDE) java运行环境是:jdk 15

    ④ 数据库:Mysql + Navicat 15 for MySQL

    此次实验需要提前安装配置好Eclipse、jdk、tomcat,tomcat的安装和servlet的基本使用请见另两篇文章:Servlet的使用 和 Tomcat的使用

    二、创建 JavaWeb 项目
    2.1 新建Dynamic Web Project项目
    image-20211121155204030
    里面的Dynamic Web module version 我使用的2.5
    image-20211121155413481

    项目的目录结构如下:
    image-20211121155735914项目的目录结构如下:

    image-20211121155615544在这里插入图片描述

    2.2 创建前端页面

    这里只展示 jsp 和 js 文件的代码,页面中的 icon 是使用的 icomoon
    2.2.1 登录页面

    1.login.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    
    
    
     	
     	
    	LARP-LOGIN
    	
      	
      	
      	
     	
      	
     	
     	
     	
      	
      	
      	
    
    
    
    	

    LARP数据可视化管理平台

    ? ?
    其他登录方式

    -Directed By Elvira-

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    1. login.js

    功能讲解:

    ① 登录方式 tab 栏切换

    最开始我是在 login.jsp 把两种登录方式的 html 代码都写上,然后再父盒子上使用的display:none 和 block 来切换实现,在显示上是可以做到切换显示和隐藏,但是再代码层两种方式的代码都存在,在表单提交时,就会出现问题,因为其提交的时两种方法中4个输入框中的内容,且无法通过 required 约束表单不能为空,造成表单不能提交(因为其要求了4个输入框都需要填内容,而有两个输入框隐藏)。

    转换思路:

    在 login.jsp 中使用一个容器来占位

    通过 js 控制具体显示的是哪种登录方法的 html 代码
    在 login.js 中定义 html 模板以及变量 isAccount,给 tab 栏的登录方式添加点击事件,点击到哪种方法就展示哪个方法对应的 html 模板和样式

    ② 邮箱登录方式下获取验证码按钮点击后禁用,10s后解禁

    思想:

    定义定时器 setInterval 和变量 second(定义要禁用的时间)
    禁用点击按钮
    用 innerText 替换按钮中的文字,定时器每隔1s刷新一次按钮中的文字,seond自减1
    若 second <= 0,清除定时器,解禁按钮,并将按钮中的文字换回:获取验证码

    // 登录方式切换
    window.addEventListener('load', function () {
      // 是否是账户登录
      var isAccount = true
    
      // html模板
      var user_template = `
        
      `
      var phone_template = `
        
    ` // 获取输入框的元素 var input_box = document.querySelector('.input_box') // 挂载用户密码登录方式的html input_box.innerHTML = user_template var account_a = document.querySelector('.account_a') var phone_a = document.querySelector('.phone_a') var forget_pwd = document.querySelector('.forget_pwd') // 给账号登录的链接添加事件 account_a.addEventListener('click', function () { // 将用户密码方法的html代码渲染,必须放在前面,不然获取不到元素 input_box.innerHTML = user_template //获取输入框中的值 var input_user = document.querySelector('#username') isAccount = true account_a.style.color = '#03a9f4' phone_a.style.color = '#666' forget_pwd.style.display = 'block' input_user.focus() // 解决切换页面后输入框的聚焦问题 }) // 给手机登录的链接添加事件 phone_a.addEventListener('click', function () { // 将手机号验证码方法的html代码渲染,必须放在前面,不然获取不到元素 input_box.innerHTML = phone_template //获取元素 var phone_input = document.querySelector('.phone_input') //输入框中的值 var input_phone = document.querySelector('#phone') isAccount = false account_a.style.color = '#666' phone_a.style.color = '#03a9f4' forget_pwd.style.display = 'none' phone_input.style.marginBottom = '8.1vh' input_phone.focus() // 创建XMLHttpRequest function CreateXmlHttp() { // 定义XMLHttpRequest对象 var xhr = null // 创建XMLHttpRequest对象 if (window.XMLHttpRequest) { // 其他浏览器 xhr = new XMLHttpRequest() } else if (window.ActiveXObject) { // IE浏览器 IE5 IE6 xhr = new ActiveXObject('Microsoft.XMLHTTP') } return xhr } // 获取点击获取验证码的按钮 var getCodeBtn = document.querySelector(".getCode"); // 获取验证码点击按钮点击后禁用 getCodeBtn.addEventListener("click", function() { // 点击按钮后,将按钮禁用10秒钟 getCodeBtn.disabled = true; var second = 10; var timer = setInterval(function () { getCodeBtn.innerText = second + "s 后可重新获取" if (second <= 0) { clearInterval(timer); getCodeBtn.innerText = "获取验证码" getCodeBtn.disabled = false; } second--; }, 1000); // 发送post请求 // 创建XMLHttpRequest var xhr = CreateXmlHttp() var email = input_phone.value // 指定响应函数(回调函数) xhr.onreadystatechange = function () { if (xhr.readyState == 4) { // 请求已经完成,信息已经成功返回,开始处理信息 if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { // 将从服务器端返回是JSON格式数据转换成JavaScript对象 var res = xhr.responseText var jsonObj = eval("("+res+")") console.log("res:"+res) if(jsonObj.type == 0) { alert(jsonObj.error); } else { alert("邮箱发送成功,请查阅邮箱,尽快认证") } } else { alert("邮箱发送失败") } } } xhr.open('POST','/my_login/EmailServlet',true) // 设置HTTP的输出内容类型为json格式数据:application/x-www-form-urlencoded xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded') // 设置浏览器不使用缓存,服务器不从缓存中找,重新执行代码,而且服务器返回给浏览器的时候,告诉浏览器也不要保存缓存。 xhr.setRequestHeader('If-Modified-Since', '0') // 发送请求 xhr.send("email="+email); }) }) })
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    1. time.js

    顶部导航栏动态显示时间

    // 获取时间
    window.addEventListener('load', function () {
      // 获取元素
      var date = document.querySelector('.date')
      var sec = document.querySelector('.second')
      setInterval(function () {
        // 获取当前时间
        var d = new Date()
        var year = d.getFullYear() // 获取年
        var month = d.getMonth() + 1 // 获取月
        var day = d.getDate() // 获取日期
        var hour = d.getHours() // 获取小时
        var minute = d.getMinutes() // 获取分钟
        var second = d.getSeconds() // 获取秒
    
        if (month < 10) month = '0' + month
        if (day < 10) day = '0' + day
        if (hour < 10) day = '0' + hour
        if (minute < 10) minute = '0' + minute
        if (second < 10) second = '0' + second
    
        // 拼接字符串
        var date_str = year + ' 年 ' + month + ' 月 ' + day + ' 日 '
        var sec_str = hour + ' : ' + minute + ' : ' + second
    
        date.innerHTML = date_str
        sec.innerHTML = sec_str
      }, 1000)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    1. focus.js + animate.js

    轮播图的实现,animate.js是抽象出来的元素移动的函数

    轮播图功能:

    鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮;
    点击右侧按钮一次,图片向左移动播放后一张,左侧按钮同理;
    图片播放的同时,下面小圆圈模块跟随一起变化;
    点击小圆圈,可以播放相应的图片;
    鼠标不经过轮播图,轮播图自动播放图片;
    鼠标经过轮播图模块,自动播放停止
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    轮播图功能实现思想:

    ① 动态生成小圆圈

    核心思路:小圆圈个数与图片数目一致
    具体实现步骤:
        首先的得到 ul 里面图片的张数(即 li 的个数)
        利用循环动态生成小圆圈(小圆圈放在 ol 里面)
        创建 li 节点 createElement(“li”)
        插入 ol 节点 ol.appendChild(“li”)
        第一个小圆圈添加 current 类(当前显示的元素的样式)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ② 点击小圆圈滚动图片

    核心算法:点击某个小圆圈,就让图片滚动:小圆圈索引号乘以图片的宽度作为 ul 的移动距离
    注意:
        此时用到 animate.js 函数,将 js 文件引入(因为 index.js 依赖 animate.js, 所以animate.js 要写到 index.js 上面)
        使用animate.js 动画函数的前提,该元素必须要有定位
        是移动 ul 不是 li
        需要知道小圆圈的索引号,可以在生成小圆圈的时候,给他设置一个自定义属性,点击的时候获取该自定义属性
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ③ 点击右侧按钮一次,就让图片滚动一张(左侧按钮类似)

    核心思想:声明一个变量 num,点击一次,自增1,让这个变量乘以图片宽度,就是 ul 的滚动距离
    图片无缝滚动原理:
        把 ul 第一个 li 复制一份,放到 ul 最后面
        当图片滚动到克隆的最后一张照片时, 让 ul 快速的、不做动画的跳到最左侧:left:0
        同时 num 赋值为 0, 就可以重新开始滚动图片了
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ④ 点击右侧按钮,小圆圈跟随变化

    思想:

    声明变量 circle,每次点击自增1,注意:左侧按钮也需要这个变量,所以声明全局变量
    图片有 4 张,小圆圈只有 3 个,所以添加判断条件:如果 circle == 3,就重新复原为 0
    
    • 1
    • 2

    ⑤ 自动播放

    思想:

    添加一个定时器,自动播放轮播图,就类似于点击了右侧按钮
    使用手动调用右侧按钮点击事件 arrow_r.click()
    鼠标经过轮播图就停止定时器
    鼠标离开轮播图就开启定时器
    
    • 1
    • 2
    • 3
    • 4

    ⑥ 节流阀
    作用:防止轮播图按钮连续点击造成播放过快

    目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发

    核心思路:利用回调函数,添加一个变量控制,锁住函数和解锁函数

    设置变量 var flag = true;

    if(flag){ flag = false; do something} 关闭水龙头

    focus.js

    window.addEventListener('load', function () {
      //1、获取元素
      var arrow_l = document.querySelector('.arrow-l')
      var arrow_r = document.querySelector('.arrow-r')
      var focus = document.querySelector('.focus')
      var focusWidth = focus.offsetWidth
    
      //2、鼠标经过focus 就显示隐藏的左右按钮
      focus.addEventListener('mouseenter', function () {
        arrow_l.style.display = 'block'
        arrow_r.style.display = 'block'
        clearInterval(timer)
        timer = null //清除定时器变量,让图片静止
      })
      //鼠标离开focus 就隐藏左右按钮
      focus.addEventListener('mouseleave', function () {
        arrow_l.style.display = 'none'
        arrow_r.style.display = 'none'
        timer = setInterval(function () {
          //手动调用点击事件
          arrow_r.click()
        }, 3500)
      })
    
      // 3、动态生成小圆圈,有几张图片就有几个小圆圈
      var ul = focus.querySelector('ul')
      var ol = focus.querySelector('.circle')
      for (var i = 0; i < ul.children.length; i++) {
        // 创建小li
        var li = document.createElement('li')
        // 用自定义属性记录当前小圆圈的索引号
        li.setAttribute('index', i)
        // 把小li插入到ol 里面
        ol.appendChild(li)
    
        // 4、小圆圈的排他思想,我们可以直接在生成小圆圈的同时直接绑定事件
        li.addEventListener('click', function () {
          // 干掉所有人 ,把所有的小li 清除current 类名
          for (var i = 0; i < ol.children.length; i++) {
            ol.children[i].className = ''
          }
          //留下我自己 当前的小li设置current属性
          this.className = 'current'
    
          // 5、点击小圆圈,移动图片 移动的是ul 不是li
          // 当我们点击了某个小li,就拿到当前li 的索引号
          var index = this.getAttribute('index')
          num = index //没有这句话图片不会跟着小圆点变化
          circle = index //没有这句话小圆点不会跟着变化
          animate(ul, -index * focusWidth)
        })
      }
    
      //把 ol 里面的第一个小li设置类名为 current
      ol.children[0].className = 'current'
    
      //6、克隆第一张图片放到ul最后,写在生成li的后面
      var first = ul.children[0].cloneNode(true)
      ul.appendChild(first)
    
      // 7、点击右侧按钮,图片滚动一张
      var num = 0
      // circle 控制小圆圈的播放
      var circle = 0
      // flag节流阀
      var flag = true
      arrow_r.addEventListener('click', function () {
        if (flag) {
          flag = false
          // 如果走到了最后一张复制的图片,此时ul要快速复原left为0
          if (num == ul.children.length - 1) {
            ul.style.left = 0
            num = 0
          }
          num++
          animate(ul, -num * focusWidth, function () {
            flag = true // 只有一张图片播放完了才展示下一张,点击多快都没有用
          })
    
          circle++
          //如果circle=4,说明走到克隆的那张图片了
          if (circle == ol.children.length) {
            circle = 0
          }
    
          circleChange()
        }
      })
    
      // 8.左侧按钮
      arrow_l.addEventListener('click', function () {
        if (flag) {
          flag = false
          if (num == 0) {
            num = ul.children.length - 1 //num=3
            ul.style.left = -num * focusWidth + 'px'
          }
          num--
          animate(ul, -num * focusWidth, function () {
            flag = true
          })
    
          circle-- //circle为序号
          // if (circle < 0) {
          //   circle = ol.children.length - 1;  //circle=2
          // }
          circle = circle < 0 ? ol.children.length - 1 : circle
    
          circleChange()
        }
      })
    
      function circleChange() {
        // 先清除其他小圆圈的current类名,
        for (var i = 0; i < ol.children.length; i++) {
          ol.children[i].className = ''
        }
        // 当前的留下current
        ol.children[circle].className = 'current'
      }
    
      //自动播放模块
      var timer = setInterval(function () {
        //手动调用点击事件
        arrow_r.click()
      }, 2000)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

    animate.js

    function animate(obj, target, callback) {  //前面必须要加function关键字
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
          //停止动画 本质停止定时器
          clearInterval(obj.timer);
          //回调函数写到定时器结束里面
          // if (callback) {
          //   //调用函数
          //   callback();
          // }
          callback && callback();  //短路的思想
        }
    
        obj.style.left = obj.offsetLeft + step + 'px';
      },15);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    登录页面界面如下:
    在这里插入图片描述在这里插入图片描述
    2.2.2 注册页面(register.jsp)

    <% page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    
    
    
    	
     	
    	LARP-REGISTER
    	
      	
     	
     	
    
    
    
      

    新用户注册

    用户名称:
    设置密码:
    确认密码:
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    注册页面界面如下:
    在这里插入图片描述
    2.2.3 修改密码的页面(change_pwd.jsp)

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    
    
    
    	
     	
    	修改密码
    	
        
        
    	
    
    
      

    修改密码

    登录名称:
    修改密码:
    确认密码:
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    修改密码界面如下:在这里插入图片描述2.2.4 登录成功的页面(success.jsp)

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    
    
    
    	
    	
    	success
    	
    
    
    
      
    登录成功
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    登录成功界面:
    在这里插入图片描述
    2.3 创建相关配置类
    2.3.1 DAO类(DAO.java)
    DAO 类增加数据库查询用户的功能

    JDBC开发的步骤:

    ① 导入驱动jar包 mysql-connector-java-5.1.47-bin.jar

    复制粘贴到lib

    右键点击 build path -> add to bulid path (在java 工程当中引入了 jar 包)
    ② 注册驱动

    ③ 获取数据库的连接对象 Connection

    ④ 基本操作:执行sql

    定义sql语句
    获取执行sql语句的对象 Statement
    执行sql,用 ResultSet 接收返回的结果集
    遍历处理结果集
    
    • 1
    • 2
    • 3
    • 4

    ⑤ 释放资源
    以上的步骤2、3、5 都是在 JDBCUtil.java 中完成的

    package modle;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    public class DAO {
    	// 登录验证
    	public User login(Connection conn,User user) throws Exception{
            User resultUser = null;
            // sql 查询语句
            String sql="select * from users where username=? and password=?";
            // 获得执行sql语句的对象
            PreparedStatement pstatement =conn.prepareStatement(sql);
            pstatement.setString(1, user.getUsername());
            pstatement.setString(2, user.getPassword());
            // 执行sql语句获得结果集
            ResultSet rs = pstatement.executeQuery();
            if(rs.next()){ 
                resultUser = new User();
                resultUser.setUsersname(rs.getString("username"));
                resultUser.setPassword(rs.getString("password"));
            }
            
            return resultUser;
        }
    	
    	// 修改密码查找用户
    	public User searchUser(Connection conn,User user) throws Exception {
    		User resultUser = null;
            // sql 查询语句
            String sql="select * from users where username=?";
            // 获得执行sql语句的对象
            PreparedStatement pstatement =conn.prepareStatement(sql);
            pstatement.setString(1, user.getUsername());
            // 执行sql语句获得结果集
            ResultSet rs = pstatement.executeQuery();
            if(rs.next()){ 
                resultUser = new User();
                resultUser.setUsersname(rs.getString("username"));
            }
            
            return resultUser;
    	}
    	
    	// 注册验证
    	public boolean register(Connection conn,User user) throws Exception {
    		boolean flag = false;
            // sql 查询语句
            String sql="insert into users(username,password)values(?,?)";
            // 获得执行sql语句的对象
            PreparedStatement pstatement =conn.prepareStatement(sql);
            pstatement.setString(1, user.getUsername());
            pstatement.setString(2, user.getPassword());
            // 执行sql语句获得结果集
            int res = pstatement.executeUpdate();
            if(res > 0) {
            	flag = true;
            }
            return flag;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    2.3.2 JDBCUtil类(JDBCUtil.java)

    JDBCUtil类提供与数据库连接时 jdbc 的相关配置

    package modle;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.ResultSet;
    import java.sql.Statement;
    
    public class JDBCUtil {
    	// 数据库的参数
    	private String dbUrl="jdbc:mysql://localhost:3306/my_login?useSSL=false";
        private String dbUsername="root";
        private String dbPassword="XXXX"; // 自己的密码
        
        // 与数据库连接
        public Connection getConn() {
        	try {
        		// 加载驱动
        		Class.forName("com.mysql.jdbc.Driver");
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
        	Connection conn = null;
        	
        	try {
        		// 获得连接,返回connection 对象
        		conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
        	return conn;
        }
        
        // 释放资源
        // 关闭结果集 ResultSet
        public void close(ResultSet resultSet) throws Exception {
    		if(resultSet != null) {
    			resultSet.close();
    		}
    	}
        
        // 关闭 sql 语句对象 Statement
        public void close(Statement statement) throws Exception {
        	if(statement != null) {
        		statement.close();
        	}
        }
        
        // 关闭连接对象 Connection
        public void close(Connection conn) throws Exception {
        	if(conn != null) {
        		conn.close();
        	}
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    2.3.3 JsonResult类(JsonResult.java)

    JsonResult类处理 Json 返回的数据。

    package modle;
    
    public class JsonResult {
    	private int type; //0为失败, 1为成功
    	private String error;  //错误信息
    	
    	public int getType() {
    		return type;
    	}
    	
    	public void setType(int type) {
    		this.type = type;
    	}
    	
    	public String getError() {
    		return error;
    	}
    	
    	public void setError(String error) {
    		this.error = error;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2.3.4 MailUtil类(MailUtil.java)

    MailUtil 类提供调用邮件发邮件时的相关配置。这里我用的126邮箱,记得去打开邮箱中的 POP3和SMTP服务,记住授权密码,需要导入 javax.mail.jar 包:在这里插入图片描述开启服务后的界面:
    在这里插入图片描述

    package modle;
    
    import java.util.Properties;
    
    import javax.mail.Authenticator;
    import javax.mail.Message;
    import javax.mail.PasswordAuthentication;
    import javax.mail.Session;
    import javax.mail.Transport;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeMessage;
    
    public class MailUtil {
    	/**
    	 * @param email 登录用户的邮件
    	 * @param emailMsg  发送的邮件信息
    	 * @throws Exception
    	 */
    	public void sendMail(String userEmail, String emailMsg) throws Exception {
    		
            // 1. 创建一封邮件,创建一个程序与邮件服务器会话对象session
            Properties props = new Properties();
            props.setProperty("mail.transport.protocol", "SMTP");
            props.setProperty("mail.host", "smtp.126.com"); //smtp.126.com为SMTP服务器地址,为指定这个服务器发送邮件
            props.setProperty("mail.smtp.auth", "true"); // 指定验证为true
            
            // 创建验证器
            Authenticator auth = new Authenticator() {
            	public PasswordAuthentication getPasswordAuthentication() {
    				return new PasswordAuthentication("xxxx", "xxxx"); //参数分别为:用户名和授权密码
    			}
            };
            
            // 用于连接邮件服务器的参数配置(发送邮件时需要用到)
            Session session= Session.getInstance(props,auth);  // 根据参数配置,创建会话对象(为了发送邮件准备的)
            
    
            // 2.创建邮件对象message,相当于邮件内容
            Message message = new MimeMessage(session);
    
            // From: 发件人
            // 其中 InternetAddress 的三个参数分别为: 邮箱, 显示的昵称(只用于显示, 没有特别的要求), 昵称的字符集编码
            // 真正要发送时, 邮箱必须是真实有效的邮箱。
            message.setFrom(new InternetAddress("xxxxxxx"));  
    
            // To: 收件人 设置收件人和发送方式
            message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(userEmail));
          
            // Subject: 邮件主题
            message.setSubject("邮箱验证");
    
            // Content: 邮件正文(可以使用html标签)
            message.setContent(emailMsg, "text/html;charset=UTF-8");
    
            // 3. 创建 transport 用于将邮件发出
            Transport.send(message);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    2.3.5 User类(User.java)

    User类提供用户基本信息的配置

    package modle;
    
    public class User {
    	private String username;
    	private String password;
    	
    	// 构造函数
    	public User() {}
    	 
    	public User(String username, String password) {
    		this.username = username;
    		this.password = password;
    	}
    	
    	// 获取用户名
    	public String getUsername() {
    		return username;
    	}
    	
    	// 设置用户名
    	public void setUsersname(String username) {
    		this.username = username;
    	}
    	
    	// 获取密码
    	public String getPassword() {
    		return password;
    	}
    	
    	// 设置密码
    	public void setPassword(String password) {
    		 this.password = password;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2.4 创建 Servlet
    2.4.1 登录界面的Servlet(LoginServlet)

    主要思想:

    接收前台传来的值:账号和密码、邮箱和验证码,通过判断账号和密码或是邮箱和验证码谁不为空判断出前台使用的哪种登录方式
    邮箱和验证码登录方式中:检验验证码是否正确是从 session 中取出 emailCode
    的内容(在EmailServlet.java中在随机创建出6位的验证码后就把其存入 session中了)与用户输入的进行核对。但其不够完美,因为有时间限制,超出时间后 session 就失效了。

    package controller;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.sql.Connection;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.alibaba.fastjson.JSON;
    
    import modle.DAO;
    import modle.JDBCUtil;
    import modle.JsonResult;
    import modle.User;
    
    /**
     * 登录的 Servlet
     */
    
    public class LoginServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
           
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
    		this.doPost(request, response);
    	}
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {				
    		
    		// 接收前台传来的值 账号和密码 或是 邮箱和验证码
            String username = request.getParameter("username");        
            String password = request.getParameter("password");
            String email = request.getParameter("email");
            String code = request.getParameter("code");
                    
            System.out.println(password);
            System.out.println(email);
            System.out.println(code);
            
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
            
            // 账号密码登录的方式
            if(username != null && password != null) {
                //解决中文字符乱码
                byte[] bytes = username.getBytes("ISO-8859-1");
                username = new String(bytes,"utf-8");
                System.out.println(username);
                
            	JDBCUtil db = new JDBCUtil();
                // 创建一个用户保存下将密码和用户名保存
                User user = new User(username,password);
                DAO dao = new DAO();
                try {
                    //数据库连接
                    Connection conn = db.getConn();
                    
                    if(dao.login(conn, user) != null) {
                    	request.getSession().setAttribute("user", user);
                    	response.sendRedirect("jsp/success.jsp");
                    } else {
                    	out.println("

    用户名或者密码错误,验证失败

    "); out.println("

    3秒以后跳转回登录页面

    "); response.setHeader("Refresh", "3;url=jsp/login.jsp"); } } catch (Exception e) { e.printStackTrace(); } finally { out.close(); } } else if(email != null && code != null) { //邮箱验证码方式 // 判断emailCode是否正确 String s_emailCode = (String)request.getSession().getAttribute("emailCode"); JsonResult jr = new JsonResult(); if(!code.equalsIgnoreCase(s_emailCode)) { out.println("

    邮件验证码错误,验证失败

    "); out.println("

    3秒以后跳转回登录页面

    "); response.setHeader("Refresh", "3;url=jsp/login.jsp"); } else { // 验证成功 response.sendRedirect("jsp/success.jsp"); } out.close(); } } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    2.3.2 注册界面的Servlet(RegisterServlet)

    package controller;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.sql.Connection;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import modle.DAO;
    import modle.JDBCUtil;
    import modle.User;
    
    /**
     * 注册的servlet
     */
    public class RegisterServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {		
    		this.doPost(request, response);
    	}
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		
    		// 获取注册名和密码
    		String username = request.getParameter("username").trim();
            String password = request.getParameter("password").trim();
            String again_password = request.getParameter("again_password").trim();
            
            //解决中文字符乱码
            byte[] bytes = username.getBytes("ISO-8859-1");
            username = new String(bytes,"utf-8");
            
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
            
            JDBCUtil db = new JDBCUtil();
            // 创建一个用户保存下将密码和用户名保存
            User user = new User(username,password);
            DAO dao = new DAO();
            
            try {
            	//数据库连接
                Connection conn = db.getConn();
                
                if(!password.equals(again_password)) {
                	out.println("

    两次输入的密码不一致

    "); out.println("

    3秒以后返回注册页面

    "); response.setHeader("Refresh", "3;url=jsp/register.jsp"); } else { if(dao.register(conn, user)) { out.println("

    注册新用户成功

    "); out.println("

    3秒以后跳转回注册页面

    "); response.setHeader("Refresh", "3;url=jsp/login.jsp"); } else { out.println("

    注册新用户失败,用户名已经存在

    "); out.println("

    3秒以后跳转回注册页面

    "); response.setHeader("Refresh", "3;url=jsp/register.jsp"); } } } catch (Exception e) { e.printStackTrace(); } finally { out.close(); } } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    2.3.3 修改密码的Servlet(HandlePwdServlet)

    package controller;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import modle.DAO;
    import modle.JDBCUtil;
    import modle.User;
    
    /**
     * 修改密码的 servlet
     */
    public class HandlePwdServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		this.doPost(request, response);
    	}
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		// 获取数据
    		String username = request.getParameter("username").trim();
            String password = request.getParameter("password").trim();
            String again_password = request.getParameter("again_password").trim();
            //解决中文字符乱码
            byte[] bytes = username.getBytes("ISO-8859-1");
            username = new String(bytes,"utf-8");
            
            response.setContentType("text/html;charset=UTF-8");
            PrintWriter out = response.getWriter();
            
            JDBCUtil db = new JDBCUtil();
            // 创建一个用户保存下将密码和用户名保存
            User user = new User(username,password);
            DAO dao = new DAO();
            
            try {
            	//数据库连接
                Connection conn = db.getConn();
                // 数据库中没有该用户
                if(dao.searchUser(conn, user) == null) {
    	        	out.println("

    该用户不存在,请先去注册

    "); out.println("

    3秒以后返回修改密码页面

    "); response.setHeader("Refresh", "3;url=jsp/change_pwd.jsp"); } else { if(!password.equals(again_password)) { out.println("

    两次输入的密码不一致

    "); out.println("

    3秒以后返回修改密码页面

    "); response.setHeader("Refresh", "3;url=jsp/change_pwd.jsp"); } else { String sql="update users set password=? where username=?"; // 获得执行sql语句的对象 PreparedStatement pstatement =conn.prepareStatement(sql); pstatement.setString(1, user.getPassword()); pstatement.setString(2, user.getUsername()); // 返回受影响修改的行数 int res = pstatement.executeUpdate(); if(res != 0) { out.println("

    修改密码成功

    "); out.println("

    3秒以后跳转回登录页面

    "); response.setHeader("Refresh", "3;url=jsp/login.jsp"); } else { out.println("

    修改密码失败

    "); out.println("

    3秒以后返回修改密码页面

    "); response.setHeader("Refresh", "3;url=jsp/change_pwd.jsp"); } } } } catch (Exception e) { e.printStackTrace(); } finally { out.close(); } } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    2.3.4 发送邮件的Servlet(EmailServlet)

    主要思想:

    获取前台用户的邮箱
    随机生成6位数的验证码(需要导入commons-lang3-3.12.0.jar包)
    在服务器端通过 session 保存验证码
    通过 MailUtil 中对邮箱的配置发送邮件
    
    
    package controller;
    
    import java.io.IOException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.lang3.RandomStringUtils;
    
    import com.alibaba.fastjson.JSON;
    
    import modle.JsonResult;
    import modle.MailUtil;
    import net.sf.json.JSONObject;
    
    /**
     * 处理邮件的Servlet
     */
    public class EmailServlet extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		this.doPost(request, response);
    	}
    
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 获取邮箱
    		String email = request.getParameter("email");
            System.out.println(email);
            // 获取随机的6位数
            String num = RandomStringUtils.randomNumeric(6);
            // 邮件内容
            String emailMsg = "邮箱验证码为:"+ num +"请勿泄漏给他人!";
            
            // 在服务器端保存邮件验证码
            request.getSession().setAttribute("emailCode", num);
            
            JsonResult jr = new JsonResult();
            
            try {
            	MailUtil mail = new MailUtil();
    			// 发送邮件
            	mail.sendMail(email, emailMsg);
            	jr.setType(1); // 发送成功
            	response.getWriter().write(JSON.toJSONString(jr));
            	return;
    		} catch (Exception e) {			
    			e.printStackTrace();
    			jr.setType(0); // 发送失败
    			jr.setError("邮件发送失败");
    			response.getWriter().write(JSON.toJSONString(jr));
            	return;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    注意:
    在运行过程中,mail.sendMail(email, emailMsg); 这部总是报 java.lang.ClassNotFoundException: javax.activation.DataHandler的错误,查找资料下载导入了 javax.activation-1.2.0.jar就解决问题了

    使用 JSON.toJSONString(jr)是导入了第三方的包来对 Json 进行快速处理,使用该方法可以导入两种类型的包:

    ① 导入fastjson-1.2.75.jar 一个包就行, 下载地址:Maven Repository: com.alibaba fastjson (mvnrepository.com)

    ② 导入 6 个包:

    commons-beanutils-1.9.4.jar
    
    commons-collections4-4.4.jar
    
    commons-lang3-3.12.0.jar
    
    commons-logging-1.2.jar
    
    ezmorph-1.0.6.jar
    
    json-lib-2.4-jdk15.jar
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以在下面的地址中下载:

    http://commons.apache.org/index.html
    
    http://json-lib.sourceforge.net/
    
    http://ezmorph.sourceforge.net/
    
    http://www.docjar.com/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

  • 相关阅读:
    github教程
    因为一次服务器卡顿,再来好好理解Spring事务传播
    安卓案例:利用URLConnection下载图片
    2023年H1汽车社媒营销趋势报告
    linux 给根目录扩容(lvm CentOS 7.6 &kylinx86)
    Netty编程面试题
    Jitamin 安装与配置
    [spark]action算子
    【Linux】文件权限类命令
    从0到1学会Git(第三部分):Git的远程仓库链接与操作
  • 原文地址:https://blog.csdn.net/sebeefe/article/details/126057943