实验一
实验1:通过Spring构造三层应用程序结构(控制器层+服务层+数据访问层)
1、控制器层:定义为一个Bean,类名用XxxController表示(其中Xxx为自定义部分,下同);
2、服务层:定义为一个Bean,类名用XxxService表示;
3、数据访问层(DAO层):定义为一个Bean,类名用XxxDao表示。
三个Bean的依赖关系为:XxxController类内定义了一个成员,类型为XxxService;XxxService类内定义了一个成员,类型为XxxDao类。即:三层之间存在依赖关系,我们要用Spring的依赖注入实现这些依赖关系。
用这三个Bean类完成中原铺子的充值和消费记录:
1、XxxController类负责充值和消费的输入和输出。
(1)充值:输入手机号和充值金额。充值成功,输出“充值成功!手机号:13xxxxxxxxx,余额:yyyy元”。
(2)消费:输入手机号和消费金额。如果消费成功(系统内手机号存在,且余额>=消费金额),输出“消费成功!手机号:13xxxxxxxx,本次消费:yy元,余额:zzzz元”。如果消费失败(手机号不存在,或者余额<消费金额),输出“消费失败!原因:手机号不存在/余额不足”。
2、XxxService类负责充值和消费的业务逻辑:
(1)充值:充200送10,充300送20,充500送50。
(2)消费:消费多少算多少,没有优惠和打折。
3、XxxDao类负责充值和消费的保存(数据持久化):
(1)充值:最新余额=原有余额+充值金额+赠送金额(充值>=200才有赠送金额)
(2)消费:最新余额=原有余额-消费金额
要保存的数据是:手机号、最新余额(也就是说,不保存充值/消费明细)。手机号就是用户账号,每个手机号仅保存一条数据,就是手机号+最新余额。由于同学们目前缺乏数据库基础,因此账号数据选择用Java的某种数据结构来保存,请自行选定。
整个流程:XxxController接到充值或者消费的输入后,提交给XxxService,后者完成业务逻辑(确定变动数额),再提交给XxxDao类完成保存。成功或者失败的结果由XxxDao返回给XxxService,再返回给XxxController输出。
请注意功能上的严格划分:XxxController仅作为用户接口,完成输入和输出;XxxService仅完成业务逻辑;XxxDao仅完成数据的读取和保存。
要求使用Spring框架完成上述需求,使用两种方式:
1、基于XML的装配(10分)
2、基于注解的装配(10分)
ShopDao.java
package nceu.computer.java.dao;
import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.Map;
//@Repository注解将这个类标识为Spring管理的Bean,角色是数据访问``
@Repository
public class ShopDao {
//使用HashMap模拟数据库。userBalanceMap用来存储用户余额,变量名(用户余额映射表)
private Map<String, Double> userBalanceMap = new HashMap<>();//String:键类型,存储手机号。Double:值类型,存储余额。
//充值方法
public double recharge(String phone, double rechargeAmount, double giftAmount) {
double oldBalance = userBalanceMap.getOrDefault(phone, 0.0);//获取当前余额(如果用户不存在则默认为0.0)
double newBalance = oldBalance + rechargeAmount + giftAmount;//新余额=旧余额+充值余额+赠送金额
userBalanceMap.put(phone, newBalance);//更新到Map中
return newBalance;
}
//消费方法
public double[] consume(String phone, double consumeAmount) {
if (!userBalanceMap.containsKey(phone)) {
return new double[]{-1, 0.0};//用户不存在
}
double oldBalance = userBalanceMap.get(phone);
if (oldBalance < consumeAmount) {//余额小于消费
return new double[]{-2, oldBalance};//余额不足,返回旧余额
}
double newBalance = oldBalance - consumeAmount;
userBalanceMap.put(phone, newBalance);
return new double[]{1, newBalance};//消费成功,返回新余额
}//使用double[]数组返回多个信息,索引0:状态码(-1:用户不存在,-2:余额不足,1:成功)索引1:余额信息
//查询余额方法
public Double queryBalance(String phone) {
return userBalanceMap.get(phone);//直接返回用户余额,如果用户不存在返回null
}
}
ShopService.java
package nceu.computer.java.service;
import nceu.computer.java.dao.ShopDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service//Spring注解,声明这是一个业务逻辑层的服务组件,Spring会管理其生命周期。注解标记为Spring管理的业务组件
public class ShopService {
@Autowired//Spring自动注入ShopDao的实例,依赖注入通过@Autowired获得数据访问层的能力
private ShopDao shopDao; // 变量名是shopDao,声明属性
// 需提供setShopDao方法
//可选的setter方法,用于以来注入
public void setShopDao(ShopDao shopDao) {//属性对应的set方法
this.shopDao = shopDao;
}
//充值方法
public String recharge(String phone, double rechargeAmount) {
//1.计算赠送金额(业务规则)
double giftAmount = 0.0;
if (rechargeAmount >= 200 && rechargeAmount < 300) giftAmount = 10.0;
else if (rechargeAmount >= 300 && rechargeAmount < 500) giftAmount = 20.0;
else if (rechargeAmount >= 500) giftAmount = 50.0;
//2.调用Dao层进行实际充值
double newBalance = shopDao.recharge(phone, rechargeAmount, giftAmount);
//3.返回格式化结果
return String.format("充值成功!手机号:%s,余额:%.2f元", phone, newBalance);
}
//Service中关注业务规则,具体数据操作交给Dao层。
//消费方法
public String consume(String phone, double consumeAmount) {
//调用Dao层尝试消费
double[] result = shopDao.consume(phone, consumeAmount);
int status = (int) result[0];//(int)将double类型变成int类型,result[0]就是:"打开包裹,拿出第一个物品"
//根据状态码返回不同结果
if (status == 1) {//消费成功
return String.format("消费成功!手机号:%s,本次消费:%.2f元,余额:%.2f元",
phone, consumeAmount, result[1]);
} else if (status == -1) {//用户不存在
return "消费失败!原因:手机号不存在";
} else {//余额不足(status==-2)
return "消费失败!原因:余额不足";
}
}
public String queryBalance(String phone) {
//输入手机号查询
Double balance = shopDao.queryBalance(phone);//数据库管理员查手机号有多少钱
//查询手机号是否存在
if (balance == null) {//检查是否没找到这个用户
return "查询失败!原因:手机号不存在";
}
return String.format("查询结果:手机号%s,余额:%.2f元", phone, balance);
}
}
ShopController.java
package nceu.computer.java.controller;
import nceu.computer.java.service.ShopService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller//SpringMVC注解,标识这是一个控制类,接待员角色,负责处理Web请求。
public class ShopController {
@Autowired//实现依赖注入,Spring自动将ShopService的实现类注入到shopService的实现类注入到shopService变量中。业务处理员(先按类型匹配再按名字匹配)
private ShopService shopService; // 变量名是shopService
// 需提供setShopService方法
//提供setter方法用于手动设置shopService(可选,@Autowired通常已经足够)
public void setShopService(ShopService shopService) {
this.shopService = shopService;
}
//充值方法recharge
public String recharge(String phone, double amount) {
//第一步:检查顾客信息是否合格
if (!isValidPhone(phone) || amount <= 0) {//手机号格式是否正确,金额是否是正数
return "参数格式错误!手机号需为11位有效格式,金额需大于0";
}
//合格就转交给业务专员处理
return shopService.recharge(phone, amount);
}
//消费方式同样的检查流程
public String consume(String phone, double amount) {
if (!isValidPhone(phone) || amount <= 0) {
return "参数格式错误!手机号需为11位有效格式,金额需大于0";
}
return shopService.consume(phone, amount);
}
//查询余额方法,只检查手机号格式(查询余额不需要金额参数)
public String queryBalance(String phone) {
if (!isValidPhone(phone)) {
return "参数格式错误!手机号需为11位有效格式";
}
return shopService.queryBalance(phone);
}
//工具方式
private boolean isValidPhone(String phone) {
return phone.matches("^1[3-9]\d{9}$");//必须是11位数字,13-19.
}
}
Main.java
package nceu.computer.java;
import nceu.computer.java.controller.ShopController;
import nceu.computer.java.config.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {//程序开始执行。
Scanner scanner = new Scanner(System.in);
System.out.println("=== Spring手机充值消费系统 ===");
System.out.println("请选择配置方式:");
System.out.println("1. XML配置方式");
System.out.println("2. 注解配置方式");
System.out.print("请选择: ");
int configChoice = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
ApplicationContext context;
String configType;
if (configChoice == 1) {
context = new ClassPathXmlApplicationContext("applicationContext.xml");
//XML配置读取applicationContext.xml文件。初始化Spring容器
configType = "XML";
System.out.println("使用XML配置方式启动...");
}else {
context = new AnnotationConfigApplicationContext(SpringConfig.class);
//注解配置(读取SpringConfig类)初始化Spring容器
configType = "注解";
System.out.println("使用注解配置方式启动...");
}
//从Spring容器中"取出"已经组装好的ShopController
ShopController controller = context.getBean("shopController", ShopController.class);
//持续显示菜单,等待用户输入,根据用户输入选择调用不同功能
while (true) {
System.out.println("\n=== " + configType + "配置模式 ===");
System.out.println("1. 充值");
System.out.println("2. 消费");
System.out.println("3. 查询余额");
System.out.println("4. 退出");
System.out.print("请选择操作: ");
int choice = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
switch (choice) {
case 1:
rechargeOperation(controller, scanner);
break;
case 2:
consumeOperation(controller, scanner);
break;
case 3:
queryBalanceOperation(controller, scanner);
break;
case 4:
System.out.println("谢谢使用,再见!");
if (configChoice == 1) {
((ClassPathXmlApplicationContext) context).close();//关闭Spring资源和scanner
} else {
((AnnotationConfigApplicationContext) context).close();
}
scanner.close();
return;
default:
System.out.println("无效选择,请重新输入!");
}
}
}
// 充值操作
private static void rechargeOperation(ShopController controller, Scanner scanner) {
System.out.print("请输入手机号: ");
String phone = scanner.nextLine();
System.out.print("请输入充值金额: ");
double amount = scanner.nextDouble();
scanner.nextLine(); // 消耗换行符
String result = controller.recharge(phone, amount);
System.out.println("系统结果: " + result);
}
// 消费操作
private static void consumeOperation(ShopController controller, Scanner scanner) {
//类似充值操作,调用controller.consume()
System.out.print("请输入手机号: ");
String phone = scanner.nextLine();
System.out.print("请输入消费金额: ");
double amount = scanner.nextDouble();
scanner.nextLine(); // 消耗换行符
String result = controller.consume(phone, amount);
System.out.println("系统结果: " + result);
}
// 查询余额操作
private static void queryBalanceOperation(ShopController controller, Scanner scanner) {
// 调用controller.queryBalance()
System.out.print("请输入要查询的手机号: ");
String phone = scanner.nextLine();
String result = controller.queryBalance(phone);
System.out.println("查询结果: " + result);
}
}
SpringConfig.java
package nceu.computer.java.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// @Configuration 表明SpringConfig这是一个 Spring 的配置类
@Configuration
// @ComponentScan 告诉 Spring 去 "com.example.shop" 包及其子包下扫描带有 @Component, @Service, @Repository, @Controller 注解的类
@ComponentScan("nceu.computer.java")
public class SpringConfig {
// 这个类本身不需要任何代码,它的作用就是启用组件扫描
}
applicationContext.xml
<bean id="shopDao" class="nceu.computer.java.dao.ShopDao"/>
<bean id="shopService" class="nceu.computer.java.service.ShopService">
<property
name="shopDao" ref="shopDao"/>
</bean>
<bean id="shopController" class="nceu.computer.java.controller.ShopController">
<property name="shopService" ref="shopService"/>
</bean>

浙公网安备 33010602011771号