54
Интеграция Kotlin в боевой Java проект Алексей Жидков

Интеграция Kotlin в боевой Java проект

Embed Size (px)

Citation preview

Page 1: Интеграция Kotlin в боевой Java проект

ИнтеграцияKotlin в боевой

Java проектАлексей Жидков

Page 2: Интеграция Kotlin в боевой Java проект

Обо мне

● 10 лет программирую на Java за еду

● 12 лет программирую на Java за идею

● Работаю в Эксельсиор программистом

2

@AlexeyZhidkov +AlekseyZhidkov @aleksey-zhidkov

Page 3: Интеграция Kotlin в боевой Java проект

О Kotlin

● Современный

● Объектно-ориентированный

● Компилируемый в JVM bytecode и JS код

● Статически типизированный

● 100% интероперабельный с Java

● Прагматичный

3

Page 4: Интеграция Kotlin в боевой Java проект

404 Not Found

Hello World Enterprise Editon

пал жертвой регламента

4

https://github.com/aleksey-zhidkov/HelloWorldEE

Page 5: Интеграция Kotlin в боевой Java проект

5

Курс молодого бойца по синтаксису

Котлина

Page 6: Интеграция Kotlin в боевой Java проект

Объявления переменных

6

val str = "Hello"str = "Compile error"var i: Inti = 10println("$str ${Integer.toHexString(i)}")

Page 7: Интеграция Kotlin в боевой Java проект

Non-Nullable types

7

var notNullable = "notNull"notNullable.trim()notNullable = null // Compile error

Page 8: Интеграция Kotlin в боевой Java проект

var nullable: String? = nullnullable.trim() // Compile errornullable?.trim()if (nullable != null) { nullable.trim()}val notNullable = nullable ?: “default”nullable!!.trim() // I NullPointerException

Nullable types

8♥

Page 9: Интеграция Kotlin в боевой Java проект

if and try expressions

9

val str = "1"val int = try { str.toInt() } catch (e: NumberFormatException) { 0 }

val bool = trueval strBool = if (bool) "true" else "false"

Page 10: Интеграция Kotlin в боевой Java проект

when и smart-casts

10

val num: Any? = nullval res = when (num) { is String -> num.trim() is Int -> (num + num).toString() else -> "Unknown"}

Page 11: Интеграция Kotlin в боевой Java проект

Сахар для лямбд

11

val lock = Any()synchronized(lock) { // sync code}

listOf(1, 2, 3).forEach { println(it)}

Page 12: Интеграция Kotlin в боевой Java проект

Первичный конструктор и свойства

12

public class C

constructor(val imProp: String, var mutProp: Int) {

val lazyProp by Delegates.lazy { "0" }

}

Page 13: Интеграция Kotlin в боевой Java проект

13

Айда конвертить

код!!!

Page 14: Интеграция Kotlin в боевой Java проект

Жертва

● Секретный проект

● Отправка большых файлов по емейлу

● Иерархия провайдеров

● Брендированные сайты

14

Page 15: Интеграция Kotlin в боевой Java проект

Технологии

● Java 8/JavaScript

● Spring

● MySql/JPA/EclipseLink

● HTTP-RPC aka REST

● Angular

15

Page 16: Интеграция Kotlin в боевой Java проект

Запускаем тесты

Не задерживаемся, здесь нет ничего интересного.

Тупо чтобы не забыть:)

А то практика показывает, что забываю:)

16

Page 17: Интеграция Kotlin в боевой Java проект

Конвертация кода в IDEA

● Скопипастить настройки мавена

○ Репозитории для плагина и стдлиба

○ Подключить плагин

○ Добавить стдлиб в зависимости

● Ctrl+Shift+Alt + J

● Слегка обработать напильником

17

Page 18: Интеграция Kotlin в боевой Java проект

Что было сконвертировано● Spring AOP

● Spring Security

● BaseEntity

● BaseEntityDao

● BaseAction

● Загрузка и обработка

файлов

● Отправка почты

● Entity, Dao, Service,

Controller для ключевых

сущностей

● Сущности со сложной

структурой

18

Page 19: Интеграция Kotlin в боевой Java проект

Результаты конвертации

● 55 файлов ядра системы сконвертировано

● Все тесты всё ещё проходят

● Найдено 0 блокеров

● Найдено 6 грабель в языке

● Найдено 3 грабли в конверторе

● Найден 1 баг в компиляторе

19

Page 20: Интеграция Kotlin в боевой Java проект

Проверяем тесты

Не задерживаемся, здесь нет ничего интересного.

Тупо чтобы не забыть:)

А то практика показывает, что забываю:)

20

Page 21: Интеграция Kotlin в боевой Java проект

21

Собранные грабли

Page 22: Интеграция Kotlin в боевой Java проект

final-классы и CgLib

22

Component

public class FileProcessingBean {

Async

public fun processFile(file: File) {...}

}

Page 23: Интеграция Kotlin в боевой Java проект

final-классы и CgLib

23

Component

public class FileProcessingBean {

Async

public fun processFile(file: File) {...}

}

Component

public open class FileProcessingBean {

Async

public fun processFile(file: File) {...}

}

Page 24: Интеграция Kotlin в боевой Java проект

final-методы и CgLib

24

Component

public open class FileProcessingBean {

Async

public fun processFile(file: File) {...}

}

Page 25: Интеграция Kotlin в боевой Java проект

final-методы и CgLib

25

Component

public open class FileProcessingBean {

Async

public fun processFile(file: File) {...}

}

Component

public open class FileProcessingBean {

Async

public open fun processFile(file: File) {...}

}

Page 26: Интеграция Kotlin в боевой Java проект

Переопределение аксессоров

26

public abstract class BaseEntity : Serializable{

public open fun getId(): String?

}

public class Entity(

override var id: String?) : BaseEntity

Page 27: Интеграция Kotlin в боевой Java проект

Переопределение аксессоров

27

public abstract class BaseEntity : Serializable{

public open fun getId(): String?

}

public class Entity(

override var id: String?) : BaseEntity

public abstract class BaseEntity : Serializable{

public open val getId: String?

}

public class Entity(

override var id: String?) : BaseEntity

Page 28: Интеграция Kotlin в боевой Java проект

Аннотации на аксессорах

28

public class Provider : BaseEntity() {

public override var id: String? = null

@deprecated("") set(value) {

$id = value

}

}

Page 29: Интеграция Kotlin в боевой Java проект

Assignment != expression

29

val block = ByteArray(ENCRYPTION_BUFFER_SIZE)

var i: Int

while ((i = `in`.read(block)) > 0) {

cos.write(block, 0, i)

}

Page 30: Интеграция Kotlin в боевой Java проект

Assignment != expression

30

val block = ByteArray(ENCRYPTION_BUFFER_SIZE)

var i: Int

while ((i = `in`.read(block)) > 0) {

cos.write(block, 0, i)

}

`in`.copyTo(cos, ENCRYPTION_BUFFER_SIZE)

Page 31: Интеграция Kotlin в боевой Java проект

Дефолтные методы в Java

31

public interface Iface {

public fun foo(): String = "bar"

}

public class Cls implements Iface {

public String foo() { return "foo"; }

}

Page 32: Интеграция Kotlin в боевой Java проект

Дефолтные методы в Java

32

public interface Iface {

public fun foo(): String = "bar"

}

public class Cls implements Iface {

public String foo() { return "foo"; }

}

Починят к релизу

Page 33: Интеграция Kotlin в боевой Java проект

== и identityEquals

33

override fun equals(obj: Any?): Boolean {

if (this == obj)

return true

// …

}

Page 34: Интеграция Kotlin в боевой Java проект

== и identityEquals

34

override fun equals(obj: Any?): Boolean {

if (this == obj)

return true

// …

}

override fun equals(obj: Any?): Boolean {

if (this identityEquals obj)

return true

// …

}

Page 35: Интеграция Kotlin в боевой Java проект

public class Message : BaseEntity() {

OneToMany(mappedBy = "message")

private val attachments

= ArrayList<Attachment>()

}

Вывод типа JPA коллекции

35

Page 36: Интеграция Kotlin в боевой Java проект

public class Message : BaseEntity() {

OneToMany(mappedBy = "message")

private val attachments

= ArrayList<Attachment>()

}

public class Message : BaseEntity() {

OneToMany(mappedBy = "message")

private val attachments: MutableList<Attachment>

= ArrayList<Attachment>()

}

Вывод типа JPA коллекции

36

Page 37: Интеграция Kotlin в боевой Java проект

String.format

37

String.format("Tpl %s", count));

Page 38: Интеграция Kotlin в боевой Java проект

String.format

38

String.format("Tpl %s", count));"Tpl $count"

Page 39: Интеграция Kotlin в боевой Java проект

public enum class FeatureType(val id: String) {

DROPSPOT("feature.dropspot");

companion object {

val byId =

values().map { Pair(it.id, it) }.toMap()

}}

Enum.<clinit>

39

Page 40: Интеграция Kotlin в боевой Java проект

public enum class FeatureType(val id: String) {

DROPSPOT("feature.dropspot");

companion object {

val byId =

values().map { Pair(it.id, it) }.toMap()

}}

public enum class FeatureType(val id: String) {

DROPSPOT("feature.dropspot");

companion object {

val byId by Delegates.lazy {

values().map { Pair(it.id, it) }.toMap()

}}}

Enum.<clinit>

40

Page 41: Интеграция Kotlin в боевой Java проект

41

Рефакторинг с использованием

фич Котлина

Page 42: Интеграция Kotlin в боевой Java проект

Servicepublic class BlacklistManagerImpl :BlacklistManager{

Autowired private val userDao: UserDao? = null

Autowired private val currentsHelper: CurrentsHelper? = null // ...}

42

Page 43: Интеграция Kotlin в боевой Java проект

Servicepublic class BlacklistManagerImpl :BlacklistManager{

Autowired private val userDao: UserDao? = null

Autowired private val currentsHelper: CurrentsHelper? = null // ...}

43

import org.spring.stereotype.Service as service

public service class BlacklistManagerImpl

@Autowired constructor ( private val userDao: UserDao, private val currentsHelper: CurrentsHelper

) : BlacklistManager {

Page 44: Интеграция Kotlin в боевой Java проект

Контроль доступа к данным

public fun updatePassword(

SecuredUser userId: String,

password: String,

newPassword: String): Boolean

44

Page 45: Интеграция Kotlin в боевой Java проект

45

for (int i = 0, n = paramAnnotations.length; i < n; i++) {

Object object = invocation.getArguments()[i];

Annotation[] annotations = paramAnnotations[i];

for (Annotation annotation : annotations) {

if (annotation instanceof SecuredUser) {

if (deniedForSecuredUser(object, auth)) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredMessage) {

if (deniedForSecuredMessage(object, auth)) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredProvider) {

if (deniedForSecuredProvider(object, auth, ((SecuredProvider) annotation).owningType())) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredEntity) {

if (deniedForSecuredEntity(object, auth, ((SecuredEntity) annotation))) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredResource) {

if (deniedForSecuredResource(object, auth, ((SecuredResource) annotation))) {

return ACCESS_DENIED;

}

}

}

}

return ACCESS_GRANTED;

Page 46: Интеграция Kotlin в боевой Java проект

46

for (int i = 0, n = paramAnnotations.length; i < n; i++) {

Object object = invocation.getArguments()[i];

Annotation[] annotations = paramAnnotations[i];

for (Annotation annotation : annotations) {

if (annotation instanceof SecuredUser) {

if (deniedForSecuredUser(object, auth)) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredMessage) {

if (deniedForSecuredMessage(object, auth)) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredProvider) {

if (deniedForSecuredProvider(object, auth, ((SecuredProvider) annotation).owningType())) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredEntity) {

if (deniedForSecuredEntity(object, auth, ((SecuredEntity) annotation))) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredResource) {

if (deniedForSecuredResource(object, auth, ((SecuredResource) annotation))) {

return ACCESS_DENIED;

}

}

}

}

return ACCESS_GRANTED;

for (int i = 0, n = paramAnnotations.length; i < n; i++){ Object object = invocation.getArguments()[i]; for (Annotation annotation : annotations) { if (annotation instanceof SecuredProvider) { if (deniedForSecuredProvider(object, auth, ((SecuredProvider) annotation).owningType())){ return ACCESS_DENIED; } } }}return ACCESS_GRANTED;

Page 47: Интеграция Kotlin в боевой Java проект

47

for (int i = 0, n = paramAnnotations.length; i < n; i++) {

Object object = invocation.getArguments()[i];

Annotation[] annotations = paramAnnotations[i];

for (Annotation annotation : annotations) {

if (annotation instanceof SecuredUser) {

if (deniedForSecuredUser(object, auth)) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredMessage) {

if (deniedForSecuredMessage(object, auth)) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredProvider) {

if (deniedForSecuredProvider(object, auth, ((SecuredProvider) annotation).owningType())) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredEntity) {

if (deniedForSecuredEntity(object, auth, ((SecuredEntity) annotation))) {

return ACCESS_DENIED;

}

} else if (annotation instanceof SecuredResource) {

if (deniedForSecuredResource(object, auth, ((SecuredResource) annotation))) {

return ACCESS_DENIED;

}

}

}

}

return ACCESS_GRANTED;

for (int i = 0, n = paramAnnotations.length; i < n; i++){ Object object = invocation.getArguments()[i]; for (Annotation annotation : annotations) { if (annotation instanceof SecuredProvider) { if (deniedForSecuredProvider(object, auth, ((SecuredProvider) annotation).owningType())){ return ACCESS_DENIED; } } }}return ACCESS_GRANTED;

val paramAnnotations = method.getParameterAnnotations()val argumentAnnotations = invocation.getArguments(). zip(paramAnnotations). flatMap { arg -> arg.second.map { Pair(arg.first, it) } }val shouldBeDenied = argumentAnnotations.any { val (arg, anno) = it when (anno) { is SecuredUser -> deniedForSecuredUser(arg, auth) is SecuredMessage -> deniedForSecuredMessage(arg, auth) is SecuredProvider -> deniedForSecuredProvider(arg, auth, anno.owningType) is SecuredEntity -> deniedForSecuredEntity(arg, auth, annotation) else -> false }}return if (shouldBeDenied) AccessDecisionVoter.ACCESS_DENIED else AccessDecisionVoter.ACCESS_GRANTED

Page 48: Интеграция Kotlin в боевой Java проект

private fun getLogo(provider: Provider?): DataSource? {

if (provider != null && !provider.isGFI()) {

val branding = brandingManager.getBrandingByProvider(provider)

if (branding != null && branding.logo != null) { return ByteArrayDataSource(branding.logo, branding.mimeType) } } try { return ByteArrayDataSource(defaultLogo, "image/png") } catch (e: IOException) { log.error("Unable to load icon for logo", e) return null }} 48

Page 49: Интеграция Kotlin в боевой Java проект

private fun getLogo(provider: Provider?): DataSource? {

if (provider != null && !provider.isGFI()) {

val branding = brandingManager.getBrandingByProvider(provider)

if (branding != null && branding.logo != null) { return ByteArrayDataSource(branding.logo, branding.mimeType) } } try { return ByteArrayDataSource(defaultLogo, "image/png") } catch (e: IOException) { log.error("Unable to load icon for logo", e) return null }} 49

return try { provider?. let { if (it.isGFI()) null else it }?. let { brandingManager.getBrandingByProvider(it) }?. let { if (it.logo != null) ByteArrayDataSource(it.logo, it.mimeType) else null }?. ?: ByteArrayDataSource(defaultLogo, "image/png") } catch (e: IOException) { log.error("Unable to load icon for logo", e) null }

Page 50: Интеграция Kotlin в боевой Java проект

Осталось за кадром

50

● Фичи языка

● Система типов

● Идиомы языка

● Стандартная либа

● Android

● eDSL

● Функциональный подход

● Прагматика Котлина

● Тулы

● Hello World EE

Да практически всё

Page 51: Интеграция Kotlin в боевой Java проект

Заключение

51

● Релиз Котлина ожидается в этом году

● Блокеров использования Котлина в продакшене мне

найти не удалось

● Котлин отлично подходит для быстрого

прототипирования

● Котлин позволяет быстрее писать более читаемые и

безопасные программы

Page 52: Интеграция Kotlin в боевой Java проект

Ссылка на автора иконкок

Icons made by

http://www.flaticon.com/authors/freepik

Freepik from www.flaticon.com

is licensed by CC BY 3.0

52

Page 53: Интеграция Kotlin в боевой Java проект

Ссылки

53

https://twitter.com/alexeyzhidkov

@AlexeyZhidkov

https://plus.google.com/+AlekseyZhidkov

+AlekseyZhidkov

http://kotlinlang.orgОфициальный сайт Котлина

https://github.com/aleksey-zhidkov

@aleksey-zhidkovhttps://github.com/aleksey-zhidkov/HelloWorldEE

Репозиторий HelloWorld Enterprise Edition

Page 54: Интеграция Kotlin в боевой Java проект

54

Вопросы