-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
298 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<parent> | ||
<groupId>com.epam.training</groupId> | ||
<artifactId>unideb-prog2-parent</artifactId> | ||
<version>1.0-SNAPSHOT</version> | ||
<relativePath>../..</relativePath> | ||
</parent> | ||
|
||
<artifactId>dependency-injection-framework</artifactId> | ||
|
||
</project> |
40 changes: 40 additions & 0 deletions
40
week-8/dependency-injection-framework/src/main/java/com/epam/training/Main.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package com.epam.training; | ||
|
||
import com.epam.training.di.Configuration; | ||
import com.epam.training.di.DiContext; | ||
import com.epam.training.di.annotation.Bean; | ||
import com.epam.training.di.annotation.Qualifier; | ||
import com.epam.training.di.impl.DiContextBuilder; | ||
|
||
public class Main { | ||
|
||
public static void main(String[] args) { | ||
DiContext context = new DiContextBuilder().build(new Main.ContextConfig()); | ||
System.out.println(context.getBean("stringBuilder", StringBuilder.class).get().toString()); | ||
} | ||
|
||
public static class ContextConfig implements Configuration { | ||
|
||
@Bean | ||
public String stringBeanA() { | ||
return "StringA"; | ||
} | ||
|
||
@Bean | ||
public String stringBeanB() { | ||
return "StringB"; | ||
} | ||
|
||
@Bean | ||
public Integer integerBean() { | ||
return 10; | ||
} | ||
|
||
@Bean | ||
public StringBuilder stringBuilder(@Qualifier(name = "stringBeanA") String string, Integer integer) { | ||
return new StringBuilder(string).append(integer); | ||
} | ||
|
||
} | ||
|
||
} |
5 changes: 5 additions & 0 deletions
5
week-8/dependency-injection-framework/src/main/java/com/epam/training/di/Configuration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.epam.training.di; | ||
|
||
public interface Configuration { | ||
|
||
} |
13 changes: 13 additions & 0 deletions
13
week-8/dependency-injection-framework/src/main/java/com/epam/training/di/DiContext.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.epam.training.di; | ||
|
||
import java.util.Optional; | ||
|
||
public interface DiContext { | ||
|
||
public void addBean(String beanName, Object bean); | ||
|
||
public <T> Optional<T> getBean(String beanName, Class<T> clazz); | ||
|
||
public <T> Optional<T> getBean(Class<T> clazz); | ||
|
||
} |
12 changes: 12 additions & 0 deletions
12
...-8/dependency-injection-framework/src/main/java/com/epam/training/di/annotation/Bean.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.epam.training.di.annotation; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target(ElementType.METHOD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface Bean { | ||
|
||
} |
14 changes: 14 additions & 0 deletions
14
...pendency-injection-framework/src/main/java/com/epam/training/di/annotation/Qualifier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.epam.training.di.annotation; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Target(ElementType.PARAMETER) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface Qualifier { | ||
|
||
public String name(); | ||
|
||
} |
132 changes: 132 additions & 0 deletions
132
...endency-injection-framework/src/main/java/com/epam/training/di/impl/DiContextBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package com.epam.training.di.impl; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.lang.reflect.Parameter; | ||
import java.util.ArrayList; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import com.epam.training.di.Configuration; | ||
import com.epam.training.di.DiContext; | ||
import com.epam.training.di.annotation.Bean; | ||
import com.epam.training.di.annotation.Qualifier; | ||
|
||
public class DiContextBuilder { | ||
|
||
public DiContext build(Configuration contextConfiguration) { | ||
DiContext context = new DiContextImpl(); | ||
|
||
List<BeanDefinition> beanDefinitions = new LinkedList<BeanDefinition>(); | ||
|
||
for (Method method : contextConfiguration.getClass().getMethods()) { | ||
if (isAnnotatedWithBean(method)) { | ||
List<BeanDefinitionParameter> beanDependencies = new LinkedList<DiContextBuilder.BeanDefinitionParameter>(); | ||
for (Parameter parameter : method.getParameters()) { | ||
String name = null; | ||
if (parameter.getAnnotation(Qualifier.class) != null) { | ||
name = parameter.getAnnotation(Qualifier.class).name(); | ||
} | ||
beanDependencies.add(new BeanDefinitionParameter<>(name, parameter.getType())); | ||
} | ||
beanDefinitions.add(new BeanDefinition(method.getName(), method.getReturnType(), method, beanDependencies)); | ||
} | ||
} | ||
|
||
int beanDefinitionsSize = beanDefinitions.size(); | ||
context.addBean("context", context); | ||
|
||
while (beanDefinitions.size() > 0) { | ||
List<BeanDefinition> resolvedBeanDefinitions = new LinkedList<>(); | ||
for (BeanDefinition beanDefinition : beanDefinitions) { | ||
if (isBeanDefinitionResolvable(beanDefinition, context)) { | ||
context.addBean(beanDefinition.name, createBean(beanDefinition, context, contextConfiguration)); | ||
resolvedBeanDefinitions.add(beanDefinition); | ||
} | ||
} | ||
beanDefinitions.removeAll(resolvedBeanDefinitions); | ||
|
||
if (beanDefinitionsSize == beanDefinitions.size()) { | ||
throw new IllegalArgumentException("Circular dependency!"); | ||
} else { | ||
beanDefinitionsSize = beanDefinitions.size(); | ||
} | ||
} | ||
|
||
return context; | ||
} | ||
|
||
private Object createBean(BeanDefinition beanDefinition, DiContext context, Object contextConfiguration) { | ||
List<Object> dependencies = new ArrayList<>(beanDefinition.dependencies.size()); | ||
|
||
for (BeanDefinitionParameter parameter : beanDefinition.dependencies) { | ||
Optional<Object> dependency; | ||
if (parameter.name != null) { | ||
dependency = context.getBean(parameter.name, parameter.type); | ||
} else { | ||
dependency = context.getBean(parameter.type); | ||
} | ||
dependencies.add(dependency.get()); | ||
} | ||
|
||
try { | ||
return beanDefinition.builderMethod.invoke(contextConfiguration, dependencies.toArray()); | ||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { | ||
throw new IllegalArgumentException(e); | ||
} | ||
} | ||
|
||
private boolean isBeanDefinitionResolvable(BeanDefinition beanDefinition, DiContext context) { | ||
for (BeanDefinitionParameter parameter : beanDefinition.dependencies) { | ||
Optional<Object> dependency; | ||
if (parameter.name != null) { | ||
dependency = context.getBean(parameter.name, parameter.type); | ||
} else { | ||
dependency = context.getBean(parameter.type); | ||
} | ||
if (dependency.isEmpty()) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
private boolean isAnnotatedWithBean(Method method) { | ||
for (Annotation annotation : method.getDeclaredAnnotations()) { | ||
if (annotation.annotationType().equals(Bean.class)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
private class BeanDefinition { | ||
String name; | ||
Class type; | ||
Method builderMethod; | ||
List<BeanDefinitionParameter> dependencies; | ||
|
||
public BeanDefinition(String name, Class type, Method builderMethod, | ||
List<BeanDefinitionParameter> dependencies) { | ||
super(); | ||
this.name = name; | ||
this.type = type; | ||
this.builderMethod = builderMethod; | ||
this.dependencies = dependencies; | ||
} | ||
} | ||
|
||
private class BeanDefinitionParameter<T> { | ||
String name; | ||
Class<T> type; | ||
|
||
public BeanDefinitionParameter(String name, Class<T> type) { | ||
super(); | ||
this.name = name; | ||
this.type = type; | ||
} | ||
} | ||
|
||
} |
56 changes: 56 additions & 0 deletions
56
...dependency-injection-framework/src/main/java/com/epam/training/di/impl/DiContextImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package com.epam.training.di.impl; | ||
|
||
import java.util.HashMap; | ||
import java.util.LinkedList; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
import com.epam.training.di.DiContext; | ||
|
||
public class DiContextImpl implements DiContext { | ||
|
||
private Map<String, Object> context = new HashMap<>(); | ||
private Map<Class<?>, List<Object>> contextMappedByType = new HashMap<>(); | ||
|
||
public void addBean(String beanName, Object bean) { | ||
if (context.containsKey(beanName)) { | ||
throw new IllegalArgumentException("Bean has been already created: " + beanName); | ||
} | ||
context.put(beanName, bean); | ||
if (contextMappedByType.containsKey(bean.getClass())) { | ||
contextMappedByType.get(bean.getClass()).add(bean); | ||
} else { | ||
List<Object> objectList = new LinkedList<>(); | ||
objectList.add(bean); | ||
contextMappedByType.put(bean.getClass(), objectList); | ||
} | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
public <T> Optional<T> getBean(String beanName, Class<T> clazz) { | ||
Optional<T> ret = null; | ||
if (context.containsKey(beanName)) { | ||
ret = Optional.of((T) context.get(beanName)); | ||
} else { | ||
ret = Optional.empty(); | ||
} | ||
return ret; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
@Override | ||
public <T> Optional<T> getBean(Class<T> clazz) { | ||
Optional<T> ret = null; | ||
List<Object> objestList = contextMappedByType.get(clazz); | ||
if (objestList == null) { | ||
ret = Optional.empty(); | ||
} else if (objestList.size() != 1) { | ||
throw new IllegalArgumentException("There are multiple bean available for type: " + clazz); | ||
} else { | ||
ret = Optional.of((T) objestList.get(0)); | ||
} | ||
return ret; | ||
} | ||
|
||
} |