Javaã§Webã¢ããªã±ã¼ã·ã§ã³ãéçºããéã®ãã¬ã¼ã ã¯ã¼ã¯ã¨ãã¦ãè¿å¹´Apache Strutsã«ä»£ããSpring Frameworkãåºã使ããã¦ããã ããã§ã¯ãSpringãæä¾ããBootstrapãã¬ã¼ã ã¯ã¼ã¯Spring Bootãç¨ãã¦ãç°¡åãªWebã¢ããªã±ã¼ã·ã§ã³ãæ¸ãã¦ã¿ãã
ç°å¢
Windows 10 ProãJava SE 8ãSpring Framework 4.3.7.RELEASEï¼Spring Boot 1.5.2.RELEASEï¼
>systeminfo OS å: Microsoft Windows 10 Pro OS ãã¼ã¸ã§ã³: 10.0.14393 N/A ãã«ã 14393 OS ãã«ãã®ç¨®é¡: Multiprocessor Free ã·ã¹ãã ã®ç¨®é¡: x64-based PC ããã»ããµ: 1 ããã»ããµã¤ã³ã¹ãã¼ã«æ¸ã¿ã§ãã [01]: Intel64 Family 6 Model 69 Stepping 1 GenuineIntel ~1596 Mhz >java -version java version "1.8.0_121" Java(TM) SE Runtime Environment (build 1.8.0_121-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
JDK 8ã®ã¤ã³ã¹ãã¼ã«
ã¾ããJavaéçºç°å¢ã§ããJDK 8ãã¤ã³ã¹ãã¼ã«ããã JDKãã¤ã³ã¹ãã¼ã«ããã¨ãå®è¡ç°å¢ã§ããJREãåæã«ã¤ã³ã¹ãã¼ã«ãããã
Spring Tool Suite (STS) ã®ãã¦ã³ãã¼ã
次ã«ãEclipseãã¼ã¹ã®IDEã§ããSpring Tool Suite (STS) ããã¦ã³ãã¼ãããã
spring-tool-suite-3.8.4.RELEASE-e4.6.3-win32-x86_64.zipãå±éããSTS.exeãå®è¡ããã ååèµ·åæã«Workspaceï¼ããã¸ã§ã¯ãã®ä¿åå ï¼ãèãããã®ã§ãé©å½ãªãã¹ãæå®ãOKãé¸æããã¨ãSTSãèµ·åããã
Spring Frameworkã¨Spring Boot
Spring Frameworkã¯ãDependency Injectionï¼DIï¼ã¨Aspect-oriented programmingï¼AOPï¼ã¨å¼ã°ããè¨è¨ææ³ãæ´»ç¨ããJavaãã¬ã¼ã ã¯ã¼ã¯ã§ããã ãããã®ææ³ã«ãããå¾æ¥ã®ãã¬ã¼ã ã¯ã¼ã¯ã«å¯¾ãã¦ãæ¯è¼çç°¡æ½ã«ããã°ã©ã ãå®è£ ãããã¨ãã§ããã
Spring Bootã¯ãSpringã®å種ããã¸ã§ã¯ãã使ã£ã¦ç°¡åã«Webã¢ããªã±ã¼ã·ã§ã³ãæ¸ããã¨ãã§ããBootstrapãã¬ã¼ã ã¯ã¼ã¯ã§ããã Spring Bootã«ã¯Apache Tomcatãä»å±ãã¦ãããå¾æ¥ã®WARãã¡ã¤ã«ãä½æãã¦ã¢ããªã±ã¼ã·ã§ã³ãµã¼ãã«ãããã¤ããæ¹æ³ã®ä»ã«ãå®è¡å¯è½JARãä½æãã¦åç¬ã§Tomcatãµã¼ããèµ·åããæ¹æ³ãé¸ã¶ãã¨ãã§ããã
Spring Bootã§ç°¡åãªWebã¢ããªã±ã¼ã·ã§ã³ãæ¸ãã¦ã¿ã
ããã§ã¯ãWebã¢ããªã±ã¼ã·ã§ã³ã¨ãã¦ç°¡åãªæ²ç¤ºæ¿ãä½ããã¨ã«ããã ã¾ããæ§ç¯ãç°¡åã«ããããã«ããã¼ã¿ãã¼ã¹ã¨ãã¦MySQLçã®ä»£ããã«Java製ã¤ã³ã¡ã¢ãªDBã§ããH2ãå©ç¨ããã
ã¾ããSTSã§ããã¸ã§ã¯ããä½æããã
- ã¡ãã¥ã¼ãã¼ãããFileãâãNewãâãSpring Starter Projectããé¸æ
- Nameãé©å½ã«è¨å®ãï¼ããã§ã¯demoã®ã¾ã¾ã¨ããï¼ãPackagingã«Warãé¸æãã¦Next
- Dependenciesã¨ãã¦WebãThymeleafãJPAãH2ãé¸æãã¦NextãFinish
- Web: MVCãã¬ã¼ã ã¯ã¼ã¯ï¼Spring MVCï¼
- Thymeleaf: ãã³ãã¬ã¼ãã¨ã³ã¸ã³
- JPA: O/Rãããã¼
- H2: ã¤ã³ã¡ã¢ãªDB
Spring Bootã®ãã³ãã¬ã¼ããå±éãããã®ã§ãé ã«å¿ è¦ãªã¯ã©ã¹ãã¡ã¤ã«ãä½æãã¦ããã ã¯ã©ã¹ãã¡ã¤ã«ãæ°è¦ä½æããã«ã¯ãä¾ãã°src/main/java/com.example/MessageController.javaã®å ´å次ã®ããã«ããã
- ãsrc/main/java/com.exampleããå³ã¯ãªãã¯ãã¦ãNewãâãClassããé¸æ
- Nameã«MessageControllerãå ¥åãã¦Finish
以éã«è¿°ã¹ããã¹ã¦ã®ãã¡ã¤ã«ãä½æããå¾ã®ãPackage Explorerã®ã¹ã¯ãªã¼ã³ã·ã§ããã次ã«ç¤ºãã
Web (Spring MVC)
Spring MVCã¯ãMVCãã¬ã¼ã ã¯ã¼ã¯ã§Webã¢ããªã±ã¼ã·ã§ã³éçºãè¡ãããã®ã©ã¤ãã©ãªã§ãããSpring Frameworkã®ä¸æ ¸ãæ ããã®ã§ããã Rubyã«ãããRailsãPythonã«ãããDjangoã«å¯¾å¿ã
HTTPãªã¯ã¨ã¹ããå¦çããControllerã¯æ¬¡ã®ããã«ãªãã
package com.example; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; 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.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @Controller public class MessageController { @Autowired private MessageService service; @GetMapping("/messages") public String messages(Model model) { model.addAttribute("messageForm", new MessageForm()); List<Message> messages = service.getRecentMessages(100); model.addAttribute("messages", messages); return "messages"; } @PostMapping("/messages") public String messagesPost(Model model, @Valid MessageForm messageForm, BindingResult bindingResult, HttpServletRequest request) { if (bindingResult.hasErrors()) { List<Message> messages = service.getRecentMessages(100); model.addAttribute("messages", messages); return "messages"; } service.save(new Message(messageForm.getName(), messageForm.getText(), request.getRemoteAddr())); return "redirect:/messages"; } }
@GetMapping
ããã³@PostMapping
ã¯ããããHTTPã®ã¨ã³ããã¤ã³ãã«å¯¾å¿ãã¦ãããã¢ããã¼ã·ã§ã³ãä»ããããé¢æ°ããªã¯ã¨ã¹ãã«å¿ãã¦å®è¡ãããã
returnã§è¿ãããæååã¯Viewã®ãã³ãã¬ã¼ããã¡ã¤ã«åã表ãã¦ãããredirect:
ãã¤ãã¦ããå ´åã¯ãã®ã¨ã³ããã¤ã³ãã«HTTPãªãã¤ã¬ã¯ããè¡ãããã
ã¾ãã@Autowired
ã¯Dependency Injectionãæå³ãã¦ãããMessageServiceã®ã¤ã³ã¹ã¿ã³ã¹ãå®è¡æã«ä»£å
¥ãããã
ãã©ã¼ã ããéä¿¡ããããã©ã¡ã¼ã¿ãå®ç¾©ããã¯ã©ã¹ã¯æ¬¡ã®ããã«ãªãã
package com.example; import javax.validation.constraints.Size; public class MessageForm { @Size(max=80) private String name; @Size(min=1, max=140) private String text; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getText() { return text; } public void setText(String text) { this.text = text; } }
@Size
ã¢ããã¼ã·ã§ã³ã«ãããåãã©ã¡ã¼ã¿ã®å¶ç´ãæå®ããã¦ããã
ãã®å¶ç´ã¯Controllerã®@Valid
ã¢ããã¼ã·ã§ã³ã«ãããã§ãã¯ãããã¨ã©ã¼ãããå ´åã¯ã¨ã©ã¼ã¡ãã»ã¼ã¸ãViewã«æ¸¡ãããã
Thymeleaf
Thymeleafã¯ãã³ãã¬ã¼ãã¨ã³ã¸ã³ã§ãããå¾æ¥ã®JSPã«ä»£ãããã®ã§ããã Rubyã«ãããERBãPythonã«ãããJinja2ã«å¯¾å¿ã
ãã³ãã¬ã¼ããã¡ã¤ã«ã¯æ¬¡ã®ããã«ãªãã
- src/main/resources/templates/messages.html
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8" /> <title>Hello, Spring Boot!</title> </head> <body> <h1>Hello, Spring Boot!</h1> <form action="#" th:action="@{/messages}" th:object="${messageForm}" method="post"> <p>Name (optional): <input type="text" th:field="*{name}" /> <em th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</em></p> <p><textarea cols="40" rows="4" placeholder="Type anything" th:field="*{text}"></textarea> <em th:if="${#fields.hasErrors('text')}" th:errors="*{text}">Text Error</em></p> <p><input type="submit" value="Submit" /></p> </form> <h2>Recent messages</h2> <dl> <th:block th:each="message : ${messages}"> <dt> <span class="name" th:text="${message.name}" th:attr="title=${message.remoteAddr}">John Doe</span> <small th:text="${#dates.format(message.createdAt, '(yyyy-MM-dd HH:mm:ss)')}">(1970-01-01 00:00:00)</small> </dt> <dd th:text="${message.text}">Lorem ipsum dolor sit amet</dd> </th:block> </dl> </body> </html>
Thymeleafã§ã¯thåå空éãç¨ãã¦æ§é ãè¨è¿°ããã
è¦ç´ ããã¹ãã®åºåã«th:text
ãç¨ãããã¨ã§ãHTMLã¨ã¹ã±ã¼ããèªåã§è¡ãããã
ãã®ã¨ãããã³ãã¬ã¼ãä¸ã®è¦ç´ ããã¹ãã¯ç¡è¦ããããããä¾ç¤ºããã¹ããè¨è¿°ãã¦ããã
å¤æ°ã¯${messages}
ã®ããã«ãã¦åç
§ããã
ã¾ããth:object
ã§ãªãã¸ã§ã¯ããæå®ãããã®ä¸ä½è¦ç´ ã§*{name}
ã®ããã«è¨è¿°ãããã¨ã§æå®ãããªãã¸ã§ã¯ãã®ããããã£ãåç
§ã§ããã
HTMLè¦ç´ ã«å¯¾å¿ããªããããã¯æ§é ã¯th:block
ã§è¡¨ããã¨ãã§ããã
JPA
JPAã¯Javaã®ãªãã¸ã§ã¯ãã¨DBã®ãªã¬ã¼ã·ã§ã³ãçµã³ä»ããO/Rãããã¼ã§ããã JPAãç¨ãããã¨ã§ãDBã«ä¾åããSQLæãç´æ¥è¨è¿°ãããã¨ãªããã¼ã¿ã®åå¾ãä¿åãã§ããã Rubyã«ãããActive RecordãPythonã«ãããSQLAlchemyã«å¯¾å¿ã
ä¸è¬ã«ãJPAã§ã¯EntityãRepositoryãServiceã®ä¸ã¤ãå®è£ ãããã ãã¼ãã«å®ç¾©ã«å¯¾å¿ããEntityã¯ã©ã¹ã¯æ¬¡ã®ããã«ãªãã
package com.example; import java.util.Date; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.PrePersist; import javax.persistence.Temporal; import javax.persistence.TemporalType; @Entity public class Message { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(nullable = false) private String name; @Column(nullable = false) private String text; @Column(nullable = false) private String remoteAddr; @Temporal(TemporalType.TIMESTAMP) @Column(updatable = false) private Date createdAt; // JPA requirement protected Message() {} public Message(String name, String text, String remoteAddr) { this.name = name; this.text = text; this.remoteAddr = remoteAddr; } @PrePersist public void prePersist() { this.createdAt = new Date(); } @Override public String toString() { return String.format("Message[id=%d, name='%s', text='%s']", id, name, text); } public Long getId() { return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getText() { return text; } public void setText(String text) { this.text = text; } public String getRemoteAddr() { return remoteAddr; } public void setRemoteAddr(String remoteAddr) { this.remoteAddr = remoteAddr; } public Date getCreatedAt() { return createdAt; } }
JPAã®ä»æ§ã«å¾ã空ã®ã³ã³ã¹ãã©ã¯ã¿ãprotectedã§å®è£
ããå¿
è¦ããããã¨ãDateåã«ã¯@Temporal(TemporalType.TIMESTAMP)
ãã¤ããå¿
è¦ããããã¨ã«æ³¨æã
nullãå
¥ããã¨ãæå¾
ããªããã£ã¼ã«ãã«ã¯@Column(nullable = false)
ãã¤ãã¦ããã®ãç¡é£ã§ããã
InsertæãUpdateæã®å¦çã¯ã@PrePersist
ã@PreUpdate
ã¢ããã¼ã·ã§ã³ãã¤ããã¡ã½ããã§å®ç¾©ã§ããã
ããã§ã¯ã@PrePersist
ã§createdAtã¡ã³ãã«ä½ææ¥æãã»ãããã@Column(updatable = false)
ãã¤setteræªå®ç¾©ã¨ãããã¨ã§æ´æ°ã§ããªãããã«ãã¦ããã
ã¾ããidã¡ã³ãã@GeneratedValue(strategy = GenerationType.AUTO)
ã«ããèªåçæãããããsetteræªå®ç¾©ã¨ãã¦ããã
ãªããgetter/setterã¡ã½ããã¯å³ã¯ãªãã¯ãããSourceãâãGenerate Getter and Setters...ããé¸æãã¦èªåçæããã¨æ¥½ã§ããã
DBæä½ã«å¯¾å¿ããRepositoryã¤ã³ã¿ãã§ã¼ã¹ã¯æ¬¡ã®ããã«ãªãã
package com.example; import java.util.List; import org.springframework.stereotype.Repository; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.CrudRepository; @Repository public interface MessageRepository extends CrudRepository<Message, Long> { List<Message> findByOrderByIdDesc(Pageable pageable); }
CrudRepositoryã¤ã³ã¿ãã§ã¼ã¹ãç¶æ¿ãããã¨ã§ãfindAll()
ãsave()
ãdelete()
çã®ã¡ã½ãããæé»ã«å®ç¾©ãããã
ã¾ããfindByName(String name)
ã®ãããªã¡ã½ãããå®ç¾©ããã¨ãSELECT * FROM messages WHERE name = ?
ã«ç¸å½ããæä½ãè¡ãã¡ã½ããã¨ãªãã
ä¸ã®ã³ã¼ãã«ãããfindByOrderByIdDesc()
ã¯Byã®å¾ã®ã«ã©ã åãæãããã®ã§ãSELECT * FROM messages ORDER BY id DESC
ã«ç¸å½ããã
ãªããã¤ã³ã¿ãã§ã¼ã¹ã®å®è£
ã¯å®è¡æã«èªåã§æä¾ãããã
Controllerã«å¯¾ãã¦å ¬éããServiceã¯ã©ã¹ã¯æ¬¡ã®ããã«ãªãã
package com.example; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class MessageService { @Autowired private MessageRepository repository; public List<Message> getRecentMessages(Integer n) { return repository.findByOrderByIdDesc(new PageRequest(0, n)); } @Transactional public void save(Message message) { repository.save(message); } }
save()
ã®ãããªDBã«å¤æ´ãå ããã¡ã½ããã¯ã@Transactional
ãã¤ãã¦ä¾å¤çºçæã«ãã¼ã«ããã¯ãããããã«ããã
Controllerã¨åæ§ã«ãä¸ã®ã³ã¼ãã§ã@Autowired
ã«ããDependency Injectionãå®ç¾©ããã¦ãããMessageRepositoryã®ã¤ã³ã¹ã¿ã³ã¹ãå®è¡æã«ä»£å
¥ãããã
ä»å±ã®Tomcatãµã¼ãã§åããã¦ã¿ã
ä»å±ã®Tomcatãµã¼ãã§Webã¢ããªã±ã¼ã·ã§ã³ãåããããã©ã¦ã¶ããã¢ã¯ã»ã¹ãã¦ã¿ãã
- ãdemo [boot]ããå³ã¯ãªãã¯ãã¦ãRun AsãâãSpring Boot Appããé¸æ
- Tomcatãèµ·åãããããã©ã¦ã¶ãã http://localhost:8080/messages ã«ã¢ã¯ã»ã¹
ãã©ã¦ã¶ã§è¡¨ç¤ºããå¾ã®ã¹ã¯ãªã¼ã³ã·ã§ããã次ã«ç¤ºãã
å®è¡ãä¸æ¢ãTomcatãµã¼ããåæ¢ããã«ã¯ãStopãã¿ã³ãæ¼ãã°ããã
Pivotal tc Serverã§åããã¦ã¿ã
STSã§ã¯ããããã¤ç¨ãµã¼ãã¨ãã¦Pivotal tc Serverãç¨æããã¦ããã ãã®ãµã¼ãã®ä¸ã§Webã¢ããªã±ã¼ã·ã§ã³ãåããã«ã¯ã次ã®ããã«ããã
- ãdemo [boot]ããå³ã¯ãªãã¯ãã¦ãRun Asãâã1 Run on Serverããé¸æ
- ãµã¼ãã¨ãã¦localhostã®Pivotal tc Serverãé¸æãã¦Next
- å³å´ã®Configuredã«ä½æããããã¸ã§ã¯ãï¼ããã§ã¯demoï¼ãå ¥ã£ã¦ãããã¨ã確èªãã¦Finish
- ãµã¼ããèµ·åããå¾ãSTSã®ä¸å¤®ãã¤ã³ã§Webãã©ã¦ã¶ãéãã®ã§ãã®ã¾ã¾ http://localhost:8080/demo/messages ã«ã¢ã¯ã»ã¹
ãµã¼ããåæ¢ãããã«ã¯ãä»å±ã®Tomcatãµã¼ãã®å ´åã¨åæ§ã«Stopãã¿ã³ãæ¼ãã°ããã
WARãã¡ã¤ã«ãä½æãã¦ã¿ã
ä»ã®ã¢ããªã±ã¼ã·ã§ã³ãµã¼ãã«ãããã¤ããããã®WARãã¡ã¤ã«ãä½æããã«ã¯æ¬¡ã®ããã«ããã
- ãdemo [boot]ããå³ã¯ãªãã¯ãã¦ãExport...ããé¸æ
- ãWebãâãWAR fileããé¸æãã¦Next
- Destinationã«ä¿åå ãã£ã¬ã¯ããªãæå®ãã¦Finish
é¢é£ãªã³ã¯
- Getting Started · Serving Web Content with Spring MVC
- Getting Started · Handling Form Submission
- Getting Started · Validating Form Input
- Getting Started · Accessing Data with JPA
- å¿ è¦æå°éã®ãµã³ãã«ã§Thymeleafãå®å ¨ãã¹ã¿ã¼ - Java EE äºå§ãï¼
- SpringBootå ¥éï¼JPAã§ãã¼ã¿ã¢ã¯ã»ã¹ - Webç³»éçºã¡ã¢
- java - Why does JPA require a no-arg constructor for domain objects? - Stack Overflow
- Eclipseã·ã§ã¼ãã«ãããã¼ã¾ã¨ã - wyukawaâs blog