SlideShare a Scribd company logo
1 of 261
Download to read offline
Spring Boot
The Ripper
@tolkv
@lavcraft
@jekaborisov
@jeka1978
ЗАБАСТОВКА БИНОВ ПОДАВЛЕНА,
СИНГЛТОНЫ НЕ ВЫЙДУТ ИЗ
КОНТЕЙНЕРА ДО КОНЦА СРОКА
ЛЕНИВЫЕ СИНГЛТОНЫ МОГУТ НЕ
СОЗДАВАТЬСЯ С ВЕРСИИ 4.3
Application Listener-ы требуют получать
дополнительные ивенты
ApplicationListener
Они объявили голодовку и их жизни в
опасности
Йорген Холлер не подписал заявление
XmlBeanDefinitionReader-а об уходе на пенсию
Они ещё Spring Boot-у послужат,
прокомментировал он на Javaday 2016.
ClassPathBeanDefinitionScanner ищет
компоненты, но на находит сервисы
BeanPostProcessor-ы требуют
льготных условий. Spring
держится лишь на нас
BeanFactory изменяет Single Responsibility
Теперь он выглядит вот так:
Ретросперктива
Bean
Factory
Application
Listener
ClassPatBeanDefinitionScanner
BPP
BPP
Bean
Factory
BPP
Application
Listener
ClassPatBeanDefinitionScanner
BPP
BPP
Bean
Factory
BPP
Context
Application
Listener
ClassPatBeanDefinitionScanner
BPP
BPP
Bean
Factory
BPP
ContextWAI
EPP
SApp
ENV
system prop
application.
yml
SpringFactoriesLoader
RDB
CFL
Starter mn,,spring.factories
Spring Boot Project
start.spring.io -да?
Но мы сделаем по более модному...
Как выглядит наш pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.3.RELEASE</version>
</parent>
В чём проблема получить
dependency management таким путём?
Это чужой parent!
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Brussels-SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Кто всё пакует
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Jar как Jar, но если зайти внутрь...
Как выглядит наш build.gradle
plugins {
id "org.springframework.boot" version "1.5.3.RELEASE"
}
Пакует
приложение
Как выглядит наш build.gradle
plugins {
id "org.springframework.boot" version "1.5.3.RELEASE"
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
}
Добавляем
зависимости
Как выглядит наш build.gradle
plugins {
id "org.springframework.boot" version "1.5.3.RELEASE"
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
}
Automatic
Dependency
Management
Как выглядит наш build.gradle
plugins {
id "org.springframework.boot" version "1.5.3.RELEASE"
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
}
dependencyManagement {
imports {
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Dalston.RELEASE'
}
}
Как запускать
1. tomcat war
2. idea
3. java -jar /war
Анатомия War
war
|- META-INF
|- WEB-INF
|- lib
|- classes
А в Tomcat не запустилось!
В Tomcat`e
не пашет
Посмотрим под капот
Жизнь без web.xml
<failOnMissingWebXml>false</failOnMissingWebXml>
Java Servlet Specification 3.+
Tomcat 7+
Что будем имплементировать
вместо web.xml?
WebApplicationInitializer
ServletContainerInitializer
WebApplicationInitializer
ServletContainerInitializer
§ 8.2.4 Shared libraries / runtimes pluggability
The ServletContainerInitializer class is looked up via
the jar services API. For each application, an instance
of the ServletContainerInitializer is created by the
container at application startup time. The framework
providing an implementation of the
ServletContainerInitializer MUST bundle in the
META-INF/services directory of the jar file a file called
javax.servlet.ServletContainerInitializer, as per the jar
services API, that points to the implementation class of
the ServletContainerInitializer.
3.+
Как? SPI
tomcat
|→ get javax.servlet.ServletContainerInitializer impl via SPI
§ 8.2.4 Shared libraries / runtimes pluggability
The ServletContainerInitializer’s onStartup method get's
a Set of Classes that either extend / implement the
classes that the initializer expressed interest in or if
it is annotated with any of the classes specified via the
@HandlesTypes annotation.
3.+
Как? SPI
tomcat
|→ get javax.servlet.ServletContainerInitializer impl via SPI
|→ get all MyInitializer classes (from @HandlesTypes)
Как? SPI
tomcat
|→ get javax.servlet.ServletContainerInitializer impl via SPI
|→ get all WebApplicationInitializer classes (from @HandlesTypes)
org.springframework.web.WebApplicationInitializer
Как? SPI
tomcat
|→ get javax.servlet.ServletContainerInitializer impl via SPI
|→ get all WebApplicationInitializer classes (from @HandlesTypes)
|→ call ServletContainerInitializer.onStartup(classes,servletCtx)
according to @Order order
SPI
org.springframework.web.SpringServletContainerInitializer
content
§ Class ServiceLoader<S>
loader = ServiceLoader.load(ServletContainerInitializer.class)
6.+
§ Class ServiceLoader<S>
loader = ServiceLoader.load(ServletContainerInitializer.class)
6.+
Все имплементации ServletContainerInitializer
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements
ServletContainerInitializer {
...
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements
ServletContainerInitializer {
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements
ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses,
ServletContext servletContext)
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements
ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses,
ServletContext servletContext)
...
AnnotationAwareOrderComparator.sort(initializers);
...
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements
ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses,
ServletContext servletContext)
...
AnnotationAwareOrderComparator.sort(initializers);
...
initializers.forEach(initializer -> initializer.onStartup(ctx));
}
Дружок, дай ка мне
ServletContainerInitializer
tomcat 7
Spring
Jar
Spring
ServletContainer
Initializer
tomcat 7
Spring
Jar
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer
implements ServletContainerInitializer
WebApplicationInitializer
W
AI
WAI
tomcat 7
JARS
W
AI
WAI
SpringServletContainerInitializer
tomcat 7
public class WebStarter implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext webContext = new
AnnotationConfigWebApplicationContext();
webContext.register(WebConfig.class);
ServletRegistration.Dynamic servlet =
servletContext.addServlet("dispatcher", new DispatcherServlet(webContext));
servlet.setLoadOnStartup(1);
AnnotationConfigWebApplicationContext appContext = new
AnnotationConfigWebApplicationContext();
appContext.register(AppConfig.class);
servletContext.addListener(new ContextLoaderListener(appContext));
servlet.addMapping("/*");
}
}
А попроще нельзя?
public class WebStarter implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
SpringApplication.run(RipperApplication.class);
}
}
public class WebStarter implements WebApplicationInitializer{
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
SpringApplication.run(RipperApplication.class);
}
}
Tomcat в Tomcat`е
Читаем документацию
§ 85.1 Create a deployable war file
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
return app.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
§ 85.1 Create a deployable war file
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
return app.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
§ 85.1 Create a deployable war file
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
return app.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Работает по
разному
§ 85.1 Create a deployable war file
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
return app.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
§ 85.1 Create a deployable war file
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
} Никогда не запустится в tomcat
Только java -jar
§ 85.1 Create a deployable war file
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Без main не соберется
см. maven/gradle плагины
Обязательно
§ 85.1 Create a deployable war file
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
Опционально
§ 85.1 Create a deployable war file
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
} Никогда не запустится в embedded режиме
Только в Tomcat контейнере
Опционально
Анатомия War
war
|- META-INF
|- WEB-INF
|- lib
|- classes
|- org/springframework/boot/loader
|- WarLauncher and friends
А как убрать tomcat из tomcat?
If you are using a version of Gradle that supports compile only dependencies
(2.12 or later), you should continue to use providedRuntime. Among other
limitations, compileOnly dependencies are not on the test classpath so any
web-based integration tests will fail.
Анатомия War
war
|- META-INF
|- WEB-INF
|- lib
|- lib-provided
|- classes
|- org/springframework/boot/loader
|- WarLauncher and friends
2. Как запускать
1. tomcat war
2. Idea run
3. java -jar *.jar
4. java -jar *.war
Жмяк и Работает
executable jar для бабушки
Хочу executable jar: Maven
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>
Хочу executable jar: Gradle
springBoot {
executable = true
}
Demo. Executable jar
Внутренний мир Jar
● Бабушка хочет чтобы жмя и работало
● windows “click click”
● $ ./app.jar
● ...
Demo. Executable jar structure
Как выглядит Jar?
Script
0xf4ra -> а это ZIP!
jar archive
Как выглядит Jar?
Script
0xf4ra -> а это ZIP!
jar archive
Начало файла
Как выглядит Jar?
Script
0xf4ra -> а это ZIP!
jar archive
Начало файла
Начало zip архива
Другое дело!
А что же происходит когда java -jar
???
java -jar spring-boot-app.jar main
java запустит мой –
main-class?
java -jar spring-boot-app.jar main
java запустит мой –
main-class?
нет
Как выбирается main class
● Манифест
○ Main-Class
→ WarLauncher
→ JarLauncher
● Start-Class – как выбирается (gradle/maven)
○ сканирует проект – ищет мейны
○ если больше одного – смотрит на SpringBootApplication и выбирает
○ если SpringBootApplication нет или на каждому – ошибка о множественных main
Анатомия SpringBoot Jar
jar
|- META-INF
|- BOOT-INF
|- libs
|- classes
|- classes
|- JarLauncher and friends
Анатомия SpringBoot Jar
jar
|- META-INF
|- BOOT-INF
|- libs
|- classes
|- org/springframework/boot/loader
|- JarLauncher and friends
war
|- META-INF
|- WEB-INF
|- lib
|- lib-provided
|- classes
|- org/springframework/boot/loader
|- WarLauncher and friends
Jar стал похож на War
Перед запуском main – сформируй classpath
Кто формирует classpath
java -jar app.jar → JarLauncher
java -jar app.war → WarLauncher
cp app.war $TOMCAT_HOME/webapps → tomcat
Как выбирается main class
● Манифест
○ Main-Class
→ WarLauncher
→ JarLauncher
● Start-Class – как выбирается (gradle/maven)
○ сканирует проект – ищет мейны
○ если больше одного – смотрит на SpringBootApplication и выбирает
○ если SpringBootApplication нет или на каждому – ошибка о множественных main
public static void main(String[] args) {
SpringApplication.run(RipperApplication.class, args);
…
Наконец то добрались до SpringApplication.run(...)
Как выбирается main class
● Манифест
● WarLauncher
● JarLauncher
● Start-Class
○ сканирует проект – ищет мейны
○ если больше одного – смотрит на SpringBootApplication и выбирает
○ если SpringBootApplication нет или на каждому – ошибка о множественных main
public static void main(String[] args) {
SpringApplication.run(RipperApplication.class, args);
…
А что будет, если так?
public static void main(String[] args) {
SpringApplication.run(String.class, args);
…
Загадочные аргументы
Дано: RipperApplication.class
public… main(String[] args) {
SpringApplication.run(?,args);
}
1. RipperApplication.class
2. String.class
3. "context.xml"
4. new ClassPathResource("context.xml")
5. java.lang.Package.getPackage("conference.spring.boot.
ripper")
Загадочные аргументы
Дано: RipperApplication.class
public… main(String[] args) {
SpringApplication.run(?,args);
}
1. RipperApplication.class
2. String.class
3. "context.xml"
4. new ClassPathResource("context.xml")
5. Package.getPackage("conference.spring.boot.ripper")
Голосуем!
Все ответы верны
Вернёмся к нашему мэйну
public static void main(String[] args) {
SpringApplication.run(RipperApplication.class, args);
…
Найдите одно отличие
Позвольте представить - SpringApplication
Кто главный? Я главный
SpringApplication
SpringApplication.run(Object[] sources, String[] args)
SpringApplication.run(Object[] sources, String[] args)
# APPLICATION SETTINGS (SpringApplication)
spring.main.sources= # class name, package name, xml location
spring.main.web-environment= # true/false
spring.main.banner-mode=console # log/off
Ну если всё так, то почему вот это падает?
$ java -jar spring-app.jar
public static void main(String[] args) {
SpringApplication.run(String.class, args);
}
Ну если всё так, то почему вот это падает?
$ java -jar spring-app.jar
public static void main(String[] args) {
SpringApplication.run(String.class, args);
}
Caused by: ...ApplicationContextException: Unable to start
EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory
bean.
Нет никакой связи
- Отключи web starter и всё будет
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Давайте поговорим о контексте
Малыш знает про
ClassPathXmlApplicationContext
А какие контексты знаешь ТЫ?
Типы ConfigurableApplicationContext
Это я решаю какой контекст создать
Я до создания
контекста ещё много
всего делаю, но об
этом потом.
SpringApplication
Web Context Generic Context
Web Context Generic Context
Если в класспасе
есть Servlet.class...
AnnotationConfigApplicationContext
и
AnnotationConfigEmbeddedWebApplicationContext
А когда какой? По умолчанию
AnnotationConfigApplicationContext
Если есть
javax.servlet.Servlet и
….ConfigurableWebApplicationContext
то
AnnotationConfigEmbeddedWebApplicationContext
Хочу напомнить, что мы говорили об этом
$ java -jar spring-app.jar
public static void main(String[] args) {
SpringApplication.run(String.class, args);
}
Caused by: ...ApplicationContextException: Unable to start
EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory
bean.
Ну что ты сделал?! Ну что!?
SpringApplication.run(String.class,...)
Говори гад!
Что за стартеры такие??
Слушай, а можно в двух словах про стартеры?
§ 44.1 Understanding auto-configured beans
Under the hood, auto-configuration is implemented with standard @Configuration classes. Additional @Conditional annotations
are used to constrain when the auto-configuration should apply. Usually auto-configuration classes use @ConditionalOnClass and
@ConditionalOnMissingBean annotations. This ensures that auto-configuration only applies when relevant classes are found and
when you have not declared your own @Configuration.
You can browse the source code of spring-boot-autoconfigure to see the @Configuration classes that we provide (see
theMETA-INF/spring.factories file).
Когда Йорген Холер был молодым...
SpringFactoriesLoader
Когда Йорген Холер был молодым...
Spring 3.2
Когда Йорген Холер был молодым...
Spring 3.2
spring.factories
spring.factories
spring.factories
spring.factories
Когда Йорген Холер был молодым...
Spring 3.2
spring.factories
SpringFactoriesLoader
spring.factories
spring.factories
spring.factories
Spring Factories Loader
Jar/Starter
spring.factories
SpringFactoriesLoader
SpringFactoriesLoader
static <T> List<T> loadFactories(
Class<T> factoryClass,
ClassLoader cl
)
static List<String> loadFactoryNames(
Class<?> factoryClass,
ClassLoader cl
)
А как исправить?
SpringApplication.run(String.class, args);
Что же делать?...
SpringApplication.run(Object[] sources, String[] args)
# APPLICATION SETTINGS (SpringApplication)
spring.main.sources= # class name, package name, xml location
spring.main.web-environment= # true/false
spring.main.banner-mode=console # log/off
Можно
$ java -jar spring-app.jar --spring.main.sources=conf.ripper
public static void main(String[] args) {
SpringApplication.run(String.class, args);
}
Вот, оно работает даже не удаляя web starter
$ java -jar spring-app.jar --spring.main.sources=conf.ripper
public static void main(String[] args) {
SpringApplication.run(String.class, args);
}
…RipperApplication: Started RipperApplication in 0.254
seconds (JVM running for 0.634)
Who merge the sources?
$ java -jar spring-app.jar --spring.main.sources=conf.ripper
Who merge the sources?
$ java -jar spring-app.jar --spring.main.sources=conf.ripper
Тузик, фас!
Прошло 6 лет...
Тузик, ты где?
RIP
2005-2017
RIP
2005-2017
writeMethod.invoke(getWrappedInstance(), value);
writeMethod.invoke(getWrappedInstance(), value);
BeanWrapperIml
setPropertyValues(pv);
RelaxedDataBinder
onApplicationEnvironmentPreparedEvent
ConfigFileApplicationListener
multicastEvent(event, resolveDefaultEventType(event));
SimpleApplicationEventMulticaster
listeners.environmentPrepared(environment);
SpringApplication
Почему умер Тузик?
setSources conf.ripper
writeMethod.invoke(getWrappedInstance(), value);
BeanWrapperIml
setPropertyValues(pv);
RelaxedDataBinder
onApplicationEnvironmentPreparedEvent
ConfigFileApplicationListener
multicastEvent(event, resolveDefaultEventType(event));
SimpleApplicationEventMulticaster
listeners.environmentPrepared(environment);
SpringApplication
Почему умер Тузик?
setSources conf.ripper
writeMethod.invoke(getWrappedInstance(), value);
BeanWrapperIml
setPropertyValues(pv);
RelaxedDataBinder
onApplicationEnvironmentPreparedEvent
ConfigFileApplicationListener
multicastEvent(event, resolveDefaultEventType(event));
SimpleApplicationEventMulticaster
listeners.environmentPrepared(environment);
SpringApplication
RelaxedDataBinder
writeMethod.invoke(getWrappedInstance(), value);
BeanWrapperIml
setPropertyValues(pv);
RelaxedDataBinder
onApplicationEnvironmentPreparedEvent
ConfigFileApplicationListener
multicastEvent(event, resolveDefaultEventType(event));
SimpleApplicationEventMulticaster
listeners.environmentPrepared(environment);
SpringApplication
RelaxedDataBinder
writeMethod.invoke(getWrappedInstance(), value);
BeanWrapperIml
setPropertyValues(pv);
RelaxedDataBinder
onApplicationEnvironmentPreparedEvent
ConfigFileApplicationListener
multicastEvent(event, resolveDefaultEventType(event));
SimpleApplicationEventMulticaster
listeners.environmentPrepared(environment);
SpringApplication
Почему умер Тузик?
ApplicationListener
ApplicationListener
EPP
BFPP
BFPP
ApplicationListener
EPP
EnvironmentPostProcessor
public interface EnvironmentPostProcessor {
void postProcessEnvironment(ConfigurableEnvironment env,
S SpringApplication app);
}
EPP
Environment
Property sources:
● systemProperties
● system environment
● random property
● application.properties
● …
Active Profiles
Default Profiles
ENV
system prop
application.yml
...
§ 24.1 Configuring random values - пример EPP
The RandomValuePropertySource is useful for injecting random values (e.g. into secrets or test cases). It can produce integers, longs,
uuids or strings, e.g.
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]} EPP
А давайте свой напишем...
EPP
BFPP
ApplicationListener
EPP
BFPP
EPP
ApplicationListener
BFPP
ApplicationListener
EPP
ConfigFileApplicationListener
writeMethod.invoke(getWrappedInstance(), value);
BeanWrapperIml
setPropertyValues(pv);
RelaxedDataBinder
onApplicationEnvironmentPreparedEvent
ConfigFileApplicationListener
multicastEvent(event, resolveDefaultEventType(event));
SimpleApplicationEventMulticaster
listeners.environmentPrepared(environment);
SpringApplication
Почему умер Тузик?
Карлсон строит environment
Карлсон дает листенеру environment и себя
ConfigFileApplicationListener
Слушает
● ApplicationPreparedEvent
● ApplicationEnvironmentPreparedEvent
Загружает
● application.yml
● application.properties
● env vars
● cmd args
onApplicationEnvironmentPreparedEvent
SpringFactoriesLoader
Сгоняй за
EnvironmentPostProc
essor-ами
onApplicationEnvironmentPreparedEvent
SpringFactoriesLoader
SpringFactoriesLoader.
loadFactories(
EnvironmentPostProcessor.class
…)
EPP
onApplicationEnvironmentPreparedEvent
SpringFactoriesLoader
EPP
onApplicationEnvironmentPreparedEvent
EPPEPP
postProcessEnvironment
ща, отсортирую и
поедем...
onApplicationEnvironmentPreparedEvent
EPPEPP
postProcessEnvironment
Как то он
на нас
похож...
onApplicationEnvironmentPreparedEvent
EPPEPP
postProcessEnvironment
Я с вами
пацаны...
onApplicationEnvironmentPreparedEvent
EPPEPP
postProcessEnvironment
ConfigFileApplicationListener
Слушает
● ApplicationPreparedEvent
● ApplicationEnvironmentPreparedEvent
Загружает
● application.yml
● application.properties
● env vars
● cmd args
spring.factories
SpringFactoriesLoader
А я слышала
про другие события!
ContextStartedEvent
ContextStoppedEvent
ContextRefreshedEvent
ContextClosedEvent
Это Spring Boot,
Тут больше event-ов
Application Events
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent
ContextRefreshedEvent
EmbeddedServletContainerInitializedEvent
ApplicationReadyEvent
ApplicationFailedEvent
...
Application Events
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent
ContextRefreshedEvent
EmbeddedServletContainerInitializedEvent
ApplicationReadyEvent
ApplicationFailedEvent
ContextClosedEvent
Application Events
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent
ContextRefreshedEvent
EmbeddedServletContainerInitializedEvent
ApplicationReadyEvent
ApplicationFailedEvent
ContextClosedEvent
Application Events
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent
ContextRefreshedEvent
EmbeddedServletContainerInitializedEvent
ApplicationReadyEvent
ApplicationFailedEvent
ContextClosedEvent
Application Events
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent
ContextRefreshedEvent
EmbeddedServletContainerInitializedEvent
ApplicationReadyEvent
ApplicationFailedEvent
ContextClosedEvent
А это, забыли?
ContextStartedEvent
ContextStoppedEvent
Найди их место
ApplicationStartingEvent
ApplicationEnvironmentPreparedEvent
ApplicationPreparedEvent
ContextRefreshedEvent
EmbeddedServletContainerInitializedEvent
ApplicationReadyEvent
ApplicationFailedEvent
ContextClosedEvent
ContextStartedEvent
ContextStoppedEvent
1
2
3
4
5
?
Эти ивенты
никогда не работают
А ты их вызывать не
пробовал?
А ты их вызывать не
пробовал?
ctx.start(); →
ctx.stop(); →
ContextStartedEvent
ContextStoppedEvent
Где вылетит эксепшен
ctx.stop(); (1)
ctx.start(); (2)
ctx.close(); (3)
ctx.start(); (4)
Где вылетит эксепшен
ctx.stop(); (1)
ctx.start(); (2)
ctx.close(); (3)
ctx.start(); (4)
1. на строчке (1)
2. на строчке (2)
3. на строчке (3)
4. на строчке (4)
Где вылетит эксепшен
ctx.stop(); (1)
ctx.start(); (2)
ctx.close(); (3)
ctx.start(); (4)
1. на строчке (1)
2. на строчке (2)
3. на строчке (3)
4. на строчке (4)
5. не вылетит вообще
Где вылетит эксепшен
ctx.stop(); (1)
ctx.start(); (2)
ctx.close(); (3)
ctx.start(); (4)
1. на строчке (1)
2. на строчке (2)
3. на строчке (3)
4. на строчке (4)
5. не вылетит вообще
Stop before Start
Где вылетит эксепшен
ctx.stop(); (1)
ctx.start(); (2)
ctx.close(); (3)
ctx.start(); (4)
1. на строчке (1)
2. на строчке (2)
3. на строчке (3)
4. на строчке (4)
5. не вылетит вообще
Вся цепочка по порядку
@SpringBootApplication
public class RipperApplication {
public static void main(String[] args) {
SpringApplication.run(RipperConfiguration.class,args);
}
}
Пакуем разные виды листенеров
SpringFactoriesLoader
Строим и заполняем environment
Находим все ApplicationInitializer
ApplicationContext
Initializer
context = createApplicationContext();
prepareContext(context, environment, listeners,..)
● run all ApplicationContextInitializer
Demo
ApplicationContextInitializer
prepareContext(context, environment, listeners,..)
● run all ApplicationContextInitializer
● load sources
...о том, как малыш стал BeanDefinitionLoader-ом
Мало кто знает…
Чем играл малыш пока хотел собаку
Мелкие, но конкретные ридеры
@
Первая встреча Малыша и Карлсона
Я правда буду первый
наполнять registry?
Конечно ты, ща только
мусор выкину
Вот тебе Registry
@
Вот, твои
BeanDefenitions
BeanDefinitionLoader
int load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
return load((Class<?>) source);
}
if (source instanceof Resource) {
return load((Resource) source);
}
if (source instanceof Package) {
return load((Package) source);
}
if (source instanceof CharSequence) {
return load((CharSequence) source);
}
А load возвращает int
Где же сама регистрация?
BeanDefinitionRegistry
BeanDefinitionRegistry
BeanDefinitionRegistry
BeanDefinitionRegistry
Refresh context
Разочарование
@SpringBootApplication
@SpringBootApplication
→ @ComponentScan
→ @Configuration
→ @EnableAutoConfiguration
@EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
ImportSelector
@Import(Some.class)
@Configuration
ImportSelector
ImportBeanDefinitionRegistrar
@Configuration
ImportBeanDefinitionRegistrar
Вызывается единственный метод:
registerBeanDefinitions
Передается :
BeanDefinitionRegistry, AnnotationMetadata
BeanDefinitionRegistryPostProcessor
Вызывается единственный метод:
void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry
)
Передается:
BeanDefinitionRegistry BeanDefinitionRegistryPostPr
BeanDefinitionRegistryPostProcessor
Путь BeanDefinition
– ImportBeanDefinitionRegistrar
– BeanDefinitionRegistryPostProcessor
– ImportSelector
– postProcessBeanFactory
Путь BeanDefinition
BFPP
ImportS
elector
ImportSelector
String[] selectImports
Возвращает список
названий конфигураций,
которые будут
резолвится
EnableAutoConfigurationImportSelector
& Starters
ImportSelector
spring.factories
spring.factories
spring.factories
EnableAutoConfigurationImportSelector & Starters
ImportSelector
Starter
spring.factories
org.springframework.beans.Bean
InfoFactory=org.springframework.
beans.ExtendedBeanInfoFactory
spring.factories
spring.factories
spring.factories
Spring Application & Starters
Starter
spring.factories
org.springframework.beans.Bean
InfoFactory=org.springframework.
beans.ExtendedBeanInfoFactory
Starter
spring.factories
spring.factories
spring.factories
ImportSelector
@EnableAutoConfiguration
ImportSelector
Web Starter Boot Starter
…
Starter
Mongo Starter
spring-boot-autoconfigure.jar/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJm
xAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
….
spring-boot-autoconfigure.jar
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {
Где то внутри CacheAutoConfiguration
for (int i = 0; i < types.length; i++) {
imports[i] =
CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
CacheConfigurations
private static final Map<CacheType, Class<?>> MAPPINGS;
static {
Map<CacheType, Class<?>> mappings = new HashMap<CacheType, Class<?>>();
mappings.put(CacheType. GENERIC, GenericCacheConfiguration. class);
mappings.put(CacheType. EHCACHE, EhCacheCacheConfiguration. class);
mappings.put(CacheType. HAZELCAST, HazelcastCacheConfiguration. class);
mappings.put(CacheType. INFINISPAN, InfinispanCacheConfiguration. class);
mappings.put(CacheType. JCACHE, JCacheCacheConfiguration. class);
mappings.put(CacheType. COUCHBASE, CouchbaseCacheConfiguration. class);
mappings.put(CacheType. REDIS, RedisCacheConfiguration. class);
mappings.put(CacheType. CAFFEINE, CaffeineCacheConfiguration. class);
addGuavaMapping(mappings);
mappings.put(CacheType. SIMPLE, SimpleCacheConfiguration. class);
mappings.put(CacheType. NONE, NoOpCacheConfiguration. class);
MAPPINGS = Collections. unmodifiableMap(mappings);
}
Захардкодили
Demo: “AutoConfiguration”
Кто ищет все ImportSelectors и находит осла?
Кто ищет все ImportSelectors и находит осла?
ConfigurationClassParser
Он никто и создаётся через new
Его задача искать в тех конфигурациях,
которые ему дают:
@Import
@ImportResource
@Component
@ComponentScan
И вытягивать дополнительные конфигурации
ConfigurationClassParser
Он никто и создаётся через new
Его задача искать в тех конфигурациях,
которые ему дают:
@Import
@ImportResource
@Component
@ComponentScan
И вытягивать дополнительные конфигурации
Lite Configuration
Parser Puzzler
@ComponentScan
public class SuperConfig0 {
@ComponentScan
public static class SuperConfig1 {
@ComponentScan("rrr")
public static class SuperConfig2 {
}
}
}
Parser Puzzler
@ComponentScan
public class SuperConfig0 {
@ComponentScan
public static class SuperConfig1 {
@ComponentScan("rrr")
public static class SuperConfig2 {
}
}
}
Parser Puzzler
@ComponentScan
public class SuperConfig0 {
@ComponentScan
public static class SuperConfig1 {
@ComponentScan("rrr")
public static class SuperConfig2 {
}
}
}
А кто даёт шреку начальные конфигурации?
А кто даёт шреку начальные конфигурации?
BeanDefinitionRegistryPostProcessor
ConfigurationClassPostProcessor
А что делает ConfigurationClassPostProcessor
BeanDefinitionRegistryPostProcessor
ConfigurationClassPostProcessor
● его задача развернуть из sources
остальные конфигурации/бины
Два вопроса
1. Откуда взялся ConfigurationClassPostProcessor?
2. Какие у него начальные конфигурации и где он их взял?
Найди ответ на оба вопроса
А что на данном этапе есть в registry?
Только наши sources из SpringApplication
А что на данном этапе есть в registry?
Только наши sources из SpringApplication
ConfigurationClassParser
ConfigurationClassParser
● метод parse рекурсивно сканирует полученный список конфигураций
● добавляет и парсит BeanDefinition из
ImportSelector/ImportBeanDefinitionRegistrar/ComponentSca
n/Import
● фильтрует BeanDefintion с помощью ConditionEvaluator
● возращает все BeanDefinition найденных конфигураций, которые
передаются в BeanDefinitionReader
shouldSkip, shoudSkip!
Указ
Детям до 3х лет имена не
давать!
Царь.
BeanDefinitionReader
● Обходит полученные конфигурации
● Фильтрует не нужные с помощью @Condition
● загружает @Bean
● загружает *.xml *.groovy @ImportResource
● загружает @Import(*Registrar)
● именует BeanDefinition и добавляет в registry
BeanDefinitions
shouldSkip, shoudSkip!
Conditional и друзья
@ConditionalOnBean
@ConditionalOnClass
@ConditionalOnCloudPlatform
@ConditionalOnExpression
@ConditionalOnJava
@ConditionalOnJndi
@ConditionalOnMissingBean
@ConditionalOnMissingClass
@ConditionalOnNotWebApplication
@ConditionalOnProperty
@ConditionalOnResource
@ConditionalOnSingleCandidate
@ConditionalOnWebApplication
ConditionalOnPuzzler
@Configuration
public class КонфигурацияКазни {
@Bean
@ConditionalOnClass ({Гильотина.class, ХорошееНастроение. class})
@ConditionalOnMissingBean ({ФабрикаКазни. class})
public ФабрикаКазни гильотины() { return new ФабрикаГильотин( "хрусть хрусть"); }
@Bean
@ConditionalOnClass ({Стул.class, Ток.class})
@ConditionalOnMissingBean ({ФабрикаКазни. class})
public ФабрикаКазни cтулья() { return new ФабрикаЭлектрическихСтульев( "вж вж"); }
@Bean
@ConditionalOnClass ({Мыло.class, Веревка.class})
@ConditionalOnMissingBean ({ФабрикаКазни. class})
public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
}
Demo: Conditional
Хотите автокомплит на application.yml?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
Немного выводов
● Spring Boot вышел за пределы Spring Context
● Starter от команды Spring отличаются от public convention
○ не всегда в лучшую сторону
● Три этапа построения контексты
○ #1 Загрузка исходны классов/конфигураций – sources
○ #2 Загрузка BeanDefinition с помощью ImportSelector
○ #3 Загрузка @Bean и прочего внутри BeanDefinition`ов из #2
● в Spring Boot это космос, не магия
Немного выводов
● Spring Boot вышел за пределы Spring Context
● Starter от команды Spring отличаются от public convention
○ не всегда в лучшую сторону
● Три этапа построения контексты
○ #1 Загрузка исходны классов/конфигураций – sources
○ #2 Загрузка BeanDefinition с помощью ImportSelector
○ #3 Загрузка @Bean и прочего внутри BeanDefinition`ов из #2
● в Spring Boot это космос, не магия
Вопросы?
Spring Boot Ripper

More Related Content

What's hot

Kubernetes
KubernetesKubernetes
KubernetesSQALab
 
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»FDConf
 
Разговор про Java 9. Extended version
Разговор про Java 9. Extended versionРазговор про Java 9. Extended version
Разговор про Java 9. Extended versionIvan Krylov
 
Разработка Enterprise-приложения на основе Spring Framework
Разработка Enterprise-приложения на основе Spring FrameworkРазработка Enterprise-приложения на основе Spring Framework
Разработка Enterprise-приложения на основе Spring FrameworkCUSTIS
 
Konstantin slisenko - Spring Framework
Konstantin slisenko - Spring FrameworkKonstantin slisenko - Spring Framework
Konstantin slisenko - Spring Frameworkbeloslab
 
"Web Vitals monitoring & optimizations", Erik Himiranov
"Web Vitals monitoring & optimizations", Erik Himiranov"Web Vitals monitoring & optimizations", Erik Himiranov
"Web Vitals monitoring & optimizations", Erik HimiranovFwdays
 
Codefest-2015 Reactive Streams
Codefest-2015 Reactive StreamsCodefest-2015 Reactive Streams
Codefest-2015 Reactive StreamsAlexey Romanchuk
 
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Andrey Rebrov
 
Пользователь точно оценит! Повышение производительности мобильных приложений ...
Пользователь точно оценит! Повышение производительности мобильных приложений ...Пользователь точно оценит! Повышение производительности мобильных приложений ...
Пользователь точно оценит! Повышение производительности мобильных приложений ...Ontico
 
Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016Кирилл Толкачёв
 
Владимир Еремин. Extending Openstack. PyCon Belarus 2015
Владимир Еремин. Extending Openstack. PyCon Belarus 2015Владимир Еремин. Extending Openstack. PyCon Belarus 2015
Владимир Еремин. Extending Openstack. PyCon Belarus 2015Alina Dolgikh
 
RxJava + Retrofit
RxJava + RetrofitRxJava + Retrofit
RxJava + RetrofitDev2Dev
 
Wild Async .NET world: AID Kit for boy-scouts
Wild Async .NET world: AID Kit for boy-scoutsWild Async .NET world: AID Kit for boy-scouts
Wild Async .NET world: AID Kit for boy-scoutsHYS Enterprise
 
Апгрейд и миграция на SharePoint 2016
Апгрейд и миграция на SharePoint 2016Апгрейд и миграция на SharePoint 2016
Апгрейд и миграция на SharePoint 2016Boris Zhurkin
 
What to expect from Java 9
What to expect from Java 9What to expect from Java 9
What to expect from Java 9JavaDayUA
 
Основы и нюансы параллельного тестрования
Основы и нюансы параллельного тестрованияОсновы и нюансы параллельного тестрования
Основы и нюансы параллельного тестрованияbearoff
 
Автоматизация тестирования многопоточности
Автоматизация тестирования многопоточностиАвтоматизация тестирования многопоточности
Автоматизация тестирования многопоточностиSQALab
 
How to build solid CI-CD pipeline / Илья Беда (beda.software)
How to build solid CI-CD pipeline / Илья Беда (beda.software)How to build solid CI-CD pipeline / Илья Беда (beda.software)
How to build solid CI-CD pipeline / Илья Беда (beda.software)Ontico
 

What's hot (20)

Kubernetes
KubernetesKubernetes
Kubernetes
 
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
«Continuous Integration — A to Z или Непрерывная интеграция — кто всё сломал?»
 
Разговор про Java 9. Extended version
Разговор про Java 9. Extended versionРазговор про Java 9. Extended version
Разговор про Java 9. Extended version
 
Curse of spring boot test [VRN]
Curse of spring boot test [VRN]Curse of spring boot test [VRN]
Curse of spring boot test [VRN]
 
Разработка Enterprise-приложения на основе Spring Framework
Разработка Enterprise-приложения на основе Spring FrameworkРазработка Enterprise-приложения на основе Spring Framework
Разработка Enterprise-приложения на основе Spring Framework
 
Konstantin slisenko - Spring Framework
Konstantin slisenko - Spring FrameworkKonstantin slisenko - Spring Framework
Konstantin slisenko - Spring Framework
 
"Web Vitals monitoring & optimizations", Erik Himiranov
"Web Vitals monitoring & optimizations", Erik Himiranov"Web Vitals monitoring & optimizations", Erik Himiranov
"Web Vitals monitoring & optimizations", Erik Himiranov
 
Codefest-2015 Reactive Streams
Codefest-2015 Reactive StreamsCodefest-2015 Reactive Streams
Codefest-2015 Reactive Streams
 
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
 
Пользователь точно оценит! Повышение производительности мобильных приложений ...
Пользователь точно оценит! Повышение производительности мобильных приложений ...Пользователь точно оценит! Повышение производительности мобильных приложений ...
Пользователь точно оценит! Повышение производительности мобильных приложений ...
 
Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016Эволюционный дизайн. Joker Students Day 2016
Эволюционный дизайн. Joker Students Day 2016
 
Владимир Еремин. Extending Openstack. PyCon Belarus 2015
Владимир Еремин. Extending Openstack. PyCon Belarus 2015Владимир Еремин. Extending Openstack. PyCon Belarus 2015
Владимир Еремин. Extending Openstack. PyCon Belarus 2015
 
Spring in java
Spring in javaSpring in java
Spring in java
 
RxJava + Retrofit
RxJava + RetrofitRxJava + Retrofit
RxJava + Retrofit
 
Wild Async .NET world: AID Kit for boy-scouts
Wild Async .NET world: AID Kit for boy-scoutsWild Async .NET world: AID Kit for boy-scouts
Wild Async .NET world: AID Kit for boy-scouts
 
Апгрейд и миграция на SharePoint 2016
Апгрейд и миграция на SharePoint 2016Апгрейд и миграция на SharePoint 2016
Апгрейд и миграция на SharePoint 2016
 
What to expect from Java 9
What to expect from Java 9What to expect from Java 9
What to expect from Java 9
 
Основы и нюансы параллельного тестрования
Основы и нюансы параллельного тестрованияОсновы и нюансы параллельного тестрования
Основы и нюансы параллельного тестрования
 
Автоматизация тестирования многопоточности
Автоматизация тестирования многопоточностиАвтоматизация тестирования многопоточности
Автоматизация тестирования многопоточности
 
How to build solid CI-CD pipeline / Илья Беда (beda.software)
How to build solid CI-CD pipeline / Илья Беда (beda.software)How to build solid CI-CD pipeline / Илья Беда (beda.software)
How to build solid CI-CD pipeline / Илья Беда (beda.software)
 

Similar to Spring Boot Ripper

08-170327133157.pdf
08-170327133157.pdf08-170327133157.pdf
08-170327133157.pdfssuser0562f1
 
Введение в Spring
Введение в SpringВведение в Spring
Введение в SpringUnguryan Vitaliy
 
The Old New ASP.NET
The Old New ASP.NETThe Old New ASP.NET
The Old New ASP.NETVitaly Baum
 
C# Web. Занятие 10.
C# Web. Занятие 10.C# Web. Занятие 10.
C# Web. Занятие 10.Igor Shkulipa
 
Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.
Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.
Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.Fedor Malyshkin
 
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Noveo
 
C# Web. Занятие 11.
C# Web. Занятие 11.C# Web. Занятие 11.
C# Web. Занятие 11.Igor Shkulipa
 
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)Ontico
 
Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Oleksii Okhrymenko
 
Adn@it'summer - Изоморфные приложения с React и Redux
Adn@it'summer - Изоморфные приложения с React и ReduxAdn@it'summer - Изоморфные приложения с React и Redux
Adn@it'summer - Изоморфные приложения с React и ReduxADN Digital Studio
 
Чуть сложнее чем Singleton: аннотации, IOC, АОП
Чуть сложнее чем Singleton: аннотации, IOC, АОПЧуть сложнее чем Singleton: аннотации, IOC, АОП
Чуть сложнее чем Singleton: аннотации, IOC, АОПKirill Chebunin
 
Functional Testing with Selenium
Functional Testing with SeleniumFunctional Testing with Selenium
Functional Testing with SeleniumQA Club Kiev
 
AndroidMVPHelper
AndroidMVPHelperAndroidMVPHelper
AndroidMVPHelperDataArt
 
Eclipse Monkey
Eclipse MonkeyEclipse Monkey
Eclipse Monkeyilja.panin
 
CodeFest 2011. Высоцкий С. — Crawljax. Четвертый закон робототехники
CodeFest 2011. Высоцкий С. — Crawljax. Четвертый закон робототехникиCodeFest 2011. Высоцкий С. — Crawljax. Четвертый закон робототехники
CodeFest 2011. Высоцкий С. — Crawljax. Четвертый закон робототехникиCodeFest
 
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGвебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGAndrey Rebrov
 

Similar to Spring Boot Ripper (20)

введение в Laravel 5
введение в Laravel 5введение в Laravel 5
введение в Laravel 5
 
servlets.pdf
servlets.pdfservlets.pdf
servlets.pdf
 
servlets1.pdf
servlets1.pdfservlets1.pdf
servlets1.pdf
 
08-170327133157.pdf
08-170327133157.pdf08-170327133157.pdf
08-170327133157.pdf
 
Введение в Spring
Введение в SpringВведение в Spring
Введение в Spring
 
The Old New ASP.NET
The Old New ASP.NETThe Old New ASP.NET
The Old New ASP.NET
 
C# Web. Занятие 10.
C# Web. Занятие 10.C# Web. Занятие 10.
C# Web. Занятие 10.
 
Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.
Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.
Методики «Inversion of Control» и «Dependency Injection». Применение в Spring.
 
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
Многопоточность, работа с сетью (Lecture 12 – multithreading, network)
 
C# Web. Занятие 11.
C# Web. Занятие 11.C# Web. Занятие 11.
C# Web. Занятие 11.
 
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
Превышаем скоростные лимиты с Angular 2 / Алексей Охрименко (IPONWEB)
 
Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2Превышаем скоростные лимиты с Angular 2
Превышаем скоростные лимиты с Angular 2
 
RoboGuice
RoboGuiceRoboGuice
RoboGuice
 
Adn@it'summer - Изоморфные приложения с React и Redux
Adn@it'summer - Изоморфные приложения с React и ReduxAdn@it'summer - Изоморфные приложения с React и Redux
Adn@it'summer - Изоморфные приложения с React и Redux
 
Чуть сложнее чем Singleton: аннотации, IOC, АОП
Чуть сложнее чем Singleton: аннотации, IOC, АОПЧуть сложнее чем Singleton: аннотации, IOC, АОП
Чуть сложнее чем Singleton: аннотации, IOC, АОП
 
Functional Testing with Selenium
Functional Testing with SeleniumFunctional Testing with Selenium
Functional Testing with Selenium
 
AndroidMVPHelper
AndroidMVPHelperAndroidMVPHelper
AndroidMVPHelper
 
Eclipse Monkey
Eclipse MonkeyEclipse Monkey
Eclipse Monkey
 
CodeFest 2011. Высоцкий С. — Crawljax. Четвертый закон робототехники
CodeFest 2011. Высоцкий С. — Crawljax. Четвертый закон робототехникиCodeFest 2011. Высоцкий С. — Crawljax. Четвертый закон робототехники
CodeFest 2011. Высоцкий С. — Crawljax. Четвертый закон робототехники
 
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGвебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
 

More from Кирилл Толкачёв

More from Кирилл Толкачёв (9)

Wild microservices and imaginary DevOps
Wild microservices and imaginary DevOpsWild microservices and imaginary DevOps
Wild microservices and imaginary DevOps
 
Jenkins Imperative Pipeline vs Declarative Pipeline
Jenkins Imperative Pipeline vs Declarative Pipeline Jenkins Imperative Pipeline vs Declarative Pipeline
Jenkins Imperative Pipeline vs Declarative Pipeline
 
Gradle in Enterprise, Is it possible?
Gradle in Enterprise, Is it possible?Gradle in Enterprise, Is it possible?
Gradle in Enterprise, Is it possible?
 
Дикие микросервисы на JUG Екатеринбург
Дикие микросервисы на JUG ЕкатеринбургДикие микросервисы на JUG Екатеринбург
Дикие микросервисы на JUG Екатеринбург
 
Jenkins в docker in mesos in ...
Jenkins в docker in mesos in ...Jenkins в docker in mesos in ...
Jenkins в docker in mesos in ...
 
CD with Jenkins. Lessons Learned
CD with Jenkins. Lessons LearnedCD with Jenkins. Lessons Learned
CD with Jenkins. Lessons Learned
 
Release management with Gradle #JokerConf2016
Release management with Gradle #JokerConf2016Release management with Gradle #JokerConf2016
Release management with Gradle #JokerConf2016
 
Java Day Minsk 2016 Keynote about Microservices in real world
Java Day Minsk 2016 Keynote about Microservices in real worldJava Day Minsk 2016 Keynote about Microservices in real world
Java Day Minsk 2016 Keynote about Microservices in real world
 
Joker 2015 Wild microSERVICES
Joker 2015 Wild microSERVICESJoker 2015 Wild microSERVICES
Joker 2015 Wild microSERVICES
 

Spring Boot Ripper