Spring boot 入门(技术分享)

构建一个Spring Boot应用

先决条件
  • Java开发神器IDEA
  • JDK 1.8或更高版本
  • Maven 3.2+ 或者 Gradle 4+

通过Maven来构建

如果您不还熟悉maven,请参阅 使用Maven构建Java项目

接下来,我们通过IDEA 来快速创建maven项目。

  1. 点击New->Project...
  1. 选择Maven,点击Next
  1. 输入GroupIdArtifactIdVersion,点击 Next
  1. 输入Project nameProject location,点击 Finish
  1. 项目创建成功后,你会得到如下界面

添加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文件中指定parentspring-boot-starter-parentspring-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 告诉Springhello包中寻找其他componentsconfigurationsservices

main()方法使用Spring BootSpringApplication.run()方法来启动应用程序。 您是否注意到没有一行XML? 也没有web.xml文件。 此Web应用程序是100%Java,您无需处理配置任何管道或基础结构。

还有一个标记为@BeanCommandLineRunner方法,它在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请求返回数据而不是视图。


检查服务
  1. 第一个终端执行mvn spring-boot:run
  2. 第二个终端执行
$ 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

这以管理员身份连接到MySQLbash

  1. 创建一个新的数据库
mysql> create database db_example; -- 创建新的数据库
mysql> create user 'springuser'@'localhost' identified by 'ThePassword'; -- 创建一个用户
mysql> grant all on db_example.* to 'springuser'@'localhost'; -- 对新创建的数据库的所有权限给予新创建的用户
  1. 查看创建的数据库
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| db_example         |
| mydatabase         |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
6 rows in set (0.00 sec)
  1. 查看新创建的user
mysql> SELECT User FROM mysql.user;
+---------------+
| User          |
+---------------+
| springuser    |
| mysql.session |
| mysql.sys     |
| root          |
+---------------+
4 rows in set (0.00 sec)

配置spring-boot
  1. 修改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可以是noneupdatecreatecreate-drop,有关详细信息,请参阅Hibernate文档

  • none: 这是MySQL的默认值,不会更改数据表结构。
  • update: Hibernate根据给定的实体结构更改数据表结构。
  • create 每次创建数据表,但在关闭时不会删除数据表。
  • create-drop 创建数据表,然后在SessionFactory关闭时删除它。

我们这里以create开头,因为我们还没有数据表结构。 第一次运行后,我们可以根据程序要求将其切换为updatenone。 如果要对数据表结构进行一些更改,请使用update

在数据库处于生产状态后,您可以使用none并从连接到Spring应用程序的MySQL用户撤消所有权限,然后只给他SELECTUPDATEINSERTDELETE,这是一种很好的安全做法。
本指南最后详细介绍了这一点。

  1. 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);
    }

}

上面的示例没有明确指定GETPUTPOST等,因为@GetMapping@RequestMapping(method = GET)的快捷方式。 @RequestMapping默认映射所有HTTP操作。 使用@RequestMapping(method = GET)或其他快捷方式注解来限制映射。


测试我们的服务
  1. 添加一个user对象
$ curl 'http://localhost:8080/demo/add2?name=First&email=someemail@someemailprovider.com'

响应应该如下

Saved
  1. 查询所有的user
$ curl 'http://localhost:8080/demo/all'

响应应该如下

[{"id":1,"name":"First","email":"someemail@someemailprovider.com"}]
  1. 更新用户通过id
$ curl --request POST --header "Content-Type: application/json" http://localhost:8080/demo/add --data '{"id":1,"name":"Test","email":"someemail@someemailprovider.com"}'
Saved
  1. 查询用户通过id
$ curl 'http://localhost:8080/demo/find-by-id?id=1'
{"id":1,"name":"Test","email":"someemail@someemailprovider.com"}
  1. 删除用户通过id
$ curl 'http://localhost:8080/demo/delete?id=1'
"ok"
$ curl 'http://localhost:8080/demo/find-by-id?id=1'
null
  1. 你可能注意到对于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"}]
  1. 简化服务端的编码

当一个接口的参数过多时,我们可以通过@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。 或者,更好的是,使用专用的迁移工具,如FlywayLiquibase


响应式的方式访问 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


更多spring-boot技术资料
`

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容