Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLZT-71 add @PostConstruct functionality #89

Merged
merged 8 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}