构建一个Spring Boot
应用
先决条件
-
Java
开发神器IDEA
-
JDK 1.8
或更高版本 -
Maven 3.2+
或者Gradle 4+
通过Maven
来构建
如果您不还熟悉maven
,请参阅 使用Maven构建Java项目。
接下来,我们通过IDEA
来快速创建maven
项目。
- 点击
New->Project...
- 选择
Maven
,点击Next
- 输入
GroupId
、ArtifactId
、Version
,点击Next
- 输入
Project name
、Project location
,点击Finish
- 项目创建成功后,你会得到如下界面
添加classpath
依赖项
pom.xml
描述了用于构建项目的策略,为了使得我们的项目支持spring-boot
,我们需要在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.sunmi</groupId>
<artifactId>sping-boot-study</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<!--父节点启动器-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<dependencies>
<!--spring-boot依赖项-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
Spring Boot
提供了许多“Starters”
,可以将jar
添加到classpath
中。 我们的示例应用程序已经在pom.xml
文件中指定parent
为spring-boot-starter-parent
。 spring-boot-starter-parent
是一个特殊的启动器,提供有用的maven
默认值。 它还提供了一个依赖项的version
,以便您可以省略依赖项的版本标记。
创建一个Application
类
在这里,您将使用组件创建一个应用程序类:
src/main/java/hello/Application.java
package hello;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
return args -> {
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
};
}
}
@SpringBootApplication
是一个便利注释,添加了以下所有内容:
-
@Configuration
标记该类作为应用程序上下文的bean
定义的来源。 -
@EnableAutoConfiguration
告诉Spring Boot
开始根据classpath
设置,其他bean
和各种属性设置添加bean
。 - 通常你会为
Spring MVC
应用添加@EnableWebMvc
注解,但Spring Boot
会在classpath
上看到webmvc
时自动添加它。 这会将应用程序标记为Web
应用程序,并激活关键行为以及设置调度程序servlet
。
这会将应用程序标记为Web
应用程序,并在安装DispatcherServlet
时激活关键行为。 -
@ComponentScan
告诉Spring
在hello
包中寻找其他components
,configurations
和services
。
main()
方法使用Spring Boot
的SpringApplication.run()
方法来启动应用程序。 您是否注意到没有一行XML
? 也没有web.xml
文件。 此Web
应用程序是100%
纯Java
,您无需处理配置任何管道或基础结构。
还有一个标记为@Bean
的CommandLineRunner
方法,它在spring boot
启动时运行。 示例中它将检索由您的应用程序创建或Spring Boot
自动添加的所有bean
。 它对它们进行分类并打印出来。
运行该应用程序
mvn spring-boot:run
创建一个简单的Web
应用程序
现在,我们为简单的Web
应用程序创建Web controller
:
src/main/java/hello/HelloController.java
package hello;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController
public class HelloController {
@RequestMapping("/")
public String index() {
return "Greetings from Spring Boot!";
}
}
该类被标记为@RestController
,这意味着Spring MVC
可以使用它来处理Web
请求。 @RequestMapping
将/
映射到index()
方法。 从浏览器访问或在命令行上使用curl
时,该方法返回纯文本。 这是因为@RestController
结合了@Controller
和@ResponseBody
,两个注释导致Web
请求返回数据而不是视图。
检查服务
- 第一个终端执行
mvn spring-boot:run
- 第二个终端执行
$ curl localhost:8080
Greetings from Spring Boot!
添加单元测试
您将需要为endpoint
添加测试,Spring Test
已经为此提供了一些机制,并且很容易包含在您的项目中。
在maven
中加入如下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
现在编写一个简单的单元测试,通过endpoint
模拟servlet
请求和响应:
package hello;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void getHello() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo("Greetings from Spring Boot!")));
}
}
MockMvc
对象来自Spring Test
,它允许您通过一组便利构造器类将HTTP
请求发送到DispatcherServlet
并对结果进行断言。 请注意@AutoConfigureMockMvc
与@SpringBootTest
一起使用以注入MockMvc
实例。 使用@SpringBootTest
后,web
应用程序会被自动创建。
除了模拟HTTP
请求周期之外,我们还可以使用Spring Boot
编写一个非常简单的全栈集成测试。 例如,我们可以这样做,而不是上面的模拟测试:
package hello;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.net.URL;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerIT {
@LocalServerPort
private int port;
private URL base;
@Autowired
private TestRestTemplate template;
@Before
public void setUp() throws Exception {
this.base = new URL("http://localhost:" + port + "/");
}
@Test
public void getHello() throws Exception {
ResponseEntity<String> response = template.getForEntity(base.toString(),
String.class);
assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
}
}
嵌入式服务器通过webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
在随机端口上启动,并在运行时使用@LocalServerPort
发现实际端口。
创建一个可执行的Jar
我们通过创建一个包含全部依赖的可执行jar
文件来完成我们的示例,我们可以在生产中运行它。 可执行jar
(有时称为“fat jar”
)是包含已被编译的代码以及需要的全部依赖jar的归档。
要创建可执行jar
,我们需要将spring-boot-maven-plugin
添加到我们的pom.xml
中。 为此,请在依赖项部分下方插入以下行:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
接着在终端执行如下命令
mvn package
查看target
目录,则应该看到sping-boot-study-1.0-SNAPSHOT.jar
。 该文件大小应为10 MB
左右。 如果你想查看jar
的内部,你可以使用jar tvf
,如下所示:
jar tvf target/sping-boot-study-1.0-SNAPSHOT.jar
要运行该应用程序,请使用java -jar
命令,如下所示:
java -jar target/sping-boot-study-1.0-SNAPSHOT.jar
和以前一样,要退出应用程序,请按ctrl-c
。
Spring-boot
使用MySQL
访问数据
创建数据库
打开终端,例如在Linux/Mac
上,我们使用命令
$ sudo mysql --password
这以管理员身份连接到MySQL
的bash
。
- 创建一个新的数据库
mysql> create database db_example; -- 创建新的数据库
mysql> create user 'springuser'@'localhost' identified by 'ThePassword'; -- 创建一个用户
mysql> grant all on db_example.* to 'springuser'@'localhost'; -- 对新创建的数据库的所有权限给予新创建的用户
- 查看创建的数据库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| db_example |
| mydatabase |
| mysql |
| performance_schema |
| sys |
+--------------------+
6 rows in set (0.00 sec)
- 查看新创建的
user
mysql> SELECT User FROM mysql.user;
+---------------+
| User |
+---------------+
| springuser |
| mysql.session |
| mysql.sys |
| root |
+---------------+
4 rows in set (0.00 sec)
配置spring-boot
- 修改
src/main/resources/application.yml
文件
spring:
jpa:
hibernate:
ddl-auto: update
datasource:
url: jdbc:mysql://localhost:3306/db_example
username: springuser
password: ThePassword
spring.jpa.hibernate.ddl-auto
可以是none
,update
,create
,create-drop
,有关详细信息,请参阅Hibernate文档。
-
none
: 这是MySQL
的默认值,不会更改数据表结构。 -
update
:Hibernate
根据给定的实体结构更改数据表结构。 -
create
每次创建数据表,但在关闭时不会删除数据表。 -
create-drop
创建数据表,然后在SessionFactory
关闭时删除它。
我们这里以create
开头,因为我们还没有数据表结构。 第一次运行后,我们可以根据程序要求将其切换为update
或none
。 如果要对数据表结构进行一些更改,请使用update
。
在数据库处于生产状态后,您可以使用none并从连接到Spring应用程序的
MySQL
用户撤消所有权限,然后只给他SELECT
,UPDATE
,INSERT
,DELETE
,这是一种很好的安全做法。
本指南最后详细介绍了这一点。
- 在
pom.xml
添加如下依赖
<!--提供了spring boot java持久化API-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--提供了操作mysql的依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
创建 @Entity
模型
src/main/java/hello/User.java
package com.sunmi.bean;
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity // 这告诉Hibernate从这个类中创建一个表
@Data //省去get、set 等方法的编写
public class User {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
}
Hibernate
将把带有@Entity
注解的实体类自动转换为数据库表的结构。
创建操作数据表的接口
package com.sunmi.repository;
import com.sunmi.bean.User;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Integer> {
}
spring
将会自动创建一个实现了 UserRepository
接口的 bean
,包括CRUD
等接口。
创建控制器
package com.sunmi.controller;
import com.sunmi.bean.User;
import com.sunmi.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(path = "/demo") // 这意味着访问url以/demo开始
public class MainController {
//这意味着spring会自动生成userRepository bean对象并注入,我们用这个对象来和mysql交互
@Autowired
private UserRepository userRepository;
@PostMapping(path = "/add")
public String addNewUser(@RequestBody User user) {
userRepository.save(user);
return "Saved";
}
@GetMapping(path = "/add2")
public String addNewUser(String name
, @RequestParam String email) {
User user = new User();
user.setName(name);
user.setEmail(email);
userRepository.save(user);
return "Saved2";
}
@GetMapping(path = "/all")
public Iterable<User> getAllUsers() {
// 返回所有的用户
return userRepository.findAll();
}
@GetMapping(path = "/delete")
public String delete(@RequestParam Integer id) {
userRepository.deleteById(id);
return "ok";
}
@GetMapping(path = "/find-by-id")
public Optional<User> find(@RequestParam Integer id) {
return userRepository.findById(id);
}
}
上面的示例没有明确指定GET
与PUT
,POST
等,因为@GetMapping
是@RequestMapping(method = GET)
的快捷方式。 @RequestMapping
默认映射所有HTTP
操作。 使用@RequestMapping(method = GET)
或其他快捷方式注解来限制映射。
测试我们的服务
- 添加一个
user
对象
$ curl 'http://localhost:8080/demo/add2?name=First&email=someemail@someemailprovider.com'
响应应该如下
Saved
- 查询所有的
user
$ curl 'http://localhost:8080/demo/all'
响应应该如下
[{"id":1,"name":"First","email":"someemail@someemailprovider.com"}]
- 更新用户通过
id
$ curl --request POST --header "Content-Type: application/json" http://localhost:8080/demo/add --data '{"id":1,"name":"Test","email":"someemail@someemailprovider.com"}'
Saved
- 查询用户通过
id
$ curl 'http://localhost:8080/demo/find-by-id?id=1'
{"id":1,"name":"Test","email":"someemail@someemailprovider.com"}
- 删除用户通过
id
$ curl 'http://localhost:8080/demo/delete?id=1'
"ok"
$ curl 'http://localhost:8080/demo/find-by-id?id=1'
null
- 你可能注意到对于
add2
接口,其第二个参数带有@RequestParam注解,这表明来自客户端的请求必须包含该字段。
$ curl 'http://localhost:8080/demo/add2?name=First'
你将得到如下错误
{"timestamp":"2019-03-22T04:49:29.239+0000","status":400,"error":"Bad Request","message":"Required String parameter 'email' is not present","path":"/demo/add2"}>
如果只指定了email
字段
$ curl 'http://localhost:8080/demo/add2?email=someemail@someemailprovider.com'
Saved
$ curl 'http://localhost:8080/demo/all'
[{"id":1,"name":"First","email":"someemail@someemailprovider.com"},
{"id":2,"name":null,"email":"someemail@someemailprovider.com"}]
- 简化服务端的编码
当一个接口的参数过多时,我们可以通过@RequestBody
注解,将这些参数组合成一个bean对象,客户端访问只需要传递一个json
对象(其中的字段是可选的),服务端接收到请求后会自动将json
对象系列化为bean
。
访问此接口,并传递json
对象
$ curl --request POST --header "Content-Type: application/json" http://localhost:8080/demo/add --data '{"name":"Second","email":"someemail@someemailprovider.com"}'
Saved
$ curl 'http://localhost:8080/demo/all'
[{"id":1,"name":"First","email":"someemail@someemailprovider.com"},
{"id":2,"name":null,"email":"someemail@someemailprovider.com"},
{"id":3,"name":Second,"email":"someemail@someemailprovider.com"}]
进行一些安全性更改
现在,当您处于生产环境中时,可能会遇到SQL
注入攻击。 黑客可能会注入DROP TABLE
或任何其他破坏性SQL
命令。 因此,作为安全实践,在将应用程序公开给用户之前,请对数据库进行更改。
mysql> revoke all on db_example.* from 'springuser'@'localhost';
这将撤消与Spring
应用程序关联的用户的所有权限。 现在Spring
应用程序无法在数据库中执行任何操作。 我们不希望如此。
mysql> grant select, insert, delete, update on db_example.* to 'springuser'@'localhost';
这为Spring
应用程序提供了仅更改数据库数据而不是数据表结构所需的权限。
并修改src/main/resources/application.yml
文件
spring.jpa.hibernate.ddl-auto=none
而不是第一次运行时用的create
。
如果要对数据库进行更改,请重新授予权限,将spring.jpa.hibernate.ddl-auto
更改为update
,然后重新运行应用程序,然后再重新授予权限,并将spring.jpa.hibernate.ddl-auto
改为none
。 或者,更好的是,使用专用的迁移工具,如Flyway
或Liquibase
。
响应式的方式访问 Redis
数据
待更新...
参考文档
https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started-first-application.html
https://spring.io/guides/gs/spring-boot/#scratch
https://spring.io/guides/gs/accessing-data-mysql/
https://www.linuxprobe.com/mysql-show-all-users.html
https://blog.csdn.net/mccand1234/article/details/53456411
https://stackoverflow.com/questions/7172784/how-to-post-json-data-with-curl-from-terminal-commandline-to-test-spring-rest
https://github.com/spring-guides/gs-accessing-data-mysql/blob/master/complete/pom.xml