Совсем недавно вышла Java 8. И у меня возникло желание написать что-то с использованием новых плюшечек, которые дает 8-ерка.
Конкретно Лямбы, новый collection api, позволяющий работать с коллекциями в более функциональном стиле и default-методы в интерфейсах.
Статья является кратким обзором моего опыта по интегрированию Java 8, Spring MVC, Hibernate и SSP.
Кому интересно, прошу под кат.
Я долгое время(и все еще) продолжаю восхищаться языком Scala, но к сожалению, мне все еще мой пропитанный ынтырпрайзом мозг мешает перейти на чистую Scala.
В первую очередь из-за привязки ко внешним библиотекам (Hibernate, Spring, Spring MVC), к которым я до сих пор питаю слабость.
Я пытался их и использовать в Scala-проектах, но остается постоянное впечатление, что занимаешься постоянной расстановкой костылей и подпорок и не удается писать в Scala-стиле,
скорее пишешь на Java, но со Scala синтаксисом + костыли и подпорки в виде неявных преобразований Java-коллекций в Scala-коллекции и обратно.
Поэтому я решил пойти немного более «мягким» путем и использовать знакомый стек. Единственное изменение, к которому я пришел — использовать SSP(Scala Server Pages) вместо JSP(Java Server Pages),
что бы получить статическую поддержку на стороне View и не иметь сильной головной боли с тем,
что что-то ломается при рефакторинге и ты узнаешь это уже после деплоймента(когда какой-то блок тупо перестает отображаться либо что еще хуже подпортит данные в БД)
Итак, начнем.
Будем использовать мой любимый Maven.
Дадим Maven'у нампек, что проект у нас будет использовать Java 8:
Добавляем нужные зависимости на Spring(4-ой версии, который поддерживает изменения в новой версией JDK, а так же тянет библиотеку, которая умеет работать с байткодом, сгенерированной 8-ой Java'ой)/Hibernate и SSP. Все остальное по вкусу. Версии вынесены в «dependency management» секцию в parent pom'e.
Первая проблема, на которую натолкнулся — несовместимость Scala компилятора, который идет зависимостью с библиотекой «Scalate»(именно благодаря ей мы имеем поддержку SSP) с байт-кодом Java 8.
Пришлось явно прописать зависимость в проект на Scala компилятор, что бы все взлетело:
//изначально хотел проапгрейдить зависимость до 2.11, но «кишки» сильно поменялись и в последней доступной версии Scalate (1.6.1) это пока что еще не поддержано.
Так же мы хотим, что бы наши SSP были прекомпилированы и мы узнали о проблеме при компиляции, а ни на продакшне.
Поэтому добавляем плагин для этого:
//как заметно, добавлен тот же хачек со Scala компилятором
Ну с конфигурацией почти все
Теперь можно начать баловаться с кодом и радоваться плюшкам JDK 8:
Мой базовый DAO:
К сожалению, без дополнптиельной прослойки в виде абстрактного класса обойтись не удалось:
Конкретный интерфейс DAO:
Ну и соотвественно имплементация:
В результате мы получаем CRUD для репозитория и, на мой взгляд, очищаем имплементацию от побочного шума.
//Конечно можно было использовать Spring Data JPA и тогда ручками CRUD вообще не пришлось бы писать, но некоторые вещи оттуда мне не нравятся: В случае вручную генерируемых/присвоенных ID он будет всегда делать merge вместо persist. Да и таким образом довольно проще контролировать поведение системы.
Так же избавляемся от нужды использовать сторонние библиотеки типа Guava, который позволяют писать в более функциональном стиле и получаем все из коробки:
View для отображения списка:
Сборка проекта проста. В случае если вы выкачали мой проект и хотите его подеплоить в сервлет контейнер, скажем в Tomcat, то запустим:
mvn -P=build clean package
И видим как пре-компилируются наши SSP'шечки:
Так что если не дай бог что-то пошло не так и мы что-то поломали в них из того, что компилируется, то мы узнаем об этом сейчас, а не после деплоймента на dev/qa/staging/production environment.
В примере использовал Twitter Bootstrap, ибо нравится он мне.
Код примера:
github.com/dargiri/spring-mvc-java8-web-app-template
В качестве бесплатного бонуса:
Тоже самое на Java 7:
github.com/dargiri/spring-mvc-java-web-app-template
Тоже самое на Scala:
github.com/dargiri/spring-mvc-scala-web-app-template
Я предпочитаю запускать из-под IDE, а не пользоваться плагинами для IDE.
Поэтому в модуле web-app-launcher находим класс Launcher.
Если вы пользуетесь Idea, то все запустится без проблем.
Если вы пользуетесь Eclipse/Netbeans, то нужны некоторые манипуляции.
Для Eclipse — достать изначально Eclipse с поддержкой JDK 8: www.eclipse.org/downloads/index-java8.php
P.P.S. Пишите код и да пребудет с вами сила.
Далее для проекта нужно выбрать maven-профайл build.
И в классе Launcher значение переменной MULTI_MODULE_DEFAULT_PATH сменить с «web/src/main/webapp» на "../web/src/main/webapp" либо на полный путь от корня вашей файловой системы.
Apache Maven — maven.apache.org
Scalate — scalate.fusesource.org
Scala — www.scala-lang.org
Apache Tomcat — tomcat.apache.org
Twitter Bootstrap — getbootstrap.com
Spring Data JPA — projects.spring.io/spring-data-jpa
Hibernate ORM — hibernate.org/orm
JDK 8 — www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
Spring — projects.spring.io/spring-framework
Конкретно Лямбы, новый collection api, позволяющий работать с коллекциями в более функциональном стиле и default-методы в интерфейсах.
Статья является кратким обзором моего опыта по интегрированию Java 8, Spring MVC, Hibernate и SSP.
Кому интересно, прошу под кат.
Предисловие
Я долгое время(и все еще) продолжаю восхищаться языком Scala, но к сожалению, мне все еще мой пропитанный ынтырпрайзом мозг мешает перейти на чистую Scala.
В первую очередь из-за привязки ко внешним библиотекам (Hibernate, Spring, Spring MVC), к которым я до сих пор питаю слабость.
Я пытался их и использовать в Scala-проектах, но остается постоянное впечатление, что занимаешься постоянной расстановкой костылей и подпорок и не удается писать в Scala-стиле,
скорее пишешь на Java, но со Scala синтаксисом + костыли и подпорки в виде неявных преобразований Java-коллекций в Scala-коллекции и обратно.
Поэтому я решил пойти немного более «мягким» путем и использовать знакомый стек. Единственное изменение, к которому я пришел — использовать SSP(Scala Server Pages) вместо JSP(Java Server Pages),
что бы получить статическую поддержку на стороне View и не иметь сильной головной боли с тем,
что что-то ломается при рефакторинге и ты узнаешь это уже после деплоймента(когда какой-то блок тупо перестает отображаться либо что еще хуже подпортит данные в БД)
Начало
Итак, начнем.
Будем использовать мой любимый Maven.
Дадим Maven'у нампек, что проект у нас будет использовать Java 8:
... <build> <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <executions> <execution> <phase>compile</phase> <goals> <goal>compile</goal> </goals> </execution> </executions> <configuration> <_source>1.8</_source> <target>1.8</target> </configuration> </plugin> ... </plugins> </build> ...
Добавляем нужные зависимости на Spring(4-ой версии, который поддерживает изменения в новой версией JDK, а так же тянет библиотеку, которая умеет работать с байткодом, сгенерированной 8-ой Java'ой)/Hibernate и SSP. Все остальное по вкусу. Версии вынесены в «dependency management» секцию в parent pom'e.
<dependencies> ... <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> </dependency> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.0-api</artifactId> </dependency> <!-- Scalate (SSP) support--> <dependency> <groupId>org.fusesource.scalate</groupId> <artifactId>scalate-core_2.10</artifactId> </dependency> <dependency> <groupId>org.fusesource.scalate</groupId> <artifactId>scalate-spring-mvc_2.10</artifactId> </dependency> ... </dependencies>
Первая проблема, на которую натолкнулся — несовместимость Scala компилятора, который идет зависимостью с библиотекой «Scalate»(именно благодаря ей мы имеем поддержку SSP) с байт-кодом Java 8.
Пришлось явно прописать зависимость в проект на Scala компилятор, что бы все взлетело:
<dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-compiler</artifactId> <version>2.10.4</version> </dependency>
//изначально хотел проапгрейдить зависимость до 2.11, но «кишки» сильно поменялись и в последней доступной версии Scalate (1.6.1) это пока что еще не поддержано.
Так же мы хотим, что бы наши SSP были прекомпилированы и мы узнали о проблеме при компиляции, а ни на продакшне.
Поэтому добавляем плагин для этого:
<build> <plugins> ... <plugin> <groupId>org.fusesource.scalate</groupId> <artifactId>maven-scalate-plugin_2.10</artifactId> <version>1.6.1</version> <!--Support jdk 8--> <dependencies> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-compiler</artifactId> <version>2.10.4</version> </dependency> </dependencies> <executions> <execution> <goals> <goal>precompile</goal> </goals> </execution> </executions> </plugin> ... </plugins> </build>
//как заметно, добавлен тот же хачек со Scala компилятором
Немного кода
Ну с конфигурацией почти все
Теперь можно начать баловаться с кодом и радоваться плюшкам JDK 8:
Мой базовый DAO:
public interface BaseDAO<T extends Model<ID>, ID extends Serializable> extends EntityManagerAware { Class<T> getEntityClass(); default void persist(T entity) { if (entity.isNew()) { entity.assignId(); } getEntityManager().persist(entity); getEntityManager().flush(); } default T find(ID id) { return getEntityManager().find(getEntityClass(), id); } default void delete(T entity) { getEntityManager().remove(entity); } default List<T> findByQuery(String jpqlQueryString) { return findByQueryWithParams(jpqlQueryString, Collections.emptyMap()); } default List<T> findByQueryWithParams(String jpqlQueryString, Map<String, Object> params) { TypedQuery<T> query = getEntityManager().createQuery(jpqlQueryString, getEntityClass()); for (Map.Entry<String, Object> entry : params.entrySet()) { query.setParameter(entry.getKey(), entry.getValue()); } return query.getResultList(); } }
К сожалению, без дополнптиельной прослойки в виде абстрактного класса обойтись не удалось:
public abstract class AbstractBaseDAO<T extends Model<ID>, ID extends Serializable> implements BaseDAO<T, ID> { @PersistenceContext EntityManager entityManager; @Override public EntityManager getEntityManager() { return entityManager; } }
Конкретный интерфейс DAO:
public interface PersonDAO extends BaseDAO<Person, UUID> { @Override default Class<Person> getEntityClass() { return Person.class; } List<Person> findAll(); }
Ну и соотвественно имплементация:
@Repository public class PersonDAOImpl extends AbstractBaseDAO<Person, UUID> implements PersonDAO { @Override public List<Person> findAll() { return findByQuery("select p from Person p"); } }
В результате мы получаем CRUD для репозитория и, на мой взгляд, очищаем имплементацию от побочного шума.
//Конечно можно было использовать Spring Data JPA и тогда ручками CRUD вообще не пришлось бы писать, но некоторые вещи оттуда мне не нравятся: В случае вручную генерируемых/присвоенных ID он будет всегда делать merge вместо persist. Да и таким образом довольно проще контролировать поведение системы.
Так же избавляемся от нужды использовать сторонние библиотеки типа Guava, который позволяют писать в более функциональном стиле и получаем все из коробки:
List<PersonForm> all = personService.findAll().stream().map(PersonForm::from).collect(Collectors.<PersonForm>toList());
View для отображения списка:
<%@ val people: java.util.List[name.dargiri.web.controller.PeopleController.PersonForm]%> <div class="page-header"> <h1>People</h1> </div> <table class="table table-striped"> <thead> <tr> <th>#</th> <th>Username</th> <th>Action</th> </tr> </thead> <tbody> <% for(person <- people ) { %> <tr> <td> <%=person.id%> </td> <td> <%=person.username%> </td> <td> <a href="<%=uri("/people/edit/" + person.id)%>">Edit</a> | <a href="<%=uri("/people/delete/" + person.id)%>">Delete</a> </td> </tr> <% } %> </tbody> </table>
Сборка проекта проста. В случае если вы выкачали мой проект и хотите его подеплоить в сервлет контейнер, скажем в Tomcat, то запустим:
mvn -P=build clean package
И видим как пре-компилируются наши SSP'шечки:
... [INFO] --- maven-scalate-plugin_2.10:1.6.1:precompile (default) @ web --- [INFO] Precompiling Scalate Templates into Scala classes... [INFO] processing /Users/dionis/projects/spring-mvc-java8-web-app-template/web/src/main/webapp/WEB-INF/views/scalate/main/person.ssp [INFO] processing /Users/dionis/projects/spring-mvc-java8-web-app-template/web/src/main/webapp/WEB-INF/views/scalate/main/people.ssp [INFO] processing /Users/dionis/projects/spring-mvc-java8-web-app-template/web/src/main/webapp/WEB-INF/scalate/layouts/default.ssp ...
Так что если не дай бог что-то пошло не так и мы что-то поломали в них из того, что компилируется, то мы узнаем об этом сейчас, а не после деплоймента на dev/qa/staging/production environment.
В примере использовал Twitter Bootstrap, ибо нравится он мне.
Скриншоты
Создание пользователя:

Список пользователей:

Редактирование пользователя:


Список пользователей:

Редактирование пользователя:

Код примера:
github.com/dargiri/spring-mvc-java8-web-app-template
В качестве бесплатного бонуса:
Тоже самое на Java 7:
github.com/dargiri/spring-mvc-java-web-app-template
Тоже самое на Scala:
github.com/dargiri/spring-mvc-scala-web-app-template
И если вы все еще это читаете и вытащили себе код и хотите с ним поиграться.
Я предпочитаю запускать из-под IDE, а не пользоваться плагинами для IDE.
Поэтому в модуле web-app-launcher находим класс Launcher.
Если вы пользуетесь Idea, то все запустится без проблем.
Если вы пользуетесь Eclipse/Netbeans, то нужны некоторые манипуляции.
Для Eclipse — достать изначально Eclipse с поддержкой JDK 8: www.eclipse.org/downloads/index-java8.php
P.P.S. Пишите код и да пребудет с вами сила.
Далее для проекта нужно выбрать maven-профайл build.
И в классе Launcher значение переменной MULTI_MODULE_DEFAULT_PATH сменить с «web/src/main/webapp» на "../web/src/main/webapp" либо на полный путь от корня вашей файловой системы.
Ссылки:
Apache Maven — maven.apache.org
Scalate — scalate.fusesource.org
Scala — www.scala-lang.org
Apache Tomcat — tomcat.apache.org
Twitter Bootstrap — getbootstrap.com
Spring Data JPA — projects.spring.io/spring-data-jpa
Hibernate ORM — hibernate.org/orm
JDK 8 — www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
Spring — projects.spring.io/spring-framework