当前位置: 首页 > news >正文

Spring AOP基础动态代理基于JDK动态代理实现

目录

1. 预备知识-动态代理

1.1 什么是动态代理

1.2 动态代理的优势

1.3 基于JDK动态代理实现

2. AOP

2.1 基本概念

2.2 AOP带来的好处

3. Spring AOP

3.1 前置通知

3.2 后置通知

3.3 环绕通知

3.4 异常通知

3.5 适配器


1. 预备知识-动态代理

1.1 什么是动态代理

动态代理利用Java的反射技术(Java Reflection)生成字节码,在运行时创建一个实现某些给定接口的新类(也称"动态代理类")及其实例。

1.2 动态代理的优势

动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)
spring中的AOP是动态代理使用的经典场景。

1.3 基于JDK动态代理实现

在基于JDK的动态代理的实现中有两个重要的类:InvocationHandler, Proxy

  • InvocationHandler
    是代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
  • Proxy
    JDK中动态生成代理类的工具类

一个动态代理的示例:

定义一个接口(基于JDK的动态代理只能使用接口)

public interface ISubject {void hello(String param);
}

为接口定义实现类

public class SubjectImpl implements ISubject {@Overridepublic void hello(String param) {System.out.println("hello  " + param);}
}

实现一个代理类:

public class JDKProxy implements InvocationHandler {private Object target;public JDKProxy(Object target) {this.target = target;}//创建代理public Object newProxy() {return (ISubject)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}@Override   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("---------- 在业务方法调用之前可以进行前置增强   ------------");//利用反射机制调用方法,invoke为返回值,如果没有返回nullObject invoke = method.invoke(target, args);System.out.println("---------- 在业务方法调用之前可以进行后置增强   ------------");return invoke;}}

编写代理类实际的调用,利用Proxy类创建代理之后的Subject类

public class JDKProxyDemo {public static void main(String[] args) {ISubject subject = new SubjectImpl();JDKProxy subjectProxy = new JDKProxy(subject);ISubject proxyInstance = (ISubject)subjectProxy.newProxy();proxyInstance.hello("world");}}

运行结果:

---------- 在业务方法调用之前可以进行前置增强   ------------
hello  world
---------- 在业务方法调用之前可以进行后置增强   ------------

2. AOP

2.1 基本概念

  • 连接点 (Joinpoint)
    程序执行过程中明确的点,如方法的调用,或者异常的抛出.
  • 目标(Target)
    被通知(被代理)的对象,如上例中的SubjectImpl
  • 通知(Advice)
    在某个特定的连接点上执行的动作,同时Advice也是程序代码的具体实现,例如一个实现日志记录的代码(通知有些书上也称为处理),可以理解为AOP真正要实现的功能
  • 代理(Proxy)
    将通知应用到目标对象后创建的对象(代理=目标+通知),请注意:只有代理对象才有AOP功能,而AOP的代码是写在通知的方法里面的,如上例中的JDKProxy
  • 切入点(Pointcut)
    多个连接点的集合,定义了通知应该应用到那些连接点。也将Pointcut理解成一个条件 ,此条件决定了容器在什么情况下将通知和目标组合成代理返回给外部程序
  • 适配器(Advisor)
    适配器=通知(Advice)+切入点(Pointcut)

AOP运行原理:目标对象只负责业务逻辑,通知只负责AOP增强逻辑(如日志,数据验证等),而代理对象则将业务逻辑而AOP增强代码组织起来(组织者)

2.2 AOP带来的好处

AOP是公用的框架代码放置的理想地方,将公共代码与业务代码分离,使我们在处理业务时可以专心的处理业务。
伪代码:

public void doSameBusiness (long lParam,String sParam){// 记录日志log.info("调用 doSameBusiness方法,参数是:"+lParam);// 输入合法性验证if (lParam<=0){throws new IllegalArgumentException("xx应该大于0");}if (sParam==null || sParam.trim().equals("")){throws new IllegalArgumentException("xx不能为空");}// 异常处理try{ 真正的业务处理}catch(...){}catch(...){}// 事务控制tx.commit();}

通过使用AOP我们可以将日志记录,数据合法性验证,异常处理等功能放入AOP中,那么在编写业务时就可以专心实现真正的业务逻辑代码。

3. Spring AOP

在spring中org.springframework.aop.framework.ProxyFactoryBean用来创建代理对象,在一般情况下它需要注入一下三个属性:

  • proxyInterfaces 代理应该实现的接口列表(List)
  • interceptorNames 需要应用到目标对象上的通知Bean的名字
  • target 目标对象 (Object)

准备工作:创建一个IBookService接口及其实现类,用于演示spring AOP开发示例:

public interface IBookService {// 购书public boolean buy(String userName, String bookName, Double price);// 发表书评public void comment(String userName, String comments);}
public class BookServiceImpl implements IBookService {private Logger logger = LoggerFactory.getLogger(this.getClass());public BookServiceImpl() {super();}public boolean buy(String userName, String bookName, Double price) {//logger.info("userName={},bookName={},price={}", userName, bookName, price);// 通过控制台的输出方式模拟购书logger.info(userName + " buy " + bookName + ", spend " + price);return true;}public void comment(String userName, String comments) {logger.info(userName + " say:" + comments);}}

将service配置到spring配置文件中,以便于被spring管理,按自己的实际情况配置

<!-- 目标 --><bean id="bookServiceTarget" class="com.zking.sp02.impl.BookServiceImpl"/>

3.1 前置通知

前置通知需要实现org.springframework.aop.MethodBeforeAdvice,前置通知将在目标对象调用前调用。示例实现购书系统AOP方式实现日志,简单打印调用的方法及参数

1)首先实现一个前置通知类,实现接口MethodBeforeAdvice,并实现接口中的before方法

public class MyMethodBeforeAdvice implements MethodBeforeAdvice {private Logger logger  = LoggerFactory.getLogger(this.getClass());@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {String s = "[前置通知]: " + this.getClass() + "." + method.getName() + "将被调用,参数为:" + Arrays.toString(args);logger.info(s);}}

2)将实现的前置通知配置到Spring.xml中,一遍与被spring管理。需要根据自己的实际情况配置。

<bean id="myMethodBeforeAdvice" class="com.zking.springdemo.aop.MyMethodBeforeAdvice"/>

3)现在需要解决如何将通知和目标联系起来,需要一个组织者 - 代理

<bean id="bookService" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 配置代理目标 --><property name="target" ref="bookServiceTarget"/><!-- 配置拦截器列表,拦截器就是通知 --><property name="interceptorNames"><list><value>myMethodBeforeAdvice</value></list></property><!-- 代理要实现的接口,代理类与被代理类需要实现相同接口 --><property name="proxyInterfaces"><list><value>com.zking.springdemo.aop.IBookService</value></list></property></bean>

写一个测试类,测试前置通知

public class Demo {public static void main(String[] args) {ApplicationContext <u>cxt</u> = new ClassPathXmlApplicationContext("/spring.xml");IBookService bookService = (IBookService)cxt.getBean("bookService");System.out.println(bookService.getClass().getName());        bookService.buy("zs", "hlm", 10D);}}

3.2 后置通知

在连接点正常完成后执行的通知。定义的后置通知类需要实org.springframework.aop.AfterReturningAdvice

示例:在线购书系统中,要求不修改BookServiceImpl代码的情况下增加如下功能:对买书的用户进行返利:每买本书返利10元,简单打印类似于“[后置通知] 返利10元”即可。开发步骤与前置通知类似

1) 编写一个后置通知实现类

public class MyAfterReturnAdvice implements AfterReturningAdvice {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {       logger.info("[后置通知]:  返利10元");        }
}

2) 将后置通知实现类配置到spring配置文件中,以便于spring管理

<bean id="myAfterReturnAdvice" class="com.zking.springdemo.aop.MyAfterReturnAdvice"/>

3)解决如何将通知和目标联系起来,在实现前置通知时已经配置了代理对象,现在只要将后置通知也配置到拦截器列表当中即可。

<!-- 配置拦截器列表,拦截器就是通知 -->
<property name="interceptorNames"><list><!-- 前置通知 --><value>myMethodBeforeAdvice</value><!-- 后置通知 --><value>myAfterReturnAdvice</value></list>
</property>

运行上例已经实现的测试类,查看后置通知的运行效果。

3.3 环绕通知

包围一个连接点的通知,最大特点是可以修改返回值,由于它在方法前后都加入了自己的逻辑代码,因此功能很强大。自定义的环绕通知需要实现org.aopalliance.intercept.MethodInterceptor接口。
示例:在环绕通知中输出日志和返回值

1)实现一个环绕通知,该类要实现MethodInterceptor接口

public class MyMethodInterceptor implements MethodInterceptor {private Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {//获取目标对象Object target = invocation.getThis();//获取参数Object[] args = invocation.getArguments();//获取方法Method method = invocation.getMethod();logger.info("[环绕通知] 前:将调用{}.{}方法,参数为{}",target.getClass(),method.getName(), Arrays.toString(args));//调用目标对象上的方法   Object val = invocation.proceed();logger.info("[环绕通知] 后,已调用{}.{}, 返回值:{}", target.getClass(),method.getName(), val);return val;}}

2)在spring的配置文件中配置环绕通知,以便于spring管理

<bean id="myMethodInterceptor" class="com.zking.springdemo.aop.MyMethodInterceptor"/>

3)解决如何将通知和目标联系起来,在实现前置通知时已经配置了代理对象,现在只要将环绕通知也配置到拦截器列表当中即可。

<!-- 配置拦截器列表,拦截器就是通知 -->
<property name="interceptorNames"><list><!--前置通知--><value>myMethodBeforeAdvice</value><!--后置通知--><value>myAfterReturnAdvice</value><!--环绕通知--><value>myMethodInterceptor</value></list>
</property>

运行上例已经实现的测试类,查看后置通知的运行效果。

3.4 异常通知

异常通知需要实现ThrowsAdvice接口,这个通知会在方法抛出异常退出时执行,与以上演示的前置、后置、环绕通知不同,主要有一下特点:

  • 这个接口里面没有定义方法,要求我们的类必须实现afterThrows这个方法
  • 以异常类型作为参数,无返回值

示例
1)定义一个自定义异常,继承RuntimeException运行时异常

public class PriceException extends RuntimeException {public PriceException() {super();}public PriceException(String message, Throwable cause) {super(message, cause);}public PriceException(String message) {super(message);}public PriceException(Throwable cause) {super(cause);}
}

2)创建异常通知类,该类实现ThrowsAdvice接口,并实现afterThrowing方法

public class MyThrowsAdvice implements ThrowsAdvice {private Logger logger = LoggerFactory.getLogger(this.getClass());//以异常类型作为参数,无返回值public void afterThrowing(PriceException e) {logger.info("程序发生了PriceException异常");}}

剩下的步骤是将异常通知配置到spring配置文件中,及在代理配置中加入异常通知的配置,可参考上面的环绕通知等示例。

3.5 适配器

适配器, 通过正则表达式来定义方法切入点,也就是说定义哪些方法将被拦截器处理。适配器=通知(Advice)+切入点(Pointcut)。
在配置适配器时需要使用org.springframework.aop.support.RegexpMethodPointcutAdvisor

配置适配器示例:

<!-- 配置适配器 -->
<bean id="myAdisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><!-- 定义正则表达式,定义需要拦截的方法名 ,本例定义了所有以buy结尾的方法  --><property name="patterns"><list><value>.*buy</value></list></property><!-- 定义由那个通知(或者叫拦截器)来处理匹配的方法 --><property name="advice"><ref bean="myAfterReturnAdvice"/></property>
</bean>

在代理中使用刚刚配置的适配器

<!-- 将直接使用后置拦截器 ,改为使用适配器 -->
<!-- <value>myMethodAfterReturnAdvice</value> --><!-- 通过适配器使用后置拦截器 -->
<value>myAdisor</value>

修改代理中的拦截器列表(spring配置文件,代理部分),将配置器直接配置在拦截器列表中即可。

相关文章:

Spring AOP基础动态代理基于JDK动态代理实现

目录 1. 预备知识-动态代理 1.1 什么是动态代理 1.2 动态代理的优势 1.3 基于JDK动态代理实现 2. AOP 2.1 基本概念 2.2 AOP带来的好处 3. Spring AOP 3.1 前置通知 3.2 后置通知 3.3 环绕通知 3.4 异常通知 3.5 适配器 1. 预备知识-动态代理 1.1 什么是动态代理…...

第一章 计算机系统概述 五、中断和异常、系统调用

目录 一、中断的作用 二、中断的类型 1、内中断&#xff08;异常&#xff09; 2、外中断 三、中断机制的基本原理 四、系统调用 1、定义&#xff1a; 2、与库函数的区别 3、按功能分类 4、作用 一、中断的作用 1、“中断”是让操作系统内核夺回CPU使用权的唯一途径 …...

【C语言】文件操作(上)

一.什么是文件 文件是磁盘上的文件,文件中存放的数据不随程序的退出而销毁. 二.文件的打开与关闭 1.文件指针 每个被使用的文件都在内存中开辟了一个相应的文件信息区&#xff0c;用来存放文件的相关信息&#xff08;如文件的名字&#xff0c;文件状态及文件当前的位置等&…...

【Linux】让笔记本发挥余热,Ubuntu20.04设置WiFi热点

Ubuntu20.04设置WiFi热点 由于卧室距离客厅较远&#xff0c;wifi信号太弱&#xff0c;体验极差。鉴于卧室的笔记本电脑是通过网线连接的客厅路由器&#xff0c;因此考虑将这台老破笔记本作为“路由器”&#xff0c;以便发挥它的余热。实验证明&#xff0c;上网速度提升数十倍&a…...

【云平台】遥感地信云平台收录

文章目录 国内1 航天宏图PIE-Engine2 商汤科技3 AI Earth4 EarthDataMiner国外结语国内 1 航天宏图PIE-Engine https://engine.piesat.cn/live-show-list 在这里插入图片描述 2 商汤科技 https://senseearth-cloud.com/map 3 AI Earth https://engine-aiearth.aliyun.com…...

23种设计模式之---单例模式

闲来无事学一下设计模式&#xff0c;希望这23种可以一直更下去&#xff0c;什么时候能更完呢&#xff0c;也许一个月&#xff0c;也许一年&#xff0c;也许断更 设计模式六大原则 本文是23篇的第一篇&#xff0c;在学习设计模式之前&#xff0c;你需要了解下六大原则。 1、开…...

蓝桥杯官网练习题(纸牌三角形)

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 A,2,3,4,5,6,7,8,9 共 99 张纸牌排成一个正三角形&#xff08;A 按 1 计算&#xff09;。要求每个边的和相等。 下图就是一种排法。 这样的排法可能会有很多。 如果…...

一辆新能源汽车的诞生之旅:比亚迪常州工厂探营

作为在新能源汽车领域首屈一指的国产品牌&#xff0c;比亚迪近年来可以说是捷报频传&#xff0c;高奏凯歌。 以比亚迪常州工厂为例&#xff0c;据介绍该工厂当初规划设计时定下的生产目标&#xff0c;是年产量能够达到20万辆。然而在2023年上半年&#xff0c;该工厂光是主要销往…...

【算法专题突破】双指针 - 最大连续1的个数 III(11)

目录 1. 题目解析 2. 算法原理 3. 代码编写 写在最后&#xff1a; 1. 题目解析 题目链接&#xff1a;1004. 最大连续1的个数 III - 力扣&#xff08;Leetcode&#xff09; 这道题不难理解&#xff0c;其实就是求出最长的连续是1的子数组&#xff0c; 但是&#xff0c;他支…...

java实现备忘录模式

备忘录模式是一种行为设计模式&#xff0c;它允许您捕获一个对象的内部状态&#xff0c;并在稍后的时间点将其恢复。这对于需要撤销操作或恢复到先前状态的应用程序非常有用。以下是在 Java 中实现备忘录模式的一般步骤&#xff1a; 创建一个原发器类&#xff08;Originator&am…...

aardio语言的通用数据表维护

import win.ui; /*DSG{{*/ var winform win.form(text"通用数据表维护";right617;bottom427;bgcolor15780518) winform.add( buttonAdd{cls"button";text"增加空行";left469;top40;right564;bottom80;flat1;z2}; buttonDel{cls"button&quo…...

手写RPC框架--7.封装响应

RPC框架-Gitee代码(麻烦点个Starred, 支持一下吧) RPC框架-GitHub代码(麻烦点个Starred, 支持一下吧) 封装响应 封装响应a.封装响应b.请求id生成器(雪花算法)c.抽象序列化d.建立序列化工厂e.hessian的序列化方式&#xff08;拓展&#xff09; 封装响应 a.封装响应 在core模块…...

Linux入门教程||Linux系统目录结构

登录系统后&#xff0c;在当前命令窗口下输入命令&#xff1a; ls / 你会看到如下图所示: 树状目录结构&#xff1a; 以下是对这些目录的解释&#xff1a; /bin&#xff1a; bin是Binary的缩写, 这个目录存放着最经常使用的命令。 /boot&#xff1a; 这里存放的是启动Linux时…...

LeetCode 88. 合并两个有序数组

文章目录 一、题目二、C# 题解 一、题目 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&a…...

C语言实现扫雷小游戏

1.首先扫雷游戏要存储布置好的雷信息&#xff0c;需要一个二维数组 不是雷放* 雷&#xff1a;# 不是雷&#xff1a;0 雷&#xff1a;1 2. 给2个二维数组 9*9 一个存放雷的信息&#xff0c;一个存放布置好雷的信息 3.为了防止在统计坐标周围的…...

【linux基础(五)】Linux中的开发工具(上)---yum和vim

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到开通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux中的开发工具 1. 前言2.…...

C++学习之list的实现

在了解学习list实现之前我们首先了解一下关于迭代器的分类&#xff1a; 按功能分类&#xff1a; 正向迭代器 反向迭代器 const正向迭代器 const反向迭代器 按性质分类&#xff1a; 单向迭代器 只能 例如单链表 双向迭代器 可&#xff0c;也可-- 例如双…...

一种高效且节约内存的聚合数据结构的实现

一种高效且节约内存的聚合数据结构的实现 在特定的场景中&#xff0c;特殊定制数据结构能够得到更加好的性能且更节约内存。 聚合函数GroupArray的问题 GroupArray聚合函数是将分组内容组成一个个数组&#xff0c;例如下面的例子&#xff1a; SELECT groupArray(concat(ABC…...

机器学习(10)---特征选择

文章目录 一、概述二、Filter过滤法2.1 过滤法说明2.2 方差过滤2.3 方差过滤对模型影响 三、相关性过滤3.1 卡方过滤3.2 F检验3.3 互信息法3.4 过滤法总结 四、Embedded嵌入法4.1 嵌入法说明4.2 以随机森林为例的嵌入法 五、Wrapper包装法5.1 包装法说明5.2 以随机森林为例的包…...

Python之数据库(MYSQL)连接

一&#xff09;数据库SQL语言基础 MySQL是一个关系型数据库管理系统&#xff0c;由瑞典MySQL AB 公司开发&#xff0c;目前属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一&#xff0c;在 WEB 应用方面&#xff0c;MySQL是最好的 RDBMS (Relational Database…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

Sklearn 机器学习 缺失值处理 获取填充失值的统计值

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...

Python实现简单音频数据压缩与解压算法

Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中&#xff0c;压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言&#xff0c;提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...

篇章二 论坛系统——系统设计

目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...