├── src └── main │ ├── resources │ └── application.properties │ └── java │ └── com │ └── epam │ ├── Room.java │ ├── Recommendator.java │ ├── Announcer.java │ ├── Policeman.java │ ├── ObjectConfigurator.java │ ├── ProxyConfigurator.java │ ├── Singleton.java │ ├── Config.java │ ├── InjectByType.java │ ├── AngryPoliceman.java │ ├── InjectProperty.java │ ├── ConsoleAnnouncer.java │ ├── RecommendatorImpl.java │ ├── PolicemanImpl.java │ ├── Main.java │ ├── Application.java │ ├── CoronaDesinfector.java │ ├── InjectByTypeAnnotationObjectConfigurator.java │ ├── ApplicationContext.java │ ├── JavaConfig.java │ ├── InjectPropertyAnnotationObjectConfigurator.java │ ├── DeprecatedHandlerProxyConfigurator.java │ └── ObjectFactory.java └── pom.xml /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | alcohol=beer corona 2 | wisky=sinlgeton -------------------------------------------------------------------------------- /src/main/java/com/epam/Room.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | public class Room { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/epam/Recommendator.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | public interface Recommendator { 7 | void recommend(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/epam/Announcer.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | public interface Announcer { 7 | void announce(String message); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/epam/Policeman.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | public interface Policeman { 7 | void makePeopleLeaveRoom(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/epam/ObjectConfigurator.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | public interface ObjectConfigurator { 7 | void configure(Object t,ApplicationContext context); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/epam/ProxyConfigurator.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | public interface ProxyConfigurator { 7 | Object replaceWithProxyIfNeeded(Object t, Class implClass); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/epam/Singleton.java: -------------------------------------------------------------------------------- 1 | package com.epam;/** 2 | * @author Evgeny Borisov 3 | */ 4 | 5 | import java.lang.annotation.Retention; 6 | 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Retention(RUNTIME) 10 | public @interface Singleton { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/epam/Config.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import org.reflections.Reflections; 4 | 5 | /** 6 | * @author Evgeny Borisov 7 | */ 8 | public interface Config { 9 | Class getImplClass(Class ifc); 10 | 11 | Reflections getScanner(); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/epam/InjectByType.java: -------------------------------------------------------------------------------- 1 | package com.epam;/** 2 | * @author Evgeny Borisov 3 | */ 4 | 5 | import java.lang.annotation.Retention; 6 | 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Retention(RUNTIME) 10 | public @interface InjectByType { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/epam/AngryPoliceman.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | public class AngryPoliceman implements Policeman { 7 | @Override 8 | public void makePeopleLeaveRoom() { 9 | System.out.println("Всех убью! Вон пошли"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/epam/InjectProperty.java: -------------------------------------------------------------------------------- 1 | package com.epam;/** 2 | * @author Evgeny Borisov 3 | */ 4 | 5 | import java.lang.annotation.Retention; 6 | 7 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 8 | 9 | @Retention(RUNTIME) 10 | public @interface InjectProperty { 11 | String value() default ""; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/epam/ConsoleAnnouncer.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | public class ConsoleAnnouncer implements Announcer { 7 | @InjectByType 8 | private Recommendator recommendator; 9 | @Override 10 | public void announce(String message) { 11 | System.out.println(message); 12 | recommendator.recommend(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/epam/RecommendatorImpl.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | @Singleton 7 | public class RecommendatorImpl implements Recommendator { 8 | @InjectProperty("wisky") 9 | private String alcohol; 10 | 11 | public RecommendatorImpl() { 12 | System.out.println("recommendator was created"); 13 | } 14 | 15 | @Override 16 | public void recommend() { 17 | System.out.println("to protect from covid-2019, drink "+alcohol); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/epam/PolicemanImpl.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import javax.annotation.PostConstruct; 4 | 5 | /** 6 | * @author Evgeny Borisov 7 | */ 8 | public class PolicemanImpl implements Policeman { 9 | 10 | @InjectByType 11 | private Recommendator recommendator; 12 | 13 | @PostConstruct 14 | public void init() { 15 | System.out.println(recommendator.getClass()); 16 | } 17 | 18 | @Override 19 | public void makePeopleLeaveRoom() { 20 | System.out.println("пиф паф, бах бах, кыш, кыш!"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/epam/Main.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author Evgeny Borisov 8 | */ 9 | public class Main { 10 | public static void main(String[] args) { 11 | // CoronaDesinfector desinfector = ObjectFactory.getInstance().createObject(CoronaDesinfector.class); 12 | ApplicationContext context = Application.run("com.epam", new HashMap<>(Map.of(Policeman.class, PolicemanImpl.class))); 13 | CoronaDesinfector desinfector = context.getObject(CoronaDesinfector.class); 14 | desinfector.start(new Room()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/epam/Application.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * @author Evgeny Borisov 7 | */ 8 | public class Application { 9 | public static ApplicationContext run(String packageToScan, Map ifc2ImplClass) { 10 | JavaConfig config = new JavaConfig(packageToScan, ifc2ImplClass); 11 | ApplicationContext context = new ApplicationContext(config); 12 | ObjectFactory objectFactory = new ObjectFactory(context); 13 | //todo homework - init all singletons which are not lazy 14 | context.setFactory(objectFactory); 15 | return context; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/epam/CoronaDesinfector.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | /** 4 | * @author Evgeny Borisov 5 | */ 6 | 7 | public class CoronaDesinfector { 8 | 9 | @InjectByType 10 | private Announcer announcer; 11 | @InjectByType 12 | private Policeman policeman; 13 | 14 | 15 | public void start(Room room) { 16 | announcer.announce("Начинаем дезинфекцию, всё вон!"); 17 | policeman.makePeopleLeaveRoom(); 18 | desinfect(room); 19 | announcer.announce("Рискните зайти обратно"); 20 | } 21 | 22 | private void desinfect(Room room){ 23 | System.out.println("зачитывается молитва: 'корона изыди!' - молитва прочитана, вирус низвергнут в ад"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/epam/InjectByTypeAnnotationObjectConfigurator.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import lombok.SneakyThrows; 4 | 5 | import java.lang.reflect.Field; 6 | 7 | /** 8 | * @author Evgeny Borisov 9 | */ 10 | public class InjectByTypeAnnotationObjectConfigurator implements ObjectConfigurator { 11 | @Override 12 | @SneakyThrows 13 | public void configure(Object t, ApplicationContext context) { 14 | for (Field field : t.getClass().getDeclaredFields()) { 15 | if (field.isAnnotationPresent(InjectByType.class)) { 16 | field.setAccessible(true); 17 | Object object = context.getObject(field.getType()); 18 | field.set(t, object); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/epam/ApplicationContext.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * @author Evgeny Borisov 11 | */ 12 | public class ApplicationContext { 13 | @Setter 14 | private ObjectFactory factory; 15 | private Map cache = new ConcurrentHashMap<>(); 16 | @Getter 17 | private Config config; 18 | 19 | public ApplicationContext(Config config) { 20 | this.config = config; 21 | } 22 | 23 | public T getObject(Class type) { 24 | if (cache.containsKey(type)) { 25 | return (T) cache.get(type); 26 | } 27 | 28 | Class implClass = type; 29 | 30 | if (type.isInterface()) { 31 | implClass = config.getImplClass(type); 32 | } 33 | T t = factory.createObject(implClass); 34 | 35 | if (implClass.isAnnotationPresent(Singleton.class)) { 36 | cache.put(type, t); 37 | } 38 | 39 | return t; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/epam/JavaConfig.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import lombok.Getter; 4 | import org.reflections.Reflections; 5 | 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | /** 10 | * @author Evgeny Borisov 11 | */ 12 | public class JavaConfig implements Config { 13 | 14 | @Getter 15 | private Reflections scanner; 16 | private Map ifc2ImplClass; 17 | 18 | public JavaConfig(String packageToScan, Map ifc2ImplClass) { 19 | this.ifc2ImplClass = ifc2ImplClass; 20 | this.scanner = new Reflections(packageToScan); 21 | } 22 | 23 | @Override 24 | public Class getImplClass(Class ifc) { 25 | return ifc2ImplClass.computeIfAbsent(ifc, aClass -> { 26 | Set> classes = scanner.getSubTypesOf(ifc); 27 | if (classes.size() != 1) { 28 | throw new RuntimeException(ifc + " has 0 or more than one impl please update your config"); 29 | } 30 | 31 | return classes.iterator().next(); 32 | }); 33 | 34 | } 35 | } 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/main/java/com/epam/InjectPropertyAnnotationObjectConfigurator.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import lombok.SneakyThrows; 4 | 5 | import java.io.BufferedReader; 6 | import java.io.FileReader; 7 | import java.lang.reflect.Field; 8 | import java.util.Map; 9 | import java.util.stream.Stream; 10 | 11 | import static java.util.stream.Collectors.toMap; 12 | 13 | /** 14 | * @author Evgeny Borisov 15 | */ 16 | public class InjectPropertyAnnotationObjectConfigurator implements ObjectConfigurator { 17 | 18 | private Map propertiesMap; 19 | 20 | @SneakyThrows 21 | public InjectPropertyAnnotationObjectConfigurator() { 22 | String path = ClassLoader.getSystemClassLoader().getResource("application.properties").getPath(); 23 | Stream lines = new BufferedReader(new FileReader(path)).lines(); 24 | propertiesMap = lines.map(line -> line.split("=")).collect(toMap(arr -> arr[0], arr -> arr[1])); 25 | 26 | } 27 | 28 | @Override 29 | @SneakyThrows 30 | public void configure(Object t,ApplicationContext context) { 31 | Class implClass = t.getClass(); 32 | for (Field field : implClass.getDeclaredFields()) { 33 | InjectProperty annotation = field.getAnnotation(InjectProperty.class); 34 | if (annotation != null) { 35 | String value = annotation.value().isEmpty() ? propertiesMap.get(field.getName()) : propertiesMap.get(annotation.value()); 36 | field.setAccessible(true); 37 | field.set(t,value); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.epam 8 | corona-desinfector-life-demo 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | cglib 15 | cglib 16 | 3.3.0 17 | 18 | 19 | org.reflections 20 | reflections 21 | 0.9.12 22 | 23 | 24 | org.projectlombok 25 | lombok 26 | 1.18.10 27 | 28 | 29 | 30 | javax.annotation 31 | jsr250-api 32 | 1.0 33 | 34 | 35 | 36 | 37 | 38 | 39 | maven-compiler-plugin 40 | 41 | 11 42 | 11 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/com/epam/DeprecatedHandlerProxyConfigurator.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import net.sf.cglib.proxy.Enhancer; 4 | 5 | import java.lang.reflect.InvocationHandler; 6 | import java.lang.reflect.InvocationTargetException; 7 | import java.lang.reflect.Method; 8 | import java.lang.reflect.Proxy; 9 | 10 | /** 11 | * @author Evgeny Borisov 12 | */ 13 | public class DeprecatedHandlerProxyConfigurator implements ProxyConfigurator { 14 | @Override 15 | public Object replaceWithProxyIfNeeded(Object t, Class implClass) { 16 | //todo make support for @Deprecate above methods, not class 17 | if (implClass.isAnnotationPresent(Deprecated.class)) { 18 | 19 | if (implClass.getInterfaces().length == 0) { 20 | return Enhancer.create(implClass, new net.sf.cglib.proxy.InvocationHandler() { 21 | @Override 22 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 23 | return getInvocationHandlerLogic(method, args, t); 24 | } 25 | }); 26 | } 27 | 28 | 29 | return Proxy.newProxyInstance(implClass.getClassLoader(), implClass.getInterfaces(), new InvocationHandler() { 30 | @Override 31 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 32 | return getInvocationHandlerLogic(method, args, t); 33 | } 34 | }); 35 | } else { 36 | return t; 37 | } 38 | 39 | } 40 | 41 | private Object getInvocationHandlerLogic(Method method, Object[] args, Object t) throws IllegalAccessException, InvocationTargetException { 42 | System.out.println("********** что ж ты делаешь урод!! "); 43 | return method.invoke(t, args); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/epam/ObjectFactory.java: -------------------------------------------------------------------------------- 1 | package com.epam; 2 | 3 | import lombok.Setter; 4 | import lombok.SneakyThrows; 5 | 6 | import javax.annotation.PostConstruct; 7 | import java.io.BufferedReader; 8 | import java.io.FileReader; 9 | import java.lang.reflect.*; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.stream.Stream; 15 | 16 | import static java.util.stream.Collectors.toMap; 17 | 18 | /** 19 | * @author Evgeny Borisov 20 | */ 21 | public class ObjectFactory { 22 | private final ApplicationContext context; 23 | private List configurators = new ArrayList<>(); 24 | private List proxyConfigurators = new ArrayList<>(); 25 | 26 | 27 | 28 | @SneakyThrows 29 | public ObjectFactory(ApplicationContext context) { 30 | this.context= context; 31 | for (Class aClass : context.getConfig().getScanner().getSubTypesOf(ObjectConfigurator.class)) { 32 | configurators.add(aClass.getDeclaredConstructor().newInstance()); 33 | } 34 | for (Class aClass : context.getConfig().getScanner().getSubTypesOf(ProxyConfigurator.class)) { 35 | proxyConfigurators.add(aClass.getDeclaredConstructor().newInstance()); 36 | } 37 | } 38 | 39 | 40 | @SneakyThrows 41 | public T createObject(Class implClass) { 42 | 43 | T t = create(implClass); 44 | 45 | configure(t); 46 | 47 | invokeInit(implClass, t); 48 | 49 | 50 | t = wrapWithProxyIfNeeded(implClass, t); 51 | 52 | 53 | return t; 54 | 55 | } 56 | 57 | private T wrapWithProxyIfNeeded(Class implClass, T t) { 58 | for (ProxyConfigurator proxyConfigurator : proxyConfigurators) { 59 | t = (T) proxyConfigurator.replaceWithProxyIfNeeded(t, implClass); 60 | } 61 | return t; 62 | } 63 | 64 | private void invokeInit(Class implClass, T t) throws IllegalAccessException, InvocationTargetException { 65 | for (Method method : implClass.getMethods()) { 66 | if (method.isAnnotationPresent(PostConstruct.class)) { 67 | method.invoke(t); 68 | } 69 | } 70 | } 71 | 72 | private void configure(T t) { 73 | configurators.forEach(objectConfigurator -> objectConfigurator.configure(t,context)); 74 | } 75 | 76 | private T create(Class implClass) throws InstantiationException, IllegalAccessException, java.lang.reflect.InvocationTargetException, NoSuchMethodException { 77 | return implClass.getDeclaredConstructor().newInstance(); 78 | } 79 | } 80 | 81 | 82 | 83 | 84 | --------------------------------------------------------------------------------