开源项目分析解读——基于Spring Cloud的在线考试系统

一 开源项目地址

https://gitee.com/wells2333/spring-microservice-exam

二 前端运行方法

1 在F:\springcloud\spring-microservice-exam\frontend\spring-microservice-exam-ui目录下运行命令

cnpm install

2 在F:\springcloud\spring-microservice-exam\frontend\spring-microservice-exam-ui目录下运行命令

cnpm run dev

3 测试是否正常运行

三  consul简介

因为项目涉及consul,所以简单介绍一下,参考下面链接。

参考: https://blog.csdn.net/chengqiuming/article/details/102990022

四 Redis简介

因为项目涉及Redis,所以简单介绍一下,参考下面链接。

参考: https://blog.csdn.net/chengqiuming/article/details/102990197

五 Mysql数据库

需要建立4个数据库

  • microservice-user

  • microservice-exam

  • microservice-auth

  • microservice-gateway

建立完数据库后,导入相关脚本,脚本从开源项目获取。

六 在启动服务前,先确保下面4个服务能正常运行

consul、MySQL、redis、rabbitMq

七 修改配置文件

主要涉及consul、MySQL、redis、rabbitMq中间件的IP和端口信息

如果这些中间件都安装在本地,不用修改。

八 启动顺序

1 config-service

2 auth-service

3 user-service

4 exam-service

5 gateway-service

需要监控功能再启动:

6 monitor-service

7 msc-service

内存不足的可以限制每个服务的内存:config-service可以分配64M(-Xmx64m -Xms64m)、其它服务分配128M(-Xmx128m -Xms128m)

配置方法见下面链接:

https://blog.csdn.net/chengqiuming/article/details/81429641

如果阿里的Maven镜像仓库有问题,可用华为的Maven镜像仓库。

配置方法见下面链接:

https://blog.csdn.net/anqi114179/article/details/80031148

开源项目分析解读——基于Spring Cloud的在线考试系统_第1张图片

九 先不急于分析代码,先分析pom中的依赖,可以了解项目用到哪些技术,做到心中有数

已将相关依赖进行总结。参考下面文档

https://blog.csdn.net/chengqiuming/article/details/102963298

十 按照文件粒度进行代码分析

文件名

相关技术说明

CustomGlobalExceptionHandler.java

该文件是全局异常处理,需要注意@ControllerAdvice和@ExceptionHandler(CommonException.class)注解的用法。

具体用法请参考: https://blog.csdn.net/chengqiuming/article/details/81711751

SwaggerConfig.java

该文件主要是Swagger配置,需要关注WebMvcConfigurer,具体用法参考:https://yq.aliyun.com/articles/617307

该文件还大量用到stream流操作。

关于Docket的用法,可参考: https://www.jianshu.com/p/6c2d416bdf10和 https://github.com/rocknsm/docket

关于Swagger2的用法,可参考 https://www.cnblogs.com/jtlgb/p/8532433.html

CustomRedisCacheWriter.java

重写了RedisCacheWriter接口,在RedisConfig中会用到该类,RedisCacheManager会用到此类。

 

MultitenantCacheManager.java

@Slf4j:用法参考: https://www.jianshu.com/p/6e137ee836a1

该文件作用:扩展CacheManager支持多租户

AppStartupRunner.java

@AllArgsConstructor:用法参考:https://www.cnblogs.com/huey/p/4693484.html

@Component:把普通pojo实例化到spring容器中,相当于配置文件中的

用法参考: https://www.cnblogs.com/lyjing/p/8427832.html

RedisConfig.java

Redis的配置类。

@Configuration:相对于Spring配置文件中的

@Bean:相对于Spring配置文件中的

@EnableCaching:参考 https://segmentfault.com/a/1190000011069802

SnowFlake

该类是ID生成配置类

@Autowired:用在JavaBean中的注解,通过byType形式,用来给指定的字段或方法注入所需的外部资源。简言之:给类的属性或方法参数注入值,值来自Javabean,是通过byType匹配的。

https://blog.csdn.net/horacehe16/article/details/79811763

CommonConstant

项目的一些公共常量

MqConstant

关于Rabbitmq的一些常量

ServiceConstant

关于服务的一些常量,常量定义格式稍微注意下:  public static final String USER_SERVICE = "user-service";

SysConfigDto

它是系统配置的数据传输单元,会返回给前端。

@Data:具体用法参考: https://blog.csdn.net/qq_37433657/article/details/83275051

LoginType

这是一个枚举类,但因为使用了lombok插件,所以看上去比较的简洁。

枚举类比较传统的用法参考: https://blog.csdn.net/lihaogn/article/details/90290084

@Getter:具体用法参考: https://blog.csdn.net/qq_37433657/article/details/83275051

excpeptions

该文件夹下定义了一个公共异常类和由该公共异常类派生出的5个特殊异常类。我们写异常类可以参考这种写法。

Log

日志定义

@NotBlank(message = "日志标题不能为空")的用法参考: https://blog.csdn.net/qq_39964694/article/details/81183701

ResponseBean

该类是返回前端的响应数据,并且用到泛型类,这种用法我们可以借鉴。

BaseEntity

实体类的基类,该类定义了子类的共同属性,是定义实体类的一种常用手法。注意该类是个泛型类。

BaseMapper

Dao的基类接口

CrudMapper

继承BaseMapper,也是一个泛型类,提供了基本的增、删、查、改操作。提供的接口有:获取单条数据、获取列表数据、获取全部列表数据、根据id获取列表数据、插入单条数据、更新单条数据、删除单条数据、批量删除

TreeEntity

树形实体(节点),继承BaseEntity,它是一个泛型类,关键属性有:code、父节点、父节点id、排序号,子节点列表

SnowflakeProperties

Snowflake的配置属性,属性值从各微服务配置文件中获取。

SysProperties

系统配置属性,属性值从各微服务配置文件中获取。

BaseService

Service基类,它是一个抽象类。

CrudService

基本的增删查改服务,它继承自BaseService,也是一个抽象类。还用到了定义类型形参时设定上限语法。

该类实现基本的增删查改服务,提供的服务包含:根据id获取、获取单条数据、查询列表、查询分页、插入或更新、删除、批量删除、插入数据、更新数据

@Transactional:一般加在Service层的增、删、改方法上。

其中get方法用到反射技术,findPage用到分页技术,这两个函数可以仔细研究下。

LogInterceptor

定义了okhttp的日志拦截器,该拦截器最终会被配置到OkHttpClient中。

具体用法可参考: https://blog.csdn.net/bunny1024/article/details/53504556

OkHttpRequestBodyUtil

该类用于OKHttp,它是一个工具类,用于生成RequestBody。

该类只提供了一个方法create,它用来生成RequestBody,RequestBody是一个抽象类。此处用到了匿名内部类。具体用法参考:

https://blog.csdn.net/hellocsz/article/details/81974251

匿名内部类会立即创建一个该类的实例,类体部分实现了RequestBody的接口:contentType、writeTo、contentLength

OkHttpUtil

okHttp工具类封装,该类是一个单例对象,封装了OkHttpClient,在构造函数中,根据配置并定制了OkHttpClient。

该工具对提供了一系列的OkHttp的操作,包括:get请求、get请求获取响应体、post请求、用于上传文件的put请求、上传字节数组、上传流、patch请求、删除请求、Map转JSONObject对象。

具体使用请参考: https://blog.csdn.net/fxjzzyo/article/details/78761373

SSLSocketClient

该类用于配置OkHttpClient的SSL特性。

该类的SSLSocketClientSSLContext用到了单例模式

X509TrustManager是一个匿名内部类,实现了TrustManager接口的三个方法。

getHostnameVerifier方法用到了lamada表达式,实现了HostnameVerifier接口。

Assert

实现了对象的非空校验。

ClassUtils

类工具类,该类用到反射机制,主要实现:获取构造函数的方法参数信息,获取一般方法的方法参数信息

DateUtils

日期工具类,DateTimeFormatter用于格式化日期时间。实现以下功能:日期转String、LocalDate转Date、LocalDateTime转Date、Date转LocalDate、Date转LocalDateTime、两个时间之差。

ExcelToolUtil

Excle工具类,主要实现:根据数据和标题导出为Excle表格、设置表格样式、将Excle数据导入到系统,并保存到List>中、数据格式转换。

FileUtil

文件工具类,获得文件的扩展名。

IdGen

id生成工具类,提供了两个生成器,一个是改良版的UUID生成器,一个是基于snowflake算法生成ID。

JsonMapper

将Json字符串转换为对象,将对象转换为Json字符串。

关于JSON的用法,请参考: https://blog.csdn.net/xingfei_work/article/details/76572550

MapUtil

Map工具类,主要实现如下功能:获取map中key对应的值,并根据需求和实际,可转换为字符串、Date、Double类型、布尔类型、JavaBean集合对象。该类还实现了将Map对象转化成 JavaBean对象,将JavaBean集合对象转化成Map集合对象,以及将JavaBean对象转化成Map对象。

ObjectUtil

将字符串转换为double,如果字符串为空或者null,则自动转换为0.0。

PageUtil

分页查询工具类

ReflectionUtil

反射工具类:实现如下功能:返回某个类的第index个泛型类型,直接读取对象属性值、根据属性字符串获得某个对象对应的属性,改变private/protected的成员变量为public,直接设置对象属性值。

getSuperclass的用法参考:https://www.cnblogs.com/xiohao/p/4269704.html

getDeclaringClass的用法参考:https://www.yiibai.com/javareflect/javareflect_field_getdeclaringclass.html

for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass())对这个循环的理解:可以理解为传进来的Object为水果类,obj实际为苹果,水果类有一个属性重量。依照这个思路去理解循环中的内容。

ResponseUtil

Response工具类:根据ResponseBean内容,判断响应是否成功。

Servlet

Servlet工具类:获取在不同浏览器下的下载文件名的转码规则。

String downloadName = URLEncoder.encode(fileName, "UTF-8");  

当fileName = 中国

downloadName = %E4%B8%AD%E5%9B%BD

SnowflakeIdWorker

Snowflake的ID生成器算法

/**

* Twitter_Snowflake

* SnowFlake的结构如下(每部分用-分开):

* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0

* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)

* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69

* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId

* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

* 加起来刚好64位,为一个Long型。

* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。

*/

SpringContextHolder

具体说明参考:https://www.cnblogs.com/cuijiade/p/9664789.html

SysUtil

系统工具类,主要实现以下功能:

获取当前登录的用户名

获取系统编号

获取租户编号

AES解密

buildTree

两层循环实现建树,参数传入的是根和树实体列表,此时的各实体不包含孩子节点,建立完后包含孩子节点。

vo(AttachmentVo、DeptVo、LogVo、MenuVo、RoleVo、UserVo)

vo、po、dto、bo、pojo、entity、mode的区分,请参考: https://blog.csdn.net/qq_41604862/article/details/79675223

BaseController

控制器基类,被具体控制器继承。

CustomFeignConfig

服务间调用携带Authorization、Tenant-Code请求头。

FeignOkHttpConfig

功能点: 创建一个Builder对象,并对OkHttpClient的相关属性初始化。

技术点:

@ConditionalOnClass:当给定的类名在类路径上存在,则实例化当前Bean。参考: https://www.cnblogs.com/qdhxhz/p/11027546.html

@AutoConfigureBefore:说明FeignOkHttpConfig在FeignAutoConfiguration之前加载,参考: https://blog.csdn.net/yangchao1125/article/details/100934881

用到了builder模式,具体参考: https://www.jdon.com/51691

RestTemplateConfig

技术点:配置类,在spring容器中注入一个RestTemplate对象restTemplate

LogInterceptor

技术点:

okhttp自定义日志拦截器:https://blog.csdn.net/weixin_43807869/article/details/86489494

拦截器的使用:  https://blog.csdn.net/stven_king/article/details/84500778

SpringCloudHystrixConcurrencyStrategy

技术点:并发策略定义

参考《重新定义Spring cloud实战》的P141

Log

定义了一个日志注解,注解了@Log的方法会打印日志

LogAspect

功能点:

日志切面,异步记录日志,不仅打印了日志,还通过异步的方法,将日志存到数据库中。

SpringContextHolder.publishEvent(new LogEvent(logVo));发布一个事件

技术点:

    @Around("@annotation(log)")   //这里的log指的是下面定义中的log,整个意思是在@Log的方法处会切入打印

    public Object around(ProceedingJoinPoint point, Log log) throws Throwable {

用到了AOP面向切面编程,可参考下面文章

https://blog.csdn.net/qq_41981107/article/details/85260765

https://www.cnblogs.com/ssslinppp/p/5845659.html

https://zhidao.baidu.com/question/1306182545009100499.html

LogEvent

功能点:

定义日志事件

技术点:

日志事件是继承ApplicationEvent这个类的

LogListener

功能点:

异步监听日志事件,当有日志事件发布时候,此类会监听到。

技术点:

@Async:基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。

参考 https://www.cnblogs.com/jpfss/p/10273129.html

@Order:参考 https://blog.csdn.net/Thinkingcao/article/details/84801093

@EventListener(LogEvent.class):参考 https://www.jianshu.com/p/e2d257ce410d?from=timeline&isappinstalled=0

LogUtil

功能点:

日志工具类,初始化日志基本信息。

技术点:

LogUtilRequestContextHolder的用法:

https://www.cnblogs.com/panbin/p/11386022.html

SecurityContextHolder的用法:

https://www.cnblogs.com/longfurcat/p/9417912.html

LogAutoConfiguration

功能点:

日志功能自动化配置,注入日志监听器和日志切面类。

技术点:

@EnableAsync:

https://blog.csdn.net/qq_39385706/article/details/79365849

@ConditionalOnWebApplication:如果是web应用

https://www.cnblogs.com/zhixiang-org-cn/p/9703736.html

CustomResourceServerConfig

功能点:

资源服务器配置,注入了开发权限的URL、手机登录配置、微信登录配置等信息。

该自定义类主要是对ResourceServerSecurityConfigurer和HttpSecurity所对应的bean进行配置

技术点:

ResourceServerConfigurerAdapter: https://www.jianshu.com/p/fe1194ca8ecd

MobileLoginConfig

功能点:手机登录配置

主要是注入:MobileSecurityConfigurer和AuthenticationSuccessHandler这两个Bean。

其中AuthenticationSuccessHandler用到了Builder模式。

WxLoginConfig

功能点:微信登录配置

主要是注入:WxSecurityConfigurer和AuthenticationSuccessHandler这两个Bean。

其中WxLoginSuccessHandler用到了Builder模式。

SecurityConstant

功能点:跟安全相关的常量定义

ClientDetailsServiceImpl

功能点:jdbc客户端service

技术点:该类主要是继承JdbcClientDetailsService这个类,重写了loadClientByClientId方法,实际上还是调用基本的方法。

JdbcClientDetailsService关于该类的说明: https://cloud.tencent.com/developer/article/1413422

CustomUserDetailsService

定义查询用户信息接口,包括下面三个接口

根据用户名和租户标识查询

根据社交账号和租户标识查询

根据微信openId和租户标识查询

GrantedAuthorityImpl

GrantedAuthority接口的封装,实现了getAuthority这个接口方法,从方法实现来看说明权限是通过角色类体现的。

UserDetailsImpl 

实现UserDetails(用户详细信息)接口,通过权限、密码、用户名、启动禁用状态和租户标识来体现用户详情。并实现了相关接口。

CustomOauthException

定制OAuth2Exception,注意如果该类如果序列化,需要使用CustomOauthExceptionSerializer类的serialize方法进行序列化。

TenantTokenFilter

获取请求头里的租户code,并将它保存到线程变量TenantContextHolder中。

关于过滤器中的@Order注解,参考下面文章。

https://www.cnblogs.com/zxf330301/p/8579507.html

MobileAuthenticationFilter

关于密码认证,请参考:http://www.spring4all.com/article/420,从这篇文章可以很好的理解MobileAuthenticationFilter

MobileAuthenticationFilter类继承AbstractAuthenticationProcessingFilter

UsernamePasswordAuthenticationFilter类也是继承AbstractAuthenticationProcessingFilter

两个实现类的写法有同工异曲之妙。可以对比MobileAuthenticationFilter和UsernamePasswordAuthenticationFilter这两个类一起学习。

AntPathRequestMatcher关于这个类的用法,请参考 https://blog.csdn.net/qq_35809876/article/details/102743762

关键代码:authResult = this.getAuthenticationManager().authenticate(mobileAuthenticationToken); 认证就是通过这行代码完成的。

MobileAuthenticationProvider

请看看 http://www.spring4all.com/article/420 这篇文章,里面提到了AbstractUserDetailsAuthenticationProvider这个类,它实现了AuthenticationProvider这个接口中的authenticate方法,类似MobileAuthenticationProvider也实现了AuthenticationProvider这个接口中的authenticate方法,所以这两个类可以类别学习。这个类中的authenticate方法主要就是验证手机用户是否合法。

它被上面这个类的authResult = this.getAuthenticationManager().authenticate(mobileAuthenticationToken);所调用。

未完待续......

你可能感兴趣的:(微服务)