Хочу рассказать о том, как мне пришла в голову идея заняться веб-разработкой на Java.
Итак, как только я созрел для этого дела, составил небольшой список, которому должны соответствовать выбранные мною инструменты разработки:
Усиленно погуглив, я нашел то, что мне нужно: Spark, jade4j и OrmLite.
Итак, инструменты выбраны, рассмотрим каждый из них в отдельности.
Я выбрал последнюю версию, для работы которой требуется Java 8. Сам фреймворк позиционируется как Sinatra-inspired, так как я с синатрой знаком н�� слишком близко, он мне скорее напомнил Express для Node.js.
Идея была близка, тем более что позитивный опыт работы с Express уже был. Документация кажется бедноватой, но в процессе разработки убеждаешься, что её более чем достаточно. Большим его плюсом я считаю и то, что для разработки не требуется предварительная настройка окружения, установка контейнера сервлетов и так далее. Всё, что нужно — это добавить зависимость в pom.xml, написать пару строк — и всё взлетит.
Если вы работали с Node.js шаблонизаторами, в частности с jade, то вам наверняка захочется его использовать ещё. Легкочитаемый код, хорошая документация, множество красивых фич и опыт использования ранее заставили меня остановить свой выбор именно на этом шаблонизаторе.
Небольшой пример layout.jade:
Элементы {block blockname} реализуем в других файлов таким образом:
Скорее всего, некоторые андроид-разработчики хорошо знакомы с этой ORM. Она проста в использовании, легковесна и конфигурируется аннотациями.
Вот простой пример pojo класса Пользователь:
Взаимодействие с БД может осуществляться через встроенный в ORM Dao. Я написал небольшой синглтон-класс, чтобы упростить себе жизнь (возможно, код ниже вас немного расстроит, поэтому оговорюсь ещё раз: главной целью ставилась простота реализации и использования):
Начав процесс кодинга, я наткнулся на несколько проблем, которые надо было решать каким-либо путём.
Проблемой номер один было то, что Spark из коробки не поддерживал jade, поэтому пришлось гуглить и разбираться, как их подружить. Решение проблемы оказалось достаточно простым. Нужно просто отнаследоваться от класса TemplateEngine и реализовать интерфейсный метод render.
В той же гуглогруппе я нашел вот этот вполне рабочий код:
Следующая проблема была связана с тем, что я не знал, как толком разбить маппинг запросов на разные классы. В результате я решил создать интерфейс с тремя методами:
Далее я создал базовый класс маршрута, в котором проинициализировал хелперы рендеринга шаблонов:
Реализация конечного класса-маппера тут:
Теперь все классы-мапперы надо как-то инстанциировать. Я это делаю прямо в main классе, ибо ничего умнее пока не придумал (буду благодарен, если кто-нибудь наведет на мысль как это можно сделать лучше):
Все цели, которые я перед собой поставил, были реализованы. Инструментами остался доволен. Основные проблемы, с которыми столкнулся — это крайняя скудность информации в интернете о вышеуказанных инструментах. А вообще — фреймворк классный, простой и заставляет радоваться.
Всем спасибо за внимание! Конструктивная критика приветствуется.
Итак, как только я созрел для этого дела, составил небольшой список, которому должны соответствовать выбранные мною инструменты разработки:
- Сборка при помощи maven;
- Простой процесс деплоя и запуска;
- Библиотеки должны быть легковесны;
- Возможность использования шаблонизатора.
Усиленно погуглив, я нашел то, что мне нужно: Spark, jade4j и OrmLite.
Итак, инструменты выбраны, рассмотрим каждый из них в отдельности.
Spark Framework
Я выбрал последнюю версию, для работы которой требуется Java 8. Сам фреймворк позиционируется как Sinatra-inspired, так как я с синатрой знаком н�� слишком близко, он мне скорее напомнил Express для Node.js.
Идея была близка, тем более что позитивный опыт работы с Express уже был. Документация кажется бедноватой, но в процессе разработки убеждаешься, что её более чем достаточно. Большим его плюсом я считаю и то, что для разработки не требуется предварительная настройка окружения, установка контейнера сервлетов и так далее. Всё, что нужно — это добавить зависимость в pom.xml, написать пару строк — и всё взлетит.
Jade4j
Если вы работали с Node.js шаблонизаторами, в частности с jade, то вам наверняка захочется его использовать ещё. Легкочитаемый код, хорошая документация, множество красивых фич и опыт использования ранее заставили меня остановить свой выбор именно на этом шаблонизаторе.
Небольшой пример layout.jade:
doctype html html head block headsection body header div#main block content footer
Элементы {block blockname} реализуем в других файлов таким образом:
extends layout block headsection title Привет, Хабр! block content h1 Hello, habr!
OrmLite
Скорее всего, некоторые андроид-разработчики хорошо знакомы с этой ORM. Она проста в использовании, легковесна и конфигурируется аннотациями.
Вот простой пример pojo класса Пользователь:
package com.vagga.pojo; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; @DatabaseTable(tableName = "users") public class User { @DatabaseField(columnName = "user_id", generatedId = true) private int userId; @DatabaseField(columnName = "username", unique = true, canBeNull = false) private String userName; @DatabaseField(columnName = "user_pass") private String password; public User() { } /* * Далее геттеры-сеттеры */ }
Взаимодействие с БД может осуществляться через встроенный в ORM Dao. Я написал небольшой синглтон-класс, чтобы упростить себе жизнь (возможно, код ниже вас немного расстроит, поэтому оговорюсь ещё раз: главной целью ставилась простота реализации и использования):
package com.vagga.utils; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.dao.DaoManager; import com.j256.ormlite.jdbc.JdbcConnectionSource; import com.vagga.pojo.Category; import com.vagga.pojo.Comment; import com.vagga.pojo.Post; import com.vagga.pojo.User; import java.sql.SQLException; public class DbUtil { private static DbUtil ourInstance = new DbUtil(); private JdbcConnectionSource dbSource; private Dao<User, Integer> userDao; private Dao<Post, Integer> postDao; private Dao<Category, Integer> categoryDao; private Dao<Comment, Integer> commentDao; public static DbUtil getInstance() { return ourInstance; } private DbUtil() { try { dbSource = new JdbcConnectionSource("jdbc:mysql://localhost:3306/vagga?user=root&characterEncoding=utf8"); userDao = DaoManager.createDao(dbSource, User.class); postDao = DaoManager.createDao(dbSource, Post.class); categoryDao = DaoManager.createDao(dbSource, Category.class); commentDao = DaoManager.createDao(dbSource, Comment.class); } catch (SQLException e) { e.printStackTrace(); System.out.println("Cannot establish DB connection " + e.getMessage() + " " + e.getCause()); } } public Dao<User, Integer> getUserDao() { return userDao; } public Dao<Post, Integer> getPostDao() { return postDao; } public Dao<Category, Integer> getCategoryDao() { return categoryDao; } public Dao<Comment, Integer> getCommentDao() { return commentDao; } }
Несколько решений, которые, возможно, вас удручат
Начав процесс кодинга, я наткнулся на несколько проблем, которые надо было решать каким-либо путём.
Проблемой номер один было то, что Spark из коробки не поддерживал jade, поэтому пришлось гуглить и разбираться, как их подружить. Решение проблемы оказалось достаточно простым. Нужно просто отнаследоваться от класса TemplateEngine и реализовать интерфейсный метод render.
В той же гуглогруппе я нашел вот этот вполне рабочий код:
package com.vagga.utils; import de.neuland.jade4j.JadeConfiguration; import de.neuland.jade4j.exceptions.JadeException; import de.neuland.jade4j.model.JadeModel; import de.neuland.jade4j.template.FileTemplateLoader; import de.neuland.jade4j.template.JadeTemplate; import de.neuland.jade4j.template.TemplateLoader; import spark.ModelAndView; import spark.TemplateEngine; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.util.Map; public class JadeEngine extends TemplateEngine { private JadeConfiguration configuration; private String directory = new File(".").getCanonicalPath(); public JadeEngine() throws IOException { this.configuration = new JadeConfiguration(); this.directory = this.directory + "/src/main/resources/templates/"; TemplateLoader loader = new FileTemplateLoader(directory, "UTF-8"); configuration.setTemplateLoader(loader); } @SuppressWarnings("unchecked") @Override public String render(ModelAndView modelAndView) { StringWriter stringWriter = new StringWriter(); try { JadeTemplate template = this.configuration.getTemplate(modelAndView.getViewName()); JadeModel jadeModel = new JadeModel((Map<String, Object>) modelAndView.getModel()); template.process(jadeModel, stringWriter); } catch (JadeException | IOException e) { e.getCause(); } return stringWriter.toString(); } }
Следующая проблема была связана с тем, что я не знал, как толком разбить маппинг запросов на разные классы. В результате я решил создать интерфейс с тремя методами:
package com.vagga.routes; public interface BaseRoute { public void initBeforeAction(); public void initActions(); public void initAfterActions(); }
Далее я создал базовый класс маршрута, в котором проинициализировал хелперы рендеринга шаблонов:
package com.vagga.routes; import com.vagga.utils.JadeEngine; import com.vagga.utils.JsonTransformer; import java.io.IOException; public class Route { protected JadeEngine templateEngine; protected JsonTransformer jsonTransformer; protected Route() throws IOException { templateEngine = new JadeEngine(); jsonTransformer = new JsonTransformer(); } }
Реализация конечного класса-маппера тут:
package com.vagga.routes; import com.vagga.pojo.Category; import com.vagga.pojo.User; import com.vagga.utils.DbUtil; import spark.ModelAndView; import java.io.IOException; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; import static spark.Spark.*; public class HomeRoute extends Route implements BaseRoute { public HomeRoute() throws IOException { super(); } @Override public void initBeforeAction() { } @Override public void initActions() { get("/", (req, res) -> { Map<String, Object> model = new HashMap<String, Object>(); try { List<Category> categories = DbUtil.getInstance().getCategoryDao().queryForAll(); User user = DbUtil.getInstance().getUserDao().queryForId(1); model.put("user", user); model.put("categories", categories); model.put("title", "Главная"); } catch (SQLException e) { e.printStackTrace(); } return new ModelAndView(model, "home/index.jade"); }, this.templateEngine); } @Override public void initAfterActions() { } }
Теперь все классы-мапперы надо как-то инстанциировать. Я это делаю прямо в main классе, ибо ничего умнее пока не придумал (буду благодарен, если кто-нибудь наведет на мысль как это можно сделать лучше):
public class VaggaMain { public static void main(String[] args) { try { ArrayList<BaseRoute> routes = new ArrayList<>(); routes.add(new HomeRoute()); routes.add(new AdminRoute()); routes.add(new ApiRoute()); routes.forEach((route) -> { route.initBeforeAction(); route.initActions(); route.initAfterActions(); }); } catch (IOException e) { e.printStackTrace(); } } }
Итог
Все цели, которые я перед собой поставил, были реализованы. Инструментами остался доволен. Основные проблемы, с которыми столкнулся — это крайняя скудность информации в интернете о вышеуказанных инструментах. А вообще — фреймворк классный, простой и заставляет радоваться.
Всем спасибо за внимание! Конструктивная критика приветствуется.
