JavaWeb综合旅游网项目(二)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: JavaWeb综合旅游网项目(二)

5 登录


5.1 分析

7.png


用户输入信息点击登录按钮后,向后台发送ajax请求,调用 LoginServlet ,实现以下几个主要功能:


1.获取用户输入的信息

2.调用service层查询该用户是否存在

3.如果该用户存在且已激活则跳转至主页 index.html

4.否则输出错误信息


5.2 前台代码


5.2.1 登录页面login.html

<script>
   $(function () {
      // 登录按钮绑定单击事件
      $("#btn_sub").click(function () {
         // 发送ajax请求
         $.post("loginServlet",$("#loginForm").serialize(),function (data) {
            // data:{flag:false,errorMsg:''}
            if (data.flag){
               location.href = "index.html";
            }else {
               $("#errorMsg").html(data.errorMsg);
            }
         });
      });
   });
</script>


5.3 后台代码


5.3.1 LoginServlet


根据5.1分析部分的描述,servlet的代码如下:


protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 先校验验证码
    String check = request.getParameter("check");
    HttpSession session = request.getSession();
    String checkcode_server = (String)session.getAttribute("CHECKCODE_SERVER");
    session.removeAttribute("CHECKCODE_SERVER");    // 保证验证码只能使用一次
    if (checkcode_server == null || !checkcode_server.equalsIgnoreCase(check)){
        ResultInfo info = new ResultInfo();
        info.setFlag(false);
        info.setErrorMsg("验证码错误");
        // 将info序列化为json
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(info);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(json);
        return;
    }
    // 获取用户名密码
    Map<String, String[]> map = request.getParameterMap();
    User user = new User();
    try {
        BeanUtils.populate(user, map);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    UserService service = new UserServiceImpl();
    User u = service.login(user);
    ResultInfo info = new ResultInfo();
    if (u == null){
        info.setFlag(false);
        info.setErrorMsg("用户名或密码错误");
    }
    if (u != null && !"Y".equals(u.getStatus())){
        info.setFlag(false);
        info.setErrorMsg("您尚未激活,请登录邮箱激活");
    }
    if (u != null && "Y".equals(u.getStatus())){
        info.setFlag(true);
        session.setAttribute("user", u);
    }
    ObjectMapper mapper = new ObjectMapper();
    response.setContentType("application/json;charset=utf-8");
    mapper.writeValue(response.getOutputStream(), info);
}


5.3.2 service层 Userservice


servlet要调用该函数完成用户的查询,用户只输入了用户名和密码,所以就根据这两个值进行查询


@Override
public User login(User user) {
    return userDao.findByUsernameAndPassword(user.getUsername(), user.getPassword());
}


5.3.3 dao层 UserDao


注意:如果用户输入了错误的用户名或密码导致查询数据库失败的话,template.queryForObject 方法会报错,所以这里应该将异常catch一下,使得查询失败时返回一个空 User 对象


@Override
public User findByUsernameAndPassword(String username, String password) {
    User user = null;
    try {
        String sql = "select * from tab_user where username = ? and password = ?";
        // 第二个参数是指定返回结果要封装成的类型
        user = template.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), username, password);
    } catch (Exception e) {
    }
    return user;
}


5.3.4 登录成功后右上角显示用户姓名


index.html中,页面的头部和尾部的部分是由include.js动态加载的


<script type="text/javascript" src="js/include.js"></script>


观察include.js


$(function () {
    $.get("header.html",function (data) {
        $("#header").html(data);
    });
    $.get("footer.html",function (data) {
        $("#footer").html(data);
    });
});


可以看出是页面加载完成后再动态加载头部和尾部的信息的(分别封装在header.html和footer.html)


所以在header.html中添加发送异步请求查询当前登录用户的代码


<script>
    $(function () {
        $.get("findUserServlet",[],function (data) {
            // {uid:1,name:"李四"}
            var msg = "欢迎回来," + data.name;
            $("#span_username").html(msg);
        });
    });
</script>


相应的servlet,从session中获取user对象,以json格式写回前端


@WebServlet("/findUserServlet")
public class FindUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object user = request.getSession().getAttribute("user");
        //将user写回客户端
        ObjectMapper mapper = new ObjectMapper();
        response.setContentType("application/json;charset=utf-8");
        mapper.writeValue(response.getOutputStream(),user);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

6 退出登录


6.1 分析


session中有user对象说明用户登陆了,所以退出登录的实现可以如下:


1.访问servlet,将session销毁

2.跳转到登录页面


6.2 代码


6.2.1 为header.html中退出按钮添加事件


<a href="javascript:location.href = 'exitServlet';">退出</a>


6.2.2 退出操作服务端 ExitServlet


注意response重定向要加虚拟目录


protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    // 销毁session
    request.getSession().invalidate();
    // 跳转
    response.sendRedirect(request.getContextPath() + "/login.html");
}


7 优化服务端servlet代码


7.1 目的


减少Servlet的数量,现在是一个功能一个Servlet,将其优化为一个模块一个Servlet,相当于在数据库中一张表对应一个Servlet,在Servlet中提供不同的方法,完成用户的请求。


由于UserServlet继承了BaseServlet,BaseServlet又继承了HttpServlet所以当浏览器访问UserServlet时会自动调用BaseServlet的service方法,实现分发。

8.png


Idea控制台中文乱码解决:-Dfile.encoding=gb2312

9.jpeg


7.2 分发类BaseServlet


首先获取方法名,然后根据方法名采用反射机制加载UserServlet中相应的方法,并传入response和request来调用,但注意servlet中的方法是由protected修饰的,所以在使用反射机制加载方法时应当忽略访问修饰符。并在执行方法之前进行暴力反射。


Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
method.setAccessible(true);
method.invoke(this,req,resp);


但更好的方法是将UserServlet中的方法修饰符改为public


package cn.itcast.travel.web.servlet;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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 java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// 不需要被浏览器输入访问到
public class BaseServlet extends HttpServlet {
    // 访问UserServlet是自动执行该方法
    // 完成方法分发
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();// /travel/user/add
        // 获取方法名称
        String methodName = uri.substring(uri.lastIndexOf("/") + 1);
        try {
            // this这里代表的是调用 BaseServlet 的 UserServlet
            Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
            method.invoke(this,req,resp);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    /**
     * 将传入的对象序列化为json并写会客户端
     * @param obj
     */
    public void writeValue(Object obj, HttpServletResponse response) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        response.setContentType("application/json;charset=utf-8");
        mapper.writeValue(response.getOutputStream(), obj);
    }
    /**
     * 将传入的对象序列化为json返回
     * @param obj
     * @return
     */
    public String writeValueAsString(Object obj) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        return mapper.writeValueAsString(obj);
    }
}


7.3 提供用户服务的UserServlet


只需创建几个方法

10.png


并将之前写过的相应Servlet的逻辑复制到方法里面即可,具体代码如下(就是将前面的servlet做一个整合):


package cn.itcast.travel.web.servlet;
import cn.itcast.travel.domain.ResultInfo;
import cn.itcast.travel.domain.User;
import cn.itcast.travel.service.UserService;
import cn.itcast.travel.service.impl.UserServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.beanutils.BeanUtils;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@WebServlet("/user/*")
public class UserServlet extends BaseServlet {
    private UserService service = new UserServiceImpl();
    /**
     * 注册功能
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("regist");
        // 先校验验证码
        String check = request.getParameter("check");
        HttpSession session = request.getSession();
        String checkcode_server = (String)session.getAttribute("CHECKCODE_SERVER");
        session.removeAttribute("CHECKCODE_SERVER");    // 保证验证码只能使用一次
        if (checkcode_server == null || !checkcode_server.equalsIgnoreCase(check)){
            ResultInfo info = new ResultInfo();
            info.setFlag(false);
            info.setErrorMsg("验证码错误");
            // 将info序列化为json
            ObjectMapper mapper = new ObjectMapper();
            String json = writeValueAsString(info);
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(json);
            return;
        }
        // 获取数据
        Map<String, String[]> map = request.getParameterMap();
        // 封装
        User user = new User();
        try {
            BeanUtils.populate(user,map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        // 调用service层
        boolean flag = service.regist(user);
        // 响应结果
        ResultInfo info = new ResultInfo();
        if (flag){
            info.setFlag(true);
        }else {
            info.setFlag(false);
            info.setErrorMsg("注册失败");
        }
        // 将info序列化为json
        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(info);
        // 将json写回客户端
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(json);
    }
    /**
     * 登录功能
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("login");
        // 先校验验证码
        String check = request.getParameter("check");
        HttpSession session = request.getSession();
        String checkcode_server = (String)session.getAttribute("CHECKCODE_SERVER");
        session.removeAttribute("CHECKCODE_SERVER");    // 保证验证码只能使用一次
        if (checkcode_server == null || !checkcode_server.equalsIgnoreCase(check)){
            ResultInfo info = new ResultInfo();
            info.setFlag(false);
            info.setErrorMsg("验证码错误");
            // 将info序列化为json
            ObjectMapper mapper = new ObjectMapper();
            String json = mapper.writeValueAsString(info);
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(json);
            return;
        }
        // 获取用户名密码
        Map<String, String[]> map = request.getParameterMap();
        User user = new User();
        try {
            BeanUtils.populate(user, map);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        User u = service.login(user);
        ResultInfo info = new ResultInfo();
        if (u == null){
            info.setFlag(false);
            info.setErrorMsg("用户名或密码错误");
        }
        if (u != null && !"Y".equals(u.getStatus())){
            info.setFlag(false);
            info.setErrorMsg("您尚未激活,请登录邮箱激活");
        }
        if (u != null && "Y".equals(u.getStatus())){
            info.setFlag(true);
            session.setAttribute("user", u);
        }
        writeValue(info, response);
    }
    /**
     * 查询单个用户
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void findOne(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("findOne");
        Object user = request.getSession().getAttribute("user");
        //将user写回客户端
        writeValue(user, response);
    }
    /**
     * 退出功能
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void exit(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("exit");
        // 销毁session
        request.getSession().invalidate();
        // 跳转
        response.sendRedirect(request.getContextPath() + "/login.html");
    }
    /**
     * 激活功能
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void active(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("active");
        // 获取激活码
        String code = request.getParameter("code");
        if (code != null){
            // 激活
            boolean flag = service.active(code);
            // 判断标记
            String msg;
            if (flag){
                msg = "激活成功,请<a href = 'http://localhost/travel/login.html'>登录</a>";
            }else {
                msg = "激活失败,请联系管理员";
            }
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write(msg);
        }
    }
}


7.4 修改html页面中之前写的servlet的路径


7.4.1 登录页面login.html


$.post("user/login",$("#loginForm").serialize(),function (data) {
   // data:{flag:false,errorMsg:''}
   if (data.flag){
      location.href = "index.html";
   }else {
      $("#errorMsg").html(data.errorMsg);
   }
});


7.4.2 头部页面header.html


$.get("user/findOne",[],function (data) {
    // {uid:1,name:"李四"}
    var msg = "欢迎回来," + data.name;
    $("#span_username").html(msg);
});


7.4.3 注册页面regist.html

$.post("user/regist",$(this).serialize(),function (data) {
   // 处理服务器响应的数据
   if (data.flag){
      // 注册成功
      location.href = "register_ok.html";
   }else {
      $("#errorMsg").html(data.errorMsg);
   }
});


7.4.4 service层UserServiceImpl


修改邮件的激活路径


String content = "<a href = 'http://localhost/travel/user/active?code="+ user.getCode() +"'>点击激活</a>";


8 分类数据展示


11.png


网页顶部导航栏


8.1 分析


12.png


用户点击主页导航栏上的按钮,如:国内游,之后向后台发送ajax请求,调用相应的 CategoryServlet 类中的 findAll 方法查询所有的数据以json格式返回,前台循环遍历结果数组并展示。


servlet调用service层,service层调用dao层,都是最简单的查询所有sql语句。


8.2 后台代码


8.2.1 CategoryServlet


package cn.itcast.travel.web.servlet;
import cn.itcast.travel.domain.Category;
import cn.itcast.travel.service.CategoryService;
import cn.itcast.travel.service.impl.CategoryServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
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 java.io.IOException;
import java.util.List;
@WebServlet("/category/*")
public class CategoryServlet extends BaseServlet {
    private CategoryService service = new CategoryServiceImpl();
    /**
     * 查询所有
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void findAll(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Category> cs = service.findAll();
        writeValue(cs, response);
    }
}


8.2.2 service层CategoryService


接口


package cn.itcast.travel.service;
import cn.itcast.travel.domain.Category;
import java.util.List;
public interface CategoryService {
    public List<Category> findAll();
}


实现类


package cn.itcast.travel.service.impl;
import cn.itcast.travel.dao.CategoryDao;
import cn.itcast.travel.dao.impl.CategoryDaoImpl;
import cn.itcast.travel.domain.Category;
import cn.itcast.travel.service.CategoryService;
import java.util.List;
public class CategoryServiceImpl implements CategoryService {
    private CategoryDao categoryDao = new CategoryDaoImpl();
    @Override
    public List<Category> findAll() {
        return categoryDao.findAll();
    }
}


8.2.3 CategoryDao


接口


package cn.itcast.travel.dao;
import cn.itcast.travel.domain.Category;
import java.util.List;
public interface CategoryDao {
    /**
     * 查询所有分类
     * @return
     */
    public List<Category> findAll();
}


实现类


package cn.itcast.travel.dao.impl;
import cn.itcast.travel.dao.CategoryDao;
import cn.itcast.travel.domain.Category;
import cn.itcast.travel.util.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class CategoryDaoImpl implements CategoryDao {
    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
    @Override
    public List<Category> findAll() {
        String sql = "select * from tab_category";
        return template.query(sql, new BeanPropertyRowMapper<>(Category.class));
    }
}


8.3 前台代码


8.3.1 header.html


页面加载后,发送ajax请求,请求路径:category/findAll


// 查询分类列表
$.get("category/findAll",[],function (data) {
   // [{cid:1,cname="国内游"},{cid:2,cname="国外游"}]
    var lis = '<li class="nav-active"><a href="index.html">首页</a></li>';
    // 遍历数组,字符串拼接
    for (var i = 0; i < data.length; i++) {
        var li = '<li><a href="route_list.html">' + data[i].cname + '</a></li>';
        lis += li;
    }
    lis += '<li><a href="favoriterank.html">收藏排行榜</a></li>';
    // 将lis设置的ul中
    $("#category").html(lis);
});


8.4 对分类数据进行缓存优化


分类的数据在每一次页面加载后都会重新请求数据库来加载,对数据库的压力比较大,而且分类的数据不会经常产生变化,所以可以使用redis来缓存这个数据。

13.png


在CategoryService中可以添加redis缓存操作,先从redis中查询数据,如果没有相应的数据则再调用dao层从mysql中取出数据存入缓存中并返回集合


操作redis数据库的jedis工具类:


package cn.itcast.travel.util;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
 * Jedis工具类
 */
public final class JedisUtil {
    private static JedisPool jedisPool;
    static {
        //读取配置文件
        InputStream is = JedisPool.class.getClassLoader().getResourceAsStream("jedis.properties");
        //创建Properties对象
        Properties pro = new Properties();
        //关联文件
        try {
            pro.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取数据,设置到JedisPoolConfig中
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
        config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));
        //初始化JedisPool
        jedisPool = new JedisPool(config, pro.getProperty("host"), Integer.parseInt(pro.getProperty("port")));
    }
    /**
     * 获取连接方法
     */
    public static Jedis getJedis() {
        return jedisPool.getResource();
    }
    /**
     * 关闭Jedis
     */
    public static void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}


期望数据中存储的顺序就是将来展示的顺序,使用redis的sortedset


还需要注意,如果使用redis中的sortedset,则取出来的是Set,由于findAll()方法返回的需要是List,所以需要手动做一个转换。如果除了取出sortedset中存储的数据之外,还要取出每一条数据对应的score,则需要zrangeWithScores方法,返回的Set中存储了Tuple类型,Tuple有两个成员变量,一个element对应存进去的数据,一个score对应sortedset的score


@Override
public List<Category> findAll() {
    // 查redis
    Jedis jedis = JedisUtil.getJedis();
    // 查询sortedset中的分数(cid)和值(cname)
    Set<Tuple> categorys = jedis.zrangeWithScores("category", 0, -1);
    List<Category> cs = null;
    // 没有命中查mysql
    if (categorys == null || categorys.size() == 0){
        System.out.println("数据库查询");
        cs = categoryDao.findAll();
        // 存入redis
        for (int i = 0; i < cs.size(); i ++){
            // 以id作为排序的分数
            jedis.zadd("category",cs.get(i).getCid(),cs.get(i).getCname());
        }
    }else { // 返回的需要是list
        System.out.println("redis查询");
        cs = new ArrayList<Category>();
        for (Tuple tuple : categorys) {
            Category category = new Category();
            category.setCname(tuple.getElement());
            category.setCid((int)tuple.getScore());
            cs.add(category);
        }
    }
    return cs;
}


相关文章
|
2月前
|
安全 Java API
Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发
本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。
322 1
|
2月前
|
JavaScript Java 大数据
基于JavaWeb的销售管理系统设计系统
本系统基于Java、MySQL、Spring Boot与Vue.js技术,构建高效、可扩展的销售管理平台,实现客户、订单、数据可视化等全流程自动化管理,提升企业运营效率与决策能力。
|
3月前
|
前端开发 Java API
2025 年 Java 全栈从环境搭建到项目上线实操全流程指南:Java 全栈最新实操指南(2025 版)
本指南涵盖2025年Java全栈开发核心技术,从JDK 21环境搭建、Spring Boot 3.3实战、React前端集成到Docker容器化部署,结合最新特性与实操流程,助力构建高效企业级应用。
1017 1
|
3月前
|
JavaScript Java 微服务
现代化 Java Web 在线商城项目技术方案与实战开发流程及核心功能实现详解
本项目基于Spring Boot 3与Vue 3构建现代化在线商城系统,采用微服务架构,整合Spring Cloud、Redis、MySQL等技术,涵盖用户认证、商品管理、购物车功能,并支持Docker容器化部署与Kubernetes编排。提供完整CI/CD流程,助力高效开发与扩展。
445 65
|
2月前
|
IDE 安全 Java
Lombok 在企业级 Java 项目中的隐性成本:便利背后的取舍之道
Lombok虽能简化Java代码,但其“魔法”特性易破坏封装、影响可维护性,隐藏调试难题,且与JPA等框架存在兼容风险。企业级项目应优先考虑IDE生成、Java Records或MapStruct等更透明、稳健的替代方案,平衡开发效率与系统长期稳定性。
163 1
|
2月前
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
4月前
|
安全 Java 测试技术
Java 大学期末实操项目在线图书管理系统开发实例及关键技术解析实操项目
本项目基于Spring Boot 3.0与Java 17,实现在线图书管理系统,涵盖CRUD操作、RESTful API、安全认证及单元测试,助力学生掌握现代Java开发核心技能。
174 0
|
4月前
|
SQL 前端开发 Java
JavaWeb 学习日记案例详解及 javaweb 完整项目案例实战指南
本文介绍了一个基于Spring Boot的JavaWeb企业员工管理系统完整案例,涵盖部门管理、员工管理、登录、异常处理、事务管理及AOP等核心功能实现,结合CSDN相关技术文章,提供详细技术方案与应用实例,适合JavaWeb开发者学习与参考。
171 0
|
4月前
|
安全 JavaScript Java
java Web 项目完整案例实操指南包含从搭建到部署的详细步骤及热门长尾关键词解析的实操指南
本项目为一个完整的JavaWeb应用案例,采用Spring Boot 3、Vue 3、MySQL、Redis等最新技术栈,涵盖前后端分离架构设计、RESTful API开发、JWT安全认证、Docker容器化部署等内容,适合掌握企业级Web项目全流程开发与部署。
272 0