Skip to content

Commit

Permalink
BLZT-71 add PostConstruct functionality
Browse files Browse the repository at this point in the history
* BLZT-71 add annotation

* BLZT-71 add PostConstruct functionality

* BLZT-71 refactoring

* BLZT-71 add documentation

* BLZT-71 fix imports in ReflectionUtils

* BLZT-71 fixes
  • Loading branch information
andrewpikozh authored Nov 29, 2023
1 parent 100af02 commit 72c261c
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 7 deletions.
14 changes: 7 additions & 7 deletions Core.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ It initializes the list with default post-processors such as the ScheduleBeanPos
If we need diagram classes we should use Wiki and add link to it.

- Dependency Injection
- [Constructor](core/Constructor.md)
- [Setter](core/Setter.md)
- [Field](core/Field.md)
- [Constructor](/features/core/Constructor.md)
- [Setter](/features/core/Setter.md)
- [Field](/features/core/Field.md)
- Collections
- @Primary
- @Qualifier
- @Order
- [@Value](core/Value.md)
- [@Value](/features/core/Value.md)
- Prototype Beans into a Singleton


Expand All @@ -40,15 +40,15 @@ If we need diagram classes we should use Wiki and add link to it.


- Dependency Injection exceptions
- [Circular Dependencies](core/CircularDependencies.md)
- [Circular Dependencies](/features/core/CircularDependencies.md)
- No such bean exception
- No unique bean exception
- No constructor with Autowired annotation
- etc


- addition items:
- [Scheduling](core/Scheduling.md)
- [Scheduling](/features/core/Scheduling.md)
- Properties file support


Expand All @@ -60,6 +60,6 @@ If we need diagram classes we should use Wiki and add link to it.


- [Logging](/features/core/Logging.md)
- PostConstruct
- [@PostConstruct](/features/core/PostConstruct.md)
- PreDestroy
- Logo
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.bobocode.bring.core.annotation;

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

/**
* <p>The {@code PostConstruct} annotation is used on a method that needs
* to be executed after dependency injection is done to perform any
* initialization. This method is called immediately after the bean's
* properties have been set and the bean has been placed into the
* Bring container.</p>
*
* <p>Methods annotated with {@code @PostConstruct} are invoked only once
* in the bean's lifecycle, and they provide a convenient way to
* initialize resources or perform any setup logic that is required
* before the bean is ready for use.</p>
*
* <p>The method annotated with {@code @PostConstruct} must be non-static
* and should not have any parameters, as it is meant to be an
* initialization callback method for the bean instance. If multiple
* methods are annotated with {@code @PostConstruct} within a single
* class, the order of execution is not guaranteed.</p>
*
* <p>Example:</p>
* <pre>
* {@code
* import com.bobocode.bring.core.annotation.PostConstruct;
*
* public class ExampleBean {
*
* private String message;
*
* @PostConstruct
* public void init() {
* message = "Hello, this is an example!";
* // Additional initialization logic
* }
*
* public String getMessage() {
* return message;
* }
* }
* }
* </pre>
*
* <p>In this example, the {@code init} method will be automatically
* invoked after the {@code ExampleBean} is constructed, providing a
* way to perform custom initialization logic.</p>
*
* @author Blyzhnytsia Team
* @since 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PostConstruct {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.bobocode.bring.core.bpp.impl;

import com.bobocode.bring.core.annotation.BeanProcessor;
import com.bobocode.bring.core.annotation.PostConstruct;
import com.bobocode.bring.core.bpp.BeanPostProcessor;
import com.bobocode.bring.core.exception.PostConstructException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import static com.bobocode.bring.core.utils.ReflectionUtils.processBeanPostProcessorAnnotation;


@BeanProcessor
public class PostConstructBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessInitialization(Object bean, String beanName) {
Method[] declaredMethods = bean.getClass().getMethods();
try {
processBeanPostProcessorAnnotation(bean, declaredMethods, PostConstruct.class);
} catch (Exception exception) {
throw new PostConstructException(exception);
}

return BeanPostProcessor.super.postProcessInitialization(bean, beanName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.bobocode.bring.core.exception;

public class PostConstructException extends RuntimeException {
public static String POST_CONSTRUCT_EXCEPTION_MESSAGE ="@PostConstruct should be added to method without parameters";
public PostConstructException(Throwable cause) {
super(POST_CONSTRUCT_EXCEPTION_MESSAGE, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,16 @@ public static Supplier<Object> createNewInstance(Constructor<?> constructor, Obj
};
}

public static void processBeanPostProcessorAnnotation(Object bean,
Method[] declaredMethods,
Class<? extends Annotation> annotation) throws ReflectiveOperationException {
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(annotation)) {
declaredMethod.invoke(bean);
}
}
}

private static class QualifierAnnotationParanamer extends AnnotationParanamer {

public QualifierAnnotationParanamer(Paranamer fallback) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.bobocode.bring.core.bpp.impl;

import com.bobocode.bring.core.BringApplication;
import com.bobocode.bring.core.exception.PostConstructException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import testdata.postconstruct.positive.CustomPostConstruct;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

class PostConstructBeanPostProcessorTest {

@Test
void shouldFillMessage_postProcessInitialization() {
//given
var bringApplicationContext = BringApplication.run("testdata.postconstruct.positive");

//when
var myCustomPostConstruct = bringApplicationContext.getBean(CustomPostConstruct.class);

//then
assertThat(myCustomPostConstruct.getMessage()).isEqualTo("Hello!");
}

@Test
void shouldThrowException_postProcessInitialization() {
//given
var expectedMessage = "@PostConstruct should be added to method without parameters";
// when
Executable executable = () -> BringApplication.run("testdata.postconstruct.negative");

// then
PostConstructException postConstructException = assertThrows(PostConstructException.class, executable);
assertThat(postConstructException.getMessage()).isEqualTo(expectedMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package testdata.postconstruct.negative;

import com.bobocode.bring.core.annotation.Component;
import com.bobocode.bring.core.annotation.PostConstruct;
import lombok.Getter;

@Getter
@Component
public class CustomPostConstruct {

private String message;

@PostConstruct
public void fillMessage(String invalidParam) {
message = "Hello!";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package testdata.postconstruct.positive;

import com.bobocode.bring.core.annotation.Component;
import com.bobocode.bring.core.annotation.PostConstruct;
import lombok.Getter;

@Component
@Getter
public class CustomPostConstruct {

private String message;

@PostConstruct
public void fillMessage() {
message = "Hello!";
}
}
66 changes: 66 additions & 0 deletions features/core/PostConstruct.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# @PostConstruct

## Introduction

In the Bring framework, `@PostConstruct` is a method-level annotation used to indicate that a method should be invoked immediately after an instance of the bean is constructed, and before any other initialization logic occurs.
## Usage

To use `@PostConstruct`, follow these steps:

1. **Add the Annotation**: Place the `@PostConstruct` annotation on a method within your Bring bean class.

```java
import com.bobocode.bring.core.annotation.PostConstruct;

public class MyBean {

@PostConstruct
public void init() {
// Initialization logic here
// This method will be called after bean instantiation.
}
}
```


2. **Invoke the Bring Container**: Make sure that you obtain the bean from the Bring container. The `@PostConstruct` annotated method will be automatically invoked.

```java
import com.bobocode.bring.core.BringApplication;

public class MyApp {
public static void main(String[] args) {
var bringApplicationContext = BringApplication.run("your.path");
var myBean = bringApplicationContext.getBean(CustomPostConstruct.class);
// Your bean is now initialized, and @PostConstruct method has been called.
}
}
```

## Important Points

- The method annotated with `@PostConstruct` must not have any parameters.
- This annotation is generally used in conjunction with the `@Component` stereotype annotations (e.g., `@Service`, `@Repository`, `@Controller`) or in configuration classes.
- The `@PostConstruct` method will be invoked after the bean has been constructed and before any custom initialization logic specified in the bean definition.

## Example

Here is a simple example of a class using `@PostConstruct`:

```java
import com.bobocode.bring.core.annotation.PostConstruct;

public class ExampleBean {

private String message;

@PostConstruct
public void init() {
message = "Hello, this is an example!";
// Additional initialization logic
}

public String getMessage() {
return message;
}
}

0 comments on commit 72c261c

Please sign in to comment.