目录
SpringBoot入门和使用实践
一、软件环境
- JDK 1.8+
- Maven 3.2.5+
- PostMan (www.getpostman.com)
二、简介
- Springboot角色
Spring Cloud(构建Spring Boot的分布式环境)
|
Spring Boot(快速构建Spring的应用)
|
Spring Framework (底层,Java EE框架)
- Spring Boot 2.x.x 新特性
编程语言:Java 8+、Kotlin
底层框架:Spring Framework
全新特性:Web Flux(传统Spring应用使用标签或注解来描述请求或响应的映射,web flux支持声明式、函数式编程)
- 为什么选择Web Flux
函数编程:Java 8 Lambda
响应编程:Reactive Streams(java 9 Flow API、Reactive Extensions-RXjava、Reactor)
异步编程:Servlet 3.1 或 Asyc NIO
- 2.0 特性
自动装配:MVC、JDBC、事务等组件的装配
内置容器:Tomcat、Jetty等
DevOps:针对生产的开发运维模式
三、使用
1、第一个springboot项目
- 编写 Rest 程序
- 运行 Spring Boot 应用
- 使用 HTTP 请求工具:PostMan
- 场景说明:
- 定义用户模型,包括属性:用户ID和名称
- 客户端发送 Post 请求,创建用户 (Web MVC)
- 客户端发送 GET 请求,获取所有用户(Web Flux)
- Map 存储数据
1)进入 start.spring.io
- Project 选择 Maven Project
- Language 选择 Java
- Spring Boot 选择 2以上的版本
- Group 填写:com.test
- Artifact 填写:first-app-demo
- Depencies 填写:Reactive Web
- 点击 Generate Project 下载项目
2)解压项目,使用 IDEA 加载项目
3)创建文件夹和类
- domain文件夹,创建User类,定义id和name属性(get\set\toString)
package com.test.firstappdemo.domain;
/**
*
* 用户模型
* */
public class User {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
- repository文件夹,创建UserRepository类(@Repository、Java doc注释、public boolean save(User user) 、ConcurrentMap<Interger,User>、AtomicInteger)
package com.test.firstappdemo.repository;
import com.test.firstappdemo.domain.User;
import org.springframework.stereotype.Repository;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* {@link User} {@link @Repository}
* */
@Repository
public class UserRepository {
//内存型保存方式
private final ConcurrentMap<Integer, User> userRepostory = new ConcurrentHashMap<>();
private final static AtomicInteger idGenerator = new AtomicInteger();
/**
* 保存对象
* @param user {@link User} 对象
* @return 如果保存成功,返回<code>true</code>
* 否则,返回<code>false</code>
* */
public boolean save(User user){
//从1开始
Integer id = idGenerator.incrementAndGet();
user.setId(id);
return userRepostory.put(id, user) == null;
}
}
- controller文件夹,创建UserController类(@RestController、构造器注入UserRepository、@PostMapping、public User save(@RequestParam String name))
package com.test.firstappdemo.controller;
import com.test.firstappdemo.domain.User;
import com.test.firstappdemo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
// 注入用户仓储,构造器注入好处:不能修改,提前初始化
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
@PostMapping("/person/save")
public User save(@RequestParam String name){
User user = new User();
user.setName(name);
if(userRepository.save(user)){
System.out.printf("用户对象:%s 保存成功.\n", user);
}
return user;
}
}
4) 运行启动类
-
启动类名称:<项目名称+Application>,debug启动(启动后可查看Spring Boot版本和PID,web flux默认使用Netty容器进行启动)
-
PostMan:POST请求 【http://localhost:8080/person/save】 ;属性设置 name=zcheng
5)GET请求,查询所有用户
- UserRepository
package com.test.firstappdemo.repository;
import com.test.firstappdemo.domain.User;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* {@link User} {@link @Repository}
* */
@Repository
public class UserRepository {
//内存型保存方式
private final ConcurrentMap<Integer, User> userRepostory = new ConcurrentHashMap<>();
private final static AtomicInteger idGenerator = new AtomicInteger();
/**
* 保存对象
* @param user {@link User} 对象
* @return 如果保存成功,返回<code>true</code>
* 否则,返回<code>false</code>
* */
public boolean save(User user){
//从1开始
Integer id = idGenerator.incrementAndGet();
user.setId(id);
return userRepostory.put(id, user) == null;
}
public Collection<User> values() {
return userRepostory.values();
}
}
- UserController
package com.test.firstappdemo.controller;
import com.test.firstappdemo.domain.User;
import com.test.firstappdemo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
@RestController
public class UserController {
// 注入用户仓储,构造器注入好处:不能修改,提前初始化
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
@PostMapping("/person/save")
public User save(@RequestParam String name){
User user = new User();
user.setName(name);
if(userRepository.save(user)){
System.out.printf("用户对象:%s 保存成功.\n", user);
}
return user;
}
@GetMapping("/person/findAll")
public Collection<User> findAll(){
return userRepository.values();
}
}
- PostMan:GET请求【http://localhost:8080/person/findAll】
6)实现 Web Flux
- spring 官网介绍 1.8章 Reactive Libraries
- 传统NIO实现的Reactor模式,是同步非阻塞,Spring的Reactor是Reactive的实现,是异步非阻塞
- Mono表示(0:1)的对象,类似Java 8的 Optional,防止空指针,查询 Mono 到 1.5.1 HandlerFunction
- Flux表示(0:N)的对象,类似一个集合,空集合或包含很多元素
- MVC使用@RequestMapping进行映射,Web Flux 使用函数式编程声明接口,Spring 5之后将Servlet接口进行封装
- 通过路由函数定义类似@RequestMapping的映射
package com.test.firstappdemo.config;
import com.test.firstappdemo.domain.User;
import com.test.firstappdemo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.Collection;
/**
* 路由器函数
* */
//从Spring 3开始逐渐使用Configuration代替xml文件
@Configuration
public class RouterFunctionConfiguration {
/**
* Servlet:
* 请求接口:ServletRequest 或者 HttpServletRequest
* 响应接口:ServletResponse 或者 HttpServletResponse
* Spring 5.0 重新定义了服务的请求和响应:
* 请求接口:ServerRequest
* 响应接口:ServerResponse
* 请求和响应接口既支持Servlet的规范,也可以自定义规范,比如:Netty(Web Server)
* Reactive中的Flux 或 Mono 它是异步处理(非阻塞)
* 集合对象基本上是同步处理(阻塞)
* Flux 或 Mono 都是 Publisher(发布者)推模式来推送数据
* Web Flux通过异步的方式提高吞吐量
* 1.5 Functional Endpoints 介绍函数式的端点(Rest暴露)
* 1.5.2 RouterFunction 查看例子
* */
/**
* 场景:
* 定义GET请求,并且返回所有的用户对象 URL:/person/find/all
* 依赖仓储对象,@Autowired 可以不加,只是表明 仓储是方法注入
*
* */
@Bean
@Autowired
public RouterFunction<ServerResponse> personFindAll(UserRepository userRepository){
// 定义映射关系,一般s结尾都是静态类或工具类,lamda表达式,request是方法参数
// HandlerFunction参数是ServerRequest,返回Mono
return RouterFunctions.route(RequestPredicates.GET("/person/find/all"), request -> {
// 获取所有对象
Collection<User> users = userRepository.values();
//集合对象转Flux对象
Flux<User> userFlux = Flux.fromIterable(users);
return ServerResponse.ok().body(userFlux, User.class);
});
}
}
2、构建方式
- 图形化构建:http://start.spring.io
- Maven
Apache Maven官网:http://maven.apache.org/archetype/maven-archetype-plugin/generate-mojo.html
// interactiveMode=false表示非交互式
Windows -> CMD窗口:mvn archetype:generate -DinteractiveMode=false -DgroupId=com.test -DartifactId=first-app-demo -Dversion=1.0.0-SNAPSHOT
// 查看文件目录
$ dir
https://spring.io/guides/gs/rest-service/ 找到 Build with Maven,复制 pom.xml 配置
// IDEA 弹窗 Enable Auto Import
// 默认生成的jUnit测试工具,去掉版本号,因为Spring的父工程中已经存在版本控制
然后配置启动类和控制类,和之前的内容一样
3、多模块项目
- 重构
调整主(父)工程(由jar变为子项目的管理)
创建子模块工程()
模块层:model(domain)
持久层:presistence(repository)
表示层:web(controller)
子模块依赖管理()
- 具体操作
修改pom.xml(有可能最后删除src时,IDEA会自动添加,所以这一步可以尝试忽略)
pom
创建 maven 子模块
a. web -> presistence -> model 由高往低创建
b. 项目名称 -> New -> Module -> 不选择archetype -> artifactId填写 web -> Finish
c. 如果web的src中java和resources未标记文件夹类型(没问题的直接跳过)
- java 文件夹 -> 右键 -> Mark Directory as -> Sources Root
- resources 文件夹 -> 右键 -> Mark Directory as -> Resources Root
- test 文件夹 -> 右键 -> Mark Directory as -> Test Sources Root
d. 迁移代码
子模块创建 java 文件夹创建 com.test(这一步要看IDEA版本给不给直接拖到)
选择父项目的 java 下的内容 com.xxx.xxx 拖到 web 子模块的 java文件夹中
Move everything from
Yes -> Continue
选择父项目的 resources 下的内容,拖到 web 子模块的 resources文件夹中
删除父项目的src文件夹
运行启动类
接下来根据上面步骤,把其它子模块也进行重构
presistence pom.xml 设置 model 依赖;复制model pom.xml
web pom.xml 设置 presistence 依赖;复制presistence pom.xml
IDEA 头部菜单 -> Build -> Build Project -> 运行启动类
- 微服务中应用的服务职责或权限职责要单一
4、打包和运行
- 打包方式
- 构建 JAR 包
- 构建 WAR 包
- 构建 Main-Class
1)构建 jar 包
-
父pom.xml文件添加main-class:(进入启动类 -> 选中类名右键 -> copy Reference)
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.test.firstappdemo.FirstAppDemoApplication</mainClass> </configuration> </plugin> </plugins> </build>
-
将上面父pom.xml 内容剪切到web子模块的pom.xml中
// 跳过测试,更新第三方包
-
cmd窗口(IDEA Terminal):项目名称 -> 右键 -> copy path -> cd 项目路径 -> mvn -Dmaven.test.skip -U clean package
-
或者 使用IDEA右侧菜单的Maven Projects的LifeCycle
-
cmd窗口(IDEA Terminal):cd web -> cd target -> java -jar web-0.0.1-SNAPSHOT.jar
2) 构建 war 包
-
web子模块添加目录 src/main/webapp/WEB-INF/web.xml
-
web pom.xml文件新增或修改
// 默认jar
<packaging>war<packaging>
-
cmd窗口(IDEA Terminal):cd web -> cd target -> java -jar web-0.0.1-SNAPSHOT.war
3) 运行模式
- IDEA 方式(开发调试)
- JAR/WAR 方式(一般线上)
- Maven 插件方式(极端)
cmd窗口(IDEA Terminal):项目根目录 -> mvn -Dmaven.test.skip -U clean install
cmd窗口(IDEA Terminal):cd web -> mvn spring-boot:run