Создадим первое приложение на Spring boot, пусть оно будеть чуть сложнее, чем HelloWorld! Начнем рассмотрение с pom файла:
<?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>spring-boot-journal</groupId>
<artifactId>spring-boot-journal</artifactId>
<version>1.0</version>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Далее создадим сущность:
package ru.domain;
import javax.persistence.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
@Entity
public class Journal {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private Date create;
private String summary;
@Transient
private SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
public Journal(){
}
public Journal(String title, String summary,String create) throws ParseException{
this.title = title;
this.create = format.parse(create);
this.summary = summary;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Date getCreate() {
return create;
}
public void setCreate(Date create) {
this.create = create;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public String getCreatedAsShort(){
return format.format(create);
}
public String toString(){
return "JournalEntry(" + "Id: " +
id +
",Title: " +
title +
",Summary: " +
summary +
",Created: " +
getCreatedAsShort() +
")";
}
}
Здесь ничего необычного, т.к. используем JPA - значит нам нужный аннотации Entity, ID и GeneratedValue. Используем 2 коструктора, с параметрами для нашего удобства и без парамтеров - для JPA. Аннотация Transient, говорит о том, что это поле не будет мапиться на таблицу.
Далее создадим репозиторий, используя SpringData
package ru.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.domain.Journal;
public interface JournalRepository extends JpaRepository<Journal, Long> {
}
Данный интерфейс расширяет JpaRepository, что позволяет спрингу, с помощью proxy создавать не только просты CRUD действия, но и более сложные используя конвенцию имен, например, findById, findByTitle, findByTitleLike, findByTitleLikeIgnoreCase.
Теперь веб контроллер:
package ru.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import ru.domain.Journal;
import ru.repository.JournalRepository;
import javax.annotation.Resource;
import java.util.List;
@Controller
public class JournalController {
@Resource
private JournalRepository repository;
@RequestMapping("/journal")
public @ResponseBody List<Journal>getJournals(){
return repository.findAll();
}
@RequestMapping("/")
public String index(Model model){
model.addAttribute("journal", repository.findAll());
return "index";
}
}
Ну и сама веб страничка:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" >
<head>
<meta charset="UTF-8"/>
<meta http-equiv="Content-Type" content="text/html"/>
<title>Spring Boot Journal</title>
<link rel="stylesheet" type="text/css" media="all" href="css/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" media="all" href="css/bootstrap-glyphicons.css"/>
<link rel="stylesheet" type="text/css" media="all" href="css/styles.css"/>
</head>
<body>
<div class="container">
<h1>Spring Boot Journal</h1>
<ul class="timeline">
<div th:each="entry,status : ${journal}" >
<li th:attr="class=${status.odd}?'timeline-inverted':''" >
<div class="tl-circ"></div>
<div class="timeline-panel">
<div>
<h4> <span th:text="${entry.title}">TITLE</span>
</h4>
<p><small class="text-muted">
<i class="glyphicon glyphicon-time"></i>
<span th:text="${entry.createdAsShort}">CREATED</span>
</small>
</p>
</div>
<div class="tl-body">
<p> <span th:text="${entry.summary}">SUMMARY</span> </p>
</div>
</div>
</li>
</div>
</ul>
</div>
</body>
</html>
Ну и сам запуск приложения:
package ru;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
import org.springframework.context.annotation.Bean;
import ru.domain.Journal;
import ru.repository.JournalRepository;
@SpringBootApplication( exclude={ActiveMQAutoConfiguration.class} )
public class Application {
public static void main(String[] arg){
SpringApplication.run(Application.class, arg);
}
@Bean
InitializingBean saveData(JournalRepository repository){
return () -> {
repository.save(new Journal("Get to know Spring Boot","Today I will learn Spring Boot","01/01/2016"));
repository.save(new Journal("Simple Spring Boot Project","I will do my first Spring Boot Project","01/02/2016"));
repository.save(new Journal("Spring Boot Reading","Read more about Spring Boot","02/01/2016"));
repository.save(new Journal("Spring Boot in the Cloud","Spring Boot using Cloud Foundry","03/01/2016"));
};
}
}
Запустив приложение, увидим:
Отлично, все работает! Но за счет чего? Ведь нет ни web.xml, ни xml или java конфигов для сприга. Если открыть аннотацию SpringBootApplication, то мы увидим следующее:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.boot.autoconfigure;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class,
attribute = "exclude"
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class,
attribute = "excludeName"
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
}
И после этого становится все понятнее. Основная аннтоция - EnableAutoConfiguration. С ее помощью спринг использует автоконфигуратор, опираясь на classpath, аннотация, которые используются в коде. Например, спринг заметит, что у нас используется зависимость spring-boot-starter-web, и поймет, что у нас веб приложение и будет конфигурировать наше приложение, как веб. Так же к приложению будет вшит Tomcat для запуска и после сборки проекта, через mvn package мы сможем запустить наше приложение не используя дополнительный сервер приложений.