[SpringBoot2.4.3] Serviceクラスを使用する

.com.example.demo.service
RandomNumberService.java

package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class RandomNumberService {
	
	public int zeroToNine() {
		return (int)(Math.random() * 10);
	}
}

HelloController.java

package com.example.demo;

import java.util.Collections;
import java.util.Map;

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

import com.example.demo.service.RandomNumberService;

@RestController
public class HelloController {
	
	@Autowired
	RandomNumberService random;

	@RequestMapping("/random")
	public Map<String, Integer>random(){
		int value = random.zeroToNine();
		return Collections.singletonMap("value", value);
	}
}

http://localhost:8080/random
{“value”:2}

autowiredで呼び出すのか。。

[Spring Boot2.4.2] XMLで文字列表示

MainController.java

package com.example.demo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainController {
	public static void main(String[] args) {
		
		ClassPathXmlApplicationContext context =
				new ClassPathXmlApplicationContext("applicationContext.xml");
		
		ISyain syain = context.getBean("testSyain", ISyain.class);
		
		System.out.println(syain.getHello());
		context.close();		
	}
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="testSyain" class="com.example.demo.Syain"></bean>
</beans>

ISyain.java

package com.example.demo;

public interface ISyain {
	public String getHello();
}

Syain.java

package com.example.demo;

public class Syain implements ISyain {
	@Override
	public String getHello() {
		return "Hello World!";
	}
}

なるほどー

[Spring Boot2.4.2] 例外処理

TestException.java

package com.example.demo;

public class TestException extends RuntimeException {
	private static final long serialVersionUID = 1L;
	
	TestException(String msg){
		super(msg);
	}
}

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="utf-8" />
    <title>Check</title>
  </head>
  <body>
    <p th:text="${message1}"></p>
  </body>
</html>

MainController.java

package com.example.demo;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/test1")
public class MainController {

    @GetMapping
	public String index(Model model)  {
	runSample();
	 return "test1/index";
	}
    
    void runSample()   {
		int i = 5;
		if (i == 5) {
			throw new TestException("独自の例外です"); 
		}
	}
}

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sun Feb 07 16:57:28 JST 2021
There was an unexpected error (type=Internal Server Error, status=500).
独自の例外です
com.example.demo.TestException: 独自の例外です

[Spring Boot2.4.2] JSONの送受信

src/main/resources/templates/test1/index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <p id="p1"></p>
    <p id="p2"></p>
  </body>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
		$("#p1").text("");
		$("#p2").text("");
        var json1 = {
			bangou: "1",
			name: "鈴木",
		};
		$.ajax({
		url: "http://localhost:8080/test1/index",
		type: "POST",
		contentType: "application/json",
		data: JSON.stringfy(json1),
		dataType: "json",
		})
		.done(function (data1, textStatus, jqXHR){
			$("#p1").text(jqXHR.status);
			$("#p2").text(JSON.stringify(data1));
		})
		.fail(function (jqXHR, textStatus, errorThrown){
			$("#p1").text(jqXHR.status);
		})
		.always(function() {});
</script>
</html>

Syain.java

package com.example.demo;
import java.io.Serializable;

public class Syain implements Serializable {
	private static final long serialVersionUID = 1L;
	
	private String bangou;
	private String name;
	
	public String getBangou() {
		return bangou;
	}
	public void setBangou(String bangou) {
		this.bangou = bangou;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

MainController.java
L @ResponseBodyはコントローラからの戻り値を返す
L

package com.example.demo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test1")
public class MainController {
	
	@PostMapping("/index")
	@ResponseBody
    public Syain output1(
    		@RequestBody Syain syain) {
				System.out.println(syain.getBangou());
				System.out.println(syain.getName());
				return syain;
	}
}

2021-02-07 12:07:42.608 INFO 44783 — [ restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 772 ms
2021-02-07 12:07:42.743 INFO 44783 — [ restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService ‘applicationTaskExecutor’
2021-02-07 12:07:42.889 INFO 44783 — [ restartedMain] o.s.b.d.a.OptionalLiveReloadServer : LiveReload server is running on port 35729
2021-02-07 12:07:42.919 INFO 44783 — [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ”
2021-02-07 12:07:42.927 INFO 44783 — [ restartedMain] com.example.demo.TestApplication : Started TestApplication in 1.355 seconds (JVM running for 7.09)
2021-02-07 12:08:35.401 INFO 44783 — [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet ‘dispatcherServlet’
2021-02-07 12:08:35.401 INFO 44783 — [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet ‘dispatcherServlet’
2021-02-07 12:08:35.402 INFO 44783 — [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2021-02-07 12:08:37.410 WARN 44783 — [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method ‘GET’ not supported]

うーむ、上手く表示されんな。。

[Spring Boot2.4.2] AOP(アスペクト指向プログラミング)

pom.xml

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

AOPファイル TestLog.java

package com.example.demo;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TestLog {
	
	@After("within(com.example.demo.MainController)")
	public void write1() {
		System.out.println("ログイン後");
	}
}

@AspectはAOPとして動作
@After, @Before, @Around, @AfterReturning, @AfterThrowing などがある。

MainController.java

package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MainController {
	
	@GetMapping("/test1")
    public String input1() {
        return "test1";
    }
	
	@GetMapping("/test2")
	public String input2(){
        return "test2";
    }
}

Error starting ApplicationContext. To display the conditions report re-run your application with ‘debug’ enabled.
2021-02-07 11:29:27.692 ERROR 43506 — [ restartedMain] o.s.boot.SpringApplication : Application run failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘requestMappingHandlerMapping’ defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map ‘test1’ method
com.example.demo.Test1#write1()
to {GET [/test2]}: There is already ‘mainController’ bean method
com.example.demo.MainController#input2() mapped.

ん? 何故だ??

[Spring Boot2.4.2] フォームとエンティティの連携

エンティティとは?
-> relational databaseの表を表す

src/main/resources/templates/test1/index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>check</title>
</head>
<body >
<form method="post" action="#" th:action="@{/test1/testform}" th:object="${test1Form}">
<p><input type="text" id="id" name="id" th:field="*{id}"/></p>
<p><input type="text" id="name" name="name" th:field="*{name}"></p>
<p><input type="submit" value="送信ボタン"></p>
</form>
</body>
</html>

フォームクラス: Test1Form.java
L controllerのmodel.addAttribute(“test1Form”, new Test1Form());でvalidationをかけている

package com.example.demo;

public class Test1Form {
	private Integer id;
	private String name;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

エンティティのクラス: Syain.java (社員)
L formのvalidationと同じく、privateで宣言して、geter, setterを書く
L getter, setterは、データを外部から取得するメソッド、あるいはそのデータを外部から変更するメソッド

package com.example.demo;

public class Syain {
	private Integer id;
	private String name;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

MainController.java

package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Controller
@RequestMapping("/test1")
public class MainController {
	@Autowired
	private SyainRepository syainRepository;
	
    @GetMapping
    public String disp1(
    		Model model) {
    	model.addAttribute("test1Form", new Test1Form());
        return "test1/index";
    }
    @PostMapping("/testform")
	public String disp2(Test1Form test1Form) {
		    Syain syain = new Syain();
		    syain.setId(test1Form.getId());
		    syain.setName(test1Form.getName());
		    syainRepository.insertSyain(syain);
			return "redirect:/test1/";
		}
}

pom.xml

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>

SyainRepository.java

package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class SyainRepository {
	private final JdbcTemplate jdbcTemplate;
	
	@Autowired
	public SyainRepository(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}
	
	public void insertSyain(Syain syain) {
		jdbcTemplate.update("INSERT INTO syain(id,name) VALUES (?, ?)",
				syain.getId(), syain.getName());
	}
}

Description:

Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class

なるほど、何となくわかってきた。

[Spring Boot2.4.2] プロジェクト作成

Test1.java

package com.example.demo;

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

@RestController
public class Test1 {
	
	@GetMapping("/test1")
	public String write1() {
		return "Hello World1";
	}
}

RestControllerはメソッドの戻り値を画面に表示する
@GetMappingは@RequestMapping(method=RequestMethod.GET)と同じ意味
Run As -> Spring boot
http://localhost:8080/test1
Hello World1

application.properties

server.port=8756

pom.xml

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>true</scope>
		</dependency>

spring-boot-devtoolsをtrueにするとファイルを更新した際に、アプリを再起動しなくても更新される。

[Spring Boot2.4.2] postgresのCRUDの前準備

Spring Bootのアーキテクチャ

CRUDの処理は、Mapper(Repository)クラスで行なっている。
Spring Initializrで雛形をgenerateする。
DependenciesにValidation, Spring Web, MyBatis, PostgreSQL Driveを追加する。
mybatisとは?
-> カスタムSQL、ストアドプロシージャ、高度なマッピング処理に対応

src/main/resources/application.yml

# Web
server:
  port: 8081
  servlet:
    context-path: /api

postgres/initdb/01_DDL_CREATE_TABLE.sql

CREATE TABLE customer (
	id VARCHAR(10) PRIMARY KEY,
	username VARCHAR(50) NOT NULL,
	email VARCHAR(50) NOT NULL,
	phone_number VARCHAR(11) NOT NULL,
	post_code VARCHAR(7) NOT NULL
);

postgres/initdb/02_DML_INSERT_INIT_DATA.sql

INSERT INTO customer VALUES ('001', 'user001', 'test.user.001@example.com', '12345678901', '1234567');
INSERT INTO customer VALUES ('002', 'user002', 'test.user.002@example.com', '23456789012', '2345671');
INSERT INTO customer VALUES ('003', 'user003', 'test.user.003@example.com', '34567890123', '3456712');
INSERT INTO customer VALUES ('004', 'user004', 'test.user.004@example.com', '45678901234', '4567123');
INSERT INTO customer VALUES ('005', 'user005', 'test.user.005@example.com', '56789012345', '5671234');
INSERT INTO customer VALUES ('006', 'user006', 'test.user.006@example.com', '67890123456', '6712345');
INSERT INTO customer VALUES ('007', 'user007', 'test.user.007@example.com', '78901234567', '7123456');
INSERT INTO customer VALUES ('008', 'user008', 'test.user.008@example.com', '89012345678', '1234567');
INSERT INTO customer VALUES ('009', 'user009', 'test.user.009@example.com', '90123456789', '2345671');
INSERT INTO customer VALUES ('010', 'user010', 'test.user.010@example.com', '01234567890', '3456712');

test=> select * from customer;
id | username | email | phone_number | post_code
—–+———-+—————————+————–+———–
001 | user001 | test.user.001@example.com | 12345678901 | 1234567
002 | user002 | test.user.002@example.com | 23456789012 | 2345671
003 | user003 | test.user.003@example.com | 34567890123 | 3456712
004 | user004 | test.user.004@example.com | 45678901234 | 4567123
005 | user005 | test.user.005@example.com | 56789012345 | 5671234
006 | user006 | test.user.006@example.com | 67890123456 | 6712345
007 | user007 | test.user.007@example.com | 78901234567 | 7123456
008 | user008 | test.user.008@example.com | 89012345678 | 1234567
009 | user009 | test.user.009@example.com | 90123456789 | 2345671
010 | user010 | test.user.010@example.com | 01234567890 | 3456712

application.yml
-> camel caseとsnake caseの命名の差分をMyBatis側で吸収し、適切にテーブル・カラム名とクラス・フィールド名をマッピングする。

# Datasource
spring:
  datasource:
    driverClassName: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/test
    username: root
    password:
    
# MyBatis
mybatis:
  configuration:
    map-underscore-to-camel-case: true

com.example.demo.controllerにCustomerController.javaを作る
CustomerController.java

package com.example.demo.controller;

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

@RestController
public class CustomerController {
	
	@GetMapping("/hello")
	public String hello() {
		return "Hello World.";
	}
}


ここまでは基礎

build.gradleに依存性を追加

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4'
	runtimeOnly 'org.postgresql:postgresql'
	testImplementation 'org.dbunit:dbunit:2.5.3'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

[Spring Boot2.4.2] .jarファイルにしてvagrantにデプロイしたい

STSで開発したプログラムをvagrantにデプロイしたい
-> Spring Boot Mavenプラグインを追加することで、実行可能なファイルとしてパッケージ化することができる。
-> pom.xmlに追加する

<packaging>jar</packaging>
// 省略
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

run as -> maven install

targetフォルダに demo-0.0.1-SNAPSHOT.jar が生成された

vagrantに.jarファイルを配置します

$ java -version
openjdk version “1.8.0_265”
OpenJDK Runtime Environment (build 1.8.0_265-b01)
OpenJDK 64-Bit Server VM (build 25.265-b01, mixed mode)

$ wget https://d3pxv6yz143wms.cloudfront.net/11.0.2.9.3/java-11-amazon-corretto-devel-11.0.2.9-3.x86_64.rpm
$ sudo yum localinstall java-11-amazon-corretto-devel-11.0.2.9-3.x86_64.rpm
$ java -version
openjdk version “11.0.2” 2019-01-15 LTS
OpenJDK Runtime Environment Corretto-11.0.2.9.3 (build 11.0.2+9-LTS)
OpenJDK 64-Bit Server VM Corretto-11.0.2.9.3 (build 11.0.2+9-LTS, mixed mode)
$ rm -rf java-11-amazon-corretto-devel-11.0.2.9-3.x86_64.rpm
$ java -jar demo-0.0.1-SNAPSHOT.jar

http://192.168.33.10:8080/view/what-time-is-it

うお、何これ?
サーバにJavaさえ入ってれば動くやん
Sugeeeeeeeeeeeeee
マジでビビった。

[Spring Boot2.4.2] PostgresSQLに連携したい

まず、postgres側でテーブルを作ってデータを入れます。

create table weather (
	id serial primary key,
	location_id int,
	name varchar(20),
	temperature int,
	humidity int,
	date_time timestamp
);

test=> insert into weather (location_id, name, temperature, humidity, date_time) values
(1, ‘東京’, 15, 55, ‘2021-02-02 09:00:00’),
(1, ‘東京’, 16, 53, ‘2021-02-02 10:00:00’),
(1, ‘東京’, 17, 40, ‘2021-02-02 11:00:00’),
(2, ‘那覇’, 20, 65, ‘2021-02-02 09:00:00’),
(2, ‘那覇’, 22, 67, ‘2021-02-02 10:00:00’),
(2, ‘那覇’, 25, 69, ‘2021-02-02 11:00:00’);
INSERT 0 6
test=> select * from weather;
id | location_id | name | temperature | humidity | date_time
—-+————-+——+————-+———-+———————
1 | 1 | 東京 | 15 | 55 | 2021-02-02 09:00:00
2 | 1 | 東京 | 16 | 53 | 2021-02-02 10:00:00
3 | 1 | 東京 | 17 | 40 | 2021-02-02 11:00:00
4 | 2 | 那覇 | 20 | 65 | 2021-02-02 09:00:00
5 | 2 | 那覇 | 22 | 67 | 2021-02-02 10:00:00
6 | 2 | 那覇 | 25 | 69 | 2021-02-02 11:00:00

application.properties

server.port=8080
spring.jpa.database=POSTGRESQL
spring.datasource.url=jdbc:postgresql://localhost:5432/test
spring.datasource.username=postgres
spring.datasource.password=

pom.xml

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.postgresql</groupId>
			<artifactId>postgresql</artifactId>
			<scope>runtime</scope>
		</dependency>

model
Weather.java

package com.example.demo.model;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="weather")
public class Weather {
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer id;
	
	private Integer location_id;
	
	private String name;
	
	private Integer temperature;
	
	private Integer humidity;
	
	private Timestamp date_time;
	
	public Integer getId() {
		return id;
	}
	
	public void setId(Integer id) {
		this.id = id;
	}
	
	public Integer getLocation_id() {
		return location_id;
	}
	
	public void setLocation_id(Integer location_id) {
		this.location_id = location_id;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public Integer getTemperature() {
		return temperature;
	}
	
	public void setTemperature(Integer temperature) {
		this.temperature = temperature;
	}
	
	public Integer getHumidity() {
		return humidity;
	}
	
	public void setHumidity(Integer humidity) {
		this.humidity = humidity;
	}
	
	public Timestamp getDate_time() {
		return date_time;
	}
	
	public void setDate_time(Timestamp date_time) {
		this.date_time = date_time;
	}
}

repository/WeatherRepository.java

package com.example.demo.repository;

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

import com.example.demo.model.Weather;

@Repository
public interface WeatherRepository extends JpaRepository<Weather, Integer>{}

service/WeatherService.java

package com.example.demo.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.demo.model.Weather;
import com.example.demo.repository.WeatherRepository;

@Service
@Transactional
public class WeatherService {
	
	@Autowired
	WeatherRepository weatherRepository;
	
	public List<Weather> findAllWeatherData(){
		return weatherRepository.findAll();
	}
}

HelloController.java

package com.example.demo;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.demo.model.Weather;
import com.example.demo.service.WeatherService;

@Controller
public class HelloController {
	@Autowired
	WeatherService weatherService;
	
	@RequestMapping("/hello")
	public String hello(Model model) {
		
		model.addAttribute("hello", "Hello World!");
		
		List<Weather> weatherDataList = weatherService.findAllWeatherData();
		model.addAttribute("weatherDataList", weatherDataList);
		
		return "hello";
	}
}

hello.html

<!Doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>SpringBoot</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta charset="UTF-8">
<!-- <link th:href="@{/css/common.css}" rel="stylesheet"></link>
<script th:src="@{/js/common.js}"></script> -->
</head>
<body>
<p>
	<span th:text="${hello}">Hello World!</span>
</p>
<div>
	<table>
		<tr th:each="data: ${weatherDataList}" th:object="${data}">
			<td th:text="*{id}"></td>
			<td th:text="*{location_id}"></td>
			<td th:text="*{name}"></td>
			<td th:text="*{temperature}"></td>
			<td th:text="*{humidity}"></td>
			<td th:text="*{date_time}"></td>
		</tr>
	</table>
</div>
</body>
</html>

lsof -i:8080
Run As -> SpringBoot app

http://localhost:8080/hello

Hello World!

1 1 東京 15 55 2021-02-02 09:00:00.0
2 1 東京 16 53 2021-02-02 10:00:00.0
3 1 東京 17 40 2021-02-02 11:00:00.0
4 2 那覇 20 65 2021-02-02 09:00:00.0
5 2 那覇 22 67 2021-02-02 10:00:00.0
6 2 那覇 25 69 2021-02-02 11:00:00.0

うおおおおおおおおおおおおお
マジか。。。
これ、Javaでアプリ作れんじゃん。