小夫妻俩刚结婚,手里资金有限,就想着开一个路边烧烤摊。丈夫负责烤串做菜、妻子负责服务收银及上菜。这是一个典型的路边烧烤摊的经营模式。大家仔细看这种经营模式的好处在于:
这个阶段就有点像一些创业公司,刚开始创业的时候,一般做的应用都是单体应用。笔者也见过一些创业公司,上来就想搞微服务,我觉得这是不太现实。企业的架构都是一步一步衍进的,不要总想着一口吃一个胖子。如果京东淘宝从第一天做应用的时候就想做成今天的样子,他们一定活不到今天。就像一个没开过饭店的人第一次创业就要开五星级饭店,等待他的十有八九就是失败!
那么单体应用的特点有哪些:
但是,随着小夫妻俩经营有方、待客有道,开始有人愿意为了吃他们做的烧烤排队了。夫妻俩一想,我们这俩人也干不过来啊,怎么办?招人吧、扩大规模吧。
客户端负载均衡与服务端负载均衡是什么?
利与弊
也就是说:饭店(应用)现在在处理并发请求的能力和容量上增强了,但是在单个请求的处理速度上下降了。实际上,这就是服务拆分,服务拆分就是在 “用时间换空间” 。你细品!
为了解决上一阶段遇到的问题:单个请求的处理速度下降。也就是饭店针对单个订单做菜响应速度下降了,但是由于饭店的菜确实好吃、菜品精良,客流量又持续的增高。该店又再次面临扩容的问题。
新的问题又出现了,有的顾客既点烧烤又点饭菜。导致后端两组厨师之间沟通不畅,怎么组合套餐推送给前台?厨师之间怎么调用、怎么沟通啊?谁是头?谁是大脑?谁记得A厨师的烧烤和B厨师的饭菜是一桌的?
丈夫一看这种情况,我也别再做厨师了,那做什么?做菜品的配置管理、做订单的服务注册。丈夫负责主动观察问询各工种的工作状态并记录,妻子主动向丈夫问询后端厨师的状态,并根据丈夫的反馈分配订单(客户端负载均衡)。丈夫的新工作实际上就产生了微服务非常重要的组件:服务注册中心和配置管理中心。比如:Spring Cloud Alibaba nacos、eureka、consul、zookeeper、Spring cloud config等
饭店的规模越来越大了、岗位分工也越来越细了。真的成了超级大饭店了,怎么管?这就需要专业的机器人服务租用公司,这种公司专门出租各种行业专用机器人。
总之全都自动化。这时公司内部的devops团队就出现了,制定规范、集体包装、流程自动化。突然有一天,饭店承接了大型运动会大型展览,怎么办?要去招服务员么?招员工培训么?不要,租机器人就行了,用完了就还回去。每年的双十一、淘宝京东也都是用容器自动化扩容的方式应对暴涨的服务需求。容器化最大的好处就是:轻量级的发布于与销毁、自动化的扩容。
如果你使用了Spring Cloud 及 Spring Cloud Alibaba、Spring Boot,你该如何确定具体该使用哪一个版本?
本文就带你从官网提炼一下:该如何确定版本号保证兼容性?重点体现一下这个思考过程,和官网中留下的版本选型依据信息
笔者的版本号选型之路,遵循一个原则:遵循官方建议的基础上、尽量使用最新GA版(GA是指General Availability,正式发布版本)!
Why?
大牛以前都告诉我们,选型不要用最新版的。新版的bug多,我现在还用java8呢。通常来说是这样的,新版本功能性更强,老版本的稳定性更佳。
但Spring Cloud情况有点特殊,它是一个实实在在的“版本帝”,而且其组件的更新换代速度让人瞠目结舌,社区的发展速度和活跃度都非常高,这就带来一个问题,发展越快坑就越多,上一个版本的坑还没填完,新版本新功能新特性就出来了。所以很难去说:老版本维护时间长bug少,新版本的bug多。因此我们倒不如就尽量使用新版本,获得更多的功能性提升。
下面的截图,截取自Spring Boot的github仓库的wiki:https://github.com/spring-projects/spring-boot/wiki,github中最新的版本是2.3,但wiki中明确说到2.2版本是目前正在支持维护的版本。
这与Spring Boot官方网站中的说明是一致的,下图截取自Spring Boot官方网站。
Spring Cloud版本的版本号命名比较特殊,它是使用伦敦地铁站的站名作为版本号的。从A、B、C、D、E,目前是Hoxton SR3版本(我们简称H版),SR是service releases的缩写。
在Spring Cloud官网的OverView预览中https://spring.io/projects/spring-cloud/#overview,明确有如下信息:
也就是说:如果你使用Spring Cloud Hoxton,Spring Boot版本就要使用2.2.x。如果你是老项目,使用的是Spring Cloud Greenwich,Spring Boot版本就要使用2.1.x。
我们可以通过访问“/actuator/info”JSON服务端点,https://start.spring.io/actuator/info
从以上的JSON响应信息中心,我们明确的看到:如果你是用Spring Cloud Hoxton,需要使用Spring Boot 2.2.0以上,2.2.6以下。
如果你同时使用到了Spring cloud alibaba,Spring Boot 2.2.0以上,2.3.0以下。
最后我们来看一下Spring Cloud Reference文档内部:
https://cloud.spring.io/spring-cloud-static/Hoxton.SR3/reference/html/spring-cloud.html
开篇截图:
所以我们最终选型是
spring-cloud-alibaba与spring-cloud和spring-boot之间的版本说明
Netflix是一家做视频网站的公司,之所以要说一下这个公司是因为Spring Cloud在发展之初,Netflix做了很大的贡献。包括服务注册中心Eureka、服务调用Ribbon、Feign,服务容错限流Hystrix、服务网关Zuul等众多组件都是Netflix贡献给Spring Cloud社区的。
但这些组件在使用过程中也多多少少的暴露了一些弊病,比如:
所以,很多的厂商就基于Spring Cloud设计理念,开发自己的组件,其中比较著名的就是Spring Cloud Alibaba和携程的apollo。
上图中绿色对号的基本上都是Spring Cloud社区第二代组件,也是目前建议使用的组件。图中红色X号的组件,都基本上面临着淘汰与替换。
笔者一直关心着Spring Cloud社区的发展,下面将近两年社区的大事件集中展现一下:
从此,Spring Cloud逐渐告别netflix时代。
与此同时,Spring Cloud团队内部维护的组件也在积极的更新换代。
如果你的应用已经使用到了hadoop、kubernetes、docker,在Spring Cloud实施过程中可以考虑使用其关系户组件,避免搭建两套注册中心,节省资源。但是二者兼容使用说说容易,真正用起来还需要功夫。目前看,笔者觉得最佳选择应该是Nacos。
目前可选的分布式配置管理中心,有阿里的Nacos、携程的Apollo、和Spring Cloud Config。
服务网关这块就不多说了,没有任何悬念,Spring Cloud Gateway在各方面都碾压Zuul,Zuul2也基本上是胎死腹中。还有一些第三方厂商开发的微服务网关,但基本上没有形成气候!
2018年12月,Spring官方宣布Netflix的相关项目进入维护模式(Maintenance Mode)。不再开发新的功能,但是Hystrix整体上还是比较稳定的,对于老用户不必更换,影响也不大。
Hystrix停更之后,Netflix官方推荐使用resilience4j(https://github.com/resilience4j/resilience4j ),它是一个轻量、易用、可组装的高可用框架,支持熔断、高频控制、隔离、限流、限时、重试等多种高可用机制。
Sentinel(https://github.com/alibaba/Sentinel )是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。
https://github.com/alibaba/Sentinel/wiki/Guideline:-从-Hystrix-迁移到-Sentinel
在我之前的章节中,已经通过“开饭店”的例子,为大家说明了从单体应用到微服务的应用架构发展的过程。本节主要是为大家介绍一下单体应用和微服务的优缺点以及应用场景。
单体应用架构被认为是构建应用程序的传统架构方式。单体应用程序是作为一个不可分割的单元构建的。通常,这种解决方案包括客户端用户界面,服务器端应用程序和数据库。
单体架构(monolithic architecture)的优势:
单体架构的缺点:
单体架构应用程序是一个统一的整体,而微服务体系结构则将其分解为较小的独立单元的集合。
简而言之,微服务架构风格是一种将单个应用程序拆分为一组小服务的方法,每个小服务都在自己的进程中运行并使用轻量级机制(通常是HTTP资源API)进行通信。 ----马丁·福勒
在微服务架构中,功能被分解为可独立部署的模块,这些模块通过远程调用方法相互通信。每个服务都涵盖了自己的范围,每个服务可以独立更新,部署和扩展。
微服务架构的优势
微服务架构的缺点
选择单体架构
选择微服务架构
由于父项目dongbb-cloud不承担任何的业务代码逻辑,只做子模块的管理。所以创建好之后,可以将src目录删掉。
并在pom.xml中将packaging配置为pom。
<!-- 聚合工程,父工程打包方式为pom-->
<groupId>dhy.xpy</groupId>
<artifactId>StudyCloudWithMe</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
加入一些基础的属性配置
<!--基础属性设置-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
在pom中加入dependencyManagement的代码段。dependencyManagement并不会真的引入依赖包。dependencyManagement会帮助我们做版本管理。如果我们的子项目在引入依赖时,不指定版本号,会从父项目的dependencyManagement管理中查找版本号。
这样做的好处是:统一管理项目的类库版本,避免子模块之间的类库版本不同,导致的冲突及兼容性问题。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 聚合工程,父工程打包方式为pom-->
<groupId>dhy.xpy</groupId>
<artifactId>StudyCloudWithMe</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>dhy-service-sms</module>
<module>dhy-service-common</module>
<module>dhy-dao-service</module>
</modules>
<!--基础属性设置-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<!-- 统一版本管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>dhy.xpy</groupId>
<artifactId>dhy-service-common</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
dependencyManagement可以让子类继承的时候不需要写版本号,对于一些通用都需要继承的jar包,和插件可以在父项目中规定,避免子模块重复写
在前面的章节,我们已经说过要进行微服务的拆分。
其中需要新增一个服务是service-sms。
用于发送短信、邮件等的微服务,是新增的服务父项目目录右键->New->Module,使用Spring Assistant创建Spring Boot项目
在pom.xml中将spring boot子模块的父项目改为StudyCloudWithMe,如果IDEA帮我们完成了,这不就不用做了。
因为我们在父项目中使用了dependencyManagement管理版本,所以子模块中的dependency是不写version版本号的。如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dhy.xpy</groupId>
<artifactId>StudyCloudWithMe</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>dhy-service-sms</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dhy-service-sms</name>
<description>dhy-service-sms</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>dhy.xpy</groupId>
<artifactId>dhy-service-common</artifactId>
</dependency>
</dependencies>
</project>
为当前service-sms设置服务名和端口号
server:
port: 2333
spring:
application:
name: dhy-service-sms
通常情况下,使用Spring Boot搭建一个微服务,微服务模块构建完成之后,需要完成如下的一些初始化工作:
为了更方便进行Spring Boot微服务项目的构建,我们新建一个模块,该模块的作用就是初始化微服务构建过程中需要做的通用处理、通用工具类等等。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>StudyCloudWithMe</artifactId>
<groupId>dhy.xpy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dhy-service-common</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
该模块的文件如下:
AjaxResponse
import com.exception.CustomException;
import com.exception.CustomExceptionType;
import lombok.Data;
@Data
public class AjaxResponse {
private boolean isok; //请求是否处理成功
private int code; //请求响应状态码(200、400、500)
private String message; //请求结果描述信息
private Object data; //请求结果数据(通常用于查询操作)
private AjaxResponse(){}
//请求成功的响应,不带查询数据(用于删除、修改、新增接口)
public static AjaxResponse success(){
AjaxResponse ajaxResponse = new AjaxResponse();
ajaxResponse.setIsok(true);
ajaxResponse.setCode(200);
ajaxResponse.setMessage("请求响应成功!");
return ajaxResponse;
}
//请求成功的响应,带有查询数据(用于数据查询接口)
public static AjaxResponse success(Object obj){
AjaxResponse ajaxResponse = new AjaxResponse();
ajaxResponse.setIsok(true);
ajaxResponse.setCode(200);
ajaxResponse.setMessage("请求响应成功!");
ajaxResponse.setData(obj);
return ajaxResponse;
}
//请求成功的响应,带有查询数据(用于数据查询接口)
public static AjaxResponse success(Object obj,String message){
AjaxResponse ajaxResponse = new AjaxResponse();
ajaxResponse.setIsok(true);
ajaxResponse.setCode(200);
ajaxResponse.setMessage(message);
ajaxResponse.setData(obj);
return ajaxResponse;
}
//请求出现异常时的响应数据封装
public static AjaxResponse error(CustomException e) {
AjaxResponse resultBean = new AjaxResponse();
resultBean.setIsok(false);
resultBean.setCode(e.getCode());
resultBean.setMessage(e.getMessage());
return resultBean;
}
//请求出现异常时的响应数据封装
public static AjaxResponse error(CustomExceptionType customExceptionType,
String errorMessage) {
AjaxResponse resultBean = new AjaxResponse();
resultBean.setIsok(false);
resultBean.setCode(customExceptionType.getCode());
resultBean.setMessage(errorMessage);
return resultBean;
}
}
GlobalReponseAdvice
import com.msg.AjaxResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* <p>
* 全局返回值结果处理,加了泛型表示只处理特定的返回值类型
* </p>
*/
@Component
@Slf4j
public class GlobalReponseAdvice implements ResponseBodyAdvice {
/**
* <p>
* 这个方法表示对于哪些请求要执行beforeBodyWrite,返回true执行,返回false不执行
* </p>
* @param methodParameter 方法相关信息
* @param aClass
* @return
*/
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
log.info("aClass类型: "+aClass);
return true;
}
/**
* <p>
* 对于返回的对象如果不是最终对象AjaxResponse,则选包装一下
* </p>
* @param o 方法返回的镀锡
* @param methodParameter 方法参数
* @param mediaType
* @param aClass
* @param serverHttpRequest
* @param serverHttpResponse
* @return
*/
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if(!(o instanceof AjaxResponse))
{
return AjaxResponse.success(o);
}
return o;
}
}
GlobalRequestAdvice
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
import java.io.IOException;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
/**
* <p>
* RequestBodyAdvice仅对使用了@RqestBody注解的生效 , 因为它原理上还是AOP , 所以GET方法是不会操作的.
* </p>
*/
public class GlobalRequestAdvice extends RequestBodyAdviceAdapter {
/**
* <p>
* 只对日期进行格式化,对方法参数上加了@DataTimeFormatter注解的参数进行格式化处理
* 因为@DataTimeFormatter注解只对Date类型生效,对jdk8开始的LocalDateTime不生效,因此这里我们手动适配
* </p>
*/
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
Parameter parameter = methodParameter.getParameter();
//TODO:这里因为实体类不同,需要根据业务逻辑进行判断,然后进行处理
return false;
}
/**
* <p>
* 在读取参数前进行处理
* </p>
* */
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
}
}
CustomException
public class CustomException extends RuntimeException {
//异常错误编码
private int code ;
//异常信息
private String message;
private CustomException(){}
public CustomException(CustomExceptionType exceptionTypeEnum) {
this.code = exceptionTypeEnum.getCode();
this.message = exceptionTypeEnum.getDesc();
}
public CustomException(CustomExceptionType exceptionTypeEnum,
String message) {
this.code = exceptionTypeEnum.getCode();
this.message = message;
}
public int getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
CustomExceptionType
public enum CustomExceptionType
{
USER_INPUT_ERROR(400,"您输入的数据错误或您没有权限访问资源!"),
SYSTEM_ERROR (500,"系统出现异常,请您稍后再试或联系管理员!");
CustomExceptionType(int code, String desc) {
this.code = code;
this.desc = desc;
}
private String desc;//异常类型中文描述
private int code; //code
public String getDesc() {
return desc;
}
public int getCode() {
return code;
}
}
WebExceptionHandler
import com.msg.AjaxResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @author zdh
*/
@ControllerAdvice
public class WebExceptionHandler {
@ResponseStatus()
@ExceptionHandler(CustomException.class)
@ResponseBody
public AjaxResponse customerException(CustomException e) {
return AjaxResponse.error(e);
}
@ResponseStatus()
@ExceptionHandler(Exception.class)
@ResponseBody
public AjaxResponse exception(Exception e) {
return AjaxResponse.error(CustomExceptionType.SYSTEM_ERROR,e.getMessage());
}
}
先将通用模块安装到本地仓库中
父工程引入该通用服务模块依赖
<!-- 统一版本管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>dhy.xpy</groupId>
<artifactId>dhy-service-common</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
在各个微服务子模块中service-sms中引入(不带版本号)
<dependency>
<groupId>dhy.xpy</groupId>
<artifactId>dhy-service-common</artifactId>
</dependency>
在各个微服务子模块的启动类
@SpringBootApplication(scanBasePackages={"com.dhy.dhyservicesms"})
public class DhyServiceSmsApplication {
添加一个api,短信发送服务。所有程序内部异常转化为自定义异常CustomException向上抛出
import com.exception.CustomException;
import com.exception.CustomExceptionType;
import com.msg.AjaxResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/sms")
public class SmsController {
/**
* 模拟短信发送
* @param phoneNo
* @param content
* @return 短信发送结果
*/
@PostMapping(value = "/send")
public AjaxResponse send(@RequestParam String phoneNo,
@RequestParam String content) {
if(content.isEmpty() || phoneNo.isEmpty()){
throw new CustomException(CustomExceptionType.USER_INPUT_ERROR,
"消息内容或手机号不能为空!");
}
System.out.println("发送短消息:" + content);
return AjaxResponse.success("短消息发送成功!");
}
}
使用postman测试一下,正常的响应结果如下(AjaxResponse数据结构):
异常的响应结果如下:在GlobalExceptionHandler作用下
术语:持久层。就是进行应用数据存取的代码层,就是将数据持久化保存。可以是保存到文件、数据库等。通常对于web开发就是指针对数据库的操作被称为持久层。
为什么要将持久层的代码单独拆分出来?
通常来说,每一个微服务都单独对应一个数据库实例。但是不排除某些微服务业务发生了拆分,但数据之间的耦合度非常强,仍然存储在同一个数据库里面。这就可能产生一个问题:针对同一个数据库的同一个持久化方法,在不同的微服务里面定义了多次。
以,通常将持久层的代码单独抽离出来,形成一个模块。这样做的好处在于:
首先使用maven的方式,新建一个模块dhy-dao-service。
然后在父项目的pom中dependencyManagement添加版本号管理(我是用的是mybatis构建持久层)
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
在dhy-dao-service子模块中添加dependency引入jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>StudyCloudWithMe</artifactId>
<groupId>dhy.xpy</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dhy-dao-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>dhy.xpy</groupId>
<artifactId>dhy-service-common</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
持久层通用的业务逻辑操作,这里就不再重复书写了,大家依据自己的项目而进行制定
在父项目中设定统一版本号管理
<dependency>
<groupId>dhy.xpy</groupId>
<artifactId>dhy-dao-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
先将通用持久层模块安装到本地仓库
在dhy-service-sms微服务中引用该模块
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>dhy.xpy</groupId>
<artifactId>dhy-service-common</artifactId>
</dependency>
<dependency>
<groupId>dhy.xpy</groupId>
<artifactId>dhy-dao-service</artifactId>
</dependency>
</dependencies>
最后,为了让所有的mybatis mapper都能够被正确的注入Spring,加入mybatis组件的扫描路径。
@MapperScan(basePackages = {"com.mapper"})
public class Main {
当然,你还需要在application.yml中做数据库连接的配置。
到此,微服务入门就介绍完了,主要介绍了微服务的发展历史和基本微服务框架搭建的一个过程。
后面的系列就开始正式引入相关组件,敬请期待!