news 2026/10/18 22:05:42

JSP+JavaBean+SQL Server图书商城实战系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JSP+JavaBean+SQL Server图书商城实战系统

简介:本资源是一套完整的基于Java的在线购物系统课程设计实践材料,面向Java Web初学者、高校计算机专业学生及Web开发入门者,解决从需求分析到系统落地的全流程学习问题。压缩包为ZIP格式,大小1.98MB,包含源代码、系统可运行程序及配套论文文档,核心文件涵盖JSP页面(实现会员注册登录、图书浏览与搜索、购物车及订单管理等前端交互)、JavaBean组件(封装业务逻辑)、SQL Server数据库脚本及系统部署说明,结构清晰体现JSP+JavaBean+SQL Server三层架构设计思想。已有1733人学习下载,读者可直接导入开发环境运行调试,完整掌握用户管理、商品展示、购物车会话控制、订单流程处理等典型电商模块的实现细节,并通过论文深入理解系统需求分析、模块划分与技术选型依据,是Java Web项目实战与课程设计的优质参考范例。

1. 这不是又一个“学生课设Demo”:一套能跑通注册→加购→下单→查单全流程的JSP+JavaBean+SQL Server实战系统

你可能已经点开过十几个标着“Java在线购物系统”的压缩包,解压后发现:首页打不开、登录报500、数据库脚本执行失败、连Tomcat都配不起来——最后默默删掉,怀疑自己是不是连环境都不会搭。但这次不一样。这个名为“网上图书超市”的系统,是少有真正走完MVC三层落地闭环的JSP老派工程:它不用Spring Boot自动装配来掩盖细节,不靠前端框架遮掩DOM操作的原始逻辑,而是用最朴素的<jsp:useBean>调用JavaBean、用<sql:query>直连SQL Server、用纯JSTL完成页面流转。它解决的不是“能不能跑”,而是“为什么这样分层”“JavaBean里哪些字段必须和表字段严格对齐”“JSP中request.setAttribute()传参漏一步就全链路崩塌”的真实问题。适合正在啃《Head First Servlets & JSP》第7章、被三层架构绕晕的初学者;也适合想回溯Web开发底层脉络、给新团队讲清楚“Servlet生命周期怎么卡在doPost里”的带教工程师。它不炫技,但每行代码都在回答一个经典问题:当没有IOC容器时,对象是谁创建的?谁销毁的?谁负责线程安全?


2. 从零启动:环境搭建、数据库初始化与项目结构解剖

2.1 环境依赖清单:版本不是越新越好,而是“刚好能跑通”

这套系统诞生于JSP技术栈成熟期(约2010–2015年),强行套用JDK 17或Tomcat 10会直接触发类加载失败。经实测验证,以下组合可100%复现原始行为:

组件推荐版本关键原因
JDKJDK 1.6 或 JDK 1.7java.sql.Date与java.util.Date的转换逻辑在JDK 8+中变更,导致订单时间插入异常
TomcatTomcat 6.0.48 或 Tomcat 7.0.99Tomcat 8+默认禁用org.apache.jasper.compiler.JspRuntimeContext,而本系统web.xml中显式配置了该类作为JSP编译器
SQL ServerSQL Server 2005 或 2008 R2数据库脚本中使用TOP 10而非LIMIT 10,且未启用ANSI_NULLS,高版本需手动兼容
JDBC驱动sqljdbc4.jar(对应SQL Server 2005/2008)sqljdbc41.jar及以后版本强制要求JDK 7+,且连接URL参数名变更

提示:不要试图用Docker一键拉起SQL Server 2019镜像去跑这个系统——驱动不兼容、权限模型不一致、甚至sa账户默认禁用都会让你卡在第一步。老老实实用本地安装的SQL Server 2008 R2 Express版,这是血泪经验。

2.2 数据库初始化:四步完成建库、建表、插数据、配连接

系统附带的db/BookShopDB.sql脚本并非标准SQL,需按顺序执行:

-- Step 1:创建数据库(注意:不能用中文路径,否则JDBC连接失败) CREATE DATABASE BookShopDB ON PRIMARY ( NAME = 'BookShopDB_data', FILENAME = 'C:\SQLData\BookShopDB.mdf' ) LOG ON ( NAME = 'BookShopDB_log', FILENAME = 'C:\SQLData\BookShopDB.ldf' ); GO -- Step 2:切换数据库并创建表(关键:所有表必须用[dbo]前缀,否则JavaBean映射失败) USE BookShopDB; GO CREATE TABLE [dbo].[Users]( [UserID] [int] IDENTITY(1,1) NOT NULL, [UserName] [varchar](50) NOT NULL, [Password] [varchar](50) NOT NULL, [Email] [varchar](100) NULL, [RegTime] [datetime] NULL, CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ([UserID] ASC) ); -- (其余表略,共7张:Books, CartItems, Orders, OrderDetails, Categories, Admins, Comments) -- Step 3:插入基础数据(必须执行!否则登录页报空指针) INSERT INTO [dbo].[Users] ([UserName],[Password],[Email],[RegTime]) VALUES ('testuser','123456','test@example.com',GETDATE()); -- Step 4:创建SQL Server登录账户(非Windows认证模式) CREATE LOGIN bookshop_user WITH PASSWORD = 'bookshop123'; USE BookShopDB; CREATE USER bookshop_user FOR LOGIN bookshop_user; EXEC sp_addrolemember 'db_owner', 'bookshop_user';

执行完后,在WEB-INF/web.xml中核对数据库连接配置:

<context-param> <param-name>driver</param-name> <param-value>com.microsoft.sqlserver.jdbc.SQLServerDriver</param-value> </context-param> <context-param> <param-name>url</param-name> <param-value>jdbc:sqlserver://localhost:1433;databaseName=BookShopDB;user=bookshop_user;password=bookshop123;</param-value> </context-param>

参数说明:

  • databaseName必须与Step 1中创建的库名完全一致(区分大小写);
  • user/password必须与Step 4中创建的SQL Server登录凭据匹配;
  • URL末尾不能加;,否则Tomcat启动时报java.sql.SQLException: No suitable driver found;
  • 若使用Windows身份验证,需将user/password改为integratedSecurity=true;,但必须确保Tomcat服务以具有SQL Server登录权限的Windows账户运行。

2.3 项目结构解剖:看懂src/、WEB-INF/、pages/三者如何咬合

整个工程采用经典JSP Model 1(非MVC)向Model 2过渡的混合结构,理解目录关系是调试前提:

OnlineBookShop/ ├── pages/ ← 用户可见的JSP页面(首页、商品列表、购物车等) │ ├── index.jsp │ ├── login.jsp │ ├── cart.jsp │ └── ... ├── WEB-INF/ │ ├── web.xml ← 核心配置:servlet映射、上下文参数、过滤器 │ ├── lib/ ← 必须包含:sqljdbc4.jar + jstl.jar + standard.jar │ └── classes/ ← 编译后的JavaBean字节码(.class文件) ├── src/ ← Java源码(.java文件),需手动编译到classes/ │ ├── bean/ ← 所有JavaBean:UserBean.java, BookBean.java, OrderBean.java... │ └── servlet/ ← 少量控制逻辑:LoginServlet.java, AddToCartServlet.java... └── db/ ← 数据库脚本与说明

关键咬合点:

  • pages/login.jsp中<jsp:useBean id="user" class="bean.UserBean" scope="page"/>→ 从WEB-INF/classes/bean/UserBean.class加载;
  • UserBean.java中private String userName;字段名必须与Users表中[UserName]列名首字母小写后完全一致(即userName↔UserName),否则<jsp:setProperty property="*" />无法自动绑定;
  • web.xml中<servlet-mapping>将/login请求路由到LoginServlet,而该Servlet在src/servlet/LoginServlet.java中定义,其doPost()方法内调用user.validate()并重定向至index.jsp或error.jsp。

这种“手动粘合”正是理解Servlet生命周期的最佳切口:没有Spring MVC的@RequestMapping魔法,你能清晰看到request.getParameter("username")如何一步步变成数据库里的INSERT语句。


3. 核心模块实现原理:从JavaBean属性映射到购物车状态持久化

3.1 JavaBean设计哲学:不是POJO,而是“带契约的活对象”

本系统的JavaBean不是简单的getter/setter容器,而是承担了数据校验、业务规则、DAO交互三重职责。以UserBean.java为例:

package bean; import java.sql.*; import javax.naming.*; import javax.sql.*; public class UserBean { private int userID; private String userName; // 对应表字段 [UserName] private String password; // 对应 [Password] private String email; // 对应 [Email] private java.util.Date regTime; // 构造函数、getter/setter(省略) // 关键:业务方法,非纯数据载体 public boolean validate(String inputName, String inputPass) { try { Context ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/BookShopDB"); Connection conn = ds.getConnection(); PreparedStatement ps = conn.prepareStatement( "SELECT * FROM Users WHERE UserName = ? AND Password = ?"); ps.setString(1, inputName); ps.setString(2, inputPass); ResultSet rs = ps.executeQuery(); if (rs.next()) { this.userID = rs.getInt("UserID"); this.userName = rs.getString("UserName"); this.email = rs.getString("Email"); this.regTime = rs.getTimestamp("RegTime"); return true; } } catch (Exception e) { e.printStackTrace(); // 实际项目中应记录日志,而非打印堆栈 } return false; } // 持久化方法:注册新用户 public boolean register() { try { Context ctx = new InitialContext(); DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/BookShopDB"); Connection conn = ds.getConnection(); PreparedStatement ps = conn.prepareStatement( "INSERT INTO Users(UserName,Password,Email,RegTime) VALUES(?,?,?,?)", Statement.RETURN_GENERATED_KEYS); ps.setString(1, this.userName); ps.setString(2, this.password); ps.setString(3, this.email); ps.setTimestamp(4, new Timestamp(this.regTime.getTime())); int rows = ps.executeUpdate(); if (rows > 0) { ResultSet rs = ps.getGeneratedKeys(); if (rs.next()) { this.userID = rs.getInt(1); // 获取自增主键 } return true; } } catch (Exception e) { e.printStackTrace(); } return false; } }

逻辑说明与参数说明:

  • validate()方法中,PreparedStatement的?占位符顺序必须与SQL中字段顺序严格一致,否则密码比对失效;
  • register()方法使用Statement.RETURN_GENERATED_KEYS获取SQL Server自增主键,这是JDBC 3.0特性,JDK 1.6+支持,但需确保sqljdbc4.jar版本正确;
  • setTimestamp(4, new Timestamp(...))是关键:java.util.Date不能直接传入PreparedStatement.setTimestamp(),必须包装为java.sql.Timestamp,否则插入datetime类型时报错;
  • ctx.lookup("java:comp/env/jdbc/BookShopDB")要求web.xml中已配置<resource-ref>,否则抛NameNotFoundException。

3.2 购物车实现:Session级状态管理与跨页面数据传递

购物车功能未使用数据库持久化(简化设计),而是将CartItem对象存入HttpSession,这是理解Web会话机制的绝佳案例:

// pages/addtocart.jsp 中的关键逻辑 <% // 1. 从session中获取购物车List,若不存在则新建 List<bean.CartItem> cart = (List<bean.CartItem>) session.getAttribute("cart"); if (cart == null) { cart = new ArrayList<bean.CartItem>(); session.setAttribute("cart", cart); } // 2. 创建CartItem并添加(注意:必须new新对象,不能复用已有引用) bean.CartItem item = new bean.CartItem(); item.setBookID(Integer.parseInt(request.getParameter("bookID"))); item.setBookName(request.getParameter("bookName")); item.setPrice(Double.parseDouble(request.getParameter("price"))); item.setQuantity(1); // 3. 检查是否已存在同书ID商品,存在则数量+1 boolean found = false; for (bean.CartItem c : cart) { if (c.getBookID() == item.getBookID()) { c.setQuantity(c.getQuantity() + 1); found = true; break; } } if (!found) { cart.add(item); } // 4. 重定向回商品列表页(避免F5刷新重复加购) response.sendRedirect("booklist.jsp"); %>

参数说明与陷阱:

  • session.setAttribute("cart", cart)中的"cart"是硬编码Key,所有JSP页面(cart.jsp,checkout.jsp,orderconfirm.jsp)都通过此Key读取,拼写错误即购物车为空;
  • Integer.parseInt(request.getParameter("bookID"))若参数为空或非数字,抛NumberFormatException,但本系统未做try-catch,导致页面500——这是典型的学生课设疏漏,也是你加日志监控的第一处;
  • response.sendRedirect()是关键:若用<jsp:forward>,URL不变化,用户刷新时会重复提交addtocart.jsp,造成数量翻倍;
  • CartItem类中bookID字段类型为int,但数据库Books表中BookID为int identity,因此前端传参必须是整数,不能是字符串"1"(虽parseInt可转,但健壮性差)。

3.3 订单生成:事务边界在哪里?为什么OrderBean要拆成两个方法?

订单模块是检验事务理解的试金石。系统将下单分为两步:

  1. OrderBean.createOrder():插入Orders主表,获取OrderID;
  2. OrderBean.addOrderDetails():用上一步的OrderID批量插入OrderDetails子表。
// OrderBean.java 片段 public boolean createOrder(int userID, String address, String phone) { try { Connection conn = getConnection(); // 复用DataSource conn.setAutoCommit(false); // 开启事务 PreparedStatement ps = conn.prepareStatement( "INSERT INTO Orders(UserID,OrderDate,Status,Address,Phone) VALUES(?,GETDATE(),'unpaid',?,?)", Statement.RETURN_GENERATED_KEYS); ps.setInt(1, userID); ps.setString(2, address); ps.setString(3, phone); ps.executeUpdate(); ResultSet rs = ps.getGeneratedKeys(); if (rs.next()) { this.orderID = rs.getInt(1); conn.commit(); // 提交主表 return true; } } catch (Exception e) { try { conn.rollback(); } catch (SQLException ex) {} e.printStackTrace(); } return false; } public boolean addOrderDetails(List<bean.CartItem> cartItems) { try { Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement( "INSERT INTO OrderDetails(OrderID,BookID,Quantity,Price) VALUES(?,?,?,?)"); for (bean.CartItem item : cartItems) { ps.setInt(1, this.orderID); ps.setInt(2, item.getBookID()); ps.setInt(3, item.getQuantity()); ps.setDouble(4, item.getPrice()); ps.addBatch(); // 批量执行 } ps.executeBatch(); return true; } catch (Exception e) { e.printStackTrace(); return false; } }

逻辑说明:

  • createOrder()中conn.setAutoCommit(false)开启事务,但addOrderDetails()未参与同一事务——这是设计缺陷:若子表插入失败,主表订单已生成,造成数据不一致;
  • 正确做法是将两个方法合并,或在Servlet中统一管理Connection;
  • ps.addBatch()后必须调用ps.executeBatch(),否则无任何数据写入;
  • OrderDetails表无主键约束,但Orders表有OrderID PK,因此this.orderID必须在createOrder()成功后才有效,否则子表插入报外键约束失败。

4. 避坑指南:五个让开发者凌晨三点还在查日志的真实问题

4.1 现象:首页index.jsp打开空白,查看Tomcat日志显示org.apache.jasper.JasperException: /index.jsp (line: 12, column: 1) The value for the useBean class attribute bean.UserBean is invalid.

原因:bean.UserBean类未编译进WEB-INF/classes/,或web.xml中未配置<servlet>标签导致JSP引擎找不到类加载器上下文。
解决:

  1. 确认src/bean/UserBean.java已用javac -cp "WEB-INF/lib/sqljdbc4.jar;WEB-INF/lib/jstl.jar" -d WEB-INF/classes src/bean/UserBean.java编译;
  2. 检查WEB-INF/classes/bean/UserBean.class文件是否存在且非零字节;
  3. 在web.xml顶部添加<web-app xmlns="http://java.sun.com/xml/ns/javaee" ...>命名空间声明,旧版XSD缺失会导致JSP解析器拒绝加载Bean。

4.2 现象:登录成功后跳转至index.jsp,但页面顶部显示Welcome, null!,session.getAttribute("user")始终为null。

原因:login.jsp中<jsp:useBean>作用域设为scope="page",而后续页面试图从session中读取。
解决:

  • 将login.jsp中<jsp:useBean id="user" class="bean.UserBean" scope="page"/>改为scope="session";
  • 在user.validate()返回true后,显式执行session.setAttribute("user", user);
  • 确保index.jsp中读取时用<%= ((bean.UserBean)session.getAttribute("user")).getUserName() %>,而非pageContext.findAttribute()。

4.3 现象:添加商品到购物车后,cart.jsp显示数量为0,session.getAttribute("cart")返回空List。

原因:addtocart.jsp中response.sendRedirect("booklist.jsp")后,浏览器发起新请求,但booklist.jsp未重新从session读取购物车,而是直接渲染空集合。
解决:

  • 在booklist.jsp顶部添加<% List<bean.CartItem> cart = (List<bean.CartItem>) session.getAttribute("cart"); %>;
  • 在显示购物车链接处,用<%= (cart != null ? cart.size() : 0) %>动态更新角标;
  • 更佳实践:将购物车读取逻辑提取为<%@ include file="cart_header.jsp" %>,避免多页面重复代码。

4.4 现象:SQL Server连接报错java.sql.SQLException: Login failed for user 'bookshop_user'.,但SQL Server Management Studio可正常登录。

原因:SQL Server混合模式未启用,或bookshop_user账户被禁用。
解决:

  1. 在SSMS中右键服务器 → “属性” → “安全性” → 勾选“SQL Server和Windows身份验证模式”;
  2. 重启SQL Server服务;
  3. 展开“安全性” → “登录名” → 右键bookshop_user→ “属性” → “状态” → 确保“登录”设为“启用”;
  4. 在“用户映射”页,勾选BookShopDB库,并在右侧角色中勾选db_owner。

4.5 现象:订单支付后,orderconfirm.jsp显示OrderID: 0,数据库Orders表中OrderID为正确值,但JavaBean未获取到。

原因:OrderBean.createOrder()中ps.getGeneratedKeys()返回的ResultSet未调用rs.next()即尝试rs.getInt(1),导致返回0。
解决:

  • 严格按JDBC规范,在rs.getGeneratedKeys()后必须先rs.next()再取值;
  • 添加判空逻辑:if (rs.next()) { this.orderID = rs.getInt(1); } else { throw new SQLException("Failed to get generated key"); };
  • 在web.xml中为OrderServlet配置<load-on-startup>1</load-on-startup>,确保类加载时机早于JSP解析。

5. 进阶技巧:用Filter实现全局登录拦截与请求日志审计

5.1 为什么原系统没做登录拦截?以及我们如何补上

原系统所有页面(包括admin/目录下的管理页)均未校验用户登录态,任何人可直击URL访问后台。这不是疏忽,而是教学系统刻意暴露的安全缺口——它逼你思考:如果不用Spring Security,仅用Servlet Filter,该怎么堵?

答案是写一个LoginCheckFilter,在web.xml中配置其拦截/pages/*和/admin/*路径:

// src/filter/LoginCheckFilter.java package filter; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class LoginCheckFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; String uri = req.getRequestURI(); // 放行登录页、注册页、静态资源 if (uri.endsWith("/login.jsp") || uri.endsWith("/register.jsp") || uri.contains("/css/") || uri.contains("/images/")) { chain.doFilter(request, response); return; } // 检查session中是否有user对象 HttpSession session = req.getSession(false); if (session == null || session.getAttribute("user") == null) { // 未登录,重定向到登录页,并保存原请求地址 String target = req.getRequestURL().toString(); if (req.getQueryString() != null) { target += "?" + req.getQueryString(); } session.setAttribute("redirectAfterLogin", target); resp.sendRedirect(req.getContextPath() + "/pages/login.jsp"); return; } // 已登录,放行 chain.doFilter(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void destroy() {} }

在web.xml中注册:

<filter> <filter-name>LoginCheckFilter</filter-name> <filter-class>filter.LoginCheckFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginCheckFilter</filter-name> <url-pattern>/pages/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>LoginCheckFilter</filter-name> <url-pattern>/admin/*</url-pattern> </filter-mapping>

关键细节:

  • req.getSession(false)中的false表示不自动创建session,避免未登录用户也生成无效session;
  • session.setAttribute("redirectAfterLogin", target)存储原始URL,登录成功后在login.jsp中读取并重定向,实现“登录后回到原页面”;
  • <url-pattern>/pages/*</url-pattern>必须与实际JSP存放路径一致,若你的页面在/jsp/下,则需同步修改。

5.2 用Log4j 1.2注入请求审计能力(不改一行业务代码)

原系统无日志,所有e.printStackTrace()都输出到Tomcat控制台。我们用Log4j 1.2(兼容JDK 1.6)为其注入生产级日志能力:

  1. 下载log4j-1.2.17.jar放入WEB-INF/lib/;
  2. 在WEB-INF/classes/下创建log4j.properties:
log4j.rootLogger=INFO, FILE, CONSOLE log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} - %m%n log4j.appender.FILE=org.apache.log4j.RollingFileAppender log4j.appender.FILE.File=${catalina.home}/logs/bookshop.log log4j.appender.FILE.MaxFileSize=10MB log4j.appender.FILE.MaxBackupIndex=5 log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} - %m%n
  1. 在LoginCheckFilter.doFilter()中添加审计日志:
// 在chain.doFilter()前添加 String ip = req.getRemoteAddr(); String userAgent = req.getHeader("User-Agent"); log.info("Request from " + ip + " [" + userAgent + "] to " + uri); // 在chain.doFilter()后添加(捕获响应时间) long start = System.currentTimeMillis(); chain.doFilter(request, response); long end = System.currentTimeMillis(); log.info("Response to " + ip + " took " + (end - start) + "ms");

效果:

  • 所有请求IP、UA、URL、耗时被记录到$CATALINA_HOME/logs/bookshop.log;
  • 日志按10MB滚动,保留5个历史文件,避免磁盘撑爆;
  • 无需修改任何JSP或JavaBean,Filter透明织入。

从那以后我每次接手老系统,第一件事就是加Filter做登录拦截和日志埋点——它不改变业务逻辑,却让整个系统从“黑匣子”变成“可观测体”。这比写一百行业务代码更能建立信任。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 22:04:25

【选型】高温热管传热极限的计算与影响因素

摘要 传热极限是高温热管选型时最容易被忽略的参数——客户往往只看额定功率&#xff0c;但实际可用传热能力由毛细压力、蒸汽流动、界面热阻和重力方向共同决定。本文梳理传热极限的物理来源&#xff0c;逐项分析各影响因素的作用方向&#xff0c;并给出工程上可执行的裕量取…

作者头像 李华
网站建设 2026/10/18 22:02:42

Flutter MethodChannel原理解析与实战避坑指南

1. 项目概述&#xff1a;为什么Flutter里总绕不开MethodChannel&#xff1f;在某跨平台图像处理Demo的实际开发中&#xff0c;我遇到一个典型场景&#xff1a;App主界面用Flutter写得丝滑流畅&#xff0c;但核心的实时滤镜渲染必须调用原生SDK——iOS端是Metal加速的自研框架&a…

作者头像 李华
网站建设 2026/10/18 21:59:01

原生表单组件开发实战:结构设计、校验联动与可访问性优化

1. 案例背景与预期目标1.1 这不止是"一个表单"这么简单表单是前端开发里最不显眼、但最见功力的模块。你去看那些练手项目&#xff0c;十个里有八个都是"用户注册页"&#xff0c;把几个input一摆&#xff0c;加个按钮&#xff0c;就算完成。可实际上&#…

作者头像 李华
网站建设 2026/10/18 21:57:48

CANN开源科学算子:化工计算热点百倍提速实践

在化工研发里&#xff0c;最磨人的事情往往不是合成实验翻车&#xff0c;而是等计算。一个几十个原子的催化剂簇模型&#xff0c;用量化软件跑单点能&#xff0c;通宵排队是家常便饭&#xff1b;一条几百纳秒的分子动力学轨迹&#xff0c;更是动辄在机房蹲上一周。问题通常不是…

作者头像 李华
网站建设 2026/10/18 21:56:14

基于SSM的物流管理系统:Java课程设计完整源码与万字文档

简介&#xff1a;这份资源面向计算机相关专业的本科生与课程设计学习者&#xff0c;提供一套基于SSM框架的物流管理系统完整实现&#xff0c;可用于毕业设计、期末大作业或课程设计场景。系统采用B/S三层结构&#xff0c;基于JDK1.8与MySQL5.7开发&#xff0c;兼容Eclipse和IDE…

作者头像 李华