Skip to content

Commit

Permalink
Scopes were added to annotations and JavaBeanConfigurator
Browse files Browse the repository at this point in the history
  • Loading branch information
ZeLebo committed Mar 26, 2023
1 parent 9cd9bfa commit 6aedee4
Show file tree
Hide file tree
Showing 16 changed files with 205 additions and 96 deletions.
3 changes: 3 additions & 0 deletions src/main/java/team/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ public static void main(String[] args) {

ServiceB serviceB = context.getBean(ServiceB.class);
serviceB.jobB();

FrontService front = context.getBean(FrontService.class);
front.siteLoading();
}
}
11 changes: 11 additions & 0 deletions src/main/java/team/annotations/Provided.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package team.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Provided {
}
11 changes: 11 additions & 0 deletions src/main/java/team/annotations/Thread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package team.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Thread {
}
4 changes: 4 additions & 0 deletions src/main/java/team/config/BeanDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ public interface BeanDefinition {
String getBeanName();
void SetBeanName(String beanName);

Object getBean();

void setBean(Object bean);

String getBeanClassName();
void setBeanClassName(String beanClassName);

Expand Down
11 changes: 11 additions & 0 deletions src/main/java/team/config/DefaultBeanDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public class DefaultBeanDefinition implements BeanDefinition {
private String beanName;
private String beanClassName;
private String scope;
private Object bean;

@Override
public String getBeanName() {
Expand All @@ -16,6 +17,16 @@ public void SetBeanName(String beanName) {
this.beanName = beanName;
}

@Override
public Object getBean() {
return this.bean;
}

@Override
public void setBean(Object bean) {
this.bean = bean;
}

@Override
public String getBeanClassName() {
return this.beanClassName;
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/team/configurator/BeanConfigurator.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package team.configurator;

import org.reflections.Reflections;
import team.config.DefaultBeanDefinition;
import team.factory.BeanFactory;

import java.lang.reflect.InvocationTargetException;

public interface BeanConfigurator {
<T> Class<? extends T> getImplementationClass(Class<T> interfaceClass);
Reflections getScanner();

void setBeanFactory(BeanFactory beanFactory);

<T> DefaultBeanDefinition generateBean(Class<T> tClass) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException;
}
69 changes: 59 additions & 10 deletions src/main/java/team/configurator/metadata/JavaBeanConfigurator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@

import lombok.Getter;
import org.reflections.Reflections;
import team.annotations.Service;
import team.annotations.*;
import team.annotations.Thread;
import team.config.DefaultBeanDefinition;
import team.configurator.BeanConfigurator;
import team.factory.BeanFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -13,11 +20,17 @@ public class JavaBeanConfigurator implements BeanConfigurator {
@Getter
private Reflections scanner;
private final Map<Class, Class> interfaceToImplementation;
private BeanFactory beanFactory;

public JavaBeanConfigurator(Map<Class, Class> implementations) {
this.interfaceToImplementation = implementations;
}

@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}

public JavaBeanConfigurator(String packageToScan) {
this.scanner = new Reflections(packageToScan);
this.interfaceToImplementation = new ConcurrentHashMap<>();
Expand All @@ -33,19 +46,55 @@ public JavaBeanConfigurator(String packageToScan, Map<Class, Class> interfaceToI
}

@Override
public <T> Class<? extends T> getImplementationClass(Class<T> interfaceClass) {
public <T> DefaultBeanDefinition generateBean(Class<T> tClass) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
if (tClass.isInterface()) {
tClass = (Class<T>) this.getImplementationClass(tClass);
}
T bean = tClass.getDeclaredConstructor().newInstance();

// inject all the fields with annotation @inject
for (Field field : Arrays.stream(tClass.getDeclaredFields()).filter(field -> field.isAnnotationPresent(Inject.class)).toList()) {
field.setAccessible(true);
field.set(bean, this.beanFactory.getBean(field.getType()));
}

this.callPostProcessor(bean);

DefaultBeanDefinition tmp = new DefaultBeanDefinition();
tmp.setBean(bean);
tmp.setBeanClassName(bean.getClass().getName());
tmp.setScope("singleton");
if (tClass.isAnnotationPresent(Thread.class)) {
tmp.setScope("thread");
} else if (tClass.isAnnotationPresent(Provided.class)) {
tmp.setScope("provided");
}
return tmp;
}

private void callPostProcessor(Object bean) {
for (Method method: bean.getClass().getMethods()) {
if (method.isAnnotationPresent(PostConstruct.class)) {
try {
method.invoke(bean);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}

@Override
// Class<? extends T>
public <T> Class getImplementationClass(Class<T> interfaceClass) {
return interfaceToImplementation.computeIfAbsent(interfaceClass, tClass -> {
Set<Class<? extends T>> implementationClasses = scanner.getSubTypesOf(interfaceClass);
// if many classes are implement interface choose one with @Service
if (implementationClasses.size() != 1) {
// stream api
for (Class cls : implementationClasses) {
if (cls.isAnnotationPresent(Service.class)) {
return cls;
}
}

throw new RuntimeException("0 or more than 1 implementation");
return implementationClasses.stream()
.filter(aClass -> aClass.isAnnotationPresent(Service.class))
.findFirst()
.orElseThrow(() -> new RuntimeException("No or more than one implementation for " + interfaceClass.getName()));
}
return implementationClasses.stream().findFirst().get();
});
Expand Down
70 changes: 68 additions & 2 deletions src/main/java/team/configurator/metadata/XMLBeanConfigurator.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,82 @@
package team.configurator.metadata;

import lombok.Getter;
import org.reflections.Reflections;
import org.w3c.dom.Document;
import team.config.DefaultBeanDefinition;
import team.configurator.BeanConfigurator;
import team.factory.BeanFactory;

import javax.xml.parsers.DocumentBuilderFactory;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class XMLBeanConfigurator implements BeanConfigurator {
@Getter
private Reflections scanner;
private String FILENAME = "beans.xml";
private final Map<Class, Class> interfaceToImplementation;
private BeanFactory beanFactory;

private static final String BASE_PACKAGE_TAG = "base-package";
private static final String BEAN_TAG = "bean";
private static final String BEAN_NAME_ATTRIBUTE = "id";
private static final String BEAN_CLASS_NAME_ATTRIBUTE = "class";
private static final String SCOPE_ATTRIBUTE = "scope";
private static final String LAZY_INIT_ATTRIBUTE = "lazy-init";
private static final String PRIMARY_ATTRIBUTE = "primary";
private static final String FACTORY_BEAN_ATTRIBUTE = "factory-bean";
private static final String FACTORY_METHOD_ATTRIBUTE = "factory-method";
private static final String INIT_METHOD_ATTRIBUTE = "init-method";
private static final String DESTROY_METHOD_ATTRIBUTE = "destroy-method";
private static final String CONSTRUCTOR_ARGUMENT_TAG = "constructor-arg";
private static final String PROPERTY_TAG = "property";
private static final String NAME_ATTRIBUTE = "name";
private static final String REF_ATTRIBUTE = "ref";

public XMLBeanConfigurator(String filename) {
this.FILENAME = filename;
this.interfaceToImplementation = new ConcurrentHashMap<>();
}

public XMLBeanConfigurator(Map<Class, Class> interfaceToImplementation) {
this.interfaceToImplementation = interfaceToImplementation;
}

public XMLBeanConfigurator(String filename, Map<Class, Class> interfaceToImplementation) {
this.FILENAME = filename;
this.interfaceToImplementation = interfaceToImplementation;
}

public void parseXML() {
// parse xml file and put classes in map
try {
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(this.FILENAME);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@Override
public <T> Class<? extends T> getImplementationClass(Class<T> interfaceClass) {
return null;
// return class from interfaceToImplementation map
// if class doesn't exist in map -> throw exception
if (interfaceToImplementation.containsKey(interfaceClass)) {
return interfaceToImplementation.get(interfaceClass);
} else {
throw new RuntimeException("No implementation for " + interfaceClass.getName());
}

}

@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}

@Override
public Reflections getScanner() {
public <T> DefaultBeanDefinition generateBean(Class<T> tClass) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
return null;
}
}
13 changes: 12 additions & 1 deletion src/main/java/team/context/ApplicationContext.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
package team.context;

import lombok.Setter;
import team.configurator.BeanConfigurator;
import team.configurator.metadata.JavaBeanConfigurator;
import team.configurator.metadata.XMLBeanConfigurator;
import team.factory.BeanFactory;

public class ApplicationContext {
@Setter
private BeanFactory beanFactory;

public ApplicationContext(String packageToScan) {
BeanFactory beanFactory = new BeanFactory(this, packageToScan);
BeanConfigurator beanConfigurator = null;
if (packageToScan.endsWith(".xml")) {
beanConfigurator = new XMLBeanConfigurator(packageToScan);
} else {
beanConfigurator = new JavaBeanConfigurator(packageToScan);

}
BeanFactory beanFactory = new BeanFactory(beanConfigurator);
beanConfigurator.setBeanFactory(beanFactory);
this.setBeanFactory(beanFactory);
}

Expand Down
Loading

0 comments on commit 6aedee4

Please sign in to comment.