50 оттенков play!
TRANSCRIPT
50 оттенковPlay!
Андрей Солнцев
Play! framework 1
Лёгкость Ruby on Rails+
compile-safety Java
Play! framework
- NO REDEPLOY
- Легковесный как RoR. Нет XML. Нет DAO.
- легко начать! классный туториал.
- простая структура проекта (по конвенции, нет пакетов)
- роутинг: * {controller}/{method}
Масштабируемость
- Нет Java сессии
- Сессия в cookies (+кэш)
- Масштабируемость безгранична
Всё что надо из коробки
- Configuration- JPA / Hibernate / DB transactions- H2 in-memory database- Шаблоны на Groovy- генерация PDF, Excel- Тесты
Расширяемо
- плагины для всего-всего- модули (через Ivy)
require: - play 1.3+ - play -> secure - play -> pdf 0.9 - play -> guice 1.3 - play -> excel 1.2.8 - play -> press 3.1 - play -> liquibase 3.4
dependencies.yml
Play! framework 1
Спорные решения
“потому что веб
по своей сути статический”
Методы контроллеров статические
- really?
- т.е. наследование
не нужно?
Веб статический?
play.exceptions.UnexpectedException: Unexpected Errorat play.Classes.getAssignableClassesat play.Classloader.getAssignableClassesat play.utils.Java.invokeChildOrStatic(Java.java:199)at controllers.Secure$Security.invokeat controllers.Secure.logout
play-secure
public static class Security extends Controller { static boolean authenticate(String username) { return true; }
static void onAuthenticated() {} static void onDisconnect() {} static void onDisconnected() {}
…}
- наследование не нужно, говоришь?
Редирект через вызов методаpublic static class Payments extends Controller {
public static void submit(Payment payment) { payment.status = SUBMITTED; ... details(payment.id); }
public static void details(long paymentId) { render(payment); }}
WOW! Редирект!!!
Редирект через вызов методаpublic static class Payments extends Controller {
public static void submit(Payment payment) { payment.status = SUBMITTED; payment.submittedBy = getUser(); details(payment.id); }
}
А здесь не редирект?
Редирект через вызов методаpublic static class Payments extends Controller {
public static void submit(Payment payment) { payment.status = SUBMITTED; payment.submittedBy = getUser(); details(payment.id); }
public static void details(long paymentId) {...} static void getUser() {...}}
А здесь не редирект?А нет.
МАГИЯ!
“код, который мы написали, и который бежит -несколько отличается”
- Редиректы- Автогенерация геттеров/сеттеров- JPA из коробки
МАГИЯ!
- Магия - это плохо!- Ужасно плохо!!- Прям плохо!!!
- Медленно- Бажно- Возникает зависимость
WTF1
wtf BeanInfo?Oops: ClassNotFoundExceptionAn unexpected error occured caused by exception ClassNotFoundException: models.loans.LoanBeanInfo
play.exceptions.UnexpectedException: Unexpected Errorat play.classloading.ApplicationClasses.getAssignableClassesat play.classloading.ApplicationClassloader.getAssignableClassesat play.utils.Java.invokeChildOrStatic(Java.java:199)at controllers.Secure$Security.invoke(Secure.java:203)at controllers.Secure$Security.access$0(Secure.java:200)at controllers.Secure.logout(Secure.java:99)
- Чиво?
- У нас нет класса LoanBeanInfo...
После 3 лет дебага...
public class ApplicationClassloader extends ClassLoader {// ~~~~~~~~~~~~~~~~~~~~~~~public Class<?> loadApplicationClass(String name) { // name == 'LoanBeanInfo' ...
1: ApplicationClass appClass = Play.classes.getApplicationClass(name); // Эта сволочь добавляет в глобальный список: // Play.classes.put(name, new ApplicationClass(name, javaFile));
// ... здесь 45 строк кода (компиляция Loan.java) ...
if (!appClass.compile()) {2: Play.classes.classes.remove(name); return null; } return null;}
WTF2
Play.detectChanges()
public static synchronized void detectChanges() { try { pluginCollection.detectChange(); if (!Play.started) { throw new RuntimeException("Not started"); }
} catch (PlayException e) { throw e; } catch (Exception e) { // We have to do a clean refresh start(); }}
Play.detectChanges()
public static synchronized void detectChanges() { try { pluginCollection.detectChange(); if (!Play.started) { throw new RuntimeException("Not started"); }
} catch (PlayException e) { throw e; } catch (Exception e) { // We have to do a clean refresh start(); }}
Получай StackOverflowException!
WTF3
play.Logger
try {...}catch (Exception e) { Logger.error("Failed to set global render args", e);}
- Ждёте stack trace?
- Ха-ха!
- Правильно так:
Logger.error(e, "Failed to set...");
WTF4
if (newDefinitions.size() > 0) { Cache.clear(); if (HotswapAgent.enabled) { try { HotswapAgent.reload(newDefinitions); } catch (Throwable e) { throw new RuntimeException("Need reload"); } } else { throw new RuntimeException("Need reload"); }}
RELOADING
if (newDefinitions.size() > 0) { Cache.clear(); if (HotswapAgent.enabled) { try { HotswapAgent.reload(newDefinitions); } catch (Throwable e) { throw new RuntimeException("Need reload"); } } else { throw new RuntimeException("Need reload"); }}
RELOADING
● т.е. Play использует Hotswap.● Ничего больше. Только Hotswap. ● Который и так встроен в Java!
● Я чувствую себя обманутым...
if (newDefinitions.size() > 0) { Cache.clear(); if (HotswapAgent.enabled) { try { HotswapAgent.reload(newDefinitions); } catch (Throwable e) { throw new RuntimeException("Need reload"); } } else { throw new RuntimeException("Need reload"); }}
RELOADING Всё, что загруженонепосильным трудом...
Вредные советысоздателям фреймворков
от авторов Play!
Глотайте ошибки
- Молча.- или запишите туда, где никто не найдёт
Накажите юзераpublic static void init() {
URL log4jPath = getResource( "/log4j.xml");
if (log4jConf == null)
log4jConf = getResource( "/log4j.properties");
}
if (log4jConf == null) {
Properties shutUp = new Properties();
shutUp.setProperty("log4j.rootLogger", "OFF");
PropertyConfigurator.configure(shutUp);
}
}
Logger.java
Пишите javadoc/**
* You know ...
*/
protected Class<?> loadClass(String name) {
/**
* You know ...
*/
public InputStream getResourceAsStream(String name) {
/**
* You know ...
*/
public URL getResource(String name) {
Пишите комментарии
Не унывайте!
try { … 40 строк кода ...}catch (Exception e) { // Well probably a compiled optimizer // (I hope so)}
Спасибо Play!
- За революцию в Java web
- Это по-любому лучше любого
ъынтерпрайза
- Это open-source. Всё в наших руках.
Play @ Codeborne
- Наш форк:
https://github.com/codeborne/play
(ветка 1.3.x-codeborne)
- Для тестов:
https://github.com/codeborne/play-tests
-
Андрей Солнцев@asolntsev
ru.selenide.org
Бодрячком!