[Из песочницы] Простой webdev на Java 8

Хочу рассказать о том, как мне пришла в голову идея заняться веб-разработкой на Java.Итак, как только я созрел для этого дела, составил небольшой список, которому должны соответствовать выбранные мною инструменты разработки:

Сборка при помощи 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 userDao; private Dao postDao; private Dao categoryDao; private Dao 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 getUserDao () { return userDao; }

public Dao getPostDao () { return postDao; }

public Dao getCategoryDao () { return categoryDao; }

public Dao 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) 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 model = new HashMap(); try { List 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 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 (); } } } Итог Все цели, которые я перед собой поставил, были реализованы. Инструментами остался доволен. Основные проблемы, с которыми столкнулся — это крайняя скудность информации в интернете о вышеуказанных инструментах. А вообще — фреймворк классный, простой и заставляет радоваться.Всем спасибо за внимание! Конструктивная критика приветствуется.

© Habrahabr.ru