Springcloud入门父工程创建&订单模块&支付模块开发
开始学习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继承下去