From fc4123e9f37c78b90b82b2d0496ab9222647c232 Mon Sep 17 00:00:00 2001 From: AndriyPavlyuk Date: Sat, 23 Nov 2024 16:00:47 +0200 Subject: [PATCH 1/9] 24 Implement bean post processor --- .../AutowiredAnnotationBeanPostProcessor.java | 102 ++++++++++++++++++ ...nitDestroyAnnotationBeanPostProcessor.java | 74 +++++++++++++ .../config/DestructionBeanPostProcessor.java | 41 +++++++ 3 files changed, 217 insertions(+) create mode 100644 src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java create mode 100644 src/main/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessor.java create mode 100644 src/main/java/com/codeus/winter/config/DestructionBeanPostProcessor.java diff --git a/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java new file mode 100644 index 0000000..2c025d9 --- /dev/null +++ b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -0,0 +1,102 @@ +package com.codeus.winter.annotation; + +import com.codeus.winter.config.BeanFactory; +import com.codeus.winter.config.BeanPostProcessor; +import com.codeus.winter.exception.BeanNotFoundException; +import jakarta.annotation.Nullable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; + + +/** + * BeanPostProcessor implementation that autowires annotated fields, setter methods, and constructor. + * + */ +public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor { + + private BeanFactory beanFactory; + + /** + * Set BeanFactory as dependency. + * + * @param beanFactory bean factory + */ + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + /** + * Resolve dependency injection for constructors/methods/fields with @Autowired annotation. + * + * @param bean bean object + * @param beanName bean name + * @return bean object + */ + @Nullable + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeanNotFoundException { + try { + injectConstructor(bean); + injectMethod(bean); + injectField(bean); + } catch (Exception e) { + throw new BeanNotFoundException("Bean post processing failed: " + beanName, e); + } + return bean; + } + + private void injectMethod(Object bean) throws InvocationTargetException, IllegalAccessException { + Class beanType = bean.getClass(); + for (Method method : beanType.getDeclaredMethods()) { + if (method.isAnnotationPresent(Autowired.class)) { + for (Parameter parameter : method.getParameters()) { + Object dependency = beanFactory.getBean(parameter.getName()); + method.setAccessible(true); + method.invoke(bean, dependency); + } + } + } + } + + private void injectField(Object bean) throws IllegalAccessException { + Class beanType = bean.getClass(); + for (Field field : beanType.getDeclaredFields()) { + if (field.isAnnotationPresent(Autowired.class)) { + Object dependency = beanFactory.getBean(field.getType()); + field.setAccessible(true); + field.set(bean, dependency); + } + } + } + + private void injectConstructor(Object bean) throws IllegalAccessException { + Class beanType = bean.getClass(); + for (Constructor constructor : beanType.getConstructors()) { + if (constructor.isAnnotationPresent(Autowired.class)) { + for (Parameter parameter : constructor.getParameters()) { + String name = parameter.getName(); + Object dependency = beanFactory.getBean(name); + Field field = getFieldByName(beanType.getDeclaredFields(), name); + field.setAccessible(true); + field.set(bean, dependency); + } + } + } + } + + private Field getFieldByName(Field[] declaredFields, String name) { + Field field = null; + for (Field declaredField : declaredFields) { + if (declaredField.getName().equals(name)) { + field = declaredField; + } else { + throw new IllegalArgumentException(String.format("Field '%s' not found", name)); + } + } + return field; + } +} diff --git a/src/main/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessor.java b/src/main/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessor.java new file mode 100644 index 0000000..87bc1f0 --- /dev/null +++ b/src/main/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessor.java @@ -0,0 +1,74 @@ +package com.codeus.winter.annotation; + +import com.codeus.winter.config.BeanPostProcessor; +import com.codeus.winter.config.DestructionBeanPostProcessor; +import com.codeus.winter.exception.BeanNotFoundException; +import jakarta.annotation.Nullable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * BeanPostProcessor implementation that invokes annotated init and destroy methods. Processes + * methods that annotated with @PostConstruct and @PreDestroy classes + */ +public class InitDestroyAnnotationBeanPostProcessor implements BeanPostProcessor, + DestructionBeanPostProcessor { + + /** + * Invoke method that annotated with @PostConstruct after bean properties set. + * + * @param bean bean object + * @param beanName bean name + * @return bean object + */ + @Nullable + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeanNotFoundException { + try { + processInitMethod(bean); + } catch (Exception e) { + throw new BeanNotFoundException("Invocation of init method failed: " + beanName, e); + } + return bean; + } + + private void processInitMethod(Object bean) + throws InvocationTargetException, IllegalAccessException { + Class beanType = bean.getClass(); + for (Method method : beanType.getDeclaredMethods()) { + if (method.isAnnotationPresent(PostConstruct.class)) { + method.setAccessible(true); + method.invoke(bean); + } + } + } + + /** + * Invoke method that annotated with @PreDestroy before destroy bean. + * + * @param bean bean object + * @param beanName bean name + */ + @Override + public void postProcessBeforeDestruction(Object bean, String beanName) + throws BeanNotFoundException { + try { + processDestroyMethod(bean); + } catch (Exception e) { + throw new BeanNotFoundException( + "Failed to invoke destroy method on bean with name: " + beanName, e); + } + } + + private void processDestroyMethod(Object bean) + throws InvocationTargetException, IllegalAccessException { + Class beanType = bean.getClass(); + for (Method method : beanType.getDeclaredMethods()) { + if (method.isAnnotationPresent(PreDestroy.class)) { + method.setAccessible(true); + method.invoke(bean); + } + } + } +} diff --git a/src/main/java/com/codeus/winter/config/DestructionBeanPostProcessor.java b/src/main/java/com/codeus/winter/config/DestructionBeanPostProcessor.java new file mode 100644 index 0000000..7783902 --- /dev/null +++ b/src/main/java/com/codeus/winter/config/DestructionBeanPostProcessor.java @@ -0,0 +1,41 @@ +package com.codeus.winter.config; + + +import com.codeus.winter.exception.BeanNotFoundException; + +/** + * Subinterface of {@link BeanPostProcessor} that adds a before-destruction callback. + * + *

The typical usage will be to invoke custom destruction callbacks on + * specific bean types, matching corresponding initialization callbacks. + */ +public interface DestructionBeanPostProcessor extends BeanPostProcessor { + + /** + * Apply this BeanPostProcessor to the given bean instance before its + * destruction, e.g. invoking custom destruction callbacks. + *

Like DisposableBean's {@code destroy} and a custom destroy method, this + * callback will only apply to beans which the container fully manages the + * lifecycle for. This is usually the case for singletons and scoped beans. + * @param bean the bean instance to be destroyed + * @param beanName the name of the bean + * @throws BeanNotFoundException in case of errors + */ + void postProcessBeforeDestruction(Object bean, String beanName) throws BeanNotFoundException; + + /** + * Determine whether the given bean instance requires destruction by this + * post-processor. + *

The default implementation returns {@code true}. If a pre-5 implementation + * of {@code DestructionAwareBeanPostProcessor} does not provide a concrete + * implementation of this method, Spring silently assumes {@code true} as well. + * @param bean the bean instance to check + * @return {@code true} if {@link #postProcessBeforeDestruction} is supposed to + * be called for this bean instance eventually, or {@code false} if not needed + * @since 4.3 + */ + default boolean requiresDestruction(Object bean) { + return true; + } +} + From 35b4a0a8fe8bb30f40efe2af8e4b22f17ade70cd Mon Sep 17 00:00:00 2001 From: AndriyPavlyuk Date: Mon, 25 Nov 2024 09:41:03 +0200 Subject: [PATCH 2/9] Add unit tests --- .../AutowiredAnnotationBeanPostProcessor.java | 14 ++-- ...owiredAnnotationBeanPostProcessorTest.java | 84 +++++++++++++++++++ .../AutowiredConstructorTestClass.java | 15 ++++ .../annotation/AutowiredFieldTestClass.java | 13 +++ .../annotation/AutowiredMethodTestClass.java | 15 ++++ .../annotation/DependencyTestClass.java | 8 ++ ...estroyAnnotationBeanPostProcessorTest.java | 64 ++++++++++++++ .../annotation/InitDestroyTestClass.java | 18 ++++ 8 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java create mode 100644 src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java create mode 100644 src/test/java/com/codeus/winter/annotation/AutowiredFieldTestClass.java create mode 100644 src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java create mode 100644 src/test/java/com/codeus/winter/annotation/DependencyTestClass.java create mode 100644 src/test/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessorTest.java create mode 100644 src/test/java/com/codeus/winter/annotation/InitDestroyTestClass.java diff --git a/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java index 2c025d9..c3b5aa3 100644 --- a/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -54,7 +54,7 @@ private void injectMethod(Object bean) throws InvocationTargetException, Illegal for (Method method : beanType.getDeclaredMethods()) { if (method.isAnnotationPresent(Autowired.class)) { for (Parameter parameter : method.getParameters()) { - Object dependency = beanFactory.getBean(parameter.getName()); + Object dependency = beanFactory.getBean(parameter.getType()); method.setAccessible(true); method.invoke(bean, dependency); } @@ -78,9 +78,9 @@ private void injectConstructor(Object bean) throws IllegalAccessException { for (Constructor constructor : beanType.getConstructors()) { if (constructor.isAnnotationPresent(Autowired.class)) { for (Parameter parameter : constructor.getParameters()) { - String name = parameter.getName(); - Object dependency = beanFactory.getBean(name); - Field field = getFieldByName(beanType.getDeclaredFields(), name); + Class type = parameter.getType(); + Object dependency = beanFactory.getBean(type); + Field field = getFieldByType(beanType.getDeclaredFields(), type); field.setAccessible(true); field.set(bean, dependency); } @@ -88,13 +88,13 @@ private void injectConstructor(Object bean) throws IllegalAccessException { } } - private Field getFieldByName(Field[] declaredFields, String name) { + private Field getFieldByType(Field[] declaredFields, Class type) { Field field = null; for (Field declaredField : declaredFields) { - if (declaredField.getName().equals(name)) { + if (declaredField.getType().equals(type)) { field = declaredField; } else { - throw new IllegalArgumentException(String.format("Field '%s' not found", name)); + throw new IllegalArgumentException(String.format("Field '%s' not found", type)); } } return field; diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java b/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java new file mode 100644 index 0000000..069a1f2 --- /dev/null +++ b/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java @@ -0,0 +1,84 @@ +package com.codeus.winter.annotation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.codeus.winter.config.DefaultBeanFactory; +import java.lang.reflect.Field; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.reflections.Reflections; + +public class AutowiredAnnotationBeanPostProcessorTest { + + private static final String AUTOWIRED_FIELD_BEAN_NAME = "AutowiredFieldTestClass"; + private static final String AUTOWIRED_CONSTRUCTOR_BEAN_NAME = "AutowiredConstructorTestClass"; + private static final String AUTOWIRED_METHOD_BEAN_NAME = "AutowiredMethodTestClass"; + private DefaultBeanFactory beanFactory; + private AutowiredAnnotationBeanPostProcessor postProcessor; + + @BeforeEach + void setUpBeforeClass() { + beanFactory = new DefaultBeanFactory(); + postProcessor = new AutowiredAnnotationBeanPostProcessor(); + postProcessor.setBeanFactory(beanFactory); + createBeanTestClass(); + } + + @Test + void injectField() { + // given + var bean = beanFactory.getBean(AutowiredFieldTestClass.class); + + // when + Object actual = postProcessor.postProcessBeforeInitialization(bean, + AUTOWIRED_FIELD_BEAN_NAME); + + // then + Field dependency = actual.getClass().getDeclaredFields()[0]; + dependency.setAccessible(true); + assertEquals(DependencyTestClass.class, dependency.getType()); + } + + @Test + void injectConstructor() { + // given + var bean = beanFactory.getBean(AutowiredConstructorTestClass.class); + + // when + Object actual = postProcessor.postProcessBeforeInitialization(bean, + AUTOWIRED_CONSTRUCTOR_BEAN_NAME); + + // then + Field dependency = actual.getClass().getDeclaredFields()[0]; + dependency.setAccessible(true); + assertEquals(DependencyTestClass.class, dependency.getType()); + } + + @Test + void injectMethod() { + // given + var bean = beanFactory.getBean(AutowiredMethodTestClass.class); + + // when + Object actual = postProcessor.postProcessBeforeInitialization(bean, + AUTOWIRED_METHOD_BEAN_NAME); + + // then + Field dependency = actual.getClass().getDeclaredFields()[0]; + dependency.setAccessible(true); + assertEquals(DependencyTestClass.class, dependency.getType()); + } + + private void createBeanTestClass() { + Reflections refelections = new Reflections("com.codeus.winter.annotation"); + Set> classes = refelections.getTypesAnnotatedWith(Component.class); + classes.forEach(clazz -> { + try { + beanFactory.createBean(clazz); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } +} diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java b/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java new file mode 100644 index 0000000..6fb4617 --- /dev/null +++ b/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java @@ -0,0 +1,15 @@ +package com.codeus.winter.annotation; + +@Component +public class AutowiredConstructorTestClass { + + private DependencyTestClass field; + + public AutowiredConstructorTestClass() { + } + + @Autowired + public AutowiredConstructorTestClass(DependencyTestClass field) { + this.field = field; + } +} diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredFieldTestClass.java b/src/test/java/com/codeus/winter/annotation/AutowiredFieldTestClass.java new file mode 100644 index 0000000..2e0217b --- /dev/null +++ b/src/test/java/com/codeus/winter/annotation/AutowiredFieldTestClass.java @@ -0,0 +1,13 @@ +package com.codeus.winter.annotation; + +@Component +public class AutowiredFieldTestClass { + + @Autowired + private DependencyTestClass field; + + + public AutowiredFieldTestClass() { + + } +} diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java b/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java new file mode 100644 index 0000000..86a524f --- /dev/null +++ b/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java @@ -0,0 +1,15 @@ +package com.codeus.winter.annotation; + +@Component +public class AutowiredMethodTestClass { + + private DependencyTestClass field; + + public AutowiredMethodTestClass() { + } + + @Autowired + public void setField(DependencyTestClass field) { + this.field = field; + } +} diff --git a/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java b/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java new file mode 100644 index 0000000..682858b --- /dev/null +++ b/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java @@ -0,0 +1,8 @@ +package com.codeus.winter.annotation; + +@Component +public class DependencyTestClass { + + private String dependency; + +} diff --git a/src/test/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessorTest.java b/src/test/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessorTest.java new file mode 100644 index 0000000..fe1818b --- /dev/null +++ b/src/test/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessorTest.java @@ -0,0 +1,64 @@ +package com.codeus.winter.annotation; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.codeus.winter.config.DefaultBeanFactory; +import java.lang.reflect.Field; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.reflections.Reflections; + +public class InitDestroyAnnotationBeanPostProcessorTest { + + private static final String INIT_DESTROY_BEAN_NAME = "InitDestroyTestClass"; + private DefaultBeanFactory beanFactory; + private InitDestroyAnnotationBeanPostProcessor postProcessor; + + @BeforeEach + void setUpBeforeClass() { + beanFactory = new DefaultBeanFactory(); + postProcessor = new InitDestroyAnnotationBeanPostProcessor(); + createBeanTestClass(); + } + + @Test + void initPostConstructAnnotation() throws IllegalAccessException { + // given + var bean = beanFactory.getBean(InitDestroyTestClass.class); + + // when + Object actual = postProcessor.postProcessBeforeInitialization(bean, INIT_DESTROY_BEAN_NAME); + + // then + Field field = actual.getClass().getDeclaredFields()[0]; + field.setAccessible(true); + assertEquals("init", field.get(actual)); + } + + @Test + void destroyPreDestroyAnnotation() throws IllegalAccessException { + // given + var bean = beanFactory.getBean(InitDestroyTestClass.class); + + // when + postProcessor.postProcessBeforeDestruction(bean, INIT_DESTROY_BEAN_NAME); + + // then + Field field = bean.getClass().getDeclaredFields()[0]; + field.setAccessible(true); + assertEquals("destroy", field.get(bean)); + } + + private void createBeanTestClass() { + Reflections refelections = new Reflections("com.codeus.winter.annotation"); + Set> classes = refelections.getTypesAnnotatedWith(Component.class); + classes.forEach(clazz -> { + try { + beanFactory.createBean(clazz); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } +} diff --git a/src/test/java/com/codeus/winter/annotation/InitDestroyTestClass.java b/src/test/java/com/codeus/winter/annotation/InitDestroyTestClass.java new file mode 100644 index 0000000..84cf3f9 --- /dev/null +++ b/src/test/java/com/codeus/winter/annotation/InitDestroyTestClass.java @@ -0,0 +1,18 @@ +package com.codeus.winter.annotation; + +@Component +public class InitDestroyTestClass { + + private String field; + + @PostConstruct + public void init() { + field = "init"; + } + + @PreDestroy + public void destroy() { + field = "destroy"; + } + +} From 914ff709913d099ac94827157277ff7075349097 Mon Sep 17 00:00:00 2001 From: AndriyPavlyuk Date: Tue, 26 Nov 2024 12:20:02 +0200 Subject: [PATCH 3/9] Suppress checkstyle checks --- config/checkstyle/checkstyle-xpath-suppressions.xml | 2 +- version.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/checkstyle/checkstyle-xpath-suppressions.xml b/config/checkstyle/checkstyle-xpath-suppressions.xml index 0ad85ac..b400d28 100644 --- a/config/checkstyle/checkstyle-xpath-suppressions.xml +++ b/config/checkstyle/checkstyle-xpath-suppressions.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/version.gradle b/version.gradle index 08e3d9a..1286162 100644 --- a/version.gradle +++ b/version.gradle @@ -5,7 +5,7 @@ ext { 'commons_collections': '4.4', 'log4j': '2.23.1', 'junit': '5.10.3', - 'checkstyle': '10.20.0', + 'checkstyle': '10.20.1', 'jacoco': '0.8.12' ] } \ No newline at end of file From 472888028e4478eb20166cade2d9faf1dbfd8fce Mon Sep 17 00:00:00 2001 From: AndriyPavlyuk Date: Sat, 30 Nov 2024 23:52:14 +0200 Subject: [PATCH 4/9] Remove design for extension from checkstyle check --- config/checkstyle/checkstyle.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 82f9e34..980d227 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -109,7 +109,7 @@ - + From f02910b0727306f7a9d65bea1e7f90df6a507992 Mon Sep 17 00:00:00 2001 From: AndriyPavlyuk Date: Sun, 1 Dec 2024 00:20:54 +0200 Subject: [PATCH 5/9] Add comments --- .../com/codeus/winter/annotation/AutowiredFieldTestClass.java | 2 +- .../com/codeus/winter/annotation/AutowiredMethodTestClass.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredFieldTestClass.java b/src/test/java/com/codeus/winter/annotation/AutowiredFieldTestClass.java index 2e0217b..9c311b0 100644 --- a/src/test/java/com/codeus/winter/annotation/AutowiredFieldTestClass.java +++ b/src/test/java/com/codeus/winter/annotation/AutowiredFieldTestClass.java @@ -8,6 +8,6 @@ public class AutowiredFieldTestClass { public AutowiredFieldTestClass() { - + // default constructor } } diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java b/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java index 86a524f..503c584 100644 --- a/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java +++ b/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java @@ -6,6 +6,7 @@ public class AutowiredMethodTestClass { private DependencyTestClass field; public AutowiredMethodTestClass() { + // default constructor } @Autowired From 061683c0d86482ec483fc95211f3237de6f14f91 Mon Sep 17 00:00:00 2001 From: AndriyPavlyuk Date: Sun, 1 Dec 2024 00:35:42 +0200 Subject: [PATCH 6/9] Add check for null --- .../annotation/AutowiredAnnotationBeanPostProcessor.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java index c3b5aa3..f0ed18c 100644 --- a/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -81,8 +81,10 @@ private void injectConstructor(Object bean) throws IllegalAccessException { Class type = parameter.getType(); Object dependency = beanFactory.getBean(type); Field field = getFieldByType(beanType.getDeclaredFields(), type); - field.setAccessible(true); - field.set(bean, dependency); + if (field != null) { + field.setAccessible(true); + field.set(bean, dependency); + } } } } From 9c49e17885b6fae2b138bd621c85870d10727fa8 Mon Sep 17 00:00:00 2001 From: AndriyPavlyuk Date: Sun, 1 Dec 2024 00:47:30 +0200 Subject: [PATCH 7/9] Fix sonar issues --- .../AutowiredAnnotationBeanPostProcessor.java | 1 + .../InitDestroyAnnotationBeanPostProcessor.java | 1 + .../AutowiredAnnotationBeanPostProcessorTest.java | 2 +- .../annotation/AutowiredConstructorTestClass.java | 8 ++++++++ .../winter/annotation/AutowiredMethodTestClass.java | 4 ++++ .../codeus/winter/annotation/DependencyTestClass.java | 11 +++++++++++ .../InitDestroyAnnotationBeanPostProcessorTest.java | 2 +- .../winter/annotation/InitDestroyTestClass.java | 7 +++++++ 8 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java index f0ed18c..0ecce0f 100644 --- a/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -15,6 +15,7 @@ * BeanPostProcessor implementation that autowires annotated fields, setter methods, and constructor. * */ +@SuppressWarnings("java:S3011") public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor { private BeanFactory beanFactory; diff --git a/src/main/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessor.java b/src/main/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessor.java index 87bc1f0..1cd13e2 100644 --- a/src/main/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessor.java +++ b/src/main/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessor.java @@ -11,6 +11,7 @@ * BeanPostProcessor implementation that invokes annotated init and destroy methods. Processes * methods that annotated with @PostConstruct and @PreDestroy classes */ +@SuppressWarnings("java:S3011") public class InitDestroyAnnotationBeanPostProcessor implements BeanPostProcessor, DestructionBeanPostProcessor { diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java b/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java index 069a1f2..de1c184 100644 --- a/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java +++ b/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; import org.reflections.Reflections; -public class AutowiredAnnotationBeanPostProcessorTest { +class AutowiredAnnotationBeanPostProcessorTest { private static final String AUTOWIRED_FIELD_BEAN_NAME = "AutowiredFieldTestClass"; private static final String AUTOWIRED_CONSTRUCTOR_BEAN_NAME = "AutowiredConstructorTestClass"; diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java b/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java index 6fb4617..1a9def6 100644 --- a/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java +++ b/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java @@ -12,4 +12,12 @@ public AutowiredConstructorTestClass() { public AutowiredConstructorTestClass(DependencyTestClass field) { this.field = field; } + + public DependencyTestClass getField() { + return field; + } + + public void setField(DependencyTestClass field) { + this.field = field; + } } diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java b/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java index 503c584..30e5edf 100644 --- a/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java +++ b/src/test/java/com/codeus/winter/annotation/AutowiredMethodTestClass.java @@ -13,4 +13,8 @@ public AutowiredMethodTestClass() { public void setField(DependencyTestClass field) { this.field = field; } + + public DependencyTestClass getField() { + return field; + } } diff --git a/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java b/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java index 682858b..069c0d2 100644 --- a/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java +++ b/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java @@ -5,4 +5,15 @@ public class DependencyTestClass { private String dependency; + public DependencyTestClass(String dependency) { + this.dependency = dependency; + } + + public String getDependency() { + return dependency; + } + + public void setDependency(String dependency) { + this.dependency = dependency; + } } diff --git a/src/test/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessorTest.java b/src/test/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessorTest.java index fe1818b..3db3342 100644 --- a/src/test/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessorTest.java +++ b/src/test/java/com/codeus/winter/annotation/InitDestroyAnnotationBeanPostProcessorTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test; import org.reflections.Reflections; -public class InitDestroyAnnotationBeanPostProcessorTest { +class InitDestroyAnnotationBeanPostProcessorTest { private static final String INIT_DESTROY_BEAN_NAME = "InitDestroyTestClass"; private DefaultBeanFactory beanFactory; diff --git a/src/test/java/com/codeus/winter/annotation/InitDestroyTestClass.java b/src/test/java/com/codeus/winter/annotation/InitDestroyTestClass.java index 84cf3f9..b7e192e 100644 --- a/src/test/java/com/codeus/winter/annotation/InitDestroyTestClass.java +++ b/src/test/java/com/codeus/winter/annotation/InitDestroyTestClass.java @@ -15,4 +15,11 @@ public void destroy() { field = "destroy"; } + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } } From 06c40ba542a72bcc75082f8bd1fe994414af8698 Mon Sep 17 00:00:00 2001 From: AndriyPavlyuk Date: Sun, 1 Dec 2024 00:53:48 +0200 Subject: [PATCH 8/9] Fix unit tests --- .../com/codeus/winter/annotation/DependencyTestClass.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java b/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java index 069c0d2..09d9a3c 100644 --- a/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java +++ b/src/test/java/com/codeus/winter/annotation/DependencyTestClass.java @@ -5,8 +5,8 @@ public class DependencyTestClass { private String dependency; - public DependencyTestClass(String dependency) { - this.dependency = dependency; + public DependencyTestClass() { + // default constructor } public String getDependency() { From f397fa9d5bdbe422ca0b5c4c8276261a36c90b0f Mon Sep 17 00:00:00 2001 From: AndriyPavlyuk Date: Thu, 5 Dec 2024 20:08:48 +0200 Subject: [PATCH 9/9] Change logic --- .../AutowiredAnnotationBeanPostProcessor.java | 31 ------------------- ...owiredAnnotationBeanPostProcessorTest.java | 16 ---------- .../AutowiredConstructorTestClass.java | 23 -------------- 3 files changed, 70 deletions(-) delete mode 100644 src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java diff --git a/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java index 0ecce0f..b7e2e2d 100644 --- a/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/src/main/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -4,7 +4,6 @@ import com.codeus.winter.config.BeanPostProcessor; import com.codeus.winter.exception.BeanNotFoundException; import jakarta.annotation.Nullable; -import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -41,7 +40,6 @@ public void setBeanFactory(BeanFactory beanFactory) { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanNotFoundException { try { - injectConstructor(bean); injectMethod(bean); injectField(bean); } catch (Exception e) { @@ -73,33 +71,4 @@ private void injectField(Object bean) throws IllegalAccessException { } } } - - private void injectConstructor(Object bean) throws IllegalAccessException { - Class beanType = bean.getClass(); - for (Constructor constructor : beanType.getConstructors()) { - if (constructor.isAnnotationPresent(Autowired.class)) { - for (Parameter parameter : constructor.getParameters()) { - Class type = parameter.getType(); - Object dependency = beanFactory.getBean(type); - Field field = getFieldByType(beanType.getDeclaredFields(), type); - if (field != null) { - field.setAccessible(true); - field.set(bean, dependency); - } - } - } - } - } - - private Field getFieldByType(Field[] declaredFields, Class type) { - Field field = null; - for (Field declaredField : declaredFields) { - if (declaredField.getType().equals(type)) { - field = declaredField; - } else { - throw new IllegalArgumentException(String.format("Field '%s' not found", type)); - } - } - return field; - } } diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java b/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java index de1c184..38cac79 100644 --- a/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java +++ b/src/test/java/com/codeus/winter/annotation/AutowiredAnnotationBeanPostProcessorTest.java @@ -12,7 +12,6 @@ class AutowiredAnnotationBeanPostProcessorTest { private static final String AUTOWIRED_FIELD_BEAN_NAME = "AutowiredFieldTestClass"; - private static final String AUTOWIRED_CONSTRUCTOR_BEAN_NAME = "AutowiredConstructorTestClass"; private static final String AUTOWIRED_METHOD_BEAN_NAME = "AutowiredMethodTestClass"; private DefaultBeanFactory beanFactory; private AutowiredAnnotationBeanPostProcessor postProcessor; @@ -40,21 +39,6 @@ void injectField() { assertEquals(DependencyTestClass.class, dependency.getType()); } - @Test - void injectConstructor() { - // given - var bean = beanFactory.getBean(AutowiredConstructorTestClass.class); - - // when - Object actual = postProcessor.postProcessBeforeInitialization(bean, - AUTOWIRED_CONSTRUCTOR_BEAN_NAME); - - // then - Field dependency = actual.getClass().getDeclaredFields()[0]; - dependency.setAccessible(true); - assertEquals(DependencyTestClass.class, dependency.getType()); - } - @Test void injectMethod() { // given diff --git a/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java b/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java deleted file mode 100644 index 1a9def6..0000000 --- a/src/test/java/com/codeus/winter/annotation/AutowiredConstructorTestClass.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.codeus.winter.annotation; - -@Component -public class AutowiredConstructorTestClass { - - private DependencyTestClass field; - - public AutowiredConstructorTestClass() { - } - - @Autowired - public AutowiredConstructorTestClass(DependencyTestClass field) { - this.field = field; - } - - public DependencyTestClass getField() { - return field; - } - - public void setField(DependencyTestClass field) { - this.field = field; - } -}