Upload
aleksey-zhidkov
View
169
Download
3
Embed Size (px)
Citation preview
ИнтеграцияKotlin в боевой
Java проектАлексей Жидков
Обо мне
● 10 лет программирую на Java за еду
● 12 лет программирую на Java за идею
● Работаю в Эксельсиор программистом
2
@AlexeyZhidkov +AlekseyZhidkov @aleksey-zhidkov
О Kotlin
● Современный
● Объектно-ориентированный
● Компилируемый в JVM bytecode и JS код
● Статически типизированный
● 100% интероперабельный с Java
● Прагматичный
3
404 Not Found
Hello World Enterprise Editon
пал жертвой регламента
4
https://github.com/aleksey-zhidkov/HelloWorldEE
5
Курс молодого бойца по синтаксису
Котлина
Объявления переменных
6
val str = "Hello"str = "Compile error"var i: Inti = 10println("$str ${Integer.toHexString(i)}")
Non-Nullable types
7
var notNullable = "notNull"notNullable.trim()notNullable = null // Compile error
var nullable: String? = nullnullable.trim() // Compile errornullable?.trim()if (nullable != null) { nullable.trim()}val notNullable = nullable ?: “default”nullable!!.trim() // I NullPointerException
Nullable types
8♥
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"
when и smart-casts
10
val num: Any? = nullval res = when (num) { is String -> num.trim() is Int -> (num + num).toString() else -> "Unknown"}
Сахар для лямбд
11
val lock = Any()synchronized(lock) { // sync code}
listOf(1, 2, 3).forEach { println(it)}
Первичный конструктор и свойства
12
public class C
constructor(val imProp: String, var mutProp: Int) {
val lazyProp by Delegates.lazy { "0" }
}
13
Айда конвертить
код!!!
Жертва
● Секретный проект
● Отправка большых файлов по емейлу
● Иерархия провайдеров
● Брендированные сайты
14
Технологии
● Java 8/JavaScript
● Spring
● MySql/JPA/EclipseLink
● HTTP-RPC aka REST
● Angular
15
Запускаем тесты
Не задерживаемся, здесь нет ничего интересного.
Тупо чтобы не забыть:)
А то практика показывает, что забываю:)
16
Конвертация кода в IDEA
● Скопипастить настройки мавена
○ Репозитории для плагина и стдлиба
○ Подключить плагин
○ Добавить стдлиб в зависимости
● Ctrl+Shift+Alt + J
● Слегка обработать напильником
17
Что было сконвертировано● Spring AOP
● Spring Security
● BaseEntity
● BaseEntityDao
● BaseAction
● Загрузка и обработка
файлов
● Отправка почты
● Entity, Dao, Service,
Controller для ключевых
сущностей
● Сущности со сложной
структурой
18
Результаты конвертации
● 55 файлов ядра системы сконвертировано
● Все тесты всё ещё проходят
● Найдено 0 блокеров
● Найдено 6 грабель в языке
● Найдено 3 грабли в конверторе
● Найден 1 баг в компиляторе
19
Проверяем тесты
Не задерживаемся, здесь нет ничего интересного.
Тупо чтобы не забыть:)
А то практика показывает, что забываю:)
20
21
Собранные грабли
final-классы и CgLib
22
Component
public class FileProcessingBean {
Async
public fun processFile(file: File) {...}
}
final-классы и CgLib
23
Component
public class FileProcessingBean {
Async
public fun processFile(file: File) {...}
}
Component
public open class FileProcessingBean {
Async
public fun processFile(file: File) {...}
}
final-методы и CgLib
24
Component
public open class FileProcessingBean {
Async
public fun processFile(file: File) {...}
}
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) {...}
}
Переопределение аксессоров
26
public abstract class BaseEntity : Serializable{
public open fun getId(): String?
}
public class Entity(
override var id: String?) : BaseEntity
Переопределение аксессоров
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
Аннотации на аксессорах
28
public class Provider : BaseEntity() {
public override var id: String? = null
@deprecated("") set(value) {
$id = value
}
}
Assignment != expression
29
val block = ByteArray(ENCRYPTION_BUFFER_SIZE)
var i: Int
while ((i = `in`.read(block)) > 0) {
cos.write(block, 0, i)
}
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)
Дефолтные методы в Java
31
public interface Iface {
public fun foo(): String = "bar"
}
public class Cls implements Iface {
public String foo() { return "foo"; }
}
Дефолтные методы в Java
32
public interface Iface {
public fun foo(): String = "bar"
}
public class Cls implements Iface {
public String foo() { return "foo"; }
}
Починят к релизу
== и identityEquals
33
override fun equals(obj: Any?): Boolean {
if (this == obj)
return true
// …
}
== и 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
// …
}
public class Message : BaseEntity() {
OneToMany(mappedBy = "message")
private val attachments
= ArrayList<Attachment>()
}
Вывод типа JPA коллекции
35
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
String.format
37
String.format("Tpl %s", count));
String.format
38
String.format("Tpl %s", count));"Tpl $count"
public enum class FeatureType(val id: String) {
DROPSPOT("feature.dropspot");
companion object {
val byId =
values().map { Pair(it.id, it) }.toMap()
}}
Enum.<clinit>
39
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
41
Рефакторинг с использованием
фич Котлина
Servicepublic class BlacklistManagerImpl :BlacklistManager{
Autowired private val userDao: UserDao? = null
Autowired private val currentsHelper: CurrentsHelper? = null // ...}
42
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 {
Контроль доступа к данным
public fun updatePassword(
SecuredUser userId: String,
password: String,
newPassword: String): Boolean
44
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;
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;
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
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
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 }
Осталось за кадром
50
● Фичи языка
● Система типов
● Идиомы языка
● Стандартная либа
● Android
● eDSL
● Функциональный подход
● Прагматика Котлина
● Тулы
● Hello World EE
Да практически всё
Заключение
51
● Релиз Котлина ожидается в этом году
● Блокеров использования Котлина в продакшене мне
найти не удалось
● Котлин отлично подходит для быстрого
прототипирования
● Котлин позволяет быстрее писать более читаемые и
безопасные программы
Ссылка на автора иконкок
Icons made by
http://www.flaticon.com/authors/freepik
Freepik from www.flaticon.com
is licensed by CC BY 3.0
52
Ссылки
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
54
Вопросы