一 开源项目地址
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
九 先不急于分析代码,先分析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);所调用。 |
未完待续......