#Configurable not working on Subclass - spring

Take the following general abstract class:
#Configurable
public abstract class TestEntityRoot {
public abstract String print();
}
And a subclass:
#Configurable
public class TestEntity extends TestEntityRoot{
private TestEntityService testEntityService;
#Autowired
public void setTestEntityService(TestEntityService testEntityService) {
this.testEntityService = testEntityService;
}
#Override
public String print() {
return testEntityService.print();
}
}
When call controller:
#RestController
public class TestEntityController {
#GetMapping(name = "/test")
public String print() {
TestEntity entity = new TestEntity();
return entity.print();
}
}
everything ok. But if call like this:
#RestController
public class TestEntityController {
#GetMapping(name = "/test")
public String print() {
TestEntityRoot entity = new TestEntity();
return entity.print();
}
}
i get null pointer. Is it possible that second example work?

In the second case you create manually the class rather than using spring's bean. Autowire the bean instead. See
#RestController
public class TestEntityController {
#Autowired
private TestEntity entity
#GetMapping(name = "/test")
public String print() {
return entity.print();
}
}

Related

Spring Boot #ConfigurationProperties

so I'm kinda new to Springboot and I'm trying to get the value from application.properties. I want to get multiple value from the application.properties and insert it into a list. At first, I tried to get the value from controller class and it works. Now I tried to get the value from a new class, but the value won't show up and it's showing an error because it says that it's null. Am i missing an annotation or did i do something wrong in the code? Below is my code.
application.properties:
example.name[0] = asdf
example.name[1] = qwer
List Value class:
#ConfigurationProperties(prefix = "example")
#Configuration
public class NameProperties {
private List<String> name;
public List<String> getName() {
return name;
}
public void setName(List<String> name) {
this.name = name;
}
}
What i tried in controller and worked:
#RestController
#CrossOrigin
#RequestMapping("/tes/**")
public class NameController {
#Autowired
NameProperties property = new NameProperties();
#GetMapping
public String tes() {
String name = property.getName().get(0);
System.out.println(name);
return name;
}
}
In the new class that doesn't work:
#Component
public class NameConfiguration {
#Autowired
NameProperties property = new NameProperties();
public void getName(int index) {
System.out.println(property.getName().get(0));
}
}
The code to test the new class in the controller:
#RestController
#CrossOrigin
#RequestMapping("/tes/**")
public class NameController {
NameConfiguration conf = new NameConfiguration();
#GetMapping
public String tes() {
conf.getName(0);
}
}
Is it because the value doesn't get injected when I call the class or what should I do? Appreciate any kind of help. Thanks!
Hello friend when you declare your class as a Spring Bean you shouldn't initialize the object yourself other the properties define in it will not be injected by Spring, so you should let spring help you with that, try these class below
NameProperties
#Component
#ConfigurationProperties(prefix = "example")
public class NameProperties {
private List<String> name;
public List<String> getName() {
return name;
}
public void setName(List<String> name) {
this.name = name;
}
}
NameConfiguration.java
#Component
public class NameConfiguration {
#Autowired
NameProperties property;
public void getName(int index) {
System.out.println(property.getName().get(0));
}
}

Getting null value from #configuration

I'm creating pojo class and store the application.properties variable but I'm getting null values
NOTE: need to access env from my Abstract class
POJO class
package mynt.xyz.c4.pushnotif.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
#Configuration("notificationEnvironment")
#ConfigurationProperties(prefix = "app.notif")
public class NotificationEnvironment {
private String key;
private String url;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Initializing class with #autowired
public abstract class NotificationBase {
#Autowired
NotificationEnvironment notificationEnvironment;
public void getEnv(){
system.out.println(notificationEnvironment.getKey()); // null value
}
}
concrete class that extend to my NotificationBaseClass
#Component
#Qualifier("androidNotification")
public class AndroidNotification extends NotificationBase implements Notification {
public AndroidNotification(String message, String title, String datalink, List<String> instanceIds) {
super(message, title, datalink, instanceIds);
}
AndroidNotification(){
super();
}
#Override
public void send() {
this.getEnv();
}
}
application.properties
app.notif.key=jkashdkjashd
app.notif.url=https/some.url
You can auto wire #Configuration class from #Configuration class
#Configuration class may reference the instance of any other #Configuration class using #Autowired. This works because the #Configuration classes themselves are instantiated and managed as individual Spring beans.
Make your class #Component and add prefix value in #ConfigurationProperties, like this. This works for me, hope this works for you as well.
#Component
#ConfigurationProperties(prefix = "app.notif")
public class NotificationEnvironment {
private String key;
private String url;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
You can use this properties like this:
#Component
public class NotificationBase {
private static NotificationEnvironment notificationEnvironment;
#Autowired
public NotificationBase(NotificationEnvironment notificationEnvironment){
this.notificationEnvironment = notificationEnvironment;
}
public static void getEnv(){
System.out.println(notificationEnvironment.getKey()); // null value
}
}
Here is the one of the concrete class definition as OP author mentioned.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class ConcreteNotification extends NotificationBase {
#Autowired
public ConcreteNotification(NotificationEnvironment notificationEnvironment) {
super(notificationEnvironment);
}
}
updated NotificationBase as below
public abstract class NotificationBase {
NotificationEnvironment notificationEnvironment;
public NotificationBase(NotificationEnvironment notificationEnvironment) {
this.notificationEnvironment = notificationEnvironment;
}
public void getEnv(){
System.out.println(notificationEnvironment.getKey());
}
}
The controller class I am using to get configuration values
#RestController
public class ArticleCommentController {
#Autowired
ConcreteNotification concreteNotification;
#RequestMapping(value = "/health_check", method = RequestMethod.GET)
public void getDemo() {
concreteNotification.getEnv();
}
}
output:
jkashdkjashd

Spring: How to inject a Supplier<String> function as constructor parameter

I've coded this class:
#Component
public class AuditFactory {
private Supplier<String> auditIdSupplier;
public AuditFactory(Supplier<String> auditIdSupplier) {
this.auditIdSupplier = auditIdSupplier;
}
}
It's used as a dependency of a #Service class:
#Service
public class AuditService {
private AuditFactory auditFactory;
public AuditService(AuditFactory auditFactory) {
this.auditFactory = auditFactory;
}
}
How could I tell to Spring that injects a Supplier<String> when AuditFactory is injected?
EDIT
#Bean
public Supplier<String> auditIdSupplier(FrontOfficeProperties frontOfficeProperties) {
return () -> String.join(
"-",
frontOfficeProperties.getCpdId(),
frontOfficeProperties.getRedisAuditKeyPrefix(),
UUID.randomUUID().toString()
);
}
where FrontOfficeProperties is an #ConfigurationProperties annotated class.
below approach might help you to fix your issue.
also can you please share Supplier class as well.
#Component
public class AuditFactory {
private Supplier<String> auditIdSupplier;
public AuditFactory(Supplier<String> auditIdSupplier) {
this.auditIdSupplier = auditIdSupplier;
}
}
#Service
public class AuditService {
private AuditFactory auditFactory;
public AuditService(AuditFactory auditFactory) {
this.auditFactory = auditFactory;
}
}

factory pattern using spring

How can I choose a service implementation depending on a request parameter on SpringBoot? I can do this by manually instantiating the service, but that's not making use of the Spring Injection feature.
I can also Autowire the two services separately but I'm thinking that if I have more implementations that would pollute the class.
Here's a crude example:
#RestController
class RestControllerTest {
#Autowired
PizzaService pizzaService;
public void bakePizza(#RequestParam("type") String type,#RequestParam("extra") String extra) {
if (type.equals("cheese")) {
//set pizzaService Cheese Implementation
pizzaService = new CheezePizza();
} else {
//set PizzaService vegetable Impleentation;
pizzaService = new VegetablePizza();
}
pizzaService.prepareIngredients(extra);
pizzaService.bakePizza();
}
}
public abstract class PizzaService {
String ingredients;
public abstract void prepareIngredients(String exraIngredient);
public void bakePizza() {
System.out.println("baking pizza with " + ingredients);
}
}
class CheezePizza extends PizzaService {
#Override
public void prepareIngredients(String exraIngredient) {
ingredients = "Cheese " + exraIngredient;
}
}
class VegetablePizza extends PizzaService {
#Override
public void prepareIngredients(String exraIngredient) {
ingredients = "Vegetable " + exraIngredient;
}
}
You could autowire list of beans of same type. So let's say you add getType() to your PizzaService and register every type as spring bean.
public abstract class PizzaService {
abstract String getType();
}
#Component
class CheezePizza extends PizzaService {
#Override
public String getType() {
return "cheese";
}
}
#Component
class VegetablePizza extends PizzaService {
#Override
public String getType() {
return "vegetable";
}
}
#RestController
class RestControllerTest {
private final Map<String, PizzaService> pizzaServices;
public RestControllerTest(List<PizzaService> services) {
pizzaServices = services.stream().collect(Collectors.toMap(PizzaService::getType, Function.identity()));
}
public void bakePizza(#RequestParam("type") String type, #RequestParam("extra") String extra) {
PizzaService pizzaService = pizzaServices.get(type); // remember of handling missing type
pizzaService.prepareIngredients(extra);
pizzaService.bakePizza();
}
}
Another way is to use name your beans by convention i.e. cheesePizza, vegetablePizza and then use ApplicationContext#getBean(type + "Pizza") but I like first approach better, because it's less magical.

Spring Bean Factory Configuration passing input parameter

I'm trying to create a BeanFactory called TaskBeanFactory that I can Autowire into another prototype class that's running on a thread. I want a different instance of a bean returned by the Factory based on a taskName that i want to pass into it but when i start the application i get a null pointer exception because the taskName is null. I had a look at this article but i'm confused about how I should configure the Factory and then pass in the taskName.
The Factory:
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.stereotype.Component;
#Data
#Component
#NoArgsConstructor
public class TaskBeanFactory extends AbstractFactoryBean<GenericTask>{
private TaskNameEnum taskName;
public TaskBeanFactory(TaskNameEnum taskName) {
setSingleton(false);
}
#Override
public Class<?> getObjectType() {
return GenericTask.class;
}
#Override
protected GenericTask createInstance() throws Exception {
switch (taskName) {
case FILE_OPERATION:
return new FileTask();
case DATA_OPERATION:
return new DataTask();
default:
return new GenericTask();
}
}
}
The classes used by the Factory:
#Data
public class GenericTask {
private String idTask;
public void executeTask(Work work) {};
}
#Component
#Scope(value="prototype")
public class FileTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
#Component
#Scope(value="prototype")
public class DataTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
and the thread that's calling the Factory:
#Slf4j
#Data
#Scope("prototype")
#Component
public class WorkerThread implements Runnable {
#Autowired
private TaskBeanFactory taskBeanFactory;
#Autowired
private DataService dataService;
#Override
public void run() {
//iterate a Map of taskIds from the dataService
taskBeanFactory.setTaskName(TaskNameEnum.valueOf(taskEntry.getKey()));
GenericTask genericTask = taskBeanFactory.getObject();
//expecting genericTask to be of Type FileTask if called with one Key
//or of Type DataTask if called with another
}
}
}

Resources