Spring boot 入门(技术分享)

2019-03-21  本文已影响0人  mxl0814

构建一个Spring Boot应用

先决条件

通过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是一个便利注释,添加了以下所有内容:

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文档

我们这里以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技术资料
`

上一篇下一篇

猜你喜欢

热点阅读