Совсем недавно вышла 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