Автор не прогер, кодить не умеет
Я не являюсь гуру или крутым специалистом ни в Котлине, ни в Spring, ни в любой другой технологии используемой в данной статье. Я обычный java junior, который решил опробовать kotlin. Все сделано в "Сапсане" на коленке по дороге с techtrain
Для кого
Для java разработчиков, которые только слышали про котлин, но руками его пока не трогали
Для чего
Показать что kotlin отлично работает с spring boot, а в сочетании с DSL в части работы с html быть удобнее классического подхода с jsp.
Конфигурация для spring boot
Тут все мало отличается от java
- Создать проект (kotlin)
- Добавить repository jcenter, dependency на kotlin-stdlib и spring-boot-starter-web, плагин kotlin-maven-plugin
- Обязательно создать пакет для работы, иначе Spring свалится
- Создать open класс Application
- В Application добавить метод main со строчкой runApplication <Application>(*args)
- Добавить контроллер HelloWorld, который вернет приветствие миру
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>habr-sample</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<id>jcenter</id>
<name>jcenter</name>
<url>https://jcenter.bintray.com</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>RELEASE</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
</project>
package example
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
open class Application
fun main(args: Array<String>) {
runApplication<Application>(*args)
}
package example
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseBody
@Controller
class HelloWorld{
@RequestMapping("/")
@ResponseBody
fun mainPage(): String {
return "Привет Хабр"
}
}
Добавляем kotlinx.html
Kotlin имеет расширение в виде DSL для удобной работы с html. Оно позволяет смешать декларативный подход html c императивный подходом обычного языка.
table {//Объявление тега
thead{
tr {
td {
+"Имя" //+ добавляет контент в элемент
}
td {
+"Возраст"
}
}
}
for (person in PersonGenerator().generate()) { //Обычный for из kotlin
tr {
td {
+person.name
}
td {
text(person.age) //аналог "+" который принимает не только String
}
}
}
}
Этот код сформирует html страницу в которой будет табличка с именами людей и их возрастом
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>habr-sample</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<id>jcenter</id>
<name>jcenter</name>
<url>https://jcenter.bintray.com</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>RELEASE</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-html-jvm</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
</project>
package example
data class Person(val name: String,var age: Int)
package example
import java.util.*
import kotlin.collections.ArrayList
class PersonGenerator{
private val nameList = arrayOf("Петя", "Вася", "Женя", "Марк", "Иван", "Леопольд");
fun generate(): List<Person> {
val random = Random()
val personList = ArrayList<Person>()
for (i in 1..15) {
personList.add(Person(nameList.get(random.nextInt(nameList.size)), random.nextInt(30) + 18))
}
return personList;
}
}
package example
import kotlinx.html.*
import kotlinx.html.stream.createHTML
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseBody
@Controller
class HelloWorld {
@RequestMapping("/")
@ResponseBody
fun mainPage(): String {
return createHTML()
.html {
body {
table {
thead() {
tr {
td {
+"Имя"
}
td {
+"Возраст"
}
}
}
val personList= PersonGenerator().generate()
for (person in personList ) {
tr {
td {
+person.name
}
td {
text(person.age)
}
}
}
}
}
}
}
}
Добавляем css
Kotlinx.html практически не умеет работать с css, с помощью него можно добавить ссылку на уже готовый стиль или вставить свой с помощью unsafe. Все становиться намного лучше если добавить Aza-Kotlin-CSS — DSL добавляющий работу с css
head {
//Добавить стилей bootstrap
styleLink("https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css")
styleLink("https://cdn.datatables.net/1.10.19/css/dataTables.bootstrap4.min.css")
//Добавление своего стиля
style("text/css") {
unsafe {// Заходим в unsafe kotlinx.html
raw(
Stylesheet { // и формируем стиль с Aza-Kotlin-CSS
table and td and th {
color = 0xFC0Fc0
}
}.render()
)
}
}
meta {
charset = "utf-8"
}
}
Работа с js идет также через unsafe
Резюме
На мой взгляд писать view на DSL удобнее чем на JSP. Мне не нужно сначала лезть в базу, класть результат вычисления в промежуточный объект, который я потом буду вытаскивать в jsp. Я могу сразу наполнять свой jsp из кодовой базы проекта минуя ненужный мне слой абстракции. К сожалению, я использовал jsp только в своих pet-project и вообще не занимаюсь фронтом. Мне было бы интересно почитать мнение профессионалов по этому вопросу.
Полезные ссылки
Пример разработанной в статье розовой таблички
Документация по котлину
Документация подключения spring boot к котлину
Документация по kotlinx.html
Документация по Aza-Kotlin-CSS
Mishiko
— это ужасно. Как забавный эксперимент годится, но активно работать с этим невозможно. С другой стороны я как то видел сайт, где HTML генерился хранимыми процедурами, — это было хуже (но код выглядел даже проще чем у Вас)
MEJIOMAH Автор
Вы считаете что это хуже jsp?
Подскажите как вы наполняете html страницу с стороны сервера?
DzmitryT
ИМХО, вам имеет смысл посмотреть в сторону шаблонизаторов. Какой-нибудь Velocity или Groovy template engine.
balexa
Шаблонизаторами. Велосити, фримаркер и т.д.
Я не понимаю цели и смысла этой библиотеки что вы описали. Ну то есть понятно, котлин, хайпово, круто и так далее. Но при проектировании стоит исходить из того, кем и как это будет использоваться.
Как правило в проектах пытаются разделить верстку, код фронтенда и код бакенда не потому что любят слои абстракции, а потому что с большой долей вероятности этими будут заниматься разные люди, с разными компетенциями и разными предпочитаемыми инструментами.
Я достаточно далек от фронтенда, но от верстки помимо всего прочего, требуется чтобы она поддерживалась всякими адобами, или чем там сейчас принято верстать, я не в курсе. Чтобы это все легко и просто интегрировалось с NPM/BEM/etc.
Чтобы не требовалось ВООБЩЕ знания котлина и джавы для верстки — потому что верстальщик даже с минимальным знанием джавы будет стоить дороже. И наверное много еще чего.
Как описанная вами библиотека эти вещи решает — мне не совсем понятно.
Этот слой абстракции в шаблонизаторах и нужен как раз, чтобы договориться о структуре модели, затем верстка и генерация наполнения становятся совершенно независимы друг от друга (ну, в теории).
То что вы предлагаете безусловно удобно, для пет-проджектов и студенческих лаб. К сожалению, боюсь область применения ими же и заканчивается.
MEJIOMAH Автор
Согласен, что это полезно только если нет разделения на frontend и backend разработку. Но если разделения нет можно делать очень удобные компоненты, например
osigida
это только на первый взгляд выглядит удобно, но становится адом и лапшой при совершенно незначительном росте проекта
balexa
Ничего удобного тут не вижу. Каша сплошная. Вы мешаете в одну кучу логику и отображение, так нельзя делать.
Как будет выглядеть вызов таких функций? Тест покажите. Это ужас будет.
Как вы собрались сопровождать и менять верстку и проверять ее? У вас тупо чтобы проверить как поменялась верстка, для любого изменения css в таблице требуется полная пересборка и перезапуск проекта.
MEJIOMAH Автор
Mishiko
Конкретно сейчас занимаюсь разработкой чистого бэка, но раньше… ) — CGI/Perl, ASP, PHP, сервлеты, JSP/JSTL, JSF, Thymeleaf, GWT, Vaadin, JS/REST, может еще чего забыл. Но если говорить о простоте — то Вам нужен PHP)
sshikov
Это может и не хуже, но это совсем другое. И сравнивать их некорректно. При работе с разметкой есть два базовых подхода: начинать с html и начинать с DSL. Совместить их воедино и сделать это хорошо удается очень редко кому (лично у меня не было претензий только к Flex и его mxml, и их интеграции в AS3).
Ровно тоже самое, что вы нарисовали, может быть сделано (и делалось еще лет 10 назад) на javascript. Такой же точно DSL. Десятки их я только лично видел, подобных. Только вся разница в том, что:
* это при отключении js не работает
* это нельзя редактировать html редактором, про wisiwig можно забыть
Ну и у вас недостатки (и достоинства) примерно те же самые. Оно не статическое, и без выполнения кода на котлине вы вообще ничего не видите. Иногда это нормально и удобно. Но иногда — ужасно. Но в целом этот подход вполне живой (хотя и не новый). То что вы изобрели, ровно в таком же виде существует в groovy под названием MarkupBuilder. Уже тоже лет 10 как наверное.
poxvuibr
Да, хуже. Верстальщик отдаст не html, который сделан специально под этот dsl. Он отдаст произвольный текст, в котором могут быть такие теги, о существовании которых вы даже не подозреваете. В jsp эти теги можно просто скопировать, а с dsl придётся отжиматься.
MEJIOMAH Автор
Я уже соглашался выше — при жестком разделении front и back в этом нет смысла, так как front живет в своем стеке.
Насчет отжиматься, все не так грустно
Прелесть этого подхода как раз в том, что не нужно сидеть в окнах jsp и контроллера одновременно. Все что будет написано, в плане подстановки данных, будет 100% валидно, ибо за этим следит компилятор. Кстати, если нужно, можно комбинировать jsp и dsl отдавая в jsp куски html сделанного dsl.
Smokin
Я не знаток java, но не ужели там нету никаких шаблонизаторов по типу pug или jade?
osigida
да есть все, дайте человеку по-велосипедить
Beholder
Недостаток такого подхода по сравнению с JSP вот в чём:
Хренова туча мелких анонимных классов. JSP обычно компилируется в один класс.
imanushin
А в чем недостаток-то?
DimPal
Потыкав палочкой в Kotlin я увидел недоделанный долгострой. Первое что обнаружил: кросс-платформенный Reflect API отсутствует, хотя который год обещают.
Классический «for(;;)» убит (лучше бы убили «while» и «do/while»). Некоторые вещи пишутся громоздко.
Для HelloWorld работает хорошо. В прод не катит.
DimPal
Прошу НЛО удалить мой комментарий, а то фанаты заминусовали (да и статья, действительно, не про это).
zimyx
Непонятно, как такие шаблоны верстать. Когда-то Thymeleaf мне очень понравился тем, что его шаблоны можно открыть в браузере в исходном виде и они будут выглядеть правильно.
High_Tower
Такой DSL аналог JSX и активно используется в React. Я бы использовал его для отдельных компонент, которые бы собирал привычным верстальщику шаблоном.