diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/.gitignore b/2022-05/spring-05-bean-scopes-and-lifecycle/.gitignore new file mode 100644 index 00000000..e62c33c2 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/.gitignore @@ -0,0 +1,4 @@ +.idea/ +*.iml + +target/ diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/.gitignore b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/.gitignore new file mode 100644 index 00000000..789ddc9e --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/.gitignore @@ -0,0 +1,32 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +#other +*.bat +*/.idea +*.iml +*/target + +.idea +*.iml +target \ No newline at end of file diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/README.md b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/README.md new file mode 100644 index 00000000..75a9cff8 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/README.md @@ -0,0 +1,19 @@ +#### Упражнение №2 +- Изучить классы ничего в них не меняя: + - CustomBeanFactoryPostProcessor + - CustomBeanPostProcessor + - CustomLifeCycleBean +- Запуститить приложение +- Изучить распечатанный жизненный цикл + + +#### Упражнение №3 +- В application.yml выставить spring.shell.interactive.enabled в true +- Запуститить приложение командой "cfn" или "call-favorite-number" +- Запомнить результат +- В CustomBeanFactoryPostProcessor раскомментировать блок кода +- Запуститить приложение командой "cfn" или "call-favorite-number" +- Что изменилось? +- В CustomBeanPostProcessor раскомментировать строку +- Запуститить приложение командой "cfn" или "call-favorite-number" +- Что изменилось теперь? Круто, да? \ No newline at end of file diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/pom.xml b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/pom.xml new file mode 100644 index 00000000..6d76d6ed --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.0 + + + ru.otus.example + beans-lifecycle-exercise + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter + + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/BeansLifecycleDemoApplication.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/BeansLifecycleDemoApplication.java new file mode 100644 index 00000000..337afceb --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/BeansLifecycleDemoApplication.java @@ -0,0 +1,20 @@ +package ru.otus.example.beanslifecycledemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import ru.otus.example.beanslifecycledemo.domain.Phone; + +@SpringBootApplication +public class BeansLifecycleDemoApplication { + + public static void main(String[] args) { + ApplicationContext ctx = SpringApplication.run(BeansLifecycleDemoApplication.class, args); + try { + Phone phone = ctx.getBean(Phone.class); + phone.callFavoriteNumber(); + }catch (Exception e) { + } + } + +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/FriendPhoneNumber.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/FriendPhoneNumber.java new file mode 100644 index 00000000..913d7db0 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/FriendPhoneNumber.java @@ -0,0 +1,8 @@ +package ru.otus.example.beanslifecycledemo.domain; + +public class FriendPhoneNumber extends PhoneNumber { + @Override + public String getOwnerName() { + return "Друг"; + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/GirlfiendPhoneNumber.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/GirlfiendPhoneNumber.java new file mode 100644 index 00000000..54e4e173 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/GirlfiendPhoneNumber.java @@ -0,0 +1,11 @@ +package ru.otus.example.beanslifecycledemo.domain; + +import org.springframework.stereotype.Component; + +@Component +public class GirlfiendPhoneNumber extends PhoneNumber { + @Override + public String getOwnerName() { + return "Подруга"; + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/Phone.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/Phone.java new file mode 100644 index 00000000..81170317 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/Phone.java @@ -0,0 +1,18 @@ +package ru.otus.example.beanslifecycledemo.domain; + +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +@Component +@ConditionalOnProperty(name = "lifecycle.print.enabled", havingValue = "false") +@RequiredArgsConstructor +public class Phone { + private String greeting = "Погнали к родителям"; + + private final PhoneNumber favoriteNumber; + + public void callFavoriteNumber() { + System.out.println(favoriteNumber.getOwnerName() + " " + greeting); + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/PhoneNumber.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/PhoneNumber.java new file mode 100644 index 00000000..215bd09f --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/domain/PhoneNumber.java @@ -0,0 +1,7 @@ +package ru.otus.example.beanslifecycledemo.domain; + +public abstract class PhoneNumber { + public String getOwnerName() { + return "Спорт-лото"; + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomBeanDefinitionRegistrar.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomBeanDefinitionRegistrar.java new file mode 100644 index 00000000..fafd360b --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomBeanDefinitionRegistrar.java @@ -0,0 +1,62 @@ +package ru.otus.example.beanslifecycledemo.lifecycle; + +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; + +import java.util.Optional; + +public class CustomBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware { + private Environment environment; + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, + BeanDefinitionRegistry registry, + BeanNameGenerator importBeanNameGenerator) { + registerBeanDefinitions(importingClassMetadata, registry); + } + + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata, + BeanDefinitionRegistry registry) { + + registerBeanFactoryPostProcessor(registry); + registerBeanPostProcessor(registry); + + if (Optional.ofNullable(environment.getProperty("lifecycle.print.enabled", Boolean.class)).orElse(false)) { + System.out.println("Шаг #0: ImportBeanDefinitionRegistrar.registerBeanDefinitions\n"); + registerCustomLifeCycleBean(registry); + } + + } + + private void registerCustomLifeCycleBean(BeanDefinitionRegistry registry) { + GenericBeanDefinition gbd = new GenericBeanDefinition(); + gbd.setBeanClass(CustomLifeCycleBean.class); + gbd.setInitMethodName("customInitMethod"); + gbd.setDestroyMethodName("customDestroyMethod"); + registry.registerBeanDefinition("customLifeCycleBean", gbd); + } + + private void registerBeanFactoryPostProcessor(BeanDefinitionRegistry registry) { + GenericBeanDefinition gbd = new GenericBeanDefinition(); + gbd.setBeanClass(CustomBeanFactoryPostProcessor.class); + registry.registerBeanDefinition("customBeanFactoryPostProcessor", gbd); + } + + private void registerBeanPostProcessor(BeanDefinitionRegistry registry) { + GenericBeanDefinition gbd = new GenericBeanDefinition(); + gbd.setBeanClass(CustomBeanPostProcessor.class); + registry.registerBeanDefinition("customBeanPostProcessor", gbd); + } + +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomBeanFactoryPostProcessor.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomBeanFactoryPostProcessor.java new file mode 100644 index 00000000..5f82e6e5 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomBeanFactoryPostProcessor.java @@ -0,0 +1,32 @@ +package ru.otus.example.beanslifecycledemo.lifecycle; + +import org.springframework.beans.BeanMetadataAttribute; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.ScannedGenericBeanDefinition; +import ru.otus.example.beanslifecycledemo.domain.FriendPhoneNumber; +import ru.otus.example.beanslifecycledemo.domain.GirlfiendPhoneNumber; + +public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + + if (beanFactory.containsBean("customLifeCycleBean")) { + System.out.println("Шаг #1: BeanFactoryPostProcessor.postProcessBeanFactory\n"); + } + +/* + for (String beanName : beanFactory.getBeanDefinitionNames()) { + BeanDefinition d = beanFactory.getBeanDefinition(beanName); + + if (GirlfiendPhoneNumber.class.getName().equalsIgnoreCase(d.getBeanClassName())) { + d.setBeanClassName(FriendPhoneNumber.class.getName()); + ((ScannedGenericBeanDefinition) d).addMetadataAttribute(new BeanMetadataAttribute("className", FriendPhoneNumber.class.getName())); + d.setAutowireCandidate(true); + } + } +*/ + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomBeanPostProcessor.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomBeanPostProcessor.java new file mode 100644 index 00000000..c4dc21ab --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomBeanPostProcessor.java @@ -0,0 +1,48 @@ +package ru.otus.example.beanslifecycledemo.lifecycle; + +import org.springframework.beans.BeansException; +import org.springframework.beans.InvalidPropertyException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import ru.otus.example.beanslifecycledemo.domain.Phone; + +import java.lang.reflect.Field; + +public class CustomBeanPostProcessor implements BeanPostProcessor { + + public static final String GREETING_PROPERTY = "greeting"; + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean.getClass().equals(CustomLifeCycleBean.class)) { + System.out.println("Шаг #5: BeanPostProcessor.postProcessBeforeInitialization\n"); + } + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean.getClass().equals(CustomLifeCycleBean.class)) { + System.out.println("Шаг #9: BeanPostProcessor.postProcessAfterInitialization\n"); + } + +/* + if (bean.getClass().isAssignableFrom(Phone.class)) { + updateGreeting(bean); + } +*/ + return bean; + } + + private void updateGreeting(Object bean) { + Class aClass = Phone.class; + try { + Field greetingField = aClass.getDeclaredField(GREETING_PROPERTY); + + greetingField.setAccessible(true); + greetingField.set(bean, "Ай-да в гараж. Стихи читать!"); + } catch (Exception e) { + throw new InvalidPropertyException(Phone.class, GREETING_PROPERTY, + "Bean class does not have expected property", e); + } + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomLifeCycleBean.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomLifeCycleBean.java new file mode 100644 index 00000000..1f65c281 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/CustomLifeCycleBean.java @@ -0,0 +1,56 @@ +package ru.otus.example.beanslifecycledemo.lifecycle; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.*; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +public class CustomLifeCycleBean implements InitializingBean, DisposableBean, BeanNameAware, + BeanFactoryAware, ApplicationContextAware { + + @Override + public void setBeanName(String s) { + System.out.println("Шаг #2: BeanNameAware\n"); + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + System.out.println("Шаг #3: BeanFactoryAware\n"); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + System.out.println("Шаг #4: ApplicationContextAware\n"); + } + + @PostConstruct + public void postConstruct() { + System.out.println("Шаг #6: @PostConstruct\n"); + } + + @Override + public void afterPropertiesSet() throws Exception { + System.out.println("Шаг #7: InitializingBean.afterPropertiesSet\n"); + } + + public void customInitMethod() { + System.out.println("Шаг #8: CustomLifeCycleBean.customInitMethod\n"); + } + + @PreDestroy + public void preDestroy() { + System.out.println("Шаг #10: @PreDestroy\n"); + } + + @Override + public void destroy() throws Exception { + System.out.println("Шаг #11: DisposableBean.destroy\n"); + } + + public void customDestroyMethod() { + System.out.println("Шаг #12: CustomLifeCycleBean.customDestroyMethod\n"); + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/LifeCycleConfig.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/LifeCycleConfig.java new file mode 100644 index 00000000..a1cb405b --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/java/ru/otus/example/beanslifecycledemo/lifecycle/LifeCycleConfig.java @@ -0,0 +1,30 @@ +package ru.otus.example.beanslifecycledemo.lifecycle; + +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Import(CustomBeanDefinitionRegistrar.class) +@Configuration +public class LifeCycleConfig { +/* + @Bean + public BeanFactoryPostProcessor customBeanFactoryPostProcessor() { + return new CustomBeanFactoryPostProcessor(); + } + + @Bean + public BeanPostProcessor customBeanPostProcessor() { + return new CustomBeanPostProcessor(); + } + + @ConditionalOnProperty(name = "lifecycle.print.enabled", havingValue = "true") + @Bean(initMethod = "customInitMethod", destroyMethod = "customDestroyMethod") + public CustomLifeCycleBean customLifeCycleBean() { + return new CustomLifeCycleBean(); + } +*/ +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/resources/application.yml b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/resources/application.yml new file mode 100644 index 00000000..54b14aef --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-lifecycle-exercise/src/main/resources/application.yml @@ -0,0 +1,7 @@ +logging: + level: + root: ERROR + +lifecycle: + print: + enabled: true diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/.gitignore b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/.gitignore new file mode 100644 index 00000000..789ddc9e --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/.gitignore @@ -0,0 +1,32 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +#other +*.bat +*/.idea +*.iml +*/target + +.idea +*.iml +target \ No newline at end of file diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/README.md b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/README.md new file mode 100644 index 00000000..2a7c2a04 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/README.md @@ -0,0 +1,9 @@ +#### Упражнение №1 +- Расставить скоупы над сервисами так, чтобы приложение запустилось и заработало, как указано на странице http://localhost:8080 +- Подсказки в названии сервисов +- Не забываем про proxyMode для некоторых скоупов + +``` +Задать скоуп можно с помощью аннотации @Scope +Возможные значения: singleton, prototype, session, request +``` \ No newline at end of file diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/pom.xml b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/pom.xml new file mode 100644 index 00000000..faa8d789 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.7.0 + + + + ru.otus.example + beans-scopes-exercise + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/BeansScopesDemoApplication.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/BeansScopesDemoApplication.java new file mode 100644 index 00000000..b482b9a7 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/BeansScopesDemoApplication.java @@ -0,0 +1,15 @@ +package ru.otus.example.beansscopesdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class BeansScopesDemoApplication { + + public static void main(String[] args) { + var ctx = SpringApplication.run(BeansScopesDemoApplication.class, args); + var serverPort = ctx.getEnvironment().getProperty("local.server.port"); + System.out.printf("Чтобы смотреть результат переходи сюда: http://localhost:%s", serverPort); + } + +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/controllers/GreetingController.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/controllers/GreetingController.java new file mode 100644 index 00000000..142fce6e --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/controllers/GreetingController.java @@ -0,0 +1,39 @@ +package ru.otus.example.beansscopesdemo.controllers; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import ru.otus.example.beansscopesdemo.services.GreetingService; + +@Controller +public class GreetingController { + private final GreetingService singletonGreetingService; + private final GreetingService prototypeGreetingService1; + private final GreetingService prototypeGreetingService2; + private final GreetingService sessionGreetingService; + private final GreetingService requestGreetingService; + + public GreetingController(@Qualifier("SingletonGreetingService") GreetingService singletonGreetingService, + @Qualifier("PrototypeGreetingService")GreetingService prototypeGreetingService1, + @Qualifier("PrototypeGreetingService")GreetingService prototypeGreetingService2, + @Qualifier("SessionGreetingService")GreetingService sessionGreetingService, + @Qualifier("RequestGreetingService")GreetingService requestGreetingService + ) { + this.singletonGreetingService = singletonGreetingService; + this.prototypeGreetingService1 = prototypeGreetingService1; + this.prototypeGreetingService2 = prototypeGreetingService2; + this.sessionGreetingService = sessionGreetingService; + this.requestGreetingService = requestGreetingService; + } + + @GetMapping("/") + public String greetingPage(Model model) { + model.addAttribute("singletonGreeting", singletonGreetingService.greeting()); + model.addAttribute("sessionGreeting", sessionGreetingService.greeting()); + model.addAttribute("requestGreeting", requestGreetingService.greeting()); + model.addAttribute("prototype1Greeting", prototypeGreetingService1.greeting()); + model.addAttribute("prototype2Greeting", prototypeGreetingService2.greeting()); + return "index"; + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/AbstractGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/AbstractGreetingServiceImpl.java new file mode 100644 index 00000000..3199df44 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/AbstractGreetingServiceImpl.java @@ -0,0 +1,23 @@ +package ru.otus.example.beansscopesdemo.services; + + +import java.util.concurrent.atomic.AtomicInteger; + +public abstract class AbstractGreetingServiceImpl implements GreetingService { + + private final AtomicInteger counter; + + public AbstractGreetingServiceImpl() { + this.counter = new AtomicInteger(0); + } + + @Override + public String greeting() { + return currentGreeting(); + } + + private String currentGreeting() { + return String.format("Привет! Это наша встреча №%d. Меня зовут: %s", + counter.incrementAndGet(), Integer.toHexString(hashCode())); + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/GreetingService.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/GreetingService.java new file mode 100644 index 00000000..3fb338b1 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/GreetingService.java @@ -0,0 +1,5 @@ +package ru.otus.example.beansscopesdemo.services; + +public interface GreetingService { + String greeting(); +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/PrototypeGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/PrototypeGreetingServiceImpl.java new file mode 100644 index 00000000..73ebdcd6 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/PrototypeGreetingServiceImpl.java @@ -0,0 +1,7 @@ +package ru.otus.example.beansscopesdemo.services; + +import org.springframework.stereotype.Service; + +@Service("PrototypeGreetingService") +public class PrototypeGreetingServiceImpl extends AbstractGreetingServiceImpl { +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/RequestGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/RequestGreetingServiceImpl.java new file mode 100644 index 00000000..4340188e --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/RequestGreetingServiceImpl.java @@ -0,0 +1,7 @@ +package ru.otus.example.beansscopesdemo.services; + +import org.springframework.stereotype.Service; + +@Service("RequestGreetingService") +public class RequestGreetingServiceImpl extends AbstractGreetingServiceImpl { +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/SessionGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/SessionGreetingServiceImpl.java new file mode 100644 index 00000000..045b0b5a --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/SessionGreetingServiceImpl.java @@ -0,0 +1,7 @@ +package ru.otus.example.beansscopesdemo.services; + +import org.springframework.stereotype.Service; + +@Service("SessionGreetingService") +public class SessionGreetingServiceImpl extends AbstractGreetingServiceImpl { +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/SingletonGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/SingletonGreetingServiceImpl.java new file mode 100644 index 00000000..b2f792a1 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/java/ru/otus/example/beansscopesdemo/services/SingletonGreetingServiceImpl.java @@ -0,0 +1,7 @@ +package ru.otus.example.beansscopesdemo.services; + +import org.springframework.stereotype.Service; + +@Service("SingletonGreetingService") +public class SingletonGreetingServiceImpl extends AbstractGreetingServiceImpl { +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/resources/application.yml b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/resources/application.yml new file mode 100644 index 00000000..f78a9f36 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/resources/application.yml @@ -0,0 +1,3 @@ +logging: + level: + root: ERROR \ No newline at end of file diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/resources/templates/index.html b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/resources/templates/index.html new file mode 100644 index 00000000..601ae82e --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-exercise/src/main/resources/templates/index.html @@ -0,0 +1,45 @@ + + + + + Advanced configuration demo + + + +

+

Демонстрация @Scope

+ +

+ +
+ +

+

+

+

+

+ + \ No newline at end of file diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/.gitignore b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/.gitignore new file mode 100644 index 00000000..789ddc9e --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/.gitignore @@ -0,0 +1,32 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +#other +*.bat +*/.idea +*.iml +*/target + +.idea +*.iml +target \ No newline at end of file diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/README.md b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/README.md new file mode 100644 index 00000000..b02345e6 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/README.md @@ -0,0 +1,15 @@ +#### Решение к упражнению №1 + +@Scope("singleton") +SingletonGreetingServiceImpl + +@Scope("prototype") +PrototypeGreetingServiceImpl + +@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES) +RequestGreetingServiceImpl + +@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES) +SessionGreetingServiceImpl + + diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/pom.xml b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/pom.xml new file mode 100644 index 00000000..aeee6b01 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.7.0 + + + + ru.otus.example + beans-scopes-solution + 0.0.1-SNAPSHOT + + + 11 + 11 + 11 + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/BeansScopesDemoApplication.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/BeansScopesDemoApplication.java new file mode 100644 index 00000000..b482b9a7 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/BeansScopesDemoApplication.java @@ -0,0 +1,15 @@ +package ru.otus.example.beansscopesdemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class BeansScopesDemoApplication { + + public static void main(String[] args) { + var ctx = SpringApplication.run(BeansScopesDemoApplication.class, args); + var serverPort = ctx.getEnvironment().getProperty("local.server.port"); + System.out.printf("Чтобы смотреть результат переходи сюда: http://localhost:%s", serverPort); + } + +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/controllers/GreetingController.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/controllers/GreetingController.java new file mode 100644 index 00000000..142fce6e --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/controllers/GreetingController.java @@ -0,0 +1,39 @@ +package ru.otus.example.beansscopesdemo.controllers; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import ru.otus.example.beansscopesdemo.services.GreetingService; + +@Controller +public class GreetingController { + private final GreetingService singletonGreetingService; + private final GreetingService prototypeGreetingService1; + private final GreetingService prototypeGreetingService2; + private final GreetingService sessionGreetingService; + private final GreetingService requestGreetingService; + + public GreetingController(@Qualifier("SingletonGreetingService") GreetingService singletonGreetingService, + @Qualifier("PrototypeGreetingService")GreetingService prototypeGreetingService1, + @Qualifier("PrototypeGreetingService")GreetingService prototypeGreetingService2, + @Qualifier("SessionGreetingService")GreetingService sessionGreetingService, + @Qualifier("RequestGreetingService")GreetingService requestGreetingService + ) { + this.singletonGreetingService = singletonGreetingService; + this.prototypeGreetingService1 = prototypeGreetingService1; + this.prototypeGreetingService2 = prototypeGreetingService2; + this.sessionGreetingService = sessionGreetingService; + this.requestGreetingService = requestGreetingService; + } + + @GetMapping("/") + public String greetingPage(Model model) { + model.addAttribute("singletonGreeting", singletonGreetingService.greeting()); + model.addAttribute("sessionGreeting", sessionGreetingService.greeting()); + model.addAttribute("requestGreeting", requestGreetingService.greeting()); + model.addAttribute("prototype1Greeting", prototypeGreetingService1.greeting()); + model.addAttribute("prototype2Greeting", prototypeGreetingService2.greeting()); + return "index"; + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/AbstractGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/AbstractGreetingServiceImpl.java new file mode 100644 index 00000000..3199df44 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/AbstractGreetingServiceImpl.java @@ -0,0 +1,23 @@ +package ru.otus.example.beansscopesdemo.services; + + +import java.util.concurrent.atomic.AtomicInteger; + +public abstract class AbstractGreetingServiceImpl implements GreetingService { + + private final AtomicInteger counter; + + public AbstractGreetingServiceImpl() { + this.counter = new AtomicInteger(0); + } + + @Override + public String greeting() { + return currentGreeting(); + } + + private String currentGreeting() { + return String.format("Привет! Это наша встреча №%d. Меня зовут: %s", + counter.incrementAndGet(), Integer.toHexString(hashCode())); + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/GreetingService.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/GreetingService.java new file mode 100644 index 00000000..3fb338b1 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/GreetingService.java @@ -0,0 +1,5 @@ +package ru.otus.example.beansscopesdemo.services; + +public interface GreetingService { + String greeting(); +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/PrototypeGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/PrototypeGreetingServiceImpl.java new file mode 100644 index 00000000..5ea82da6 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/PrototypeGreetingServiceImpl.java @@ -0,0 +1,10 @@ +package ru.otus.example.beansscopesdemo.services; + +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; + +@Scope(scopeName = "prototype") +@Service("PrototypeGreetingService") +public class PrototypeGreetingServiceImpl extends AbstractGreetingServiceImpl { +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/RequestGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/RequestGreetingServiceImpl.java new file mode 100644 index 00000000..a1559ea3 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/RequestGreetingServiceImpl.java @@ -0,0 +1,11 @@ +package ru.otus.example.beansscopesdemo.services; + +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; +import org.springframework.web.context.WebApplicationContext; + +@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES) +@Service("RequestGreetingService") +public class RequestGreetingServiceImpl extends AbstractGreetingServiceImpl { +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/SessionGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/SessionGreetingServiceImpl.java new file mode 100644 index 00000000..e9ad2d8e --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/SessionGreetingServiceImpl.java @@ -0,0 +1,11 @@ +package ru.otus.example.beansscopesdemo.services; + +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Service; +import org.springframework.web.context.WebApplicationContext; + +@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES) +@Service("SessionGreetingService") +public class SessionGreetingServiceImpl extends AbstractGreetingServiceImpl { +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/SingletonGreetingServiceImpl.java b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/SingletonGreetingServiceImpl.java new file mode 100644 index 00000000..fb8406a5 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/java/ru/otus/example/beansscopesdemo/services/SingletonGreetingServiceImpl.java @@ -0,0 +1,9 @@ +package ru.otus.example.beansscopesdemo.services; + +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; + +@Scope("singleton") +@Service("SingletonGreetingService") +public class SingletonGreetingServiceImpl extends AbstractGreetingServiceImpl { +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/resources/application.yml b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/resources/application.yml new file mode 100644 index 00000000..f78a9f36 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/resources/application.yml @@ -0,0 +1,3 @@ +logging: + level: + root: ERROR \ No newline at end of file diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/resources/templates/index.html b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/resources/templates/index.html new file mode 100644 index 00000000..3f9e21dc --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/beans-scopes-solution/src/main/resources/templates/index.html @@ -0,0 +1,45 @@ + + + + + Advanced configuration demo + + + +

+

Демонстрация @Scope

+ +

+ +
+ +

+

+

+

+

+ + \ No newline at end of file diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/.gitignore b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/.gitignore new file mode 100644 index 00000000..549e00a2 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/README.md b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/README.md new file mode 100644 index 00000000..edbd8776 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/README.md @@ -0,0 +1,3 @@ +#### Демо создания собственного Scope + +В зависимости от значения поля `canTakeVacation` класса `VacationCalendar`, бин отпуска либо доступен, либо нет diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/pom.xml b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/pom.xml new file mode 100644 index 00000000..9c76a12a --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.0 + + + ru.otus + custom-scope-demo + 0.0.1-SNAPSHOT + custom-scope-demo + Custom scope demo + + 11 + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/CustomScopeDemoApplication.java b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/CustomScopeDemoApplication.java new file mode 100644 index 00000000..d33a3788 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/CustomScopeDemoApplication.java @@ -0,0 +1,28 @@ +package ru.otus.customscopedemo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import ru.otus.customscopedemo.scope.VacationDoesNotAvailableException; +import ru.otus.customscopedemo.vacation.Vacation; +import ru.otus.customscopedemo.vacation.VacationCalendar; + +@SpringBootApplication +public class CustomScopeDemoApplication { + private static final Logger logger = LoggerFactory.getLogger(CustomScopeDemoApplication.class); + + public static void main(String[] args) { + var ctx = SpringApplication.run(CustomScopeDemoApplication.class, args); + var vacationCalendar = ctx.getBean(VacationCalendar.class); + var vacation = ctx.getBean(Vacation.class); + + vacationCalendar.setCanTakeVacation(true); + try { + vacation.enjoy(); + } catch (VacationDoesNotAvailableException e) { + logger.info("Извини Добби, твой отпуск в другом замке!"); + } + } + +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationDoesNotAvailableException.java b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationDoesNotAvailableException.java new file mode 100644 index 00000000..2e5d915c --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationDoesNotAvailableException.java @@ -0,0 +1,4 @@ +package ru.otus.customscopedemo.scope; + +public class VacationDoesNotAvailableException extends RuntimeException { +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationScope.java b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationScope.java new file mode 100644 index 00000000..ca542f6d --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationScope.java @@ -0,0 +1,70 @@ +package ru.otus.customscopedemo.scope; + +import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.config.Scope; +import ru.otus.customscopedemo.vacation.VacationCalendar; + +import java.util.HashMap; +import java.util.Map; + + +public class VacationScope implements Scope { + + private final Map destructionCallbacks; + private final Map contextualObjects; + + private final VacationCalendar vacationCalendar; + + public VacationScope(VacationCalendar vacationCalendar) { + this.destructionCallbacks = new HashMap<>(); + this.contextualObjects = new HashMap<>(); + this.vacationCalendar = vacationCalendar; + } + + // This is the central operation of a Scope, and the only operation that is absolutely required. + @Override + public Object get(String name, ObjectFactory objectFactory) { + var res = resolveContextualObject(name); + if (res != null) { + return res; + } + + if (vacationCalendar.isCanTakeVacation()) { + res = objectFactory.getObject(); + contextualObjects.put(name, res); + return res; + } + + throw new VacationDoesNotAvailableException(); + } + + // Note: This is an optional operation. Implementations may throw UnsupportedOperationException + // if they do not support explicitly removing an object. + @Override + public Object remove(String name) { + destructionCallbacks.remove(name); + return contextualObjects.remove(name); + } + + @Override + public void registerDestructionCallback(String name, Runnable runnable) { + destructionCallbacks.put(name, runnable); + } + + @Override + public Object resolveContextualObject(String name) { + if (!vacationCalendar.isCanTakeVacation() && contextualObjects.containsKey(name)) { + remove(name); + } + return contextualObjects.get(name); + } + + @Override + public String getConversationId() { + return null; + } + + public void onDestroy() { + destructionCallbacks.values().forEach(Runnable::run); + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationScopeConfig.java b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationScopeConfig.java new file mode 100644 index 00000000..baf3be74 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationScopeConfig.java @@ -0,0 +1,22 @@ +package ru.otus.customscopedemo.scope; + +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; +import ru.otus.customscopedemo.vacation.VacationCalendar; + +@Configuration +public class VacationScopeConfig { + + @Bean(destroyMethod = "onDestroy") + public VacationScope vacationScope(VacationCalendar vacationCalendar){ + return new VacationScope(vacationCalendar); + } + + @DependsOn("vacationScope") + @Bean + public BeanFactoryPostProcessor vacationScopeRegistrationBeanFactoryPostProcessor(VacationScope vacationScope) { + return new VacationScopeRegistrationBeanFactoryPostProcessor(vacationScope); + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationScopeRegistrationBeanFactoryPostProcessor.java b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationScopeRegistrationBeanFactoryPostProcessor.java new file mode 100644 index 00000000..4a6c3dc6 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/scope/VacationScopeRegistrationBeanFactoryPostProcessor.java @@ -0,0 +1,19 @@ +package ru.otus.customscopedemo.scope; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; + +public class VacationScopeRegistrationBeanFactoryPostProcessor implements BeanFactoryPostProcessor { + + private final VacationScope scope; + + public VacationScopeRegistrationBeanFactoryPostProcessor(VacationScope scope) { + this.scope = scope; + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + beanFactory.registerScope("vacation", scope); + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/vacation/Vacation.java b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/vacation/Vacation.java new file mode 100644 index 00000000..5bba7a02 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/vacation/Vacation.java @@ -0,0 +1,47 @@ +package ru.otus.customscopedemo.vacation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.context.annotation.ScopedProxyMode; +import org.springframework.stereotype.Component; + +import javax.annotation.PreDestroy; +import java.time.LocalDateTime; +import java.util.Random; + +@Scope(value = "vacation", proxyMode = ScopedProxyMode.TARGET_CLASS) +@Component +public class Vacation { + + private static final Logger logger = LoggerFactory.getLogger(Vacation.class); + + private static final String[] actions = {"спит", "ест", "пьет"}; + private static final int VACATION_DURATION_SECONDS = 4; + + public void enjoy(){ + logger.info("Отпуск начат"); + var random = new Random(); + var startTime = LocalDateTime.now().plusSeconds(VACATION_DURATION_SECONDS); + while (LocalDateTime.now().isBefore(startTime)) { + var actionIndex = random.nextInt(3); + logger.info("Отдыхающий - {}", actions[actionIndex]); + sleep(); + } + logger.info("Отпуск закончен (гаснет свет)"); + } + + private void sleep() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @PreDestroy + private void destroyMethod(){ + logger.info("Бин отпуска уничтожен"); + } + +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/vacation/VacationCalendar.java b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/vacation/VacationCalendar.java new file mode 100644 index 00000000..fb8a423b --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/java/ru/otus/customscopedemo/vacation/VacationCalendar.java @@ -0,0 +1,21 @@ +package ru.otus.customscopedemo.vacation; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class VacationCalendar { + private static final Logger logger = LoggerFactory.getLogger(VacationCalendar.class); + + private boolean canTakeVacation = false; + + public boolean isCanTakeVacation() { + return canTakeVacation; + } + + public void setCanTakeVacation(boolean canTakeVacation) { + logger.info("Заявление на отпуск {}", canTakeVacation? "одобрено": "отклонено"); + this.canTakeVacation = canTakeVacation; + } +} diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/resources/application.yml b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/resources/application.yml new file mode 100644 index 00000000..ac4fd192 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/custom-scope-demo/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + main: + allow-circular-references: true diff --git a/2022-05/spring-05-bean-scopes-and-lifecycle/pom.xml b/2022-05/spring-05-bean-scopes-and-lifecycle/pom.xml new file mode 100644 index 00000000..4eb4f7a9 --- /dev/null +++ b/2022-05/spring-05-bean-scopes-and-lifecycle/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + ru.otus + advanced-config-class-work + 1.0 + + pom + + + beans-scopes-exercise + beans-scopes-solution + beans-lifecycle-exercise + custom-scope-demo + +