SlideShare a Scribd company logo
О себе
• Пишу на джаве с 2001
• Преподаю джаву с 2003
• Консультирую с 2008
• Арихтектор с 2009
• Пилю стартап с 2014
• Техлид по биг дате с 2015
• Naya technologies c 2015
Spring puzzlers2
Мальчик, который доверял интерфейсам
public interface Утюг {
@PostConstruct
void разогреваться();
}
@Component
public class СуперДуперУтюг implements Утюг {
@Override
public void разогреваться() {
System.out.println("разогреваюсь");
}
@PostConstruct
@Autowired
public void гретьВоду(Вода вода){
System.out.println(вода+" грейся...");
}
}
A. Вода грейся
B. Вода грейся & Разогреваюсь
@Component
public class Вода {
@Override
public String toString() {
return "вода";
}
}
C. Разогреваюсь
D. Что то пойдёт не так
Что будет?
Spring puzzlers 2
IllegalStateException
• method annotation requires a no-arg method: public void
iLoveInterfaces.СуперДуперУтюг.гретьВоду
Как чиним?
• Убираем @Postconstruct с метода помеченного @Autowired
@PostConstruct
@Autowired
public void гретьВоду(Вода вода){
System.out.println(вода+" грейся...");
}
public interface Утюг {
@PostConstruct
void разогреваться();
}
@Component
public class СуперДуперУтюг implements Утюг {
@Override
public void разогреваться() {
System.out.println("разогреваюсь");
}
@Autowired
public void гретьВоду(Вода вода){
System.out.println(вода+" грейся...");
}
}
A. Вода грейся
B. Вода грейся & Разогреваюсь
@Component
public class Вода {
@Override
public String toString() {
return "вода";
}
}
C. Разогреваюсь
D. Вообще ничего не напечатается
Что будет?
Да ну на…
public interface Утюг {
@PostConstruct
void разогреваться();
}
@Component
public class СуперДуперУтюг implements Утюг {
@Override
public void разогреваться() {
System.out.println("разогреваюсь");
}
@Autowired
public void гретьВоду(Вода вода){
System.out.println(вода+" грейся...");
}
}
A. Вода грейся
B. Вода грейся & Разогреваюсь
@Component
public class Вода {
@Override
public String toString() {
return "вода";
}
}
C. Разогреваюсь
D. Вообще ничего не напечатается
Что будет?
АННОТАЦИИ НЕ РАБОТАЮТ
В ИНТЕРФЕЙСАХ
Придётся написать BeanPostProcessor
Достал уже со своими BeanPostProcessors
Будем сегодня писать BPP?
Будем сегодня писать BPP?
• Напишем BeanFactoryPostProcessor
Не будем повторять ошибки Спринга
• Это не правильно, что за Postconstruct отвечает BPP
BeanDefinition – это интерфейс
• AbstractBeanDefinition
• AnnotatedBeanDefinition
• ConfigurationClassBeanDefinition
• GenericBeanDefinition
• ScannedGenericBeanDefinition
BeanDefinition – это интерфейс
• AnnotatedBeanDefinition
• AbstractBeanDefinition
• ConfigurationClassBeanDefinition
• GenericBeanDefinition
• ScannedGenericBeanDefinition
• AbstractBeanDefinition
• AnnotatedBeanDefinition
• ConfigurationClassBeanDefinition
• GenericBeanDefinition
• ScannedGenericBeanDefinition
Та не…
• AnnotatedBeanDefinition
• AbstractBeanDefinition
• ConfigurationClassBeanDefinition
• GenericBeanDefinition
• ScannedGenericBeanDefinition
Хрен тебе
class ConfigurationClassBeanDefinitionReader {
}
private static class ConfigurationClassBeanDefinition
extends RootBeanDefinition implements AnnotatedBeanDefinition
Package
friendly
Когда не получается закастить по хорошему…
Пошли ковырять то, что спрятано
Сага о двух программистах
@Configuration
public class BaruchConfig {
@Bean
public String str1() {return "Groovy";}
@Bean
public String str2() {return "Spring";}
}
@Configuration
public class JekaConfig {
@Bean
public List<String> messages() {
ArrayList<String> strings = new ArrayList<>();
strings.add("Groovy");
strings.add("Spring");
return strings;
}
}
@Service
public class BaruchService {
@Autowired
private List<String> list;
@Service
public class JekaService {
@Autowired
private List<String> list;
Что заинжектится в лист?
1. Groovy, Spring, Groovy, Spring
2. Groovy, Spring
3. Groovy, Spring, [Groovy, Spring]
4. Exception
А в чём тут был
паззлер?
Как-то на корпоративе
Пока Жека дров не наломал я подстрахуюсь
@Configuration
public class BaruchConfig {
@Bean @BaruchQualifier
public String str1() {return "Groovy";}
@Bean @BaruchQualifier
public String str2() {return "Spring";}
}
@Service
public class BaruchService {
@Autowired @BaruchQualifier
private List<String> list;
@Configuration
public class JekaConfig {
@Bean
public List<String> messages() {
ArrayList<String> strings = new ArrayList<>();
strings.add("Groovy");
strings.add("Spring");
return strings;
}
}
@Service
public class JekaService {
@Autowired
private List<String> list;
1. У Баруха всё хорошо
2. У Жеки всё хорошо
3. У нас обоих всё хорошо
4. У нас обоих всё плохо
@Configuration
public class BaruchConfig {
@Bean @BaruchQualifier
public String str1() {return "Groovy";}
@Bean @BaruchQualifier
public String str2() {return "Spring";}
}
@Service
public class BaruchService {
@Autowired @BaruchQualifier
private List<String> list;
Что теперь будет?
Ну когда уже будет
паззлер?
А теперь можно добавить Артифактори
@Configuration
public class JekaConfig {
@Bean
public List<String> messages() {
ArrayList<String> strings = new ArrayList<>();
strings.add("Groovy");
strings.add("Spring");
return strings;
}
}
@Service
public class JekaService {
@Autowired
private List<String> list;
Да Нет
@Configuration
public class BaruchConfig {
@Bean @BaruchQualifier
public String str1() {return "Groovy";}
@Bean @BaruchQualifier
public String str2() {return "Spring";}
@Bean @BaruchQualifier
public String str3() {return "Artifactory";}
}
@Service
public class BaruchService {
@Autowired @BaruchQualifier
private List<String> list;
Повлияет ли это на лист Жеки?
Какого хрена тут Артифактори делает??
@Configuration
public class JekaConfig {
@Bean @JekaQualifier
public List<String> messages() {
ArrayList<String> strings = new ArrayList<>
strings.add("Groovy");
strings.add("Spring");
return strings;
}
}
@Service
public class JekaService {
@Autowired @JekaQualifier
private List<String> list;
1. Groovy, Spring
2. Groovy, Spring, Artifactory
@Configuration
public class BaruchConfig {
@Bean @BaruchQualifier
public String str1() {return "Groovy";}
@Bean @BaruchQualifier
public String str2() {return "Spring";}
@Bean @BaruchQualifier
public String str3() {return "Artifactory";}
}
@Service
public class BaruchService {
@Autowired @BaruchQualifier
private List<String> list;
Что теперь с листом Жеки?
3. Пусто
4. Exception
Spring puzzlers 2
@Configuration
public class JekaConfig {
@Bean @JekaQualifier
public List<String> messages() {
ArrayList<String> strings = new ArrayList<>
strings.add("Groovy");
strings.add("Spring");
return strings;
}
}
@Service
public class JekaService {
@Autowired @JekaQualifier
private List<String> list;
@Configuration
public class BaruchConfig {
@Bean @BaruchQualifier
public String str1() {return "Groovy";}
@Bean @BaruchQualifier
public String str2() {return "Spring";}
@Bean @BaruchQualifier
public String str3() {return "Artifactory";}
}
@Service
public class BaruchService {
@Autowired @BaruchQualifier
private List<String> list;
Какой Exception и почему?
Как мы это чиним?
1. ArrayList<String> вместо List<String>?
2. S
3. Не надо делать бин, который лист. Работаем с квалифаером
@Autowired
@JekaQualifier
private List<List<String>> list;
Spring puzzlers 2
Spring puzzlers 2
@Retention(RUNTIME)
@Qualifier
public @interface Comedy{}
@Retention(RUNTIME)
@Qualifier
public @interface Action{}
@Retention(RUNTIME)
@Qualifier
public @interface Melodrama{}
@Retention(RUNTIME)
@Qualifier
@Comedy @Action @Melodrama
public @interface AnyGenre{}
public interface Actor {
void play();
}
@Component @Comedy
public class Jim implements Actor { }
@Component
public class Katy implements Actor { }
@Component @Melodrama
public class Julia implements Actor { }
@Component @AnyGenre
public class Chuck implements Actor { }
@Component @Action @Comedy
public class Stallone implements Actor { }
@Component @Action
public class Tobey implements Actor { }
@Service
public class Film {
@Autowired
@Comedy
@Action
private List<Actor> actors;
}
Что заинжектится в этот лист?
1. Jim, Tobey
2. Jim, Tobey, Stallone, Chuck
3. Chuck, Katy
4. Chuck, Stallone
5. Что то пойдёт не так
А как же я…
@Component @Comedy
public class Jim implements Actor { }
@Component
public class Katy implements Actor { }
@Component @Melodrama
public class Julia implements Actor { }
@Component @AnyGenre
public class Chuck implements Actor { }
@Component @Action @Comedy
public class Stallone implements Actor { }
@Component @Action
public class Tobey implements Actor { }
@Service
public class Film {
@Autowired
@Comedy
@Action
private List<Actor> actors;
}
Что заинжектится в этот лист?
1. Jim, Tobey
2. Jim, Tobey, Stallone, Chuck
3. Chuck, Katy
4. Chuck, Stallone
5. Что то пойдёт не так
Как мы это чиним?
• Пишем свою обработку квалифаера
Так всё таки напишем BPP?
Загадочный PropertyPlaceholder
@Service
public class SomeService {
@Value("${JAVA_HOME}")
private void init(String javaHome) {
System.out.println(javaHome);
}
}
@Configuration
public class Config {
@Bean
public PropertySourcesPlaceholderConfigurer configurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
Что будет?
1.Будет warning, но всё сработает, как надо
2.Ничего не будет напечатано
3.Какой-то хитрый Exception
4.Напечатается JAVA_HOME, а не его значение
Вот такой warning:
• WARNING: @Bean method Config.configurer is non-static and returns
an object assignable to Spring's BeanFactoryPostProcessor interface
И чо?
@Configuration
public class Config {
@Value("${JAVA_HOME}")
private String javaHome;
@PostConstruct
public void init(){
System.out.println("javaHome = " + javaHome);
}
@Bean
public PropertySourcesPlaceholderConfigurer configurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
1.Будет warning, но всё сработает, как надо
2.Ничего не будет напечатано
3.Какой-то хитрый Exception
4.Напечатается javaHome = null
Что будет?
Как мы это чиним?
• Добавляем static
Спринговый enum
Выглядит знакомо?
public enum MpiProtocolType implements Serializable{
MAP(2, 3, 23, 56),
PRN(4), SRI4SM(560),
FSM(550, 551), LTE(316),
GTP(4000, 4001, 4002, 1000),
ISUP(1100, 1101, 1102, 1103, 1104, 1105),
SIP(5000),
ALL(0);
private final Integer[] opcodes;
MpiProtocolType(Integer... opcodes) {
this.opcodes=opcodes;
}
public boolean isMyOpcode(int opcode) {
return Arrays.asList(opcodes).contains(opcode);
}
}
А давайте спрингом
@Component
public enum Gender {
MALE, FEMALE;
private String description;
@Autowired
public void init(DescriptionConf descriptionConf) throws Exception {
Class<? extends DescriptionConf> clazz = descriptionConf.getClass();
Field field = clazz.getField(this.name().toLowerCase());
description = (String) field.get(descriptionConf);
}
public String getDescription() {
return description;
}
}
Что будет?
A. Всё отлично заработает
B. NoSuchMethodException
C. В description-е будет null
D. Что-то другое
Failed to instantiate [enums.Gender]:
No default constructor found
А давайте спрингом@Component
public enum Gender {
MALE, FEMALE;
private String description;
Gender(){}
@Autowired
public void init(DescriptionConf descriptionConf) throws Exception {
Class<? extends DescriptionConf> clazz = descriptionConf.getClass();
Field field = clazz.getField(this.name().toLowerCase());
description = (String) field.get(descriptionConf);
}
public String getDescription() {
return description;
}
}
Что теперь?
A. Всё отлично заработает
B. NoSuchMethodException
C. В description-е будет null
D. Что-то другое
Failed to instantiate [enums.Gender]:
No default constructor found
Кто знает в чём проблема?
@Component
public enum Gender {
MALE, FEMALE;
private String description;
Gender(){}
@Autowired
public void init(DescriptionConf descriptionConf) throws Exception {
Class<? extends DescriptionConf> clazz = descriptionConf.getClass();
Field field = clazz.getField(this.name().toLowerCase());
description = (String) field.get(descriptionConf);
}
public String getDescription() {
return description;
}
}
Как мы это чиним?
• Нужен кастомный classloader – открываем им джиру
Singleton vs Prototypes
@Component
@Scope("prototype")
public class T800 {
@PostConstruct
public void init(){
System.out.println("Give me your clothes");
}
@PreDestroy
public void destroy(){
System.out.println("You are terminated");
}
}
@Component
@Scope("singleton")
public class T1000 {
@Autowired
private T800 t800;
@PostConstruct
public void init(){
System.out.println("Where is John Connor");
}
@PreDestroy
public void destroy(){
System.out.println("Страшные звуки");
}
}
context.close();
1.Всё
2.Только цитаты T1000
3.Give me your close / Where is Jonh Connor
4.Give me your close / Where is Jonh Connor / Страшные звуки
Что будет напечатано?
Spring puzzlers 2
Кто знает почему не сработал
destroy method у T800?
Destroy methods не работают для прототипов
А если прописать destroy method в xml-e?
• Что изменится?
• Появится xml!
И даже warning-a не будет!
Сами разбирайтесь
Что надо сделать чтобы был Warning?
• Написать BeanFactoryPostProcessor?
Spring puzzlers 2
Мы напишем BPP
Решаем проблемы перформенса
Где destroy method будет бежать асинхронно?
@Service
public class Тормоз {
@PreDestroy
public void всёЗакрыть(){
… медленный код
}
}
@Service
public class Тормоз {
@PreDestroy
@Async
public void всёЗакрыть(){
… медленный код
}
}
@Bean(destroyMethod = "всёЗакрыть")
<bean class="Тормоз" destroy-method="всёЗакрыть"/>
1.
2.
3.
4.
A. Во всех случаях
B. Ни в каком
C. Только 2, если не забыли @EnableAsync
D. 3 & 4
А мы этой фигнёй не
занимаемся
Как мы это чиним?
• Ну например…
@PreDestroy
public void всёЗакрыть(){
new Thread(new Runnable() {
@Override
public void run() {
… Очень медленный код
}
}).start();
}
ScreenSaver
Frame Scope / Color Scope
A. Singleton / Singleton
B. Singleton / Prototype
C. Prototype / Singleton
D. Prototype / Prototype
Все ответы не правильные
Смерть XML-у
ДЕВОЧКА, КОТОРАЯ ЛЮБИЛА XML
Битва фабрик
Где Integer будет prototype?
<bean id="integer"
class="factoryBeans.IntegerFactory"
scope="prototype"/>
public class IntegerFactory implements FactoryBean<Integer> {
private Random random = new Random();
@Override
public Integer getObject() throws Exception {
return random.nextInt(10);
}
@Override
public Class<?> getObjectType() {
return Integer.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
<bean id="integerFactory"
class="factoryBeans.IntegerFactory"
scope="prototype"/>
<bean id="integer"
class="java.lang.Integer"
scope="singleton"
factory-bean="integerFactory"
factory-method="getInt"/>
A. Только 1
B. Только 2
C. Оба
D. Ни один
1) 2)
Spring puzzlers 2
Где integer будет prototype?
<bean id="integer"
class="factoryBeans.IntegerFactory"
scope="prototype"/>
public class IntegerFactory implements FactoryBean<Integer> {
private Random random = new Random();
@Override
public Integer getObject() throws Exception {
return random.nextInt(10);
}
@Override
public Class<?> getObjectType() {
return Integer.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
<bean id="integerFactory"
class="factoryBeans.IntegerFactory"
scope="prototype"/>
<bean id="integer"
class="java.lang.Integer"
scope="singleton"
factory-bean="integerFactory"
factory-method="getInt"/>
A. Только 1
B. Только 2
C. Оба
D. Ни один
1) 2)
И чо?
Попробуйте сделать scope session
<bean id="integer"
class="factoryBeans.IntegerFactory"
scope="???"/>
public class IntegerFactory implements FactoryBean<Integer> {
private Random random = new Random();
@Override
public Integer getObject() throws Exception {
return random.nextInt(10);
}
@Override
public Class<?> getObjectType() {
return Integer.class;
}
@Override
public boolean isSingleton() {
return ???;
}
}
1)
Попробуйте сделать scope session
<bean id="integer"
class="factoryBeans.IntegerFactory"
scope="session"/> //не забыть proxy-mode
public class IntegerFactory implements FactoryBean<Integer> {
private Random random = new Random();
@Override
public Integer getObject() throws Exception {
return random.nextInt(10);
}
@Override
public Class<?> getObjectType() {
return Integer.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
<bean id="integerFactory"
class="factoryBeans.IntegerFactory"
scope="singleton"/>
<bean id="integer"
class="java.lang.Integer"
factory-bean="integerFactory"
factory-method="getInt“
scope="session"/> //не забыть proxy-mode
1) 2)
Выводы
1. Спринг не идеален, но лучше ничего нет
2. Не бойтесь его чинить
3. Стройте свою платформу на спринге, не
ограничивайтесь тем, что он может сам
4. И главное :
Выводы
Аннотации – это добро!!!
Спасибо за призы
Luxoft
infopulse
Spring puzzlers 2

More Related Content

What's hot

RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на Swift
RAMBLER&Co
 
Мир Python функционалим с помощью библиотек
Мир Python  функционалим с помощью библиотекМир Python  функционалим с помощью библиотек
Мир Python функционалим с помощью библиотек
PyNSK
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?
PyNSK
 
XPath локаторы в Selenium WebDriver
XPath локаторы в Selenium WebDriverXPath локаторы в Selenium WebDriver
XPath локаторы в Selenium WebDriver
Илья Кожухов
 
Practical usage of RxJava 2
Practical usage of RxJava 2Practical usage of RxJava 2
Practical usage of RxJava 2
Evgeniy Vinogradniy
 
JPoint 2015 - Javassist на службе Java-разработчика
JPoint 2015 - Javassist на службе Java-разработчикаJPoint 2015 - Javassist на службе Java-разработчика
JPoint 2015 - Javassist на службе Java-разработчика
Anton Arhipov
 
Curse of spring boot test [VRN]
Curse of spring boot test [VRN]Curse of spring boot test [VRN]
Curse of spring boot test [VRN]
Кирилл Толкачёв
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
Anton Arhipov
 
RxJava+RxAndroid (Lecture 20 – rx java)
RxJava+RxAndroid (Lecture 20 – rx java)RxJava+RxAndroid (Lecture 20 – rx java)
RxJava+RxAndroid (Lecture 20 – rx java)
Noveo
 
Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++
Sergey Platonov
 
Spring Boot Test horror
Spring Boot Test horrorSpring Boot Test horror
Spring Boot Test horror
Кирилл Толкачёв
 
Документирование исходных текстов (javadoc)
Документирование исходных текстов (javadoc)Документирование исходных текстов (javadoc)
Документирование исходных текстов (javadoc)
Fedor Malyshkin
 
Поговорим о JavaScript, основы и современные тенденции развития языка
Поговорим о JavaScript, основы и современные тенденции развития языкаПоговорим о JavaScript, основы и современные тенденции развития языка
Поговорим о JavaScript, основы и современные тенденции развития языка
Alexander Kucherenko
 
Easy selenium test automation on python
Easy selenium test automation on pythonEasy selenium test automation on python
Easy selenium test automation on python
Mykhailo Poliarush
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности Python
PyNSK
 
Joker 2016 - Bytecode 101
Joker 2016 - Bytecode 101Joker 2016 - Bytecode 101
Joker 2016 - Bytecode 101
Anton Arhipov
 
Очередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеОчередной скучный доклад про логгирование
Очередной скучный доклад про логгирование
Python Meetup
 
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Что нам стоит DAL построить? Акуляков Артём D2D Just.NETЧто нам стоит DAL построить? Акуляков Артём D2D Just.NET
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Dev2Dev
 

What's hot (20)

RDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на SwiftRDSDataSource: Чистые тесты на Swift
RDSDataSource: Чистые тесты на Swift
 
Мир Python функционалим с помощью библиотек
Мир Python  функционалим с помощью библиотекМир Python  функционалим с помощью библиотек
Мир Python функционалим с помощью библиотек
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?
 
XPath локаторы в Selenium WebDriver
XPath локаторы в Selenium WebDriverXPath локаторы в Selenium WebDriver
XPath локаторы в Selenium WebDriver
 
Practical usage of RxJava 2
Practical usage of RxJava 2Practical usage of RxJava 2
Practical usage of RxJava 2
 
JPoint 2015 - Javassist на службе Java-разработчика
JPoint 2015 - Javassist на службе Java-разработчикаJPoint 2015 - Javassist на службе Java-разработчика
JPoint 2015 - Javassist на службе Java-разработчика
 
Curse of spring boot test [VRN]
Curse of spring boot test [VRN]Curse of spring boot test [VRN]
Curse of spring boot test [VRN]
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
 
RxJava+RxAndroid (Lecture 20 – rx java)
RxJava+RxAndroid (Lecture 20 – rx java)RxJava+RxAndroid (Lecture 20 – rx java)
RxJava+RxAndroid (Lecture 20 – rx java)
 
Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++Борис Сазонов, RAII потоки и CancellationToken в C++
Борис Сазонов, RAII потоки и CancellationToken в C++
 
Spring Boot Test horror
Spring Boot Test horrorSpring Boot Test horror
Spring Boot Test horror
 
Selenium vs AJAX
Selenium vs AJAXSelenium vs AJAX
Selenium vs AJAX
 
Bytecode
BytecodeBytecode
Bytecode
 
Документирование исходных текстов (javadoc)
Документирование исходных текстов (javadoc)Документирование исходных текстов (javadoc)
Документирование исходных текстов (javadoc)
 
Поговорим о JavaScript, основы и современные тенденции развития языка
Поговорим о JavaScript, основы и современные тенденции развития языкаПоговорим о JavaScript, основы и современные тенденции развития языка
Поговорим о JavaScript, основы и современные тенденции развития языка
 
Easy selenium test automation on python
Easy selenium test automation on pythonEasy selenium test automation on python
Easy selenium test automation on python
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности Python
 
Joker 2016 - Bytecode 101
Joker 2016 - Bytecode 101Joker 2016 - Bytecode 101
Joker 2016 - Bytecode 101
 
Очередной скучный доклад про логгирование
Очередной скучный доклад про логгированиеОчередной скучный доклад про логгирование
Очередной скучный доклад про логгирование
 
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
Что нам стоит DAL построить? Акуляков Артём D2D Just.NETЧто нам стоит DAL построить? Акуляков Артём D2D Just.NET
Что нам стоит DAL построить? Акуляков Артём D2D Just.NET
 

Similar to Spring puzzlers 2

Curse of spring boot test
Curse of spring boot testCurse of spring boot test
Curse of spring boot test
Кирилл Толкачёв
 
Как писать под Android программы, а не код
Как писать под Android программы, а не кодКак писать под Android программы, а не код
Как писать под Android программы, а не код
0leGG
 
Java/Scala Lab: Юрий Литвиненко - Lightning talk
Java/Scala Lab: Юрий Литвиненко - Lightning talkJava/Scala Lab: Юрий Литвиненко - Lightning talk
Java/Scala Lab: Юрий Литвиненко - Lightning talk
GeeksLab Odessa
 
Принципы проектирования S.O.L.I.D
Принципы проектирования S.O.L.I.DПринципы проектирования S.O.L.I.D
Принципы проектирования S.O.L.I.D
AndreyGeonya
 
Behat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и MinkBehat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и Mink
tyomo4ka
 
Spring the Ripper by Evgeny Borisov
Spring the Ripper by Evgeny BorisovSpring the Ripper by Evgeny Borisov
Spring the Ripper by Evgeny Borisov
JavaDayUA
 
MySQL Test Framework для поддержки клиентов и верификации багов
MySQL Test Framework для поддержки клиентов и верификации баговMySQL Test Framework для поддержки клиентов и верификации багов
MySQL Test Framework для поддержки клиентов и верификации багов
Sveta Smirnova
 
Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2
Oleksii Okhrymenko
 
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Ontico
 
использование Hibernate java persistence.part 1.
использование Hibernate java persistence.part 1.использование Hibernate java persistence.part 1.
использование Hibernate java persistence.part 1.
Asya Dudnik
 
Чуть сложнее чем Singleton: аннотации, IOC, АОП
Чуть сложнее чем Singleton: аннотации, IOC, АОПЧуть сложнее чем Singleton: аннотации, IOC, АОП
Чуть сложнее чем Singleton: аннотации, IOC, АОП
Kirill Chebunin
 
BDD girls Battle: Cucumber VS. JBehave
BDD girls Battle: Cucumber VS. JBehaveBDD girls Battle: Cucumber VS. JBehave
BDD girls Battle: Cucumber VS. JBehave
SQALab
 
Programming Java - Lecture 02 - Objects - Lavrentyev Fedor
Programming Java - Lecture 02 - Objects - Lavrentyev FedorProgramming Java - Lecture 02 - Objects - Lavrentyev Fedor
Programming Java - Lecture 02 - Objects - Lavrentyev Fedor
Fedor Lavrentyev
 
C# Deep Dive
C# Deep DiveC# Deep Dive
C# Deep Dive
LuxoftTraining
 
Тестирование Web API
Тестирование Web APIТестирование Web API
Тестирование Web API
Byndyusoft
 
Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозит
Elena Kotina
 
Gradle in Enterprise, Is it possible?
Gradle in Enterprise, Is it possible?Gradle in Enterprise, Is it possible?
Gradle in Enterprise, Is it possible?
Кирилл Толкачёв
 
API design in java project
API design in java projectAPI design in java project
API design in java project
chashnikov
 

Similar to Spring puzzlers 2 (20)

Curse of spring boot test
Curse of spring boot testCurse of spring boot test
Curse of spring boot test
 
Как писать под Android программы, а не код
Как писать под Android программы, а не кодКак писать под Android программы, а не код
Как писать под Android программы, а не код
 
Java/Scala Lab: Юрий Литвиненко - Lightning talk
Java/Scala Lab: Юрий Литвиненко - Lightning talkJava/Scala Lab: Юрий Литвиненко - Lightning talk
Java/Scala Lab: Юрий Литвиненко - Lightning talk
 
Принципы проектирования S.O.L.I.D
Принципы проектирования S.O.L.I.DПринципы проектирования S.O.L.I.D
Принципы проектирования S.O.L.I.D
 
Behat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и MinkBehat в PHP с использованием Behat и Mink
Behat в PHP с использованием Behat и Mink
 
Spring the Ripper by Evgeny Borisov
Spring the Ripper by Evgeny BorisovSpring the Ripper by Evgeny Borisov
Spring the Ripper by Evgeny Borisov
 
MySQL Test Framework для поддержки клиентов и верификации багов
MySQL Test Framework для поддержки клиентов и верификации баговMySQL Test Framework для поддержки клиентов и верификации багов
MySQL Test Framework для поддержки клиентов и верификации багов
 
Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2
 
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
 
использование Hibernate java persistence.part 1.
использование Hibernate java persistence.part 1.использование Hibernate java persistence.part 1.
использование Hibernate java persistence.part 1.
 
JavaScript Intro
JavaScript IntroJavaScript Intro
JavaScript Intro
 
Чуть сложнее чем Singleton: аннотации, IOC, АОП
Чуть сложнее чем Singleton: аннотации, IOC, АОПЧуть сложнее чем Singleton: аннотации, IOC, АОП
Чуть сложнее чем Singleton: аннотации, IOC, АОП
 
BDD girls Battle: Cucumber VS. JBehave
BDD girls Battle: Cucumber VS. JBehaveBDD girls Battle: Cucumber VS. JBehave
BDD girls Battle: Cucumber VS. JBehave
 
Programming Java - Lecture 02 - Objects - Lavrentyev Fedor
Programming Java - Lecture 02 - Objects - Lavrentyev FedorProgramming Java - Lecture 02 - Objects - Lavrentyev Fedor
Programming Java - Lecture 02 - Objects - Lavrentyev Fedor
 
C# Deep Dive
C# Deep DiveC# Deep Dive
C# Deep Dive
 
C sharp deep dive
C sharp deep diveC sharp deep dive
C sharp deep dive
 
Тестирование Web API
Тестирование Web APIТестирование Web API
Тестирование Web API
 
Android: Как написать приложение, которое не тормозит
Android: Как  написать приложение, которое не тормозитAndroid: Как  написать приложение, которое не тормозит
Android: Как написать приложение, которое не тормозит
 
Gradle in Enterprise, Is it possible?
Gradle in Enterprise, Is it possible?Gradle in Enterprise, Is it possible?
Gradle in Enterprise, Is it possible?
 
API design in java project
API design in java projectAPI design in java project
API design in java project
 

Spring puzzlers 2

  • 1. О себе • Пишу на джаве с 2001 • Преподаю джаву с 2003 • Консультирую с 2008 • Арихтектор с 2009 • Пилю стартап с 2014 • Техлид по биг дате с 2015 • Naya technologies c 2015
  • 4. public interface Утюг { @PostConstruct void разогреваться(); } @Component public class СуперДуперУтюг implements Утюг { @Override public void разогреваться() { System.out.println("разогреваюсь"); } @PostConstruct @Autowired public void гретьВоду(Вода вода){ System.out.println(вода+" грейся..."); } } A. Вода грейся B. Вода грейся & Разогреваюсь @Component public class Вода { @Override public String toString() { return "вода"; } } C. Разогреваюсь D. Что то пойдёт не так Что будет?
  • 6. IllegalStateException • method annotation requires a no-arg method: public void iLoveInterfaces.СуперДуперУтюг.гретьВоду
  • 7. Как чиним? • Убираем @Postconstruct с метода помеченного @Autowired @PostConstruct @Autowired public void гретьВоду(Вода вода){ System.out.println(вода+" грейся..."); }
  • 8. public interface Утюг { @PostConstruct void разогреваться(); } @Component public class СуперДуперУтюг implements Утюг { @Override public void разогреваться() { System.out.println("разогреваюсь"); } @Autowired public void гретьВоду(Вода вода){ System.out.println(вода+" грейся..."); } } A. Вода грейся B. Вода грейся & Разогреваюсь @Component public class Вода { @Override public String toString() { return "вода"; } } C. Разогреваюсь D. Вообще ничего не напечатается Что будет?
  • 10. public interface Утюг { @PostConstruct void разогреваться(); } @Component public class СуперДуперУтюг implements Утюг { @Override public void разогреваться() { System.out.println("разогреваюсь"); } @Autowired public void гретьВоду(Вода вода){ System.out.println(вода+" грейся..."); } } A. Вода грейся B. Вода грейся & Разогреваюсь @Component public class Вода { @Override public String toString() { return "вода"; } } C. Разогреваюсь D. Вообще ничего не напечатается Что будет?
  • 13. Достал уже со своими BeanPostProcessors
  • 15. Будем сегодня писать BPP? • Напишем BeanFactoryPostProcessor
  • 16. Не будем повторять ошибки Спринга • Это не правильно, что за Postconstruct отвечает BPP
  • 17. BeanDefinition – это интерфейс • AbstractBeanDefinition • AnnotatedBeanDefinition • ConfigurationClassBeanDefinition • GenericBeanDefinition • ScannedGenericBeanDefinition
  • 18. BeanDefinition – это интерфейс • AnnotatedBeanDefinition • AbstractBeanDefinition • ConfigurationClassBeanDefinition • GenericBeanDefinition • ScannedGenericBeanDefinition
  • 19. • AbstractBeanDefinition • AnnotatedBeanDefinition • ConfigurationClassBeanDefinition • GenericBeanDefinition • ScannedGenericBeanDefinition Та не…
  • 20. • AnnotatedBeanDefinition • AbstractBeanDefinition • ConfigurationClassBeanDefinition • GenericBeanDefinition • ScannedGenericBeanDefinition Хрен тебе
  • 21. class ConfigurationClassBeanDefinitionReader { } private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition Package friendly
  • 22. Когда не получается закастить по хорошему…
  • 23. Пошли ковырять то, что спрятано
  • 24. Сага о двух программистах
  • 25. @Configuration public class BaruchConfig { @Bean public String str1() {return "Groovy";} @Bean public String str2() {return "Spring";} } @Configuration public class JekaConfig { @Bean public List<String> messages() { ArrayList<String> strings = new ArrayList<>(); strings.add("Groovy"); strings.add("Spring"); return strings; } } @Service public class BaruchService { @Autowired private List<String> list; @Service public class JekaService { @Autowired private List<String> list; Что заинжектится в лист? 1. Groovy, Spring, Groovy, Spring 2. Groovy, Spring 3. Groovy, Spring, [Groovy, Spring] 4. Exception
  • 26. А в чём тут был паззлер?
  • 28. Пока Жека дров не наломал я подстрахуюсь @Configuration public class BaruchConfig { @Bean @BaruchQualifier public String str1() {return "Groovy";} @Bean @BaruchQualifier public String str2() {return "Spring";} } @Service public class BaruchService { @Autowired @BaruchQualifier private List<String> list;
  • 29. @Configuration public class JekaConfig { @Bean public List<String> messages() { ArrayList<String> strings = new ArrayList<>(); strings.add("Groovy"); strings.add("Spring"); return strings; } } @Service public class JekaService { @Autowired private List<String> list; 1. У Баруха всё хорошо 2. У Жеки всё хорошо 3. У нас обоих всё хорошо 4. У нас обоих всё плохо @Configuration public class BaruchConfig { @Bean @BaruchQualifier public String str1() {return "Groovy";} @Bean @BaruchQualifier public String str2() {return "Spring";} } @Service public class BaruchService { @Autowired @BaruchQualifier private List<String> list; Что теперь будет?
  • 30. Ну когда уже будет паззлер?
  • 31. А теперь можно добавить Артифактори
  • 32. @Configuration public class JekaConfig { @Bean public List<String> messages() { ArrayList<String> strings = new ArrayList<>(); strings.add("Groovy"); strings.add("Spring"); return strings; } } @Service public class JekaService { @Autowired private List<String> list; Да Нет @Configuration public class BaruchConfig { @Bean @BaruchQualifier public String str1() {return "Groovy";} @Bean @BaruchQualifier public String str2() {return "Spring";} @Bean @BaruchQualifier public String str3() {return "Artifactory";} } @Service public class BaruchService { @Autowired @BaruchQualifier private List<String> list; Повлияет ли это на лист Жеки?
  • 33. Какого хрена тут Артифактори делает??
  • 34. @Configuration public class JekaConfig { @Bean @JekaQualifier public List<String> messages() { ArrayList<String> strings = new ArrayList<> strings.add("Groovy"); strings.add("Spring"); return strings; } } @Service public class JekaService { @Autowired @JekaQualifier private List<String> list; 1. Groovy, Spring 2. Groovy, Spring, Artifactory @Configuration public class BaruchConfig { @Bean @BaruchQualifier public String str1() {return "Groovy";} @Bean @BaruchQualifier public String str2() {return "Spring";} @Bean @BaruchQualifier public String str3() {return "Artifactory";} } @Service public class BaruchService { @Autowired @BaruchQualifier private List<String> list; Что теперь с листом Жеки? 3. Пусто 4. Exception
  • 36. @Configuration public class JekaConfig { @Bean @JekaQualifier public List<String> messages() { ArrayList<String> strings = new ArrayList<> strings.add("Groovy"); strings.add("Spring"); return strings; } } @Service public class JekaService { @Autowired @JekaQualifier private List<String> list; @Configuration public class BaruchConfig { @Bean @BaruchQualifier public String str1() {return "Groovy";} @Bean @BaruchQualifier public String str2() {return "Spring";} @Bean @BaruchQualifier public String str3() {return "Artifactory";} } @Service public class BaruchService { @Autowired @BaruchQualifier private List<String> list; Какой Exception и почему?
  • 37. Как мы это чиним? 1. ArrayList<String> вместо List<String>? 2. S 3. Не надо делать бин, который лист. Работаем с квалифаером @Autowired @JekaQualifier private List<List<String>> list;
  • 40. @Retention(RUNTIME) @Qualifier public @interface Comedy{} @Retention(RUNTIME) @Qualifier public @interface Action{} @Retention(RUNTIME) @Qualifier public @interface Melodrama{} @Retention(RUNTIME) @Qualifier @Comedy @Action @Melodrama public @interface AnyGenre{} public interface Actor { void play(); }
  • 41. @Component @Comedy public class Jim implements Actor { } @Component public class Katy implements Actor { } @Component @Melodrama public class Julia implements Actor { } @Component @AnyGenre public class Chuck implements Actor { } @Component @Action @Comedy public class Stallone implements Actor { } @Component @Action public class Tobey implements Actor { } @Service public class Film { @Autowired @Comedy @Action private List<Actor> actors; } Что заинжектится в этот лист? 1. Jim, Tobey 2. Jim, Tobey, Stallone, Chuck 3. Chuck, Katy 4. Chuck, Stallone 5. Что то пойдёт не так
  • 42. А как же я…
  • 43. @Component @Comedy public class Jim implements Actor { } @Component public class Katy implements Actor { } @Component @Melodrama public class Julia implements Actor { } @Component @AnyGenre public class Chuck implements Actor { } @Component @Action @Comedy public class Stallone implements Actor { } @Component @Action public class Tobey implements Actor { } @Service public class Film { @Autowired @Comedy @Action private List<Actor> actors; } Что заинжектится в этот лист? 1. Jim, Tobey 2. Jim, Tobey, Stallone, Chuck 3. Chuck, Katy 4. Chuck, Stallone 5. Что то пойдёт не так
  • 44. Как мы это чиним? • Пишем свою обработку квалифаера
  • 45. Так всё таки напишем BPP?
  • 46. Загадочный PropertyPlaceholder @Service public class SomeService { @Value("${JAVA_HOME}") private void init(String javaHome) { System.out.println(javaHome); } } @Configuration public class Config { @Bean public PropertySourcesPlaceholderConfigurer configurer(){ return new PropertySourcesPlaceholderConfigurer(); } } Что будет? 1.Будет warning, но всё сработает, как надо 2.Ничего не будет напечатано 3.Какой-то хитрый Exception 4.Напечатается JAVA_HOME, а не его значение
  • 47. Вот такой warning: • WARNING: @Bean method Config.configurer is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface И чо?
  • 48. @Configuration public class Config { @Value("${JAVA_HOME}") private String javaHome; @PostConstruct public void init(){ System.out.println("javaHome = " + javaHome); } @Bean public PropertySourcesPlaceholderConfigurer configurer(){ return new PropertySourcesPlaceholderConfigurer(); } } 1.Будет warning, но всё сработает, как надо 2.Ничего не будет напечатано 3.Какой-то хитрый Exception 4.Напечатается javaHome = null Что будет?
  • 49. Как мы это чиним? • Добавляем static
  • 51. Выглядит знакомо? public enum MpiProtocolType implements Serializable{ MAP(2, 3, 23, 56), PRN(4), SRI4SM(560), FSM(550, 551), LTE(316), GTP(4000, 4001, 4002, 1000), ISUP(1100, 1101, 1102, 1103, 1104, 1105), SIP(5000), ALL(0); private final Integer[] opcodes; MpiProtocolType(Integer... opcodes) { this.opcodes=opcodes; } public boolean isMyOpcode(int opcode) { return Arrays.asList(opcodes).contains(opcode); } }
  • 52. А давайте спрингом @Component public enum Gender { MALE, FEMALE; private String description; @Autowired public void init(DescriptionConf descriptionConf) throws Exception { Class<? extends DescriptionConf> clazz = descriptionConf.getClass(); Field field = clazz.getField(this.name().toLowerCase()); description = (String) field.get(descriptionConf); } public String getDescription() { return description; } } Что будет? A. Всё отлично заработает B. NoSuchMethodException C. В description-е будет null D. Что-то другое
  • 53. Failed to instantiate [enums.Gender]: No default constructor found
  • 54. А давайте спрингом@Component public enum Gender { MALE, FEMALE; private String description; Gender(){} @Autowired public void init(DescriptionConf descriptionConf) throws Exception { Class<? extends DescriptionConf> clazz = descriptionConf.getClass(); Field field = clazz.getField(this.name().toLowerCase()); description = (String) field.get(descriptionConf); } public String getDescription() { return description; } } Что теперь? A. Всё отлично заработает B. NoSuchMethodException C. В description-е будет null D. Что-то другое
  • 55. Failed to instantiate [enums.Gender]: No default constructor found
  • 56. Кто знает в чём проблема? @Component public enum Gender { MALE, FEMALE; private String description; Gender(){} @Autowired public void init(DescriptionConf descriptionConf) throws Exception { Class<? extends DescriptionConf> clazz = descriptionConf.getClass(); Field field = clazz.getField(this.name().toLowerCase()); description = (String) field.get(descriptionConf); } public String getDescription() { return description; } }
  • 57. Как мы это чиним? • Нужен кастомный classloader – открываем им джиру
  • 59. @Component @Scope("prototype") public class T800 { @PostConstruct public void init(){ System.out.println("Give me your clothes"); } @PreDestroy public void destroy(){ System.out.println("You are terminated"); } } @Component @Scope("singleton") public class T1000 { @Autowired private T800 t800; @PostConstruct public void init(){ System.out.println("Where is John Connor"); } @PreDestroy public void destroy(){ System.out.println("Страшные звуки"); } } context.close(); 1.Всё 2.Только цитаты T1000 3.Give me your close / Where is Jonh Connor 4.Give me your close / Where is Jonh Connor / Страшные звуки Что будет напечатано?
  • 61. Кто знает почему не сработал destroy method у T800?
  • 62. Destroy methods не работают для прототипов
  • 63. А если прописать destroy method в xml-e? • Что изменится? • Появится xml!
  • 64. И даже warning-a не будет! Сами разбирайтесь
  • 65. Что надо сделать чтобы был Warning? • Написать BeanFactoryPostProcessor?
  • 69. Где destroy method будет бежать асинхронно? @Service public class Тормоз { @PreDestroy public void всёЗакрыть(){ … медленный код } } @Service public class Тормоз { @PreDestroy @Async public void всёЗакрыть(){ … медленный код } } @Bean(destroyMethod = "всёЗакрыть") <bean class="Тормоз" destroy-method="всёЗакрыть"/> 1. 2. 3. 4. A. Во всех случаях B. Ни в каком C. Только 2, если не забыли @EnableAsync D. 3 & 4
  • 70. А мы этой фигнёй не занимаемся
  • 71. Как мы это чиним? • Ну например… @PreDestroy public void всёЗакрыть(){ new Thread(new Runnable() { @Override public void run() { … Очень медленный код } }).start(); }
  • 73. Frame Scope / Color Scope A. Singleton / Singleton B. Singleton / Prototype C. Prototype / Singleton D. Prototype / Prototype
  • 74. Все ответы не правильные
  • 78. Где Integer будет prototype? <bean id="integer" class="factoryBeans.IntegerFactory" scope="prototype"/> public class IntegerFactory implements FactoryBean<Integer> { private Random random = new Random(); @Override public Integer getObject() throws Exception { return random.nextInt(10); } @Override public Class<?> getObjectType() { return Integer.class; } @Override public boolean isSingleton() { return true; } } <bean id="integerFactory" class="factoryBeans.IntegerFactory" scope="prototype"/> <bean id="integer" class="java.lang.Integer" scope="singleton" factory-bean="integerFactory" factory-method="getInt"/> A. Только 1 B. Только 2 C. Оба D. Ни один 1) 2)
  • 80. Где integer будет prototype? <bean id="integer" class="factoryBeans.IntegerFactory" scope="prototype"/> public class IntegerFactory implements FactoryBean<Integer> { private Random random = new Random(); @Override public Integer getObject() throws Exception { return random.nextInt(10); } @Override public Class<?> getObjectType() { return Integer.class; } @Override public boolean isSingleton() { return true; } } <bean id="integerFactory" class="factoryBeans.IntegerFactory" scope="prototype"/> <bean id="integer" class="java.lang.Integer" scope="singleton" factory-bean="integerFactory" factory-method="getInt"/> A. Только 1 B. Только 2 C. Оба D. Ни один 1) 2)
  • 82. Попробуйте сделать scope session <bean id="integer" class="factoryBeans.IntegerFactory" scope="???"/> public class IntegerFactory implements FactoryBean<Integer> { private Random random = new Random(); @Override public Integer getObject() throws Exception { return random.nextInt(10); } @Override public Class<?> getObjectType() { return Integer.class; } @Override public boolean isSingleton() { return ???; } } 1)
  • 83. Попробуйте сделать scope session <bean id="integer" class="factoryBeans.IntegerFactory" scope="session"/> //не забыть proxy-mode public class IntegerFactory implements FactoryBean<Integer> { private Random random = new Random(); @Override public Integer getObject() throws Exception { return random.nextInt(10); } @Override public Class<?> getObjectType() { return Integer.class; } @Override public boolean isSingleton() { return true; } } <bean id="integerFactory" class="factoryBeans.IntegerFactory" scope="singleton"/> <bean id="integer" class="java.lang.Integer" factory-bean="integerFactory" factory-method="getInt“ scope="session"/> //не забыть proxy-mode 1) 2)
  • 84. Выводы 1. Спринг не идеален, но лучше ничего нет 2. Не бойтесь его чинить 3. Стройте свою платформу на спринге, не ограничивайтесь тем, что он может сам 4. И главное :