基于JavaBean的三角形测试系统的设计与实现(SpringMVC + 动态粒子背景)
基于JavaBean的三角形测试系统的设计与实现(SpringMVC + 动态粒子背景)
一、实验概述
本次实验为《Web编程技术》课程实验八,要求使用SpringMVC框架设计一个动态网站,基于JavaBean实现三角形的判定及面积求解。用户在客户端输入三个自然数作为边长,系统判断是否能构成三角形;若不能则输出原因提示,若能则判断三角形类型(锐角/直角/钝角)并计算面积。项目同时具备美观的玻璃拟态界面与动态粒子背景,并成功部署至阿里云ECS服务器。
二、目录
1. 实验目的与要求
实验目的:掌握JavaBean技术在JSP页面设计中的应用,理解MVC设计模式的思想,熟悉SpringMVC框架的部署。
实验内容:使用SpringMVC框架设计动态网站,基于JavaBean控件实现三角形的判定及面积求解。客户端任意输入3个自然数做为边长,判断它们是否构成一个三角形,若不能输出原因提示信息,否则判断输出三角形的类型(锐角、直角、钝角)并且能计算输出该三角形的面积(不能构成三角形时面积设置为0)。
2. 实验使用环境
- 操作系统:Microsoft Windows 10 / Windows Server 2019(云端)
- 编程环境:JDK 1.8、Tomcat 9.0、Eclipse IDE for Web Developers
- 远程服务器:阿里云ECS(公网IP:118.31.44.31)
3. 项目目录结构与整体概述
项目采用MVC分层设计,以下为完整目录结构(滚动查看):
task8/ ├─Java Resources/ │ └─src/ │ ├─com.task8.bean/ │ │ ├─TriangleBean.java # 存储三角形边长,提供getter/setter │ │ ├─TriangleJudge.java # 判断三角形类型和是否构成三角形 │ │ └─TriangleArea.java # 根据三角形边长计算面积(海伦公式) │ └─com.task8.controller/ │ └─TriangleController.java # SpringMVC控制器 ├─WebContent/ │ ├─css/ │ │ ├─layout.css # 页面基础布局 │ │ ├─background.css # 动态渐变背景 │ │ ├─glass.css # 毛玻璃效果卡片 │ │ └─form.css # 表单输入框与按钮样式 │ ├─js/ │ │ └─particle.js # 粒子动画逻辑 │ └─WEB-INF/ │ ├─jsp/ │ │ ├─input.jsp # 用户输入页面 │ │ └─result.jsp # 结果显示页面 │ ├─springmvc-servlet.xml # SpringMVC配置 │ └─web.xml # Web部署描述文件
项目概述:系统基于SpringMVC框架,采用MVC分层设计。用户在浏览器输入三边长度后,控制器调用JavaBean业务逻辑判断三角形合法性、计算面积,并返回结果页面。页面采用动态粒子背景与玻璃拟态卡片,视觉效果突出。
4. 核心代码解析
4.1 JavaBean层(bean包)
TriangleBean.java – 保存三角形数据
package com.task8.bean;
public class TriangleBean {
private double a; // 三角形边长a
private double b; // 三角形边长b
private double c; // 三角形边长c
private boolean triangle; // 是否构成三角形
private String type; // 三角形类型
private double area; // 三角形面积
private String message; // 错误提示信息
public TriangleBean() {}
public TriangleBean(double a, double b, double c) {
this.a = a;
this.b = b;
this.c = c;
}
public double getA() { return a; }
public void setA(double a) { this.a = a; }
public double getB() { return b; }
public void setB(double b) { this.b = b; }
public double getC() { return c; }
public void setC(double c) { this.c = c; }
public boolean isTriangle() { return triangle; }
public void setTriangle(boolean triangle) { this.triangle = triangle; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public double getArea() { return area; }
public void setArea(double area) { this.area = area; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
TriangleJudge.java – 三角形判定与类型判断
package com.task8.bean;
import java.util.Arrays;
public class TriangleJudge {
public static boolean isTriangle(TriangleBean t) {
double a = t.getA(), b = t.getB(), c = t.getC();
if (a <= 0 || b <= 0 || c <= 0) return false;
return a + b > c && a + c > b && b + c > a;
}
public static String getType(TriangleBean t) {
double[] side = { t.getA(), t.getB(), t.getC() };
Arrays.sort(side);
double a = side[0], b = side[1], c = side[2];
double left = a*a + b*b;
double right = c*c;
if (Math.abs(left - right) < 0.0001) return "直角三角形";
if (left > right) return "锐角三角形";
return "钝角三角形";
}
}
TriangleArea.java – 面积计算(海伦公式)
package com.task8.bean;
public class TriangleArea {
public static double calculateArea(TriangleBean bean) {
if (!TriangleJudge.isTriangle(bean)) return 0;
double a = bean.getA(), b = bean.getB(), c = bean.getC();
double p = (a + b + c) / 2.0;
return Math.sqrt(p * (p - a) * (p - b) * (p - c));
}
}
4.2 Controller层(controller包)
TriangleController.java – SpringMVC控制器
package com.task8.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import com.task8.bean.*;
@Controller
public class TriangleController {
@GetMapping("/")
public String index() {
return "input";
}
@PostMapping("/calculate")
public String calculate(@RequestParam("a") double a,
@RequestParam("b") double b,
@RequestParam("c") double c,
Model model) {
TriangleBean triangle = new TriangleBean(a, b, c);
if (a <= 0 || b <= 0 || c <= 0) {
model.addAttribute("valid", false);
model.addAttribute("message", "边长必须为正数");
model.addAttribute("area", 0);
return "result";
}
boolean valid = TriangleJudge.isTriangle(triangle);
model.addAttribute("valid", valid);
if (!valid) {
model.addAttribute("message", "不能构成三角形");
model.addAttribute("area", 0);
} else {
model.addAttribute("message", "可以构成三角形");
model.addAttribute("type", TriangleJudge.getType(triangle));
model.addAttribute("area", TriangleArea.calculateArea(triangle));
}
return "result";
}
}
4.3 JSP视图层(input.jsp / result.jsp)
input.jsp – 用户输入页面
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>三角形判定系统</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/background.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/layout.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/glass.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/form.css">
</head>
<body>
<canvas id="particleCanvas"></canvas>
<div class="glass-container">
<h1>三角形判定系统</h1>
<form action="${pageContext.request.contextPath}/calculate" method="post">
<input type="number" name="a" placeholder="边长A" required>
<input type="number" name="b" placeholder="边长B" required>
<input type="number" name="c" placeholder="边长C" required>
<button type="submit">开始计算</button>
</form>
</div>
<script src="${pageContext.request.contextPath}/js/particle.js"></script>
</body>
</html>
result.jsp – 结果显示页面
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>计算结果</title>
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/background.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/layout.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/glass.css">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/form.css">
</head>
<body>
<canvas id="particleCanvas"></canvas>
<div class="glass-container">
<h1>计算结果</h1>
<%
String message = (String) request.getAttribute("message");
Boolean valid = (Boolean) request.getAttribute("valid");
Object type = request.getAttribute("type");
Object area = request.getAttribute("area");
%>
<p><%= message %></p>
<% if (valid != null && valid) { %>
<p>三角形类型:<%= type %></p>
<p>面积:<%= area %></p>
<% } else { %>
<p>面积:0</p>
<% } %>
<a href="<%= request.getContextPath() %>/">返回首页</a>
</div>
<script src="${pageContext.request.contextPath}/js/particle.js"></script>
</body>
</html>
4.4 动态粒子背景(particle.js)
window.onload = function() {
const canvas = document.getElementById("particleCanvas");
if (!canvas) return;
const ctx = canvas.getContext("2d");
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resize();
window.addEventListener("resize", resize);
const mouse = { x: null, y: null, radius: 150 };
window.addEventListener("mousemove", function(e) {
mouse.x = e.x;
mouse.y = e.y;
});
class Particle {
constructor() {
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
this.size = Math.random() * 4 + 1;
this.speedX = (Math.random() - 0.5) * 1.5;
this.speedY = (Math.random() - 0.5) * 1.5;
}
update() {
this.x += this.speedX;
this.y += this.speedY;
if (this.x < 0 || this.x > canvas.width) this.speedX *= -1;
if (this.y < 0 || this.y > canvas.height) this.speedY *= -1;
let dx = mouse.x - this.x;
let dy = mouse.y - this.y;
let distance = Math.sqrt(dx * dx + dy * dy);
if (distance < mouse.radius) {
this.x -= dx / 25;
this.y -= dy / 25;
}
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = "rgba(255,255,255,0.8)";
ctx.fill();
}
}
const particles = [];
for (let i = 0; i < 150; i++) particles.push(new Particle());
function connect() {
for (let a = 0; a < particles.length; a++) {
for (let b = a; b < particles.length; b++) {
let dx = particles[a].x - particles[b].x;
let dy = particles[a].y - particles[b].y;
let distance = dx * dx + dy * dy;
if (distance < 12000) {
ctx.strokeStyle = "rgba(255,255,255,0.08)";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(particles[a].x, particles[a].y);
ctx.lineTo(particles[b].x, particles[b].y);
ctx.stroke();
}
}
}
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
particles.forEach(p => {
p.update();
p.draw();
});
connect();
requestAnimationFrame(animate);
}
animate();
};
4.5 样式文件(glass.css / form.css等)
layout.css – 全局布局
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
font-family: "Microsoft YaHei";
}
background.css – 动态渐变背景 + Canvas定位
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
body {
background: linear-gradient(-45deg, #0f2027, #203a43, #2c5364, #1a2a6c, #0f3460);
background-size: 500% 500%;
animation: gradientMove 15s ease infinite;
}
@keyframes gradientMove {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
#particleCanvas {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: -1;
}
glass.css – 毛玻璃卡片效果
.glass-container {
width: 520px;
padding: 35px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
background: rgba(255,255,255,0.12);
border: 1px solid rgba(255,255,255,.2);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
border-radius: 20px;
color: white;
text-align: center;
box-shadow: 0 0 30px rgba(0,255,255,.25), 0 0 60px rgba(0,100,255,.2);
animation: floatCard 4s ease-in-out infinite;
}
@keyframes floatCard {
0% { transform: translate(-50%, -50%); }
50% { transform: translate(-50%, -53%); }
100% { transform: translate(-50%, -50%); }
}
.glass-container h1 {
margin-bottom: 25px;
font-size: 34px;
letter-spacing: 2px;
}
.glass-container p {
font-size: 18px;
line-height: 35px;
}
form.css – 表单输入框与按钮样式
input {
width: 90%;
padding: 12px;
margin: 12px 0;
border: none;
border-radius: 8px;
outline: none;
font-size: 16px;
transition: .3s;
}
input:focus {
box-shadow: 0 0 15px cyan;
}
button {
margin-top: 15px;
padding: 12px 35px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
color: white;
background: linear-gradient(45deg, #00c6ff, #0072ff);
transition: .3s;
}
button:hover {
transform: scale(1.08);
box-shadow: 0 0 20px cyan;
}
4.6 SpringMVC配置文件
springmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.task8"/>
<mvc:annotation-driven/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:default-servlet-handler/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>task8</display-name>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
5. 本地部署与调试过程
- Eclipse中创建Dynamic Web Project,项目名
task8,配置Tomcat 9.0运行时。 - 将Java类放入
src/com.task8.bean和src/com.task8.controller。 - 将JSP、CSS、JS文件放入
WebContent对应目录。 - 检查
springmvc-servlet.xml和web.xml编码均为UTF-8。 - 运行Tomcat,访问
http://localhost:8080/task8/。
调试问题与解决:
- 初次访问出现404,原因是
springmvc-servlet.xml中组件扫描路径写错,修改base-package="com.task8"后解决。 - 静态资源(CSS/JS)无法加载,改为使用
${pageContext.request.contextPath}动态获取项目路径解决。 - 中文乱码:统一所有JSP页面编码为UTF-8,并在
web.xml中配置字符编码过滤器。

图1:本地首页输入窗格效果

图2:本地结果页展示三角形类型与面积
6. 阿里云ECS服务器部署
- 上传项目:使用FTP将本地
task8整个文件夹上传至ECS服务器的/tomcat/webapps/目录。 - 启动Tomcat:在服务器中执行
startup.sh(Linux)或双击startup.bat(Windows)。 - 安全组配置:在阿里云控制台开放8080端口。
- 公网访问:浏览器访问
http://118.31.44.31:8080/task8/。
部署中遇到的问题:
- 最初访问出现HTTP 500,原因是缺少SpringMVC依赖JAR包,将
spring-webmvc、spring-context等JAR复制到WEB-INF/lib后解决。 - 公网访问502错误,排查发现Tomcat未启动,重新启动后正常。
- 确认ECS公网IP为
118.31.44.31后,所有功能测试通过。

图3:云端首页输入窗格

图4:云端结果页展示
7. 实验小结(小组分工与感想)
本次实验是课程中首次完整采用SpringMVC框架进行Web应用开发,小组四人继续采用模块化协同开发模式,完成从需求分析、MVC架构设计、JavaBean业务逻辑、SpringMVC配置、JSP页面设计、本地测试到阿里云ECS部署的全流程。
7.1 方致沅(系统架构设计、SpringMVC框架搭建与核心业务逻辑)
我负责整体MVC架构设计、SpringMVC框架配置(DispatcherServlet、组件扫描、视图解析器)以及TriangleController的开发。调试过程中解决了Controller扫描路径错误导致的404问题,并对输入边长进行合法性验证(正数、三角形不等式),确保系统健壮性。
7.2 李润哲(页面视觉设计、动态背景效果与前端资源管理)
我负责前端页面设计,包括input.jsp、result.jsp、四个CSS文件以及particle.js。采用深色渐变背景+毛玻璃卡片+Canvas粒子动画,使界面具有科技感。解决了静态资源路径错误(通过${pageContext.request.contextPath}统一引用),并优化了响应式布局。
7.3 王鑫杰(JavaBean开发、三角形判定算法与结果展示逻辑)
我完成了TriangleBean、TriangleJudge和TriangleArea三个核心JavaBean。实现了三角形合法性判断、类型判定(通过排序后比较平方和)以及海伦公式面积计算。调试中增加了非法边长保护(返回0面积),并解决了中文乱码问题(统一UTF-8编码)。
7.4 康奕彬(Tomcat部署、阿里云ECS发布与故障排查)
我负责本地Tomcat测试、云端环境搭建及公网发布。解决了一系列问题:补充缺失的Spring依赖JAR包、开放ECS安全组8080端口、排查Tomcat启动失败原因。最终成功通过http://118.31.44.31:8080/task8/公网访问。
7.5 团队协作总结
本次实验使我们深入理解了MVC设计模式、SpringMVC工作原理以及JavaBean在Web开发中的应用。通过解决404错误、中文乱码、静态资源失效、云端部署等工程问题,掌握了从开发到上线的完整流程。小组分工明确,沟通高效,为后续企业级Java Web开发打下了坚实基础。
8. 附录
- 《Web编程技术》,余元辉主编,清华大学出版社,2014.5
- 《JSP设计》(第三版),Hans Bergsten主编,林琪、朱涛江翻译,O'Reilly Media,Inc 2014年

浙公网安备 33010602011771号