前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C语言项目 图书管理系统 | 链表

C语言项目 图书管理系统 | 链表

作者头像
CtrlX
发布于 2023-03-21 03:53:51
发布于 2023-03-21 03:53:51
77400
代码可运行
举报
文章被收录于专栏:C++核心编程C++核心编程
运行总次数:0
代码可运行

Gaga-Geek图书管理系统(BMS)

项目介绍

本项目会搭建一个控制台操作的图书管理系统。

学习目标:链表操作、文件读写、分文件编写、

目录结构

V1.0.0 – 程序安装包

documents – 项目开发相关文档

  • 需求文档
  • 产品原型图
  • 产品流程图

projects/BMS – 项目文件夹

环境搭建

开发工具

工具

说明

版本

备注

MindMaster

思维导图设计工具

12.0.5

https://www.edrawsoft.cn/mindmaster/

ProcessOn

流程图绘

NULL

https://www.processon.com/

MODAO

原型图绘制工具

v1.2.5

https://modao.cc/brand

开发环境

工具

版本

备注

Windows

11

操作系统

Visual Studio

2022

https://visualstudio.microsoft.com/zh-hans/vs/

需求文档

产品原型图

产品流程图

软件架构:三层架构

核心类文件:

bms.cpp:main.cpp

base.cpp/h:结构体、初始化相关函数

BLL.cpp/h业务逻辑层

DAL.cpp/h数据访问层

UI.cpp/h:表现层

优化类文件:

startinterface.cpp/h:开始界面动画

map.cpp/h:操作界面外壳

tools.cpp/h:控制台优化类函数

point.cpp/h:操作界面外壳元素

详细介绍:

核心类文件:

bms.cpp:main.cpp

包含内容:

  • 初始化
    • 开始动画
    • 初始化头节点
    • 读取文件数据
  • 主循环:调用主页面
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include ..

void init() {
	//1.开始动画
	SetWindowSize(41, 32);//tools设置窗口大小
	SetColor(3);//tools设置开始动画颜色
	StartInterface* start = new StartInterface();//动态分配一个StartInterface类start
	start->Action();//开始动画
	delete start;//释放内存空间
	/*设置关标位置,并输出提示语,等待任意键输入结束*/
	SetCursorPosition(13, 26);
	std::cout << "Press any key to start... ";
	SetCursorPosition(13, 27);
	system("pause");

	//2.初始化头节点
	bHEAD = createbHead();
	mHEAD = createmHead();

	//3.读取文件操作
	readInfoFromFile_b("book.txt", bHEAD);
	readInfoFromFile_m("man.txt", mHEAD);
}

int main() {
	//初始化
	init();

	//调用主界面函数:进行登录-注册页面
	while (true) {
		switch (loginface()) {
		case 0:
			//调用管理界面函数
			SetCursorPosition(5,5);
			guanL();
			break;
		case 1:
			//调用用户界面函数
			SetCursorPosition(5,5);
			yongH();
			break;
		}
	}
	return 0;
}

base.h/cpp:结构体、初始化链表相关函数

具体内容:

  • 结构体:
    • _man/*man
    • _book/*book
  • 全局变量:
    • 单个man类型节点:记录当前登录的人
    • 链表表头:书籍链表表头/成员链表表头
  • 函数:
    • 创建链表表头
    • 创建链表节点
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include ..

typedef struct _man
{
	char Account[MAX];
	long key;//最多九位
	struct _man* next;
	int pw = 0;//0是管理员,1是用户
}*man;

extern man reader;//当前登录的人,不论是管理员还是用户
typedef struct _book
{
	int storage_num ;//编号
	char name[MAX];
	char writer[MAX];
	int num;//该书储存量
	struct _book* next;
}*book;

//创建全局链表表头
extern book bHEAD;
extern man mHEAD;

//创建表头:
book createbHead();
man createmHead();

//创建节点
book createNode(char* name, char* writer, int storage_num, int num);
man createNode(char* Account, long key, int pw);

BLL.cpp:业务逻辑层

主要内容:

  • 链表的核心操作
    • 插入节点(书籍、成员):头插法(首选)/尾插法
  • 删除节点(书籍):通过查找名称进行删除
  • 查找节点(书籍、成员):通过名称进行查找
  • 遍历节点(书籍):用于打印书籍
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//一、核心操作

//添加节点到链表头部(头节点后面的第一个节点)
void Add(book headNode, char* name, char* writer, int storage_num, int num) {
	book newNode = createNode(name, writer, storage_num, num);
	newNode->next = headNode->next;
	headNode->next = newNode;
}
man Addm(man headNode, char* Account, long key, int pw = 1) {//默认注册只能注册用户,pw=1
	man newNode = createNode(Account, key, pw);
	newNode->next = headNode->next;
	headNode->next = newNode;
	return newNode;
}

//删除书籍节点
void Delete(char* bookName)
{
	book posLeftNode = bHEAD;
	book posNode = bHEAD->next;//创建临时变量posNode记录要删除的节点
	while (posNode != NULL && strcmp(posNode->name, bookName)) //思路二:调用Find函数
    {
		posLeftNode = posNode;
		posNode = posLeftNode->next;
	}
	if (posNode == NULL) {
		SetCursorPosition(5, 15);
		printf("Error!Try again.");
	}
	else
	{
		SetCursorPosition(5, 15);
		printf("删除成功\n");
		posLeftNode->next = posNode->next;
		free(posNode);//释放临时变量指向的堆区数据
		posNode = NULL;
	}
}

//查找书籍:返回书籍结构体指针
book Find(char* fileName, book headNode = bHEAD) {
	book posNode = headNode->next;
	while (posNode != NULL && strcmp(posNode->name, fileName)) {
		posNode = posNode->next;
	}
	return posNode;
}
//查找用户:返回用户节点
man Find_man(char* Account) {
	man node;
	for (node = mHEAD->next; node != NULL; node = node->next) {
		if (!strcmp(node->Account, Account)) {
			return node;
		}
	}
	return NULL;
}
//递归查找并打印书籍
void findBook(book temp) {
	if (temp->next != NULL) {
		findBook(temp->next);
	}
	printf("\v\t%d  书名:%s\n\t\t数量:%d\t\n", temp->storage_num, temp->name, temp->num);
}

DAL.cpp:数据访问层

主要内容:将本地文件中的数据读取到链表中,将链表中的数据存储到本地。

  • 文件读操作(书籍/用户)
  • 文件写操作(书籍/用户)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//二、数据处理部分
#include ..
//文件写操作
void saveInforToFile_b(const char* fileName, book headNode) {
	FILE* fp = fopen(fileName, "w");
	book pMove = headNode->next;
	while (pMove != NULL) {
		fprintf(fp, "%d %s %s %d\n", pMove->storage_num, pMove->name, pMove->writer, pMove->num);
		pMove = pMove->next;
	}
	fclose(fp);
}
void saveInforToFile_m(const char* fileName, man headNode) {
	FILE* fp = fopen(fileName, "w");
	man pMove = headNode->next;
	while (pMove != NULL) {
		fprintf(fp, "%s %d %d\n", pMove->Account, pMove->key, pMove->pw);
		pMove = pMove->next;
	}
	fclose(fp);
}
//文件读操作 
void readInfoFromFile_b(const char* fileName, book headNode) {
	FILE* fp = fopen(fileName, "r");
	if (fp == NULL) {
		//第一次打开程序文件肯定不存在,所以要创建一个文件
		fp = fopen(fileName, "w+");
	}
	int storage_num = 0, num = 0; char name[MAX] = { 0 }, writer[MAX] = { 0 };
	while (fscanf(fp, "%d %s %s %d\n", &storage_num, name, writer, &num) != EOF) {
		Add(bHEAD, name, writer, storage_num, num);
	}
	fclose(fp);
}
void readInfoFromFile_m(const char* fileName, man headNode) {
	FILE* fp = fopen(fileName, "r");
	if (fp == NULL) {
		fp = fopen(fileName, "w+");
	}
	char Account[MAX] = { 0 }; int key = 0, pw = 0;
	while (fscanf(fp, "%s %d %d\n", Account, &key, &pw) != EOF) {
		Addm(mHEAD, Account, key, pw);
	}
	fclose(fp);
}

UI.cpp/h:表现层

主要内容:

  • 主要页面
    • 主页面
    • 注册页面
    • 登录页面
  • 分支页面
    • 用户页面
    • 管理员页面
  • 功能页面
    • 浏览信息页面
    • 查找书籍页面
    • 添加书籍界面
    • 删除书籍页面
  • 优化页面
    • 动态选择页面
    • 绘制界面边框
    • 动态结束界面
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//三、用户交互界面
#include ..
//主界面
int loginface();
//登陆成功后的初始化页面
void startface();
//开始菜单选择界面
void Select();
//登录页面
int login(long key);
//注册页面
man _register();
//1.浏览信息页面
void skimface();
void manface();
//2.查找书籍页面
void bookface();
//3.添加书籍界面
void addface();
//4.删除书籍
void DeleteBook();
//用户操作界面:1
int yongH();//1.浏览书目2.查询书籍3.个人信息4.退出
//管理员操作界面:0
int guanL();//1.浏览书目2.查询书籍3.添加书籍4.退出
//(非核心)优化界面:绘制界面边框
void DrawFace();
//结束界面
int Pause();

PS:选择效果的实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int Select()
{
	/*初始化界面选项*/
	SetColor(3);
	SetCursorPosition(13, 26);
	std::cout << "                          ";
	SetCursorPosition(13, 27);
	std::cout << "                          ";
	SetCursorPosition(6, 21);
	std::cout << "请选择功能:";
	SetCursorPosition(6, 22);
	std::cout << "(上下键选择,回车确认)";
	SetCursorPosition(27, 22);
	SetBackColor();//第一个选项设置背景色以表示当前选中
	std::cout << "用户登录";
	SetCursorPosition(27, 24);
	SetColor(3);
	std::cout << "用户注册";
	SetCursorPosition(27, 26);
	std::cout << "开发信息";
	SetCursorPosition(27, 28);
	std::cout << "退出系统";
	SetCursorPosition(0, 31);

	/*上下方向键选择模块*/
	int ch;//记录键入值
	int key = 1;//记录选中项,初始选择第一个
	bool flag = false;//记录是否键入Enter键标记,初始置为否

	while ((ch = _getch()))//检测键盘按键输入
	{
		switch (ch)//检测输入键
		{
		case 72://UP:键盘上的上方向键按钮
			if (key > 1)//当此时选中项为第一项时,UP上方向键无效
			{
				switch (key)
				{
				case 2:
					SetCursorPosition(27, 22);//给待选中项设置背景色
					SetBackColor();
					std::cout << "用户登录";

					SetCursorPosition(27, 24);//将已选中项取消我背景色
					SetColor(3);
					std::cout << "用户注册";

					--key;
					break;
				case 3:
					SetCursorPosition(27, 24);
					SetBackColor();
					std::cout << "用户注册";

					SetCursorPosition(27, 26);
					SetColor(3);
					std::cout << "开发信息";

					--key;
					break;
				case 4:
					SetCursorPosition(27, 26);
					SetBackColor();
					std::cout << "开发信息";

					SetCursorPosition(27, 28);
					SetColor(3);
					std::cout << "退出系统";

					--key;
					break;
				}
			}
			break;

		case 80://DOWN:键盘上的下方向键按钮
			if (key < 4)
			{
				switch (key)
				{
				case 1:
					SetCursorPosition(27, 24);
					SetBackColor();
					std::cout << "用户注册";
					SetCursorPosition(27, 22);
					SetColor(3);
					std::cout << "用户登录";

					++key;
					break;
				case 2:
					SetCursorPosition(27, 26);
					SetBackColor();
					std::cout << "开发信息";
					SetCursorPosition(27, 24);
					SetColor(3);
					std::cout << "用户注册";

					++key;
					break;
				case 3:
					SetCursorPosition(27, 28);
					SetBackColor();
					std::cout << "退出系统";
					SetCursorPosition(27, 26);
					SetColor(3);
					std::cout << "开发信息";

					++key;
					break;
				}
			}
			break;

		case 13://Enter回车键
			flag = true;
			break;
		default://无效按键
			break;
		}
		if (flag) return key;//输入Enter回车键确认,退出检查输入循环

		SetCursorPosition(0, 31);//将光标置于左下角,避免光标闪烁影响体验
	}
}
优化类文件:

startinterface.cpp/h:开始界面动画

实现工具:C++ vector、deque容器

  • deque是有下标的容器,它可以在开头和结尾两边快速插入及删除元素,另外,在任意一端插入元素或者删除元素不会使指向其他元素的指针或者引用失效。
  • 与vector相反,deque的元素不是相连存储的;典型实现用单独分配的固定大小数组的序列,外加额外的登记,这意味者使用下标访问必须经过二次指针的解引用,与之相比vector的下标访问只进行一次。(左边是额外的连续的内存空间,右边是固定大小的数组,用于保存数据)

点坐标图:

通过坐标(0,14)观察队列输出特点:先进先出

9

8

7

6

5

4

3

2

1

0

14

15

16

17

18

-31

-30

-29

-23

-22

-21

-20

-19

-18

-17

-16

-15

-14

-13

-6

-5

-4

14

15

16

17

18

实现流程:

  • 开始动画
    • 第一阶段:蛇从左边出现到完全出现的过程
    • 第二阶段:蛇从左向右移动的过程
    • 第三阶段:蛇从接触右边到消失的过程
  • 开始文字

实现方法:

这一部分的实现首先是建立一个deque双端队列,用于存储点的对象,这些点就是组成蛇身的元素,然后再用一个for循环将容器中的点依次打印出来,每打印一个点停顿一会,这样就达到了移动的效果。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void StartInterface::PrintFirst()//第一阶段:蛇从左边出现到完全出现的过程
{
    for (auto& point : startsnake)//只读方式遍历容器
    {
        point.Print();
        Sleep(speed);//停顿
    }
}

全部打印完后就到了第二部分,这部分蛇的每次前进都是通过计算将要移动到的下一个点的坐标,然后将这个点打印出来,与此同时将蛇尾,亦即queue中的首端点去掉,并擦除屏幕上该点颜色。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void StartInterface::PrintSecond()//第二阶段:蛇从左向右移动的过程
{
    for (int i = 10; i != 40; ++i) //蛇头需要从10移动到40
    {
        /*计算蛇头的下一个位置,并将其压入startsnake中,绘制出来,将蛇尾去掉*/
        //计算从坐标j
        int j = (((i - 2) % 8) < 4) ? (15 + (i - 2) % 8) : (21 - (i - 2) % 8);
        startsnake.emplace_back(Point(i, j));
        //queue中的首端点去掉,并擦除屏幕上该点颜色
        startsnake.back().Print();
        startsnake.front().Clear();
        startsnake.pop_front();
        //停顿
        Sleep(speed);
    }
}

第三部分就直接依次从蛇尾擦除即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void StartInterface::PrintThird()//第三阶段:蛇从接触右边到消失的过程
{
    while (!startsnake.empty() || textsnake.back().GetX() < 33) //当蛇还没消失或文字没移动到指定位置
    {
        if (!startsnake.empty()) //如果蛇还没消失,继续移动
        {
            startsnake.front().Clear();
            startsnake.pop_front();
        }
        ClearText();//清除已有文字
        PrintText();//绘制更新位置后的文字
        Sleep(speed);
    }
}

同理,文字BMS的移动也基本类似,稍微改动即可,因为无需对首尾进行操作,而是要对所以点进行移动,因此容器选用vector。

注:为什么使用emplace_back() :

  1. emplace_back函数的作用是减少对象拷贝和构造次数,是C++11中的新特性,主要适用于对临时对象的赋值。
  2. 在使用push_back函数往容器中增加新元素时,必须要有一个该对象的实例才行,而emplace_back可以不用,它可以直接传入对象的构造函数参数直接进行构造,减少一次拷贝和赋值操作。
  3. emplace_back() 和 push_back() 的区别,就在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
  4. 显然完成同样的操作,push_back() 的底层实现过程比 emplace_back() 更繁琐,换句话说,emplace_back() 的执行效率比 push_back() 高。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class StartInterface
{
public:
    StartInterface() : speed(35) {
        startsnake.emplace_back(0, 14);//Éß
        startsnake.emplace_back(1, 14);
        startsnake.emplace_back(2, 15);
        startsnake.emplace_back(3, 16);
        startsnake.emplace_back(4, 17);
        startsnake.emplace_back(5, 18);
        startsnake.emplace_back(6, 17);
        startsnake.emplace_back(7, 16);
        startsnake.emplace_back(8, 15);
        startsnake.emplace_back(9, 14);
		……
        
    }//绘制动画BMS
    void PrintFirst();//第一阶段
    void PrintSecond();//第二阶段
    void PrintThird();//第三阶段
    void PrintText();//输出文字
    void ClearText();//清除文字
    void Action();//绘制
private:
    std::deque<Point> startsnake;//开始动画
    std::vector<Point> textsnake;//开始动画中的文字
    int speed;//动画的速度
};

map.h:操作界面外壳

实现原理:C++vector容器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Map
{
public:
    //默认构造函数,将圆形各点压入initmap
    Map(){
        initmap.emplace_back(1, 1);
        initmap.emplace_back(2, 1);
        initmap.emplace_back(3, 1);
						……
        initmap.emplace_back(28, 30);
        initmap.emplace_back(29, 30);
        initmap.emplace_back(30, 30);
    }
    void PrintInitmap();//绘制初始地图
private:
    std::vector<Point> initmap;//保存初始地图
};

tools.cpp:控制台优化类函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include "tools.h"
#include <windows.h>
void SetWindowSize(int cols, int lines)//设置窗口大小
{
    system("title 图书管理系统->嘎嘎极客");//设置窗口标题
    char cmd[30];
    sprintf_s(cmd, "mode con cols=%d lines=%d", cols * 2, lines);//一个图形■占两个字符,故宽度乘以2
    system(cmd);//system(mode con cols=88 lines=88)设置窗口宽度和高度
}
void SetCursorPosition(const int x, const int y)//设置光标位置
{
    COORD position;
    position.X = x * 2;
    position.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);
}
void SetColor(int colorID)//设置文本颜色
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colorID);
}
void SetBackColor()//设置文本背景色
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
        FOREGROUND_BLUE |
        BACKGROUND_BLUE |
        BACKGROUND_GREEN |
        BACKGROUND_RED);
}

point.h:操作界面外壳元素

实现原理:C++ vector

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Point
{
public:
    Point() {}
    Point(const int x, const int y) : x(x), y(y) {}
    void Print();
    void PrintCircular();
    void Clear();
    void ChangePosition(const int x, const int y);
    bool operator== (const Point& point) { return (point.x == this->x) && (point.y == this->y); }
    int GetX() { return this->x; }
    int GetY() { return this->y; }
private:
    int x, y;
};

版本

V1.0.0

全部整合后的第一版。

V1.0.1

进一步优化了界面。

V1.0.2

修复了注册界面Bug。

Bug描述:

  • 存放密码的类型是long而不是char[],导致了用户如果在输入字符与数字的混合密码时会出现问题,可能会导致密码为空或者导致密码只保存了数字,但是无法给用户提示,从而导致用户无法登录。
  • 用户注册时如果用户名超出了数组Account的大小的问题:溢出问题
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//base.cpp
typedef struct _man
{
	char Account[MAX];
	long key;//最多九位
	struct _man* next;
	int pw = 0;//0是管理员,1是用户
}*man;

Bug1解决方案:

  1. **将key的long类型改为char***:更改难度极大,该结构体已经被应用于多个函数中,且函数之间的关系比较复杂,也需要同时对函数中的操作所涉及的一系列对字符串操作的修改。
  2. 限制只能输入数字作为密码:操作难度小,只涉及一个函数的修改。

Bug2解决方案:

  1. 使用malloc动态分配内存来存储用户名:难度大,修改量大。
  2. 截断字符串并给用户提示:操作难度小,只涉及一个函数的修改。

问题解决:TODO

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//注册页面
man _register() {

	char Account[MAX] = { 0 }; int key = 0;
	SetCursorPosition(10, 10);
	printf("\n\t\t\t注册!");
	printf("\n\n\t\t\t\t\t ________________");
	printf("\n\t\t请输入用户名:(20个字符) \b|________________|\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
	SetColor(2);
    
	//TODO2:原版scanf("%s",Account);
    //如果到达文件末尾或者没有读取到任何字符,返回一个空指针。如果发生错误,返回一个空指针。
	while ((fgets(Account, MAX, stdin)) != NULL && Account[0] != '\n') 
    {
		while (getchar() != '\n') {
			continue;
		}
		SetCursorPosition(8, 16);
		std::cout << "账号:" << Account << std::endl;
		break;
	}
	
	getchar();
	SetColor(3);
	printf("\n\t\t\t\t\t ________________");
	printf("\n\t\t请输入你的密码:\t\b |________________|\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
	SetColor(2);
	
    //TODO1:原版:scanf("ld", &key);
	while (1) {
		scanf("ld", &key);//利用scanf将换行符留在缓存区的特点
		if(getchar() == '\n')break;
        else {
			while (getchar() != '\n') //关键步骤,清空缓存区
            {
				continue;
			}
			SetCursorPosition(8, 20);
			std::cout << "请输入数字!" << std::endl;
		}
	}
	
	return Addm(mHEAD, Account, key);
}

参考:

https://blog.csdn.net/xiaoxiaoguailou/article/details/121733862

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
动态代理的两种方式以及优缺点
动态代理应用非常的广泛,在各种开源的框架中都能看到他们的身影,比如spring中的aop使用动态代理增强,mybatis中使用动态代理生成mapper,动态代理主要有JDK和CGLIB两种方式,今天来学习下这两种方式的实现,以及它们的优缺点
全栈程序员站长
2022/07/04
5790
动态代理的两种方式以及优缺点
Java 动态代理初探
对于使用过 Spring 的朋友, 应该都使用过 AOP, 那么今天我们来对 AOP 的原理: 动态代理 来一探究竟.
一份执着✘
2019/12/30
3580
jdk静态代理,jdk动态代理,cglib动态代理
代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道。如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法。如果公司卖多少钱,代理商也卖多少钱,那么代理商就赚不了钱。所以代理商在调用公司的卖方法后,加上自己的利润然后再把产品卖给客户。而客户部直接跟公司打交道,或者客户根本不知道公司的存在,然而客户最终却买到了产品。
互扯程序
2019/07/01
5050
jdk静态代理,jdk动态代理,cglib动态代理
JDK动态代理和CGLIB动态代理
Java动态代理是一种在运行时创建代理对象的技术,它允许开发者在不修改目标类代码的情况下,通过代理类对目标类的实例方法进行增强或拦截。动态代理的核心价值在于能够在程序运行阶段动态地生成一个实现了预定义接口的新类,这个新类就是所谓的“代理类”。
程序猿川子
2025/02/27
1650
JDK动态代理和CGLIB动态代理
探索Java动态代理的奥秘:JDK vs CGLIB
动态代理是一种在 运行时动态生成代理类 的技术,无需手动编写代理类代码。它通过拦截目标方法的调用,实现对核心逻辑的 无侵入式增强(如日志、事务、权限控制等)。
用户7954602
2025/02/04
1500
探索Java动态代理的奥秘:JDK vs CGLIB
Java中的动态代理以及在框架中的应用
我们先假设现在有怎么一个需求,要求你在不改动原有代码的情况下在所有类的方法前后打印日志。我们很容易想到静态代理,具体做法如下:
烂猪皮
2021/06/09
1.5K0
Java中的动态代理以及在框架中的应用
Java 动态代理详解
动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC远程调用、Java注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。
小旋锋
2019/01/21
1.1K0
Java设计模式:代理模式的静态和动态之分(八)
码到三十五 : 个人主页 心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 !
公众号:码到三十五
2024/04/09
1410
Java设计模式:代理模式的静态和动态之分(八)
Spring AOP动态代理
本文将介绍如何使用 AOP 实现动态代理,并以 GitHub 风格的方式展示其实现过程。
人不走空
2024/02/25
1740
Spring AOP动态代理
第06天 静态代理和动态代理
代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象 (real object) 的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
程序员Leo
2023/08/16
1940
第06天 静态代理和动态代理
Java 静态代理、Java动态代理、CGLIB动态代理
Java 的代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理。为啥要这样呢, 是因为使用代理有 2 个优势:
java思维导图
2019/05/21
7.1K0
【面试题精讲】JDK动态代理
JDK 动态代理是 Java 中一种实现代理模式的机制。它允许在运行时创建代理类和对象,用于替代原始对象进行方法调用,并可以在方法调用前后添加额外的逻辑。
程序员朱永胜
2023/10/10
5200
016 Java中的动态代理
代理 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。 代理可以实现过滤请求、插入横切逻辑等功能,应用场景丰富多彩。 代理的方式分为静态代理和动态代理两种。 静态代理 程序运行前代理类的字节码文件依然存在,需要程序员编写源文件。 缺点:要针对于每一个类撰写代理类;对于单个被代理的类,如果需要被代理的方法很多,又加大了工作量。 优点:直观,可读性较强。 动态代理 程序运行时动态生成
nnngu
2018/03/15
7460
016 Java中的动态代理
深度解析 Spring 源码:揭秘JDK动态代理的奥秘
JDK动态代理是Java语言提供的一种实现动态代理的方式,其基本原理是利用反射机制在运行时动态生成代理类和代理对象。
忆愿
2025/01/06
2270
深度解析 Spring 源码:揭秘JDK动态代理的奥秘
探究动态代理与CGLIB的奥秘:Java代理模式的两种实现方式
在Java开发中,代理模式是一种常见的设计模式,它允许我们创建一个代理对象,用来控制对其他对象的访问。代理模式在AOP(面向切面编程)中广泛应用,用于实现日志记录、性能监测、事务管理等功能。在代理模式中,有两种主要的实现方式:动态代理和CGLIB代理。本文将深入研究这两种代理方式的区别,分析它们的优缺点,并提供代码示例,帮助你更好地理解和应用这些概念。
疯狂的KK
2023/09/27
2.9K0
探究动态代理与CGLIB的奥秘:Java代理模式的两种实现方式
设计模式 - 动态代理
代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。(逐字理解)
用户7630333
2023/12/07
1720
设计模式 - 动态代理
java动态代理和静态代理的实现
代理模式:为其他对象提供一种代理以控制目标对象的访问,在某些情况下, 一个对象不适合或者不能直接引用另外一个对象,代理对象可以在这个客户类和目标对象中起到一个桥梁作用。
IT云清
2019/01/22
4370
静态代理与动态代理
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/details/52025353
DannyHoo
2018/09/13
3520
静态代理与动态代理
AOP中的JDK动态代理与CGLIB动态代理:深度解析与实战
这里推荐一篇实用的文章:《Java 读取寄存器数据的实现与应用》,作者:【喵手】。
小马哥学JAVA
2024/11/21
2210
(86) 动态代理 / 计算机程序的思维逻辑
前面两节,我们介绍了反射和注解,利用它们,可以编写通用灵活的程序,本节,我们来探讨Java中另外一个动态特性 - 动态代理。 动态代理是一种强大的功能,它可以在运行时动态创建一个类,实现一个或多个接口,可以在不修改原有类的基础上动态为通过该类获取的对象添加方法、修改行为,这么描述比较抽象,下文会具体介绍,这些特性使得它广泛应用于各种系统程序、框架和库中,比如Spring, Hibernate, MyBatis, Guice等。 动态代理是实现面向切面的编程(AOP - Aspect Oriented
swiftma
2018/02/01
5250
相关推荐
动态代理的两种方式以及优缺点
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • Gaga-Geek图书管理系统(BMS)
    • 项目介绍
      • 目录结构
    • 环境搭建
      • 开发工具
      • 开发环境
    • 需求文档
    • 产品原型图
    • 产品流程图
    • 软件架构:三层架构
      • 核心类文件:
      • 优化类文件:
      • 详细介绍:
    • 版本
      • V1.0.0
      • V1.0.1
      • V1.0.2
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档