Небольшое вступление
Spark — это просто чудесный микрофреймворк для создания веб-приложений на джаве без особых усилий. Spark стремится к простоте и обеспечивает только минимальный набор функций. Тем не менее он предоставляет все необходимое для создания веб-приложения, которые поместятся в несколько строк кода. С синтаксисом, вдохновленным Sinatra, код выглядит очень чистым.
Давайте начнем со вездесущого хеллоуворлда.
Создаём новый проект и импортируем Spark. Лично я использую maven для управления зависимостями.
Добавляем в pom.xml.
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.6.0</version>
</dependency>
Теперь приступим непосредственно к «хеллоу ворлду».
import static spark.Spark.*;
public class Main {
public static void main(String[] args) {
get("/hello", (req, res) -> "Hello, World!");
}
}
Вот и всё. Всё настолько просто!
Теперь spark прослушивает GET запросы на /hello. Всякий раз, когда мы переходим на localhost:4567/hello, вызывается метод handle(). Внутри него мы возвращаем объект, который должен быть отправлен клиенту (в этом случае «Hello World»).
Лично мое мнение, что данный код настолько лаконичный, что даже не требует пояснений.
Стоп-стоп. Что насчет запуска/остановки сервера?
- Остановка — надо всего лишь вызвать метод stop().
- Запуск — а вот здесь все интересно. Сервер автоматически запускается, когда вы делаете что-то, что требует запуска сервера (я знаю что звучит действительно странно). Но можно запустить и вручную, вызвав метод init().
Поддержка шаблонизаторов
Хотел бы затронуть такую тему как шаблонизаторы. Спарк имеет большую нативную поддержку оных. А именно:
- Velocity
- Freemarker
- Mustache
- Handlebars
- Jade
- Thymeleaf
- Pebble
- Water
- jTwig
- Jinjava
- Jetbrick
Для всех них есть «обёртки» от спарка. Для примера давайте рассмотрим мой любимый Freemarker.
Для начала давайте импортируем доп. зависимость.
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-template-freemarker</artifactId>
<version>2.5.5</version>
</dependency>
Создадим шаблон «hello.ftl» в директории src/main/resources.
<html>
<head>
</head>
<body>
<h1>Hello, ${name}!</h1>
</body>
</html>
Теперь надо сконфигурировать freemarker дабы он искал шаблоны в ресурсах. Это всего лишь 4 строчки кода.
FreeMarkerEngine freeMarkerEngine = new FreeMarkerEngine();
Configuration freeMarkerConfiguration = new Configuration();
freeMarkerConfiguration.setTemplateLoader(new ClassTemplateLoader(Main.class, "/"));
freeMarkerEngine.setConfiguration(freeMarkerConfiguration);
Вот и всё, можно спокойно использовать фримаркер.
get("/", (request, response) -> {
Map<String, Object> model = new HashMap<>();
model.put("name", "Freemarker");
return freeMarkerEngine.render(new ModelAndView(model, "hello.ftl"));
});
Почти полноценный проект
Давайте напишем, какой-то лёгкий, но достаточный что бы показать хотя бы малую часть возможностей спарка? Мне из того что можно реально быстро написать, пришел в голову только сокращатель ссылок. Что ж, начнем?
Надо добавить еще одну зависимость Google Guava. В итоге у нас получается 3 зависимости.
<dependencies>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-template-freemarker</artifactId>
<version>2.5.5</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
Нам Guava нужна, потому что в ней реализована хэш-функция Murmur3. Весь плюс в том что она достаточно сильна устойчива к коллизиям. Так же что бы не тратить лишнее время, мы будем все данные хранить только в переменных, без всяких бд и т.д.
Сделаем костяк программы.
staticFileLocation("/static");
get("/shortener", (request, response) -> {
});
get("/:url", (request, response) -> {
});
:url это параметр который можно достать через метод params(). К примеру:
get("/hello/:name", (request, response) -> {
return "Hello: " + request.params(":name");
});
Что-то я отвлекся, продолжим.
Создадим коллекцию(Map) для хранения, настроим шаблонизатор(как я говорил freemarker мой любимый, так что будет именно он) и укажем спарку где у нас будут храниться статистические файлы. staticFiles.location("/static") укажет ему что файлы будут лежать в src/main/resources/static. К примеру файл /static/css/style.css будет доступен по адресу http://{host}:{port}/css/style.css. Если же вы хотите хранить файлы предположим в /var/www/public_html, тогда используем метод staticFiles.externalLocation("/var/www/public_html").
ConcurrentHashMap<String, String> urls = new ConcurrentHashMap<String, String>();
FreeMarkerEngine freeMarkerEngine = new FreeMarkerEngine();
Configuration freemarkerConfiguration = new Configuration();
freemarkerConfiguration.setTemplateLoader(new ClassTemplateLoader(Main.class, "/templates/"));
freeMarkerEngine.setConfiguration(freemarkerConfiguration);
staticFileLocation("/static");
Настроим перенаправление.
get("/:url", (request, response) -> {
if(urls.containsKey(request.url()))
response.redirect(urls.get(request.url()));
response.redirect("/");
return null;
});
Теперь когда пользователь переходит по короткой ссылке, если ссылка есть в коллекции, мы перемещаем его на нужный сайт, если нету, то на главную.
Давайте поработаем с фронтендом, а то мы совсем про него забыли. Начнем с главной.
Здесь мы создадим простую минималистическую форму. И еще запихнем шрифтик Proxima Nova. Заигрался конечно… ну да ладно.
<!DOCTYPE html>
<html lang="en" style="height:100%;">
<head>
<meta charset='utf-8'>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="main">
<form action="/shortener" method="GET">
<input type="text" id="input" name="url" autocomplete="off" autofocus size="44" maxlength="512" />
</form>
</div>
</body>
</html>
@font-face {
font-family: Proxima Nova;
src: url(pn.otf);
}
* {
font-family: Proxima Nova;
background: #2a2826;
color: #9C9C9C;
margin: 0;
padding: 0;
}
body, html {
height: 100%;
}
#main {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-size: 1.5em;
}
#input {
border: none;
outline:none;
font-size: 1.5em;
}
В итоге все это добро выглядит так.
Теперь страница на которую будет выводится ссылка «shortener.ftl».
<!DOCTYPE html>
<html lang="en" style="height:100%;">
<head>
<meta charset='utf-8'>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="main" style="text-align: center">
${url}
</div>
</body>
</html>
Итоговая структура проекта.
Что ж, дописываем последние строки. В итоге Main класс, выглядит так.
import com.google.common.hash.Hashing;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import spark.ModelAndView;
import spark.Request;
import spark.Response;
import spark.template.freemarker.FreeMarkerEngine;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static spark.Spark.*;
public class Main {
public static void main(String[] args) {
ConcurrentHashMap<String, String> urls = new ConcurrentHashMap<String, String>();
FreeMarkerEngine freeMarkerEngine = new FreeMarkerEngine();
Configuration freemarkerConfiguration = new Configuration();
freemarkerConfiguration.setTemplateLoader(new ClassTemplateLoader(Main.class, "/templates/"));
freeMarkerEngine.setConfiguration(freemarkerConfiguration);
staticFileLocation("/static");
get("/shortener", (request, response) -> {
String shortURL = "http://localhost:4567/" +
Hashing.murmur3_32().hashString(request.queryParams("url"), StandardCharsets.UTF_8).toString();
Map<String, Object> model = new HashMap<>();
if(!urls.containsKey(shortURL)) {
model.put("url", shortURL);
urls.put(shortURL, request.queryParams("url"));
return freeMarkerEngine.render(new ModelAndView(model, "shortener.ftl"));
}
model.put("url", shortURL);
return freeMarkerEngine.render(new ModelAndView(model, "shortener.ftl"));
});
get("/:url", (request, response) -> {
if(urls.containsKey(request.url()))
response.redirect(urls.get(request.url()));
response.redirect("/");
return null;
});
}
}
Вот и всё. 41 строка кода и мы написали сокращатель ссылок.
Заключение
На этом моменте, я закончу эту немного затянувшуюся «Getting started» статью. Мне очень жаль что в рунете этот проект обошли стороной. Если вам понравится, я продолжу писать о Spark`е и раскрою больше его возможностей.
Ссылки
→ Сайт проекта
→ Исходные коды SparkJava