Простий webdev на Java 8

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

Отже, як тільки я дозрів для цієї справи, склав невеликий список, яким повинні відповідати вибрані мною інструменти розробки:
  • Збірка за допомогою maven;
  • Простий процес деплоя і запуску;
  • Бібліотеки повинні бути легковажна;
  • Можливість використання шаблонизатора.
Посилено погугливши, я знайшов те, що мені потрібно: Spark, jade4j і OrmLite.

Отже, обрані інструменти, розглянемо кожен з них окремо.

Spark Framework
Я вибрав останню версію, для якої потрібно Java 8. Сам фреймворк позиціонується як Sinatra-inspired, так як я з сінатрою знаком не занадто близько, він мені швидше нагадав Express для Node.js.

Ідея була близька, тим більше, що позитивний досвід роботи з Express вже був. Документація здається бедноватой, але в процесі розробки переконуєшся, що її більш ніж достатньо. Великим його плюсом я вважаю і те, що для розробки не потрібна попередня настройка оточення, установка контейнер сервлетів і так далі. Все, що потрібно — це додати в залежність pom.xml написати пару рядків — і все злетить.

Jade4j
Якщо ви працювали з Node.js шаблонізатором, зокрема з нефриту, то вам напевно захочеться його використовувати. Легкочитаємий код, хороша документація, безліч красивих фіч і досвід використання раніше змусили мене зупинити свій вибір саме на цьому шаблонизаторе.

Невеликий приклад 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("безконтрольно")
@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();
}
}
}


Підсумок
Всі цілі, які я перед собою поставив, були реалізовані. Інструментами залишився задоволений. Основні проблеми, з якими зіштовхнувся — це крайня убогість інформації в інтернеті про вищевказаних інструментах. А взагалі — фреймворк класний, простий і змушує радіти.

Всім дякую за увагу! Конструктивна критика вітається.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.