NotificationServiceClient
vs. AzureBlobStorageClient
Both classes are Spring-managed Beans, but they follow slightly different lifecycles due to how they receive dependencies.
package com.fepatex.offermodule.integration;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobContainerClientBuilder;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.InputStream;
/**
* AzureBlobStorageClient is a Spring-managed component responsible for interacting with
* Azure Blob Storage. It initializes a {@link BlobContainerClient} to handle blob operations.
*
* <p>This class uses an empty constructor and a {@link PostConstruct} method to initialize
* its {@link BlobContainerClient}. This approach is required because Spring injects the
* {@link Value}-annotated fields after the constructor is called. Using {@link PostConstruct}
* ensures that these values are available before the client is initialized.</p>
*/
@Component
public class AzureBlobStorageClient {
private BlobContainerClient containerClient;
@Value("${azure.storage.connection-string}")
private String connectionString;
@Value("${azure.storage.container-name}")
private String containerName;
/**
* Default constructor. This is intentionally left empty to allow Spring to manage the
* lifecycle of the bean. Field values annotated with @Value are injected after
* the constructor call, so initialization logic is deferred to the @PostConstruct method.
*/
public AzureBlobStorageClient() {
// Empty constructor
}
/**
* Initializes the {@link BlobContainerClient} after all {@link Value}-annotated fields
* have been injected by Spring. This method ensures the client is configured correctly
* with the injected properties.
*
* <p>Marked with {@link PostConstruct} to guarantee that it runs after dependency injection
* is complete, avoiding potential null reference issues in the constructor.</p>
*/
@PostConstruct
public void initializeBlobContainerClient() {
this.containerClient = new BlobContainerClientBuilder()
.connectionString(connectionString)
.containerName(containerName)
.buildClient();
}
public String uploadFile(String fileName, InputStream fileData, long fileSize) {
// Get a Blob Client
BlobClient blobClient = containerClient.getBlobClient(fileName);
// Upload file
blobClient.upload(fileData, fileSize, true);
// Return the URL of the uploaded blob
return blobClient.getBlobUrl();
}
}
package com.fepatex.offermodule.integration;
import com.fepatex.offermodule.dto.common.NotificationRequestDTO;
import com.fepatex.offermodule.exception.common.NotificationServiceException;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;
@Component
public class NotificationServiceClient {
private final RestClient restClient;
private static final String NOTIFICATION_SERVICE_BASE_URL =
"<http://fepatex-notification-ms-service.fepatex-notification-ms.svc.cluster.local:80>";
public NotificationServiceClient(RestClient.Builder restClientBuilder) {
this.restClient = restClientBuilder
.baseUrl(NOTIFICATION_SERVICE_BASE_URL)
.build();
}
/**
* Sends a notification request to the notification service.
*
* @param notificationRequestDTO the notification request payload
* @throws NotificationServiceException if the request fails
*/
public void sendNotification(NotificationRequestDTO notificationRequestDTO) {
try {
restClient
.post()
.uri("/api/v1/notifications/offer-creation")
.body(notificationRequestDTO)
.retrieve()
.toBodilessEntity();
} catch (RestClientException e) {
throw new NotificationServiceException("Failed to send notification: " + e.getMessage(), e);
}
}
}
NotificationServiceClient
This class uses constructor injection, so its dependencies are fully initialized during Bean instantiation.
@Component
, @Service
, @Repository
, etc.).NotificationServiceClient
and marks it for instantiation.Spring creates the object and calls the constructor:
new NotificationServiceClient(restClientBuilder);
Since RestClient.Builder
is a Spring-managed Bean, it is already available and automatically injected.
The constructor assigns this.restClient
immediately:
this.restClient = restClientBuilder.baseUrl(NOTIFICATION_SERVICE_BASE_URL).build();