開始學習springcloud,用到的主要是Spingboot+MabatisPlus + SpringdataJPA(這個是為了建表方便)。
1.父工程創建
1. 創建工程
創建maven骨架,類型選擇:maven-archetype-site,JDK版本選擇1.8。
2.設置文件編碼、注解生效以及過濾的文件
字符編碼設置:
注解允許:
JDK編譯選擇8:
選擇過濾文件:(過濾掉不必要的文件)
3. 父工程pom文件編寫
可以刪除src目錄,之后編寫pom.xml:
<?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> <groupId>cn.qz.cloud</groupId> <artifactId>cloud</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <!-- 統一管理jar包版本 --> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <junit.version>4.12</junit.version> <log4j.version>1.2.17</log4j.version> <lombok.version>1.16.18</lombok.version> <mybatispuls.spring.boot.version>2.3</mybatispuls.spring.boot.version> <jpa.version>2.3.1.RELEASE</jpa.version> <mysql.version>5.1.47</mysql.version> <druid.version>1.1.16</druid.version> </properties> <!-- 子模塊繼承之后,提供作用:鎖定版本+子modlue不用寫groupId和version --> <dependencyManagement> <dependencies> <!--spring boot 2.2.2--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.2.2.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <!--spring cloud Hoxton.SR1--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Hoxton.SR1</version> <type>pom</type> <scope>import</scope> </dependency> <!--spring cloud alibaba 2.1.0.RELEASE--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>${druid.version}</version> </dependency> <!-- spring-boot整合mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatispuls.spring.boot.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <!-- springdata jpa依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>${jpa.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <optional>true</optional> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <fork>true</fork> <addResources>true</addResources> </configuration> </plugin> </plugins> </build> </project>
這里需要注意:
(1)父工程的packagin是pom,用於父工程聚合。
(2)dependencyManagement和dependencies的區別:
dependencyManagement 通常用於項目最頂層的父pom文件。能讓所有子項目引用一個依賴而不用顯示的列出版本號。
maven子項目如果寫了版本號和scope會用自己的,如果沒寫,會沿着父子層向上走,直到找到擁有一個dependencyManagement元素的項目,然后使用此項目的version和scope。
這樣做的好處是版本便於管理,比如想要升級個jar包只需要在父類統一升級即可。
另外,dependencyManagement只是聲明依賴,不實現引入,因此子項目需要顯示的聲明所用的依賴。
2. 支付模塊構建
支付模塊使用8081端口。
1.新建moudle
選擇父工程之后新建moudle,GroupId和Version采用繼承的即可,如下:
(1) 新建完成之后查看父pom.xml文件會多了modules配置並引入新建的moudle。
(2)新的payment模塊的pom文件只有artifactId,沒有G和A,會采用父類的G和A。
2.修改pom.xml
<?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>cloud</artifactId> <groupId>cn.qz.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-provider-payment8081</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- spring-boot整合mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!--mysql-connector-java--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--jdbc--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!-- springdata jpa依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </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> </project>
3.修改application.yml
server: port: 8081 spring: application: name: cloud-payment-service datasource: type: com.alibaba.druid.pool.DruidDataSource # 當前數據源操作類型 driver-class-name: org.gjt.mm.mysql.Driver # mysql驅動包 url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 jpa: showSql: true hibernate.ddlAuto: update database-platform: org.hibernate.dialect.MySQL5InnoDBDialect mybatis-plus: mapperLocations: classpath:mapper/*.xml type-aliases-package: cn.qz.cloud.bean # 所有Entity別名類所在包
這里需要注意到mysql數據庫建立對應的database。
4. 編寫啟動類
package cn.qz.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @Author: qlq * @Description * @Date: 22:24 2020/9/24 */ @SpringBootApplication public class PaymentMain8081 { public static void main(String[] args) { SpringApplication.run(PaymentMain8081.class, args); } }
5.編寫業務代碼
(0)一個簡單的返回JSON的工具類
package cn.qz.cloud.utils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @AllArgsConstructor @NoArgsConstructor public class JSONResultUtil<T> implements Serializable { private static final long serialVersionUID = 3637122497350396679L; private boolean success; private String code; private String msg; private T data; public static <T> JSONResultUtil<T> success() { return new JSONResultUtil<>(true, "200", "", null); } public static <T> JSONResultUtil<T> successWithData(T data) { return new JSONResultUtil<>(true, "200", "", data); } public static <T> JSONResultUtil<T> error(String codeOrMsg) { return errorWithData(codeOrMsg, null); } public static <T> JSONResultUtil<T> errorWithMsg(String errorCode, String msg) { return new JSONResultUtil<T>(false, errorCode, msg, null); } public static <T> JSONResultUtil<T> errorWithData(String codeOrMsg, T data) { return new JSONResultUtil<T>(false, codeOrMsg, codeOrMsg, data); } }
(1)bean對象:
package cn.qz.cloud.bean; import com.baomidou.mybatisplus.annotations.TableField; import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.enums.IdType; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.Index; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import java.util.Date; /** * 所有實體類中相同的部分(自增ID類型的) * * @author Administrator */ @MappedSuperclass @Getter @Setter public abstract class AbstractSequenceEntity { private static final long serialVersionUID = -674790161451253326L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @TableId(type = IdType.AUTO) // 增加該注解,mybatis plus insert之后會給bean設上Id protected Long id; /** * 創建時間 */ @Index(name = "createtime") @TableField(update = "%s") // 增加該注解,MP修改的時候不會修改該值 protected Date createtime; public AbstractSequenceEntity() { this.createtime = new Date(); } }
payment類:
package cn.qz.cloud.bean; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.ToString; import javax.persistence.Entity; /** * @Author: qlq * @Description * @Date: 22:37 2020/9/24 */ @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Getter @Setter @Entity public class Payment extends AbstractSequenceEntity { private String serial; }
(2)mapper-繼承MP的BaseMapper
package cn.qz.cloud.mapper; import cn.qz.cloud.bean.Payment; import com.baomidou.mybatisplus.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; /** * @Author: qlq * @Description * @Date: 14:30 2020/9/25 */ @Mapper public interface PaymentMapper extends BaseMapper<Payment> { }
(3)service
接口繼承MP的IService:
package cn.qz.cloud.service; import cn.qz.cloud.bean.Payment; import com.baomidou.mybatisplus.service.IService; /** * @Author: qlq * @Description * @Date: 14:31 2020/9/25 */ public interface PaymentService extends IService<Payment>{ }
實現類繼承MP的serviceImpl
package cn.qz.cloud.service; import cn.qz.cloud.bean.Payment; import cn.qz.cloud.mapper.PaymentMapper; import com.baomidou.mybatisplus.service.impl.ServiceImpl; import org.springframework.stereotype.Service; /** * @Author: qlq * @Description * @Date: 14:31 2020/9/25 */ @Service public class PaymentServiceImpl extends ServiceImpl<PaymentMapper, Payment> implements PaymentService { }
(4)controller
抽象類:
package cn.qz.cloud.controller; import cn.qz.cloud.utils.JSONResultUtil; import com.baomidou.mybatisplus.service.IService; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import java.io.Serializable; import java.util.List; import java.util.Map; /** * @param <T> * @param <E> */ @RestController @Slf4j public abstract class AbstractController<T, E extends Serializable> { public abstract IService<T> getBaseService(); @PostMapping("/save") public JSONResultUtil<Object> save(@RequestBody T bean) { boolean insert = getBaseService().insert(bean); if (insert) { return JSONResultUtil.successWithData(bean); } return JSONResultUtil.error("添加失敗"); } @PostMapping("/delete/{id}") public JSONResultUtil<Object> delete(@PathVariable() E id) { boolean result = getBaseService().deleteById(id); if (result) { return JSONResultUtil.success(); } return JSONResultUtil.error("刪除失敗"); } @GetMapping("/listAll") public JSONResultUtil<List<Map<String, Object>>> listAll() { List<Map<String, Object>> datas = getBaseService().selectMaps(null); return JSONResultUtil.successWithData(datas); } }
PaymentController:
package cn.qz.cloud.controller; import cn.qz.cloud.bean.Payment; import cn.qz.cloud.service.PaymentService; import com.baomidou.mybatisplus.service.IService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author: qlq * @Description * @Date: 14:49 2020/9/25 */ @RestController @RequestMapping("/pay") public class PaymentController extends AbstractController<Payment, Long> { @Autowired private PaymentService service; @Override public IService<Payment> getBaseService() { return service; } }
6.啟動后用postman測試
(1)測試創建:
返回結果:
{ "success": true, "code": "200", "msg": "", "data": { "id": 2, "createtime": "2020-09-25T07:06:09.754+0000", "serial": "測試" } }
(2)測試查詢所有:
3. 訂單模塊完成
步驟同上面的支付模塊類似。使用80端口。主要使用RestTemplate調用支付模塊。
1.新建moudle
(1) 新建完成之后查看父pom.xml文件會多一個moudle。
(2)新的order模塊的pom文件只有artifactId,沒有G和A,會采用父類的G和A。
2.修改pom.xml
<?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>cloud</artifactId> <groupId>cn.qz.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-order80</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- spring-boot整合mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!--mysql-connector-java--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--jdbc--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!-- springdata jpa依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </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> </project>
3.修改application.yml
server:
port: 80
4. 編寫啟動類
package cn.qz.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 啟動類 */ @SpringBootApplication public class OrderConsumerMain80 { public static void main(String[] args) { SpringApplication.run(OrderConsulMain80.class, args); } }
5. 業務類
(0)JSON工具類
package cn.qz.cloud.utils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @AllArgsConstructor @NoArgsConstructor public class JSONResultUtil<T> implements Serializable { private static final long serialVersionUID = 3637122497350396679L; private boolean success; private String code; private String msg; private T data; public static <T> JSONResultUtil<T> success() { return new JSONResultUtil<>(true, "200", "", null); } public static <T> JSONResultUtil<T> successWithData(T data) { return new JSONResultUtil<>(true, "200", "", data); } public static <T> JSONResultUtil<T> error(String codeOrMsg) { return errorWithData(codeOrMsg, null); } public static <T> JSONResultUtil<T> errorWithMsg(String errorCode, String msg) { return new JSONResultUtil<T>(false, errorCode, msg, null); } public static <T> JSONResultUtil<T> errorWithData(String codeOrMsg, T data) { return new JSONResultUtil<T>(false, codeOrMsg, codeOrMsg, data); } }
(1)bean
package cn.qz.cloud.bean; import lombok.Getter; import lombok.Setter; import java.util.Date; @Getter @Setter public abstract class AbstractSequenceEntity { private static final long serialVersionUID = -674790161451253326L; protected Long id; protected Date createtime; public AbstractSequenceEntity() { this.createtime = new Date(); } }
Payment:
package cn.qz.cloud.bean; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.ToString; /** * @Author: qlq * @Description * @Date: 22:37 2020/9/24 */ @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Getter @Setter public class Payment extends AbstractSequenceEntity { private String serial; }
(2)配置類:注入RestTemplate
package cn.qz.cloud.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * 注入一個bean */ @Configuration public class ApplicationContextConfig { @Bean public RestTemplate getRestTemplate() { return new RestTemplate(); } }
(3)Controller類
package cn.qz.cloud.controller; import cn.qz.cloud.bean.Payment; import cn.qz.cloud.utils.JSONResultUtil; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.util.List; import java.util.Map; /** * @Author: qlq * @Description * @Date: 22:09 2020/9/25 */ @RestController @RequestMapping("/consumer") public class OrderController { private static final String PAYMENT_URL = "http://localhost:8081"; @Resource private RestTemplate restTemplate; @PostMapping("/pay/save") public JSONResultUtil<Payment> save(@RequestBody Payment payment) { return restTemplate.postForObject(PAYMENT_URL + "/pay/save", payment, JSONResultUtil.class); } @GetMapping("/pay/listAll") public JSONResultUtil<List<Map<String, Object>>> listAll() { return restTemplate.getForObject(PAYMENT_URL + "/pay/listAll", JSONResultUtil.class); } }
接下來測試即可。
4. 工程重構
發現上面兩個工程有許多重復的地方,因此決定抽取公共模塊。
1.新建工程
2. 修改pom.xml
抽取了公共pom,增加了工具類。
<?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>cloud</artifactId> <groupId>cn.qz.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-api-commons</artifactId> <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>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.1.0</version> </dependency> </dependencies> </project>
3.抽取代碼
主要是抽取bean和抽取utils到新工程中。
4. 發布工程
執行 mvn clean install發布到本地倉庫。
5.修改支付模塊和訂單模塊的pom,加入下面配置
<!--引入自己抽取的工具包--> <dependency> <groupId>cn.qz.cloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency>
測試即可。
補充:關於mapper層注解@Mapper、@Repository、@MapperScan的區別
@Mapper 是 Mybatis 的注解,和 Spring 沒有關系, @Mapper不需要配置掃描地址,通過xml里面的namespace里面的接口地址,生成了Bean后注入到Service層中。在 Spring 程序中,Mybatis 需要找到對應的 mapper,在編譯的時候動態生成代理類,實現數據庫查詢功能,所以我們需要在接口上添加 @Mapper 注解。
@Repository 是 Spring 的注解,用於聲明一個 Bean,需要在Spring中配置掃描地址,然后生成Dao層的Bean才能被注入到Service層中。
@MapperScan是配置掃描Mapper包的配置,可以在啟動類上添加該注解,自動掃描包路徑下的所有接口,使用這種方法接口上不用添加任何注解。
一個Mapper接口中,其注解滿足如下規則:@Mapper 一定要有,否則 Mybatis 找不到 mapper。@Repository 可有可無,可以消去依賴注入的報錯信息。@MapperScan 可以替代 @Mapper(配置了MapperScan不需要每個Mapper接口都寫@Mapper注解)。
補充:一個IDEA熱部署的工具
在settings/plugins搜索Jrebel。安裝之后以Jrebel啟動項目即可,改完某個類會熱加載。
補充:IDEA開啟RunDashboard
.idea文件夾下面workspace.xml搜索RunDashboard,之后加入:
<option name="configurationTypes"> <set> <option value="SpringBootApplicationConfigurationType" /> </set> </option>
最終的代碼如下:
<component name="RunDashboard"> <option name="ruleStates"> <list> <RuleState> <option name="name" value="ConfigurationTypeDashboardGroupingRule" /> </RuleState> <RuleState> <option name="name" value="StatusDashboardGroupingRule" /> </RuleState> </list> </option> <option name="configurationTypes"> <set> <option value="SpringBootApplicationConfigurationType" /> </set> </option> </component>
補充:springboot項目中配置server.port = 0 的時候會自動選取一個未被占用的端口啟動項目,在一個服務器啟動多個應用的時候非常有用。也可以server.port=-1;http的端口有范圍:1~65535,-1是訪問不了的,放開-1是為了:完全關閉HTTP端點,但仍創建一個WebApplicationContext.
補充:Maven中pom.xml中的properties中定義的版本信息也可以被子pom繼承下去