最近窝在家里,准备对微服务学习再深入一步,这样就跨不过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中增加
<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>
-
配置
这里的配置,使用配置类的方式来配置
@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
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个依赖
<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中增加:
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对象,通过此对象操作数据库。
@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即可
<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的配置
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 中添加对应的方法名即可
User findByUserName(String userName);
-
自定义查询
在自己本地,只实现了HQL的版本,原生的并没有实现
@Query("select u from User u where u.nickName = ?1")
User findUserByNickName(String nickName);
在实践过程中发现:如果一个自定义函数有问题,会影响其他函数的生成与执行
-
分页查询
分页有2种形式:Page与Slice,Page继承自Slice,并且多一个总数的属性
@Query("select u from User u")
Page<User> getAll(Pageable pageable);
Slice<User> getByNickName(String nickName, Pageable pageable);
定义时候比较简单,复杂的是使用,这个Pageable的生成比较复杂。
Pageable的生成通过:
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");
来生成
整个过程:
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的查询:
List<User> findTop2ByNickName(String nickName, Sort pageable);
只需要在repository的interface中增加此接口,就可以进行top2的查询了,t如果是top可以直接用User来当返回参数
-
联表查询
多表查询就是有外键的情况,在接口注解的时候,以HQL的形式注解即可
需要注意的是返回值,这里不像node,返回值需要访问,需要自定义访问接口,有getter即可。
@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);
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。
- 替换其他 Java 连接池,Druid 提供了一个高效、功能强大、可扩展性好的数据库连接池。
- 可以监控数据库访问性能,Druid 内置提供了一个功能强大的 StatFilter 插件,能够详细统计 SQL 的执行性能,这对于线上分析数据库访问性能有很大帮助。
- SQL 执行日志,Druid 提供了不同的 LogFilter,能够支持 Common-Logging、Log4j 和 JdkLog,可以按需要选择相应的 LogFilter,监控应用的数据库访问情况。
-
依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
-
配置
# 初始化大小、最小、最大链接数
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,每次有数据库访问时,都可以监控到