第6章 Spring Boot

6.1 简介

Spring Boot的设计目的是用来简化Spring应用的初始搭建以及开发过程,使用Spring Boot可以创建独立的Spring应用,简化了Maven配置,而且内嵌了Tomcat可以直接部署jar文件。

6.2 STS

在项目开发初期先在本地开发,测试或上线时再部署到服务器,首先确保本地环境安装了Java,然后到 https://spring.io/tools 下载开发Spring Boot项目的IDE,或者读者可以选择自己熟悉的IDE,本书采用STS讲解。

STS下载解压之后可以直接运行而且会自动使用本地的Java运行时环境,如果没有匹配好,可以在STS的Window > Preferences > Installed JREs里添加本地JDK,前提时本地环境已经安装Java环境。

6.3 创建项目

在STS中 File > New > Spring Starter Project来创建一个新项目。

5-1.jpg

选择项目所需的依赖,WebJPAMySQL Finish后STS会自动拉取项目依赖的jar包,稍微耗时耐心等待。

5-2.jpg

6.4 配置项目

此时项目仍然是无法启动的,因为创建项目时选择了JPA依赖,它是一个ORM框架,使用它必须要有可连接的数据库。找到配置文件src/main/resources/application.properties,添加下面的数据库配置

spring.datasource.url=jdbc:mysql://rm-2zenrqfr9iy0vbw17uo.mysql.rds.aliyuncs.com/test?useSSL=false
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

右键选中项目,Run AS > Spring Boot App就可以启动项目,因为Spring Boot内置了Tomcat所以不需要配置Server即可让项目运行起来,并可一个通过8080端口访问。

5-3.jpg

6.5 响应请求

src/main/java下创建新的包cn.mx.starter.controller,并在cn.mx.starter.controller下创建新的Class:IndexController.java

package cn.mx.starter.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class IndexController {

    @GetMapping("/")
    private String index() {
        return "Spring Boot";
    }
}

打开浏览器在地址栏输入 http://127.0.0.1:8080,即可收到到服务器返回的字符串:Spring Boot。

6.6 读写数据库

JPA

Java持久化API是一个将对象映射为关系数据库的标准技术,包含了Hibernate、Spring Data和Spring ORM等依赖,本书不想在此涉及太多JPA的细节,直接讲解如何使用。

Entity

在第4章创建了2个表: useraddress,分别创建这两个表对应的两个实体,并放在包cn.mx.starter.model下:

User

package cn.mx.starter.model;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Table(name = "user")
@Entity
public class User {

    @Id
    @GeneratedValue
    private Long id;
    
    @Column(nullable = false)
    private String name;
    
    @Column(nullable = false)
    private Integer age;
    
    @OneToMany()
    private List<Address> addresses;
    
    // Setter Getter
}

Address

package cn.mx.starter.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Table(name = "address")
@Entity
public class Address {
    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String addr;
    
    @ManyToOne()
    private User user;
    // 防止序列化JSON时循环引用
    @JsonIgnore
    public User getUser() {
        return user;
    }
    // Setter Getter
}

Address实体里的getUser()方法多了注解@JsonIgnore,该注解是为了解除User和Address序列化时循环引用,即序列化User时调用getAddresses(),而每一个Address序列化又调用getUser(),互相循环调用直至内存异常。读者可以尝试去掉该注解体会一下。

Repository

JPA提供了非常便捷的接口访问数据库,而且默认内置了很多方法直接读取数据库,比如findById。这里创建一个 UserRepository放在包cn.mx.starter.repository下:

package cn.mx.starter.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import cn.mx.starter.model.User;

public interface UserRepository extends JpaRepository<User, Long> {

}

Controller

IndexController增加新的访问路径/users返回数据库查询数据,使用注解@Autowired注入UserRepository

package cn.mx.starter.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.mx.starter.model.User;
import cn.mx.starter.repository.UserRepository;

@RestController
public class IndexController {

    @Autowired
    UserRepository userRepository;
    
    @GetMapping("/")
    private String index() {
        return "Spring Boot";
    }
    
    @GetMapping("/users")
    private List<User> users() {
        return userRepository.findAll();
    }
}

浏览器访问http://127.0.0.1:8080/users会返回查询数据的JSON格式:

[
  {
    id: 1,
    name: "john",
    age: 0,
    addresses: [
      {
        id: 1,
        addr: "朝阳区亚运村路"
      },
      {
        id: 2,
        addr: "学院路北京大学"
      }
    ]
  }
]

内置查询方法

调用http://127.0.0.1:8080/users/1可以查找id=1的用户。

//IndexController

@GetMapping("/users/{id}")
private User user(@PathVariable(name="id") Long id) {
    return userRepository.findById(id).orElse(null);
}

自定义查找方法

调用http://127.0.0.1:8080/users/?name=john可以查找name=join的用户。

//IndexController

@GetMapping("/users/")
private List<User> usersByName(@RequestParam(name="name") String name) {
    return userRepository.findByName("john");
}

因为findByName不是JPA内置方法,所以需要在UserRepository添加这个方法:

//UserRepository

public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByName(String name);
}

插入数据

终端里调用命令curl -X POST http://127.0.0.1:8080/users插入一个新的用户。

//IndexController

@PostMapping("/users")
private User postUser() {
    Address addr = new Address();
    addr.setAddr("学院路中关村");
    User user = new User();
    user.setName("Jack");
    user.setAge(10);
    user.setAddresses(Arrays.asList(addr));
    addr.setUser(user);
    userRepository.save(user);
    return user;
}

更新数据

//IndexController

@PutMapping("/users/{id}")
private User putUser(@PathVariable(name="id") Long id) {
    User user = userRepository.findById(id).orElse(null);
    if (user != null) {
        user.setName("Sara");
        userRepository.save(user);
    }
    return user;
}

打印SQL

使用JPA可以方便的用操作Java对象的方式操作数据库,如果想核对数据库SQL尤其是复杂查询对应的的SQL,可以在控制台打印出每次数据操作的的SQL语句,在配置文件添加如下配置:

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.type=trace

至此,一个简单的Spring Boot项目已经完成,连接数据库并对数据进行增删改查。JPA查询有一套内置的查找规则映射到对应的SQL查询,详情可以查阅 https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation

该项目中我们在Controller层直接调用了Repository层访问数据,实际项目业务逻辑会比较复杂,可以在中间添加Service层专门处理业务逻辑。实际项目的复杂之处在于,业务需求复杂,从而对应的数据库表之间的关联关系就复杂,处理好表之间的关联关系,捋清业务逻辑,剩下的技术层面无非是对数据库表的增删改查。

6.7 部署项目

项目完成之后,需要将项目打包放到服务器上测试。STS已经内置了Maven所以打包非常方便,右键项目 Run AS > Maven install,会将整个项目打成jar包并放在target目录下,默认是starter-0.0.1-SNAPSHOT.jar

5-4.jpg

因为Spring Boot内置了Tomcat并一起打包到jar文件里,所以可以直接在终端运行它:

java -jar target/starter-0.0.1-SNAPSHOT.jar

把打包好的jar文件传到服务器上/root目录下,scp是SSH附带的命令,通过SSH协议拷贝本地文件到远程服务器:

scp -i ~/.ssh/mx.pem target/starter-0.0.1-SNAPSHOT.jar root@59.110.173.162:/root

登录远程服务器,并且保证该服务器的网络安全组协议里开通了8080端口

ssh -i ~/.ssh/mx.pem root@59.110.173.162

运行项目,此时便可以通过IPhttp://59.110.173.162访问项目了:

java -jar starter-0.0.1-SNAPSHOT.jar

6.8 后台运行

在服务器上java -jar starter-0.0.1-SNAPSHOT.jar可以让项目运行起来,比可以通过地址正常访问,但是一旦退出远程登录则该java进程的服务就停止了,因为该服务是运行在前台。使用CentOS的Systemd来让项目可以运行在系统后台。

  1. 添加一个Systemd服务文件

    vim /etc/systemd/system/starter.service

  2. 写入如下内容,保存后退出

[Unit]
Description=starter
After=syslog.target

[Service]
ExecStart= /usr/bin/java -jar /root/starter-0.0.1-SNAPSHOT.jar

[Install]
WantedBy=multi-user.target

之后就可以通过systemctl启动、关闭和重启项目了

启动服务:

systemctl start starter.service

停止服务:

systemctl stop starter.service

服务状态:

systemctl status starter.service

开机启动:

systemctl enable starter.service

查看项目日志:

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

推荐阅读更多精彩内容