spring boot3 / spring cloud遇到的一系列问题记录(一) —— 努力成为优秀的架构师

in 学习笔记 with 0 comment 访问: 1,285 次

前言

之前做项目都是照葫芦画瓢,从来没系统性的学习过java、spring、springboot,现在下定决心从0开始,本文章只为记录个人遇到的一系列问题,并直接写出来以加深印象。

java部分和spring部分没记录,直接从spring boot开始到spring cloud结束。

知识点

自定义获取默认配置项的bean

如果标注@Configuration,则可通过Spring Boot的自动扫描机制自动加载,否则,使用@Import在启动入口引入手动加载

// application.yml配置文件
spring:
  data:
    redis:
      host: ${REDIS_HOST:localhost}
      port: ${REDIS_PORT:6379}
      password: ${REDIS_PASSWORD:}
      database: ${REDIS_DATABASE:0}
// bean文件夹
@Data
@Configuration
@ConfigurationProperties("spring.data.redis")
public class RedisConfigBean {
    private String host;
    private int port;
    private String password;
    private int database;
}
// 控制器使用 使用Bean来获取配置项,必须使用@Autowired自动注入
    @Autowired
    RedisConfigBean redisConfigBean;

    @Operation(summary = "Redis测试")
    @PostMapping(value = "/redis")
    public void list2() {
        System.out.println(redisConfigBean.getPort());
    }

使用@Bean注解来注册bean

跟上文中主要区别就是需要用@Bean进行声明,并且没有@Configuration注解

// 比如有个class,不能自己添加比如@Configuration等其他注解

public class SomeClass {
    private int a = 1;
    private int b = 2;

    public int getA() {
        return a*100;
    }

    public int getB() {
        return b*100;
    }
}
// 想让他变成一个bean注册到容器中,在config或者其他文件夹加一个挂载类
@Component
public class ThirdPartBean {
    @Bean
    public SomeClass someClass() {
        return new SomeClass();
    }
}

// 然后就可以在任何地方使用了,跟使用bean一样
@RestController
@RequestMapping("/third")
public class ThirdController {

    @Autowired
    SomeClass someClass;

    @GetMapping(value = "/getbean", produces = "application/json")
    public void getBean() {
        System.out.println(someClass.getA());
    }
}

常见注解的理解

在Spring2.5版本中,引入了更多的Spring类注解:@Component,@Service,@Controller。
@Component是一个通用的Spring容器管理的单例bean组件。而@Repository, @Service, @Controller就是针对不同的使用场景所采取的特定功能化的注解组件。

当你的一个类被@Component所注解,那么就意味着同样可以用@Repository, @Service, @Controller来替代它:

@Component最普通的组件,可以被注入到spring容器进行管理
@Repository作用于持久层,作为DAO对象(数据访问对象,Data Access Objects),可以直接对数据库进行操作
@Service作用于业务逻辑层,处理业务逻辑
@Controlle作用于表现层(spring-mvc的注解),前端请求的处理,转发,重定向

总结:@Service, @Controller, @Repository = {@Component + 一些特定的功能}。这个就意味着这些注解在部分功能上是一样的。都属于spring注解,注解后可以被spring框架所扫描并注入到spring容器来进行管理。

使用swagger生成接口文档

swagger3基于openApi3,大部分注解都跟老的不一样了,参考https://blog.csdn.net/qq_35425070/article/details/105347336

QQ截图20231013103431.png

<!-- 接口文档 swagger UI 本地访问 http://localhost:8080/swagger-ui/index.html-->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.1.0</version>
</dependency>
// 代码示例
@Operation(summary = "测试自动加载通过@Bean注入的bean")
@GetMapping(value = "/getbean", produces = "application/json")
public void getBean() {
    System.out.println(someClass.getA());
}

@Operation(summary = "swagger标题摘要:path参数以及url参数", description = "下方描述:访问示例http://localhost:8080/third/list1/12?sort=11212")
@GetMapping(value = "/list1/{id}", produces = "application/json")
public List list1(
        @PathVariable(value = "id", required = true) @Parameter(description = "path参数") int id,
        @RequestParam(value = "sort", required = false, defaultValue = "0") @Parameter(description = "url参数") int sort
) {
    List list = new ArrayList();
    list.add("A");
    list.add("B");
    list.add(32);
    list.add("32");
    list.add(id);
    list.add(sort);
    System.out.println(list);
    return list;
}

Maven 相关知识

    // # 模块管理
    // 在软件开发中,把一个大项目拆分成多个模块是降低软件复杂度的有效方法
    // 对于Maven来说,原来是一个大项目
    //
    //single-project
    //├── pom.xml
    //└── src
    //
    // 现在可以分拆成三个模块
    //
    //mutiple-project
    //├── module-a
    //│   ├── pom.xml
    //│   └── src
    //├── module-b
    //│   ├── pom.xml
    //│   └── src
    //└── module-c
    //    ├── pom.xml
    //    └── src

    // Maven可以有效的管理多个模块,我们只需要把每个模块当作一个独立的Maven项目,他们有各自独立的pom.xml
    // - 首先创建一个parent项目,把abc项目中重复的提取出来,他继承springframework,并定好各个包的版本号以及引入共同依赖包(<packaging>pom</packaging>)
    // - 之后,其他项目比如a,b,c就可以继承parent项目(<packaging>jar</packaging>)
    // - 最后,在编译的时候,还要在根目录创建一个pom.xml(也就是build pom)统一编译,其中规定了 <modules>,比如
    //    <modules>
    //        <module>parent</module>
    //        <module>module-a</module>
    //        <module>module-b</module>
    //        <module>module-c</module>
    //    </modules>
    //
    // 这样,在根目录执行 mvn clean package时,maven根据根目录的pom.xml找到包括parent在内的4个<module>,一次性编译

JDBC和连接池

jdbc:mysql://<hostname>:<port>/<db>?key1=value1&key2=value2
jdbc:mysql://localhost:3306/fmock?useSSL=false&characterEncoding=utf8
    // 1. 首先
    // 添加他的依赖
    // <dependency>
    //    <groupId>com.zaxxer</groupId>
    //    <artifactId>HikariCP</artifactId>
    //    <version>4.0.3</version>
    //    <type>bundle</type>
    // </dependency>

    // 2.接着
    // 获取数据库连接
    @SneakyThrows
    public static Connection getConnection() {
        String JDBC_URL = "jdbc:mysql://localhost:3306/fmock";
        String JDBC_USER = "root";
        String JDBC_PWD = "root";

        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(JDBC_URL);
        config.setUsername(JDBC_USER);
        config.setPassword(JDBC_PWD);

        config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒
        config.addDataSourceProperty("idleTimeout", "60000");      // 空闲超时:60秒
        config.addDataSourceProperty("maximumPoolSize", "10");     // 最大连接数:10

        // 注意创建DataSource也是一个非常昂贵的操作,所以通常DataSource实例总是作为一个全局变量存储,并贯穿整个应用程序的生命周期
        DataSource ds = new HikariDataSource(config);

        // 有了连接池以后,我们如何使用它呢?
        // 和前面的代码类似,只是获取Connection时,把DriverManage.getConnection()改为ds.getConnection()
        // 第一次调用ds.getConnection(),会迫使连接池内部先创建一个Connection,再返回给客户端使用。
        // 当我们调用conn.close()方法时(在try(resource){...}结束处),不是真正“关闭”连接,而是释放到连接池中,以便下次获取连接时能直接返回
        Connection conn = ds.getConnection();
        // Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PWD);

        return conn;
    }

    // 关闭数据库连接(释放到连接池)
    public static void closeConn(Connection conn) {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

自动生成getter和setter方法的包:lombok

// pom中添加如下依赖即可,版本号统一配置就行,在parent的dependencyManagement里有
// 注意:dependencyManagement中定义的只是依赖的声明,并不实现引入,因此子项目需要显式的声明需要用的依赖
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

// 添加完后,在类上添加 `@Data` 注解即可

关于pom配置文件抽离parent,官方spring-boot就是这么做的

参考https://blog.csdn.net/nanhuaibeian/article/details/117828768

如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号。
当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要逐个修改子项目;另外如果某个子项目需要另外的一个版本,只需要声明version即可。

想使用redis:

参考https://springdoc.cn/spring-boot-data-redis/

首先,redis/jedis是redis官方推出的面向JAVA的客户端;
其次,Spring-data-redis是spring大家族的一部分,包含了jedis依赖,同时还有很多其他包,有连接池自动管理功能,提供高度封装的RedisTemplate类,对jedis大量api进行归类封装,并提供很多便捷化方法;
然后,spring-boot-starter-data-redis是spring-boot的包,其中就包含了spring-data-redis依赖和lettuce等;
最后Redisson,Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。 (在spring-boot项目中,可以直接用专门的包redisson-spring-boot-starter,直接帮你了很多autoconfig的事,它里面就有spring-boot-starter-data-redis,所以可以用RedisTemplate类,清晰了吧。 当然,你也可以直接用Redisson,然后自己进行配置,new对象使用。就好比laravel的一些包,可以直接用"ORM"包,也可以用"Larave-ORM"的专属包)

// 引入依赖
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.4</version>
</dependency>
// 默认只有`RedisTemplate<Object, Object>`  和  `RedisTemplate<String, String>`, 所以需要
// 配置一个`RedisTemplate<String, Object>`类型的Bean,并初始序列化方式,否则存入的redis会有乱码
/**
 * 使redis保存的数据支持<string,object>,并保存的数据为json字符串
 *
 * @Author zhenhuaixiu
 * @Date 2023/11/6 10:51
 * @Version 1.0
 */
@Configuration
public class RedisTemplateConfig extends RedisTemplate<String, Object> {

    public RedisTemplateConfig(RedisConnectionFactory redisConnectionFactory) {

        // 构造函数注入 RedisConnectionFactory,设置到父类
        super.setConnectionFactory(redisConnectionFactory);

        // 使用 Jackson 提供的通用 Serializer
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
        serializer.configure(mapper -> {
            // 如果涉及到对 java.time 类型的序列化,反序列化那么需要注册 JavaTimeModule
            mapper.registerModule(new JavaTimeModule());
        });

        // String 类型的 key/value 序列化
        super.setKeySerializer(StringRedisSerializer.UTF_8);
        super.setValueSerializer(serializer);

        // Hash 类型的 key/value 序列化
        super.setHashKeySerializer(StringRedisSerializer.UTF_8);
        super.setHashValueSerializer(serializer);
    }
}
// 使用
@RestController()
@RequestMapping(value = "four")
public class FourController {

    // Java变量的初始化顺序为:静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造方法 –> @Autowired


    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    // 推荐使用基于set的自动注入,可以在构造函数执行之后才自动注入

    private RedisService redisService;

    @Autowired
    private void init(RedisService redisService) {
        this.redisService = redisService;
    }

    @Operation(summary = "query返回多条或者0条都可以")
    @GetMapping(value = "db8/{id}", produces = "application/json")
    public List<User> getUserListQuery(
            @PathVariable(value = "id") @Parameter(description = "查询大于ID的列表") long id
    ) {
        List<User> list = dbService.find3(id);
        System.out.println("第二次访问这个接口,这句话不会输出,证明走了缓存");

        this.redisService.set("aaaV", 23234234);
        this.redisTemplate.opsForValue().set("aaaV",list, 60L, TimeUnit.SECONDS);


        return list;
    }
}

使用数据库mysql spring-boot-starter-jdbc

1.首先mysql-connector-j的前身是mysql-connector-java,是MySQL提供的JDBC驱动包drive,实现了java.sql.Driver的规范,换数据库直接换drive即可。
2.spring-boot-starter-jdbcspring-boot-starter-data-jdbc都是springboot提供的,前者是基础包,后者是升级版(是data系列的包,同样的还有data-redis,data-elasticsearch等)。
3.mybatis-spring-boot-starter包含spring-boot-starter-jdbc,不再需要单独引入spring-boot-starter-jdbc了。
4.mybatis是个第三方包,任何框架都能使用,mybatis-spring-boot-starter是针对spring-boot专门配置的一个包,包含了starter自动装载等功能。
5.Spring Boot作为Spring的集大成者,spring-jdbcspring-data-jdbc 就是spring-boot-starter-jdbcspring-boot-starter-data-jdbc在spring里的东西。

// 先引入依赖包
<!--  数据库相关 mysql-connector-j是驱动包,使用mysql必须装。driver-class-name中可以指定  -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<!--  这个包里就引入了spring-jdbc, 使用JdbcTemplate  -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
// application.yml添加数据库配置
spring:
  application:
    name: ${APP_NAME:我的测试JAVA项目}
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/fmock
    username: root
    password: root

// 已经能使用了,随便写个组件,
// 增删改都是jdbcTemplate.update方法,只有查询方法不一样,有query,queryForObject,queryForList等
@Component
public class DBService {
    @Autowired
    JdbcTemplate jdbcTemplate;

    // 在JdbcTemplate中,除了查询有几个API之外,新增、删除与修改统一都使用update来操作,传入SQL即可.
    // update方法的返回值就是SQL执行受影响的行数.

    // 插入单条
    public int addUser(User user) {
        String sql = "insert into users(uuid, email, mobile, name, password) value (?, ?, ?, ?, ?)";

        // return jdbcTemplate.update(sql);
        return jdbcTemplate.update(sql, user.getUuid(), user.getEmail(), user.getMobile(), user.getName(), user.getPassword());
    }

    // 批量插入
    public int[] batchAddUser(User user) {
        String sql = "insert into users(uuid, email, mobile, name, password) value (?, ?, ?, ?, ?)";

        List<Object[]> batchValue = new ArrayList<Object[]>();
        batchValue.add(new Object[]{"uuid-tets", "8888@qq.com", "12111111111", "测试jdbctemplate", "xxxxxxxx"});
        batchValue.add(new Object[]{user.getUuid(), user.getEmail(), user.getMobile(), user.getName(), user.getPassword()});

        return jdbcTemplate.batchUpdate(sql,batchValue);
    }

    // 更新数据
    public int updateUser(User user) {
        String sql = "update users set name = ?, email = ? where id = ?";
        return jdbcTemplate.update(sql, "修改后的名字", user.getEmail(), user.getId());
    }

    // 删除
    public int delUser(long id){
        String sql = "delete from users where id = ?";
        return jdbcTemplate.update(sql, id);
    }

    // 查询,查询就不使用update方法了
    // queryForObject查询的结果不能空,且只能1条数据:nullableSingleResult
    // 使用new BeanPropertyRowMapper<>(User.class)对返回的数据进行封装,它通过名称匹配的方式,自动将数据列映射到指定类的实体类中(如果列名和属性名不同,就需要开发者自己实现 RowMapper 接口)
    public User find(long id) {
        String sql = "select * from users where id = ?";
        RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
        return jdbcTemplate.queryForObject(sql, rowMapper, id);
    }

    // queryForList返回多条或者0条都可以
    public List<Map<String, Object>> find2(long id) {
        String sql = "select * from users where id > ?";
        return jdbcTemplate.queryForList(sql, id);
    }
    // 直接使用query方法查询,能自定义映射类型rowMapper
    public List<User> find3(long id) {
        String sql = "select * from users where id > ?";
        RowMapper<User> rowMapper = new BeanPropertyRowMapper<>(User.class);
        return jdbcTemplate.query(sql, rowMapper, id);
    }
}

// 控制器调用测试
@RestController()
@RequestMapping(value = "four")
public class FourController {

    // Java变量的初始化顺序为:静态变量或静态语句块 –> 实例变量或初始化语句块 –> 构造方法 –> @Autowired

    private RedisService redisService;

    private DBService dbService;

    /* 构造方法注入
    public FourController(RedisService redisService) {
        this.redisService = redisService;
    }
    */

    // 推荐使用基于set的自动注入,可以在构造函数执行之后才自动注入
    @Autowired
    private void init(RedisService redisService, DBService dbService) {
        this.redisService = redisService;
        this.dbService = dbService;
    }

    @GetMapping(value = "redis1")
    public void redis1() {
        System.out.println(redisService.get("key111"));
        System.out.println(1221);
    }

    @GetMapping(value = "db2")
    public int addUser() {
        User user = new User();
        user.setMobile("13333333333");
        user.setEmail("13333333333@qq.com");
        user.setName("name-db2");
        user.setPassword("xxxx");
        user.setUuid("uuid-xxxx");
        return dbService.addUser(user);
    }

    @GetMapping(value = "db3")
    public int[] batchAddUser() {
        User user = new User();
        user.setMobile("13333333333");
        user.setEmail("13333333333@qq.com");
        user.setName("name-db2");
        user.setPassword("xxxx");
        user.setUuid("uuid-xxxx");
        return dbService.batchAddUser(user);
    }

    @GetMapping(value = "db4")
    public int updateUserEmail() {
        User user = new User();
        user.setEmail("fffffff@qq.com");
        user.setId(37);
        return dbService.updateUser(user);
    }

    @Operation(summary = "根据ID删除某个用户")
    @GetMapping(value = "db5/{id}", produces = "application/json")
    public int delUserById(
            @PathVariable(value = "id") @Parameter(description = "待删除的用户ID") long id
    ) {
        return dbService.delUser(id);
    }

    @Operation(summary = "使用queryForObject查询单个用户")
    @GetMapping(value = "db6/{id}", produces = "application/json")
    public User findUserById(
            @PathVariable(value = "id") @Parameter(description = "查询ID") long id
    ) {
        return dbService.find(id);
    }

    @Operation(summary = "queryForList返回多条或者0条都可以")
    @GetMapping(value = "db7/{id}", produces = "application/json")
    public List<Map<String, Object>> getUserList(
            @PathVariable(value = "id") @Parameter(description = "查询大于ID的列表") long id
    ) {
        List<Map<String, Object>> list = dbService.find2(id);
        return list;
    }

    @Operation(summary = "query返回多条或者0条都可以")
    @GetMapping(value = "db8/{id}", produces = "application/json")
    public List<User> getUserListQuery(
            @PathVariable(value = "id") @Parameter(description = "查询大于ID的列表") long id
    ) {
        List<User> list = dbService.find3(id);
        return list;
    }
}

JPA : Java Persistence API

JPA就是JavaEE的一个ORM标准,他只规定了接口,所以还需要选择一个实现产品,跟JDBC接口和Mysql驱动一个意思。
HibernateMyBatis都是优秀的第三方ORM框架,我们即可以直接使用JPA也就是jakarta.persistence这个“标准”包,也可以用第三方包如MyBatis等。

MyBatis & MyBatis-plus:ORM框架

对比Spring提供的JdbcTemplate和ORM框架,有如下差别:

  1. 查询后需要手动提供mapper实例,以便把ResultSet的每一行变成Java对象(自定义映射类型rowMapper)。
  2. 增删改操作,所有需要的参数要手动传入,比如传参user.id/user.name等这样。
  3. 但是JdbcTemplate的优势是确定性,即每次读取操作一定是数据库操作而不是缓存,确定是代码比较繁琐。

介于全自动ORM如Hibernate,和手写全部SQL的比如JdbcTemplate之间,还有一种半自动的ORM,它只负责把ResultSet自动映射到JavaBean,或者自动填充JavaBean参数,但仍需要自己写Sql, MyBatis就是这种半自动化ORM框架。

首先需要引入mybatis-spring-boot-starter包,这个包里有mybatis-springmybatis
还有spring-boot-starter-jdbc,以及自动配置的一些starter、autoconfigure包。

// 首先,spring-boot项目中使用的话,需要安装 `MyBatis-Spring-Boot-Starter` 依赖,之后就可以使用`@Mapper`注解(默认搜寻带有 @Mapper 注解的 mapper 接口)
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>
// 创建一个mapper比如/mapper/UserMapper.java,注意mapper必须是接口
// `@Select`中即可以写sql,支持使用`#{}`占位符参数,多个参数时可以用`@Param`进行标记匹配,示例如下:
// `@Select("SELECT * FROM users LIMIT #{offset}, #{maxResults}")`
// `List<User> getAll(@Param("offset") int offset, @Param("maxResults") int maxResults);`
package com.example.springbootstudy.mapper;

import com.example.springbootstudy.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;

@Mapper
public interface UserMapper {
    @Select(value = "select count(id) from users")
    int getAllCount();

    @Select("select name from users where id = #{id}")
    String getUserName(long id);

    // mybatis会根据方法的返回类型自动把ResultSet的每一行转换为User实例,不需要像jdbc那样使用RowMapper手动映射
    // 如果列名和属性名不同,可以通过select语句的别名`as`满足匹配
    @Select("select * from users where id = #{user_id}")
    User getUserInfo(@Param("user_id") long id);

    @Select("select * from users where id > #{id}")
    List<User> getUserList(@Param("id") long id);

    // 使用@Options注解,可以获取插入后的id,通过user对象
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    @Insert("insert into users(uuid, email, mobile, password, name) values(#{userObj.uuid}, #{userObj.email}, #{userObj.mobile}, #{userObj.password}, #{userObj.name})")
    void addUser(@Param("userObj") User user);

    // 更新
    @Update("UPDATE users SET name = #{user.name}, createdAt = #{user.createdAt} WHERE id = #{user.id}")
    void update(@Param("user") User user);
    // 删除
    @Delete("DELETE FROM users WHERE id = #{id}")
    void deleteById(@Param("id") long id);
}
// 创建一个控制器,依赖注入mapper,即可简单调用mybatis
@RestController
@RequestMapping(value = "/five", name = "测试mybatis的控制器")
public class FiveController {

    private final UserMapper userMapper;
    
    // 将mapper依赖注入
    public FiveController(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @GetMapping(value = "mb1", name = "测试方法mb1")
    public int mb1() {
        System.out.println("mb1");
        return userMapper.getAllCount();
    }

    @Operation(summary = "获取用户名字")
    @GetMapping(value = "mb2")
    public String mb2(
            @RequestParam(value = "id") @Parameter(description = "url参数") long id
    ) {
        return userMapper.getUserName(id);
    }

    @Operation(summary = "获取用户信息")
    @GetMapping(value = "mb3/{id}")
    public User mb3(
            @PathVariable(value = "id") @Parameter(description = "path参数") long id
    ) {
        return userMapper.getUserInfo(id);
    }

    @Operation(summary = "获取用户列表")
    @GetMapping(value = "mb4/{id}")
    public List<User> mb4(
            @PathVariable(value = "id") @Parameter(description = "path参数") long id
    ) {
        return userMapper.getUserList(id);
    }

    @Operation(summary = "新增用户,并返回ID")
    @GetMapping(value = "mb5")
    public int mb5() {
        User user = new User();
        user.setEmail("litblc@xxx.com");
        user.setUuid("litblc-uuid");
        user.setMobile("13111111111");
        user.setPassword("xxx");
        user.setName("大修哥");

        userMapper.addUser(user);

        // 获取刚刚插入的user自增id
        return user.getId();
    }
}

参考 https://www.liaoxuefeng.com/wiki/1252599548343744/1331313418174498

使用xml方式使用mybatis

在application.yml中添加mapper-locations配置,指定待解析的mapper的xml文件

mybatis:
  # 待解析的mapper的xml文件
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    default-fetch-size: 100
    default-statement-timeout: 30

在同级文件夹(也就是resources)下创建mapper文件夹,可以用别的名称,与上面配置的路径名称对应上即可。
创建一个UserMapper.xml文件(注意的是<!DOCTYPE mapper标签必须有,而且指定是mapper):

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.springbootstudy.mapper.UserMapper">
    <select id="findByUUID" resultType="com.example.springbootstudy.dao.UserInfoDao">
        SELECT * FROM users WHERE uuid=#{uuid}
    </select>
    <select id="getAll" resultType="com.example.springbootstudy.dao.UserInfoDao">
        SELECT * FROM users order by id desc
    </select>
</mapper>

这时,你的UserMapper.java中定义这个findByUUID接口,就不需要使用注解定义sql了:

@Mapper
public interface UserMapper {
    @Select(value = "select count(id) from users")
    int getAllCount();

    @Select("select name from users where id = #{id}")
    String getUserName(long id);

    // ...

    // 测试使用xml中配置sql语句
    UserInfoDao findByUUID(@Param("uuid") String uuid);

    // 返回map的话必须指定map的key用哪个字段
    // List<UserInfoDao> getAll();
    @MapKey("id")
    Map<Integer, UserInfoDao> getAll();
}

使用上是一样的:

@Operation(summary = "测试使用mybatis的xml方式")
@GetMapping(value = "mb6/{id}")
public UserInfoDao mb6(
        @PathVariable(value = "id") @Parameter(description = "path参数") String uuid
) {
    System.out.println(uuid);
    return userMapper.findByUUID(String.valueOf(uuid));
}

@Operation(summary = "测试使用mybatis的xml方式-返回list/map")
@GetMapping(value = "mb7")
public Map<Integer, UserInfoDao> mb7() {
    return userMapper.getAll();
}

总结:MyBatis完全手写,每增加一个查询都需要先编写SQL并增加接口方法。

参考 https://blog.csdn.net/m0_37989980/article/details/105588234

mybatis-plus:更方便的使用mybatis

参考 https://baomidou.com/

mybatis-plus具有无侵入、损耗小、只做增强不做改变、强大的 CRUD 操作、丰富的查询构造器等特点,在国内十分火爆。

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.2</version>
</dependency>
package com.example.springbootstudy.entity;

//import jakarta.persistence.Table;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

// `@TableName`是mp的注解,主要是实现实体类型和数据库中的表实现映射。
// 不要将`@TableName`和`@Table`注解认为是一个,虽然功能相同,但是,`@TableName`是`mybatis-plus`中的注解, `@Table`是`Hibernate`中的注解(JPA)

@Data
@TableName(value = "posts")
public class Poster {
    private Long id;
    private String uuid;
    private Long user_id;
    private String title;
    private String summary;
    private String poster;
    private String content;
}
package com.example.springbootstudy.mapper;

import com.example.springbootstudy.entity.Poster;
import org.apache.ibatis.annotations.Mapper;

// `@Mapper`注解可以在每个mapper上定义,也可以在`Spring Boot`启动类中添加 `@MapperScan`直接扫描整个文件夹
@Mapper
public interface PostsMapper extends IBaseMapper<Poster> {
    // 添加复杂的操作数据库方法,就可以在xml中写,跟mybatis用法一样
}

上面继承的IBaseMapper是自定义的,继承自mybatis-plus的BaseMapper,提供了一些封装好的通用的curd操作,通过源码可以看到。

package com.example.springbootstudy.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

// mapper父类,注意这个类不要让 mp 扫描到!!
public interface IBaseMapper<T> extends BaseMapper<T> {
    // 这里可以放一些自定义的公共的方法
}
// 使用@RestController替代@Controller后,每个方法自动变成API接口方法。直接返回json。
// @Controller
@RestController
@RequestMapping("/six")
public class SixController {

    private final PostsMapper postsMapper;

    public SixController(PostsMapper postsMapper) {
        this.postsMapper = postsMapper;
    }

    @GetMapping(value = "f3")
    public List<Poster> func3() {
        List<Poster> list = postsMapper.selectList(null);
        return list;
    }
}

插入一条数据自增ID, 并学习使用lombok的@Accessors(chain = true)注解

@Data
// 链式访问,该注解设置chain=true,生成setter方法返回this(也就是返回的是对象),代替了默认的返回void
@Accessors(chain = true)
@TableName(value = "posts")
public class Poster {
    // 指定自增策略,这样insert的时候id才是自增的
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    private String uuid;

    // 比如没开启自动转驼峰的配置项map-underscore-to-camel-case:false,或者名字不一样可以自行指定
    @TableField(value = "user_id")
    private Long userId;

    private String title;
    private String summary;
    private String poster;
    private String content;

    // 自动维护创建、更新时间
    // 如何返回创建后的实体
}

添加mybatis-plus的配置,配置带解析的mapper的xml文件地址,方便进行自定义复杂sql查询

mybatis:
  # 待解析的mapper的xml文件
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true
    default-fetch-size: 100
    default-statement-timeout: 30
    ...

mybatis-plus:
  # 待解析的mapper的xml文件,跟上面一样意思
  mapper-locations: classpath:/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: false

在PostsMapper中添加一个自定义sql的方法,并在PostsMapper.xml中补全

@Mapper
public interface PostsMapper extends IBaseMapper<Poster> {
    // 添加复杂的操作数据库方法,就可以在xml中写,跟mybatis用法一样
    List<UserInfoDao> getAll();
}

新建一个xml,主要注意namespace别写错喽,不然找不到

<mapper namespace="com.example.springbootstudy.mapper.PostsMapper">
    <select id="getAll" resultType="com.example.springbootstudy.dao.UserInfoDao">
        SELECT * FROM users order by id desc
    </select>
</mapper>

控制器调用示例

    @Operation(summary = "新增文章")
    @GetMapping(value = "f4")
    public Poster func4() {
        // 在entity上定义了`@Accessors(chain = true)`,就可以直接链式调用所有方法,因为setter方法返回当前对象了
        Poster poster = new Poster().setPoster("https://xxx/xxx.jpg");
        poster.setUuid("xxx-uuid-11");
        poster.setTitle("测试").setSummary("摘要").setUserId(10L).setContent("{\"a\":32}");

        postsMapper.insert(poster);  // 返回插入数量
        return poster;
    }

    @Operation(summary = "测试xml方式的sql查询")
    @GetMapping(value = "f6")
    public List<UserInfoDao> func6() {
        return this.postsMapper.getAll();
    }

当数据库存储json时候,建议字段仍然用textvarchar等字符串格式,entity对应也用String,保存入库的时候正常按照字符串类型操作;
查询的时候想返回JSON格式的话,可以再写个dto, 把字段改成JSONObject格式,然后手动赋值进去(JSONObject是使用com.alibabafastjson依赖)。

如果数据库字段格式是json,那entity也必须是json格式的了,比如:

        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.41</version>
        </dependency>
@Data
@Accessors(chain = true)
@TableName(value = "reports", autoResultMap = true)
public class Report {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private Long resourceId;
    private String reason;
    private String type;

    // json字段要使用`com.alibaba.fastjson.JSONObject`的这个类型,可以不定义json具体字段格式,并且不会报错没序列化错误
    // 同时要设置`typeHandler`,这样获取的时候就是json,只不过存的时候需要同样生成该类型json入库
    @TableField(value = "json_info", typeHandler = JacksonTypeHandler.class)
    private JSONObject jsonInfo;
@Mapper
public interface ReportMapper extends IBaseMapper<Report>{

}
package com.example.springbootstudy.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.springbootstudy.entity.Report;
import com.example.springbootstudy.mapper.ReportMapper;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/seven")
public class SevenController {
    private final ReportMapper reportMapper;

    public SevenController (ReportMapper reportMapper) {
        this.reportMapper = reportMapper;
    }

    @GetMapping(value = "/s1")
    public Report s1() {
        return this.reportMapper.selectById(1);
    }

    @Operation(summary = "测试json字段或者json格式的字符串读写问题")
    @GetMapping(value = "/s2")
    public Report s2() {
        
        // 使用同样的json格式化类型
        JSONObject jsonInfo = JSON.parseObject("{\"a\":3222}");

        Report report = new Report().setUserId(2L)
                .setResourceId(23L).setType("post")
                .setJsonInfo(jsonInfo)
                .setReason(String.valueOf(jsonInfo));
        this.reportMapper.insert(report);
        return report;
    }
}

首先在entity字段中添加@TableField(fill = FieldFill.INSERT)

    // 创建时自动填充
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createdAt;

    // 创建与修改时自动填充
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updatedAt;

添加了fill注解并不会字段维护,而是需要写个handle处理,比如写个组件类专门设置/component/MybatisPlusAutoFill.java

package com.example.springbootstudy.component;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.example.springbootstudy.constant.MybatisPlusAutoFillProperties;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

/**
 * 自定义实现类
 * @Author zhenhuaixiu
 * @Date 2023/10/16 10:55
 * @Version 1.0
 */

@Component
public class MybatisPlusAutoFill implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        // String format = "yyyy-MM-dd HH:mm:ss";
        // String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern(format));

        this.strictInsertFill(metaObject, MybatisPlusAutoFillProperties.CREATED_AT, LocalDateTime::now, LocalDateTime.class);
        this.strictInsertFill(metaObject, MybatisPlusAutoFillProperties.UPDATED_AT, LocalDateTime.class, LocalDateTime.now());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        System.out.println("updateFill");
        this.strictUpdateFill(metaObject, MybatisPlusAutoFillProperties.UPDATED_AT, LocalDateTime::now, LocalDateTime.class);
    }
}

上面的自动填充类调用了MybatisPlusAutoFillProperties,是我们写的所有需要自动更新的字段常量类,比如/constant/MybatisPlusAutoFillProperties.java

package com.example.springbootstudy.constant;

public class MybatisPlusAutoFillProperties {
    // 字段要与entity对应上,咱们开启了自动转驼峰,所以是驼峰形势的
    public static final String CREATED_AT = "createdAt";
    public static final String UPDATED_AT = "updatedAt";
}

这样在我们再次调用insert或者update的时候,就能看到更新了。

mybatis-plus就写到这,其他的去官网看,特别详细 https://baomidou.com/

项目代码分层解读

参考 https://www.jianshu.com/p/18c4418e9b99

  1. Entity: 实体层,一个表对应一个,相当于php的model
  2. Dao:持久层,用来封装操作数据的方法,相当于php的repositories层,会调用entity。(使用mybatis的mapper层其实就相当于Dao层)
  3. Service:业务层,与php一致,调用dao层接口,接收dao层返回的数据,完成项目的基本功能设计
  4. Controller:控制层,与php一致,前后端交互,接受前端请求,调用service层并接收返回的数据
  5. 总体流程:Controller-->service接口-->serviceImpl-->dao接口-->daoImpl-->mapper-->db

Dao VS Mapper
Dao通常被认为是传统的Java数据访问层,它将数据访问的逻辑封装在一组DAO接口和实现类中。这些接口和实现类主要用于将Java对象映射到数据库表,并执行一些数据操作,例如插入、更新、删除和查询。DAO通常使用JDBC和SQL语句来实现数据操作。在MyBatis中,DAO可以使用MyBatis的SqlSession和SqlSessionFactory来管理数据库连接和事务,并且可以使用MyBatis的动态SQL功能执行高度灵活的查询。

Mapper是MyBatis中的另一种数据访问层实现方式,它基于XML或注解的方式来描述SQL语句和参数映射,提供了更灵活、更简洁的数据访问方式。Mapper使用XML或注解来描述SQL语句和参数映射,并将它们映射到Java方法上。在执行数据操作时,Mapper会将Java方法转换为对应的SQL语句,并使用SqlSession执行该SQL语句。相对于Dao,Mapper更加灵活,并且在编写SQL语句时提供了更多的可读性和可维护性。

在实际开发中,选择使用Dao还是Mapper取决于具体的需求和个人偏好。如果您更喜欢面向接口的编程模型,并且需要使用Java代码来定义和执行数据操作,那么Dao可能是一个更好的选择。如果您需要更灵活、更简洁的方式来描述SQL语句,并且不介意使用XML或注解来描述它们,那么Mapper可能更适合您的需求。

这里要注意区分Dto,Dto是数据传输对象,类似response返回类,用来封装返回对象格式

Bean的生命周期理解

https://zhuanlan.zhihu.com/p/266126121

主要理解容器的创建、bean的分析注册、依赖注入等过程。

面向切面编程AOP: spring-boot-starter-aop的使用

https://blog.csdn.net/sco5282/article/details/122075217

java项目在服务器上的部署

https://blog.csdn.net/munangs/article/details/124345341

最后一节分析源码

先看这个 https://zhuanlan.zhihu.com/p/362984115
再看这个承上启下、温故知新 https://blog.csdn.net/cuiqwei/article/details/118329609

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

}

可以看到,除了前面四个基本注解,主要看后三个@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

可以看到,主要就是@Configuration注解,任何标注了@SpringBootConfiguration@Configuration的类都是一个 JavaConfig, 在上面学习@Bean的时候,我们知道了:

任何标注了 Bean 的方法都被定义为一个 Bean,我们可以在任何 Spring 的 IoC 容器中注入进去
如果标注@Configuration,则可通过Spring Boot的自动扫描机制自动加载,否则,使用@Import在启动入口引入手动加载
添加两个注解@Configuration 和 @ConfigurationProperties,就能自动对应配置文件的数据

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};

注解@EnableAutoConfiguration非常重要,它的作用是自动将 JavaConfig 中的 Bean 装载到 IoC 容器中

启用Spring应用程序上下文的自动配置,尝试猜测和配置您可能需要的bean

这个注解的作用是自动扫描并加载符合条件的组件(如:Component、Bean 等),我们可以通过 basePakcages 来指定其扫描的范围,如果不指定,则默认从标注了 @ComponentScan 注解的类所在包开始扫描。

因此,Spring Boot 的启动类最好放在root package下,这样不指定basePackages参数,能保证扫描到所有包。

总结
一个 @SpringBootApplication 注解就可以用来启用这三个功能:
@EnableAutoConfiguration:启用Spring Boot的自动配置机制;
@ComponentScan:对应用程序所在的包启用 @Component 扫描;
@SpringBootConfiguration:允许在Context中注册额外的Bean或导入额外的配置类。这是Spring标准的 @Configuration 的替代方案,有助于在你的集成测试中检测配置。

Spring Cloud

概念理解

  1. Spring:是JavaEE的一个轻量级开发框架,主营IoC和AOP,集成JDBC、ORM、MVC等功能便于开发
  2. Spring Boot:是基于Spring,提供开箱即用的积木式组件,目的是提升开发效率
  3. Spring Cloud:分布式应用程序,为了让分布式应用程序编写更方便,更容易而提供的一组基础设施,它的核心是Spring框架,利用Spring Boot的自动配置,力图实现最简化的分布式应用程序开发。
  4. 总结:Spring Cloud不重复造轮子,将市面上开发的好的包进行集成,提供了构建分布式系统所需的“全家桶”。

初始化创建一个spring cloud微服务项目

完整源码参考 https://github.com/ShyZhen/scd

文件-新建-项目-Maven-下一步

<packaging>pom</packaging>的理解

scd1.png

scd2.png

scd3.png

顶级目录 scd/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>com.litblc</groupId>
    <artifactId>scd</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>

    <!--  顶级pom文件,只做modules管理,不做其他  -->
    <!--  也不继承parent,其他所有modules都集成parent,方便单独拆成一个项目  -->
    <name>spring-cloud-demo</name>
    <description>spring-cloud-demo</description>

    <modules>
        <module>common</module>
        <module>config</module>
        <module>fmock</module>
        <module>push</module>
        <module>parent</module>
        <module>build</module>
    </modules>

</project>

scd/parent/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">

    <!-- 更改继承自SpringBoot Starter Parent -->
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>3.1.4</version>
        <relativePath />
    </parent>

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.litblc</groupId>
    <artifactId>parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0</version>

    <!--  统一设定一些依赖或者包的版本号  -->
    <!--  除了写死的Spring Boot版本、Java运行版本、项目版本外,其他引入的版本均以<xxx.version>1.23</xxx.version>的形式定义,以便后续可以用${xxx.version}引用版本号  -->
    <properties>
        <!-- 项目版本 -->
        <project.version>1.0</project.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <!-- Java编译和运行版本 -->
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <java.version>17</java.version>
        <!-- 定义第三方组件的版本 -->
        <mybatis-plus-boot-starter.version>3.5.3.2</mybatis-plus-boot-starter.version>                    <!-- mybatis-plus包 -->
        <redisson-spring-boot-starter.version>3.23.4</redisson-spring-boot-starter.version>               <!-- redis包 -->
        <springdoc-openapi-starter-webmvc-ui.version>2.1.0</springdoc-openapi-starter-webmvc-ui.version>  <!-- 接口文档 swagger UI 本地访问 http://localhost:8080/swagger-ui/index.html-->
        <fastjson.version>2.0.41</fastjson.version>                                                       <!-- json包 https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <spring-cloud-dependencies.version>2022.0.3</spring-cloud-dependencies.version>
    </properties>

    <!-- 共享的依赖管理 -->
    <dependencies>
        <!--  在顶级pom引入web组件,否则没有tomcat,无法发起http请求  -->
        <!--  spring-boot-starter-web包含了spring-boot-starter  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--  数据库相关 mysql-connector-j是驱动包,使用mysql必须装。driver-class-name中可以指定  -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>${redisson-spring-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>${springdoc-openapi-starter-webmvc-ui.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <!-- 引入SpringCloud依赖 -->
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud-dependencies.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- 内部模块 -->
            <dependency>
                <groupId>com.litblc</groupId>
                <artifactId>common</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.litblc</groupId>
                <artifactId>config</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.litblc</groupId>
                <artifactId>fmock</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>com.litblc</groupId>
                <artifactId>push</artifactId>
                <version>${project.version}</version>
            </dependency>

        </dependencies>
    </dependencyManagement>

    <build>
        <!-- 指定输出文件名 -->
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 引入创建可执行Jar的插件,Spring Boot项目自带这个插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

/scd/fmock/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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>parent</artifactId>
        <groupId>com.litblc</groupId>
        <version>1.0</version>
        <relativePath>../parent/pom.xml</relativePath>
    </parent>

    <artifactId>fmock</artifactId>
    <version>1.0</version>

    <name>fmock</name>
    <description>fmock</description>


    <dependencies>
    </dependencies>

    <build>
        <!-- 指定输出文件名 -->
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 引入创建可执行Jar的插件,Spring Boot项目自带这个插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

/scd/config/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>parent</artifactId>
        <groupId>com.litblc</groupId>
        <version>1.0</version>
        <relativePath>../parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>config</artifactId>
    <version>1.0</version>

    <name>config</name>
    <description>config-配置服务器</description>

    <dependencies>
        <!--  在config模块中引入spring-cloud-config-server依赖  -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>
    </dependencies>

    <build>
        <!-- 指定输出文件名 -->
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 引入创建可执行Jar的插件,Spring Boot项目自带这个插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

/scd/common/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>parent</artifactId>
        <groupId>com.litblc</groupId>
        <version>1.0</version>
        <relativePath>../parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>common</artifactId>
    <version>1.0</version>

    <name>common</name>
    <description>common</description>

    <build>
        <!-- 指定输出文件名 -->
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- 引入创建可执行Jar的插件,Spring Boot项目自带这个插件 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
赞赏支持
Responses