I have a notification library (written in Spring Boot, gradle, Java) which has a Service class (an interface) and an Implementation class (which extends the Service class and overrides the methods).
Service interface:
public interface NotificationService {
void sendNotification(String title, String message, List<String> ids);
Implementation class:
#Component
public class NotificationServiceImpl implements NotificationService {
private String notificationServiceURL;
public NotificationServiceImpl(#Value("${notificationService.url}") String notificationServiceURL) {
this.notificationServiceURL = notificationServiceURL;
}
public void sendNotification(String title, String message, List<String> employeeIds) {
// Some functionality
}
}
Application.yaml
notificationService:
url: "someURL"
I have packaged this library as a JAR and imported in some project which is also Spring boot project.
Now, in the main class, I added Component Scan for the path where the imported library classes are:
#ComponentScan({"com.abchealth.xyz.red", "com.abchealth.xyz.red.service", "com.abchealth.xyz.red.service.serviceImpl"})
public class SecondProject {
public static void main(String[] args) {
SpringApplication.run(SecondProject.class, args);
}
}
Then in one of the inner classes, I created an object of NotificationService
#Slf4j
#Component
#StepScope
public class SomeInnerClass implements Tasklet {
#Autowired
NotificationService notificationService;
// some code
When I run the application, it gives me this error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'notificationServiceImpl' defined in URL [jar:file:/Users/usera/.gradle/caches/modules-2/files-2.1/com.abchealth.xyz.red/notification-lib/1.0.0-12/f973790603b7c261b2f6a693e83ca3e8f3f021de/notification-lib-1.0.0-12-scan.jar!/com/abchealth/xyz/red/service/serviceImpl/NotificationServiceImpl.class]: Unexpected exception during bean creation; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'notificationService.url' in value "${notificationService.url}"
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:529) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]
Note: If I test this directly in Notification library by creating an object of NotificationService, it works just fine! The issue is when it is being imported in other project.
You need add notificationService.url property to your SecondProject's application.yml
Related
I have a persistance interface that implements MongoRepository interface that looks as follows:
package org.prithvidiamond1.DB.Repositories;
import org.prithvidiamond1.DB.Models.SomeModel;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface ServerRepository extends MongoRepository<SomeModel, String> {
}
Despite this, I keep getting the following error:
Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'botApplication':
Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'org.prithvidiamond1.DB.Repositories.ServerRepository' available: expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations: {}
PPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in org.prithvidiamond1.BotApplication required a bean of type 'org.prithvidiamond1.DB.Repositories.ServerRepository' that could not be found.
Action:
Consider defining a bean of type 'org.prithvidiamond1.DB.Repositories.ServerRepository' in your configuration.
I have tried many solutions (custom #ComponentScan, using #Service instead of #Component, etc.) I found on the internet but none could help me solve the problem, can someone explain to me what is wrong and how I should fix this?
Note: The directory structure is as follows (this is not the full directory structure, but I think this should be enough to get an idea):
org.prithvidiamond1
|
+--BotApplication.java
|
+--DB
|
+--Repository
|
+--ServerRepository.java
BotApplication.java looks as follows:
package org.prithvidiamond1;
import org.jc.api.Api;
import org.jc.api.ApiBuilder;
import org.jc.api.entity.server.Server;
import org.prithvidiamond1.DB.Models.Server;
import org.prithvidiamond1.DB.Repositories.ServerRepository;
import org.slf4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.Collection;
#Component
public class BotApplication {
private final Api api;
private final ServerRepository serverRepository;
public BotApplication(ServerRepository serverRepository, Logger logger){
String botToken = System.getenv().get("BOT_TOKEN");
this.api = new ApiBuilder();
this.serverRepository = serverRepository;
appRuntime(logger);
}
public void appRuntime(Logger logger){
logger.info("Bot has started!");
// Handling server entries in the database
if (this.serverRepository.findAll().isEmpty()) {
logger.trace("server data repository empty, initializing data repository...");
Collection<Server> servers = api.getServers();
for (Server server : servers) {
this.serverRepository.save(new Server(String.valueOf(server.getId())));
}
logger.trace("server data repository initialized");
}
}
#Bean
public Api getApi() {
return this.api;
}
}
Edit: Here is a link to a repository with all the code: https://github.com/prithvidiamond1/DiamondBot/tree/springboot-restructure
The problem is that you are using the primary source class(BotApplication.class) for executing custom code during start-up.
public static void main(String[] args) {
ApplicationContext AppContext = SpringApplication.run(BotApplication.class, args);
}
public BotApplication(DiscordServerRepository serverRepository, Logger logger){
...
appRuntime(logger);
}
It is not a good place for using repositories or any startup code.
In the SpringApplication.run method you need to pass only classes which provide beans definitions. These are #Configuration classes.
See the best practices Running code after Spring Boot starts
I describe one of the best solutions: ApplicationRunner
BotApplication class must implement ApplicationRunner interface. The interface is used to indicate that a bean should run when it is contained within a SpringApplication.
Execute all startup code at the run method.
#Component
public class BotApplication implements ApplicationRunner {
private final DiscordApi api;
private final DiscordServerRepository serverRepository;
private final Logger logger;
public BotApplication(DiscordServerRepository serverRepository, Logger logger){
String botToken = System.getenv().get("BOT_TOKEN");
this.logger = logger;
this.serverRepository = serverRepository;
this.api = new DiscordApiBuilder()
.setToken(botToken)
.setAllIntents()
.setWaitForServersOnStartup(true)
.setWaitForUsersOnStartup(true)
.login().exceptionally(exception -> { // Error message for any failed actions from the above
logger.error("Error setting up DiscordApi instance!");
logger.error(exception.getMessage());
return null;
})
.join();
}
public void appRuntime(Logger logger){
logger.info("Bot has started!");
// Handling server entries in the database
if (this.serverRepository.findAll().isEmpty()) {
logger.trace("Bot server data repository empty, initializing data repository...");
Collection<Server> servers = api.getServers();
for (Server server : servers) {
this.serverRepository.save(new DiscordServer(String.valueOf(server.getId()), Main.defaultGuildPrefix));
}
logger.trace("Bot server data repository initialized");
}
}
#Bean
public DiscordApi getApi() {
return this.api;
}
#Override
public void run(ApplicationArguments args) throws Exception {
appRuntime(logger);
}
}
Pass Main.class to the SpringApplication.run
#SpringBootApplication
#EnableMongoRepositories
public class Main {
public static void main(String[] args) {
ApplicationContext AppContext = SpringApplication.run(Main.class, args);
}
}
use annotation #Autowired with the constructor of BotApplication class as shown below -
#Autowired
public BotApplication(ServerRepository serverRepository, Logger logger){
String botToken = System.getenv().get("BOT_TOKEN");
this.api = new ApiBuilder();
this.serverRepository = serverRepository;
appRuntime(logger);
}
You need to use #EnableMongoRepositories on top of your main application class to enable scanning of mongo repository beans.
You have Created Repository bean (ServerRepository)within your application.
But in your BotApplication component (which itself is a bean), you are not telling spring to inject dependency of Repository bean (i.e. Dependancy Injection).
Such a dependancy injection can be achieved by constructor , field-based or setter based methodologies.
You can either remove ServerRepository serverRepository from public BotApplication(ServerRepository serverRepository, Logger logger) constructor & just use :
#Autowired
private final ServerRepository serverRepository;
Or as other answer suggested, use #Autowired in Constructor itself and remove field ServerRepository serverRepository :
`
#Autowired
public BotApplication(ServerRepository serverRepository, Logger logger)
`
Please note, here, this.serverRepository = serverRepository; is also not required in constructor as dependency injection will take care of this.
In my SpringBoot app, I have Autowired an configObject in the class that implements EnvironmentPostProcessor.
The injected class reads data from a different source on startup as this is required for the app to work.
But upon starting the application, the configObject is coming off as Null.
#SpringBootApplication
#EnableEncryptableProperties
#EnableConfigurationProperties
#EnableCaching
#Slf4j
public class SBApplication {
public static void main(String[] args) {
SpringApplication.run(SBApplication.class, args);
}
}
And the AppEnvironmentPostProcessor class where the Autowired object is called. This class is configured as org.springframework.boot.env.EnvironmentPostProcessor in spring.factories. The class gets called on start up.
#Slf4j
public class AppEnvironmentPostProcessor implements
EnvironmentPostProcessor, Ordered {
#Autowired
KeysConfig keysConfig;
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// keysConfig is null
String key = keysConfig.getSecretKeyMap().get("key12");
}
}
And in the KeysConfig class
#Component
public final class KeysConfig {
public Map getSecretKeyMap() {
//Returns key map
}
}
I am using Intellij Ultimate. How can I debug and resolve this?
EnvironmentPostProcessors are created before the application context has been created and, therefore, before dependency injection is possible. This means that #Autowired won’t work.
You’ll have to update your implementation to create an instance of KeysConfig itself, or to use a different approach that mimics whatever KeysConfig currently does.
I am creating a REST service in Spring boot. I am creating a bean in config class and trying to use in service class by auto wiring, but I am always getting null, I have tried in constructor injection as well but not working. Below is the code,
Main app
#SpringBootApplication
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
REST controller
#RestController
#RequestMapping("/v1")
public class RestController {
#Autowired
private Service service;
Service
#Service
public class ServiceImpl implements Service {
//This is the bean
#Autowired
private Record record;
public ServiceImpl() { //-----------------> tried injecting in constructor as well
System.out.println(record); //-------------------> null
}
Config class
#Configuration
public class AppConfig {
#Bean
public Record record() {
return new Record("test");
}
}
I noted whenever I remove the record() from config class I get below error
required a bean of type 'com.ns.service.Record' that could not be found
And after adding the method the error is not reported but null is returned, which indirectly means record() is considered as returning the required bean.
I can't find what I am doing wrong please advise.
Project folder structure
I think you're doing everything right conceptually
Spring creates an object first and only after that injects the values (technically done in bean post processors):
So try this:
#Service
public class ServiceImpl implements Service {
//This is the bean
#Autowired
private Record record;
public ServiceImpl() {
// here the record is null - not injected yet
System.out.println(record);
}
#PostConstruct
public void checkThisOut() {
// here print the record
}
You say you've tried constructor injection as well - it should work because spring has to inject something into the constructor of a bean (ServiceImpl) or fail. Please show the code snippet
One this that might be wrong in some level (although it doesn't sound like this from your description) is that you have to put all the #Configuration/#Service annotated classes in the package that is the same or underneath the package where you've created the main class annotated with #SpringBootApplication annotation. It instructs spring boot where to look for the beans.
So make sure your classes obey this rule...
I have an interface like so:
public interface Animal {
void setName(String animal);
String getName();
}
and I have a Class that implements the interface:
#Component
public class Dog implements Animal {
private String name;
public void setName(String name) {
this.name= name;
}
public String getName() {
return this.name;
}
}
In another class (ProcessAnimal), I AutoWire the interface:
public class ProcessAnimal {
#Autowired
public Animal animal;
public void processAnimals() {
animal.setName("Fido");
}
}
I only have one class that implements Animal so this should work, however, I get a NullPointerException when it hits the animal.setName("Fido"); line. IntelliJ is complaining that Autowired members must be defined in valid Spring bean (#Component|#Service...) which I have... I don't understand what I'm doing wrong. I've tried to add a #Qualifier, but still it didn't work and it shouldn't be necessary since I only have one implementation.
-java
-com.example.com.AnimalProcessing
-Animal
-Animal.java
-Dog.java
-ProcessAnimal.java
-AnimalProcessingApplication.java
AnimalProcessingApplication.java
#SpringBootApplication
public class AnimalProcessingApplication {
public static void main(String[] args) {
SpringApplication.run(AnimalProcessingApplication.class, args);
run();
}
public static void run() {
ProcessAnimal processAnimal = new ProcessAnimal();
processAnimal.processAnimals();
}
}
AnimalProcessingApplication class should be one level above all other classes.
Also you are using new for creation of object instead of using Dependency Injection (autowiring).
Replace below -
ProcessAnimal processAnimal = new ProcessAnimal();
with
#Autowired
ProcessAnimal processAnimal;
Also make sure that ProcessAnimal is a bean and Animal is injected in this class using autowiring.
Animal Processing Application.java must be on root folder of all classes.
Then all components in child folders are recognized automatically.
Update:
Create a config class with #Bean method to create an instance with a Dog. Also then you can get rid of the #Component annotation of the class.
The problem here is the constructor String name which cannot be injected.
Update 2:
Don't create the instances by yourself. Let spring container create them. Remove the run method.
Following are to be done to make this program work.
1.ProcessAnimal should be made a component . Annotating the class with #Component will mark the class to be autodetected during component scan.
#Component
public class ProcessAnimal {
#Autowired
public Animal animal;
public void processAnimals() {
animal.setName("Fido");
}
}
Obtain the ProcessAnimal class from the application context. The spring will prepare the ProcessAnimal bean with all its dependencies set.
You may do this in multiple ways and following is one of those
#Component
public class CheckRedRunner implements ApplicationRunner {
#Autowired
ProcessAnimal process;
#Override
public void run(ApplicationArguments args) throws Exception {
process.processAnimals();
}
}
A bean implementing ApplicationRunner will be run when the application starts.
or else
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(AnimalProcessingApplication.class, args);
ProcessAnimal process = ctx.getBean(ProcessAnimal.class);
process.processAnimals();
}
Couple of observations
the package names by convention uses lower case letters
example : com.example.process.entity
Please go through the official documentation to learn the expected way of writing Spring boot application.
I have the below bean defined as part a A.jar
package abc;
#Component
public class ParentInterceptor implements ClientInterceptor {
}
I have created another bean in a different project under a different package by extending ParentInerceptor
package xyz;
#Component
public class ChildInterceptor extends ParentInterceptor {
}
In my SpringBoot app , I have a bean defined similar to below
#ComponentScan({"abc","xyz"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
#Bean
public Data dataRepo(ParentInterceptor p){
}
}
When I run the main method , I am expecting to see NoUniqueBeanDefinitionException as 2 beans are of same type. However I see both the beans are loaded and ParentInterceptor is being used. Is there a reason why the error is not being thrown?
EDIT
However when i did the below , I was able to see the error being thrown. However I am still unable to understand why an error wasn't thrown in the case listed above.
package xyz;
#Component
public class ChildInterceptor1 extends ChildInterceptor {
}
Application Class:
#ComponentScan({"abc","xyz"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
#Bean
public Data dataRepo(ChildInterceptor p){
}
}
EDIT 2
I also tried to check if the child bean indeed extending the parent using the code below -
ctx.getBeansOfType(ParentInterceptor.class)
This returns ParentInterceptor & ChildInterceptor. So I am not really sure why Spring is not returning the error!
In your first example (Parent and Child), i have the correct behaviour, which is the NoUniqueBeanDefinitionException thrown.
If you want to specify which bean to be injected, you can use the #Qualifier annotation:
#Bean
public Data dataRepo(#Qualifier("parentInterceptor") ParentInterceptor p) {
}
Optionally, you can give a name to a component:
#Component("foo")
and change the #Qualifier accordingly.