spring-boot使用学习一

最近窝在家里,准备对微服务学习再深入一步,这样就跨不过spring cloud这个坎了,那咱就从spring boot开始学习,然后转到spring cloud,并对spring cloud中的相关组件分别进行学习。
本文为第一篇

主要参考:

spring-boot-learning

环境搭建

  • JDK
    oracle官方下载JDK1.8,解压,添加环境变量
    jdk8下载

  • Maven
    官网下载,解压,添加环境变量
    maven下载

  • IDE:VS
    几个个插件:Java Extension Pack;Spring Initializr Java Support; Spring Boot Tools; Spring Boot Dashborad;
    指导

  • docker
    另外,需要用到数据库(postgresql),redis,RabbitMQ等基础设施,用docker安装比较省事,本机已经安装了docker与docker-compose

Hello World

在 spring vs中有引导生成的方法:

  • Ctrl + Shift + P
  • 选择spring initializr maven
  • 包名
  • 依赖
  • 生成位置
  • 用VS打开
  • F5执行

Spring Web

基础

  • @SpringBootApplication

    每个应用都有一个application类,用@SpringBootApplication来注解

  • @RestController与@RequestMapping

    Contoller层用@RestController来注解,里边每个路由用@RequestMapping来注解

  • 注入
    将被注入的service用@Service(“xxx”)来注解
    注入时,使用@Autowired来进行注入即可

测试

  • @SpringBootTest与@Test
    原课程中使用被@Before注解的setUp()来创建基础环境,但在的版本中,没有@Before只用 @BeforeAll,且使用之后,就无法运行下边的测试了,所以这里改为通过声明时直接初始化的方法来创建mockMvc
    测试的使用VS会在测试类与测试方法上,自动添加Run Test、Debug Test,点击即可运行

  • @Before与@BeforeAll
    搜索了一下@BeforeAll,它是在Junit5才出现的,用来对静态方法的注解。@Before是Junit4中的注解,与Junit5中的@BeforeEach等价

  • 注入
    在测试中,如果像之前一样用MockMvcBuilders.standaloneSetup(new WebController()).build();来创建mockMvc会发现,service无法注入到controller中,但直接运行是没问题的。
    mockMvc需要用MockMvcBuilders.webAppContextSetup(this.wac).build();来定义,这个this.wac是@AutoWired的WebApplicationContext对象。

swagger

  • 依赖
    pom.xml中增加

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.8.0</version>
    </dependency>
    <dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.8.0</version>
    </dependency>
  • 配置
    这里的配置,使用配置类的方式来配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {

    @Bean
    public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo())
    .select()
    // 自行修改为自己的包路径
    .apis(RequestHandlerSelectors.basePackage("com.sun.account.controller"))
    .paths(PathSelectors.any())
    .build();
    }

    private ApiInfo apiInfo() {
    return new ApiInfoBuilder()
    .title("用户管理")
    .description("用户管理操作文档")
    .version("1.0")
    .build();
    }
    }
  • 使用
    在controller上用@Api(description=”xxx”)来装饰
    在controller每个方法上用@ApiOperation(value=”xx”, notes=”xxxx”)来装饰
    另外有@ApiImplicitParams,用来对入参进行解释;@ApiResponse,用来对返回进行解释;不过我感觉这2个用处不大,入参命名恰当,完全可以自注释。出参本身有用处,@ApiResponse返回的是对状态码的解释,如果状态码约定好了,并不需要每个都注释。

    在返回数据的class上,用@ApiModel(description=”xxx”)来注解
    class中的成员变量,用@ApiModelProperty(value=”xx”, name=”xxx”)来注解

    这里例子中给出的class BaseResult{}不错

数据库

Postgresl安装

这里说安装是值得容器化的过程,看了一下之前的容器化,将每个数据库都创建一个镜像,这种做法是费力的,正确的做法是一个镜像,可以运行多个实例,在run的时候,通过配置将环境变量注入进去。

  • 安装docker-compose
    pip install upgrade pip
    pip install docker-compose

  • docker-compose.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    version: '3.4'

    services:
    postgresql:
    image: postgres:10.5
    restart: unless-stopped
    environment:
    POSTGRES_DB: account
    POSTGRES_USER: root
    POSTGRES_PASSWORD: 123456
    volumes:
    - ./data:/var/lib/postgresql/data
    ports:
    - "5432:5432"

JDBC

  • 依赖

    需要在pom.xml中增加2个依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
    </dependency>

    一个是jdbc另外一个是postgres的驱动

  • 配置

    在application.properties中增加:

    1
    2
    3
    4
    spring.datasource.url: jdbc:postgresql://localhost:5432/account
    spring.datasource.username: root
    spring.datasource.password: 123456
    spring.datasource.driverClassName: org.postgresql.Driver
  • 使用

    在Repository的Impl类中,自动注入JdbcTemplate对象,通过此对象操作数据库。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @Repository
    public class UserRepositoryImpl implements UserRepository {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public int save(User user){

    return jdbcTemplate.update("INSERT INTO users(name, password, age) values(?, ?, ?)",
    user.getName(), user.getPassword(), user.getAge());
    }

    public int update(User user){
    return jdbcTemplate.update("UPDATE users set name=?, password=?, age=? WHERE id=?",
    user.getName(), user.getPassword(), user.getAge(), user.getId());
    }

    public int delete(long id){
    return jdbcTemplate.update("DELETE FROM users WHERE id = ?",id);
    }

    public User findById(long id){
    return jdbcTemplate.queryForObject("SELECT * from users WHERE id = ?", new Object[] { id }, new BeanPropertyRowMapper<User>(User.class));
    };
    }

Spring Boot JPA

同样的3个步骤

前奏

  • 依赖

    将jdbc改为data-jpa即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>runtime</scope>
    </dependency>
  • 配置

    除了postgres数据库的配置,增加了jpa的配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    spring.datasource.url: jdbc:postgresql://localhost:5432/account
    spring.datasource.username: root
    spring.datasource.password: 123456
    spring.datasource.driverClassName: org.postgresql.Driver
    # 可以用create\update\validate
    spring.jpa.properties.hibernate.hbm2ddl.auto=update
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
    spring.jpa.show-sql=true
    spring.jpa.properties.hibernate.format_sql=true

使用

  • entity

    entity上用@Entity来注解,如果表名与类名不同,可以增加@Table(name=”表名”)来注解表名

    主键:@Id @GeneratedValue来注解

    不同列:@Column(nullable=false, unqiue=true)来注解

    使用postgres来创建User对象时,这里有个小坑,因为postgres中默认有一个user表,会与User类重复,导致创建失败,这时候用@Table注解来重新命名表名即可

  • JpaRepository

    reppository是与JDBC不同最大的,在JDBC中,我们创建了一个interface+impl class来做的repository。而在jpa中,我们只需要一个interface,而有spring自动帮助我们实现。

    • 自动实现
      Spring Data JPA 可以根据接口方法名来实现数据库操作,主要的语法是 findXXBy、readAXXBy、queryXXBy、countXXBy、getXXBy 后面跟属性名称,利用这个功能仅需要在定义的 Repository 中添加对应的方法名即可

      1
      User findByUserName(String userName);
    • 自定义查询
      在自己本地,只实现了HQL的版本,原生的并没有实现

      1
      2
      @Query("select u from User u where u.nickName = ?1")
      User findUserByNickName(String nickName);

      在实践过程中发现:如果一个自定义函数有问题,会影响其他函数的生成与执行

  • 分页查询

    分页有2种形式:Page与Slice,Page继承自Slice,并且多一个总数的属性

    1
    2
    @Query("select u from User u")
    Page<User> getAll(Pageable pageable);
    1
    Slice<User> getByNickName(String nickName, Pageable pageable);

    定义时候比较简单,复杂的是使用,这个Pageable的生成比较复杂。
    Pageable的生成通过:

    1
    2
    3
    4
    5
    6
    7
    8
    Pageable pageable = PageRequest.of(page, size, sort);
    ``
    来生成,前两个参数是int类型,page表示第几页,size表示页的大小,sort是Sort对象,可以在这里传入order的参数。sort可以不填写。
    我经常的写法是将order写入语句中,比起这里将sort放到查询时候加入,没有这个通用性好。

    sort对象通过:
    ```java
    Sort sort = Sort.by(Sort.Direction.DESC, "id");

    来生成

    整个过程:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public void getAll()  {
    int page=0, size=2;
    Sort sort = Sort.by(Sort.Direction.DESC, "id");

    Pageable pageable = PageRequest.of(page, size, sort);
    Page<User> allUser = userRepository.getAll(pageable);

    for (User user : allUser) {
    System.out.print(user);
    }
    }

    Page是Slice的子类,Slice是Iterable的孙类,故可以遍历访问

  • top查询

    top查询将spring中约定大于配置的理念发挥得淋漓尽致,spring自动生成了TopN的查询:

    1
    List<User> findTop2ByNickName(String nickName, Sort pageable);

只需要在repository的interface中增加此接口,就可以进行top2的查询了,t如果是top可以直接用User来当返回参数

  • 联表查询

    多表查询就是有外键的情况,在接口注解的时候,以HQL的形式注解即可
    需要注意的是返回值,这里不像node,返回值需要访问,需要自定义访问接口,有getter即可。

    1
    2
    @Query("select u.id as id, u.userName as userName, u.phone as phone, i.realName as realName, i.hobby as hobby from User u join UserInfo i on u.id=i.userId  where  u.id = ?1 ")
    UserDetail getUserDetail(Long id);
    1
    2
    3
    4
    5
    6
    7
    public interface UserDetail {
    String getId();
    String getUserName();
    String getPhone();
    String getRealName();
    String getHobby();
    }
  • 疑问

spring data JPA 是从Hibernate借鉴过来的,而Hibernate是有@OneToMany等关系的注解的,这里没找到例子,后边使用的时候再研究一下使用。

Druid

  • 简介

Druid 首先是一个数据库连接池,但它不仅仅是一个数据库连接池,还包含了一个 ProxyDriver,一系列内置的 JDBC 组件库,一个 SQL Parser。

  1. 替换其他 Java 连接池,Druid 提供了一个高效、功能强大、可扩展性好的数据库连接池。
  2. 可以监控数据库访问性能,Druid 内置提供了一个功能强大的 StatFilter 插件,能够详细统计 SQL 的执行性能,这对于线上分析数据库访问性能有很大帮助。
  3. SQL 执行日志,Druid 提供了不同的 LogFilter,能够支持 Common-Logging、Log4j 和 JdkLog,可以按需要选择相应的 LogFilter,监控应用的数据库访问情况。
  • 依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
    </dependency>
  • 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 初始化大小、最小、最大链接数
    spring.datasource.druid.initial-size=3
    spring.datasource.druid.min-idle=3
    spring.datasource.druid.max-active=10

    # 配置获取连接等待超时的时间
    spring.datasource.druid.max-wait=60000

    # StatViewServlet 配置
    spring.datasource.druid.stat-view-servlet.login-username=admin
    spring.datasource.druid.stat-view-servlet.login-password=admin

    # 配置 StatFilter
    spring.datasource.druid.filter.stat.log-slow-sql=true
    spring.datasource.druid.filter.stat.slow-sql-millis=2000

    另外有个spring.datasource.type: com.alibaba.druid.pool.DruidDataSource配置,放在jdbc连接配置之上

  • 使用

    http://localhost:8080/druid访问即可,登录的用户名用户密码都是admin,每次有数据库访问时,都可以监控到