Spring #Configuration behavior with #Autowired field - java

I was expecting following code to throw some kind of Initialization or Circular dependency exception but it seems to work. I can #Autowire SomeOtherBean in other classes and I checked that it wasn't null.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MyConfig {
#Autowired
private MyBean myBean;
#Bean
public MyBean createMyBean() {
return new MyBean();
}
#Bean
public SomeOtherBean createSomeOtherBean() {
return new SomeOtherBean(this.myBean);
}
}
3.4.1.3 Dependency resolution process says,
You can generally trust Spring to do the right thing. It detects configuration problems, such as references to non-existent beans and circular dependencies, at container load-time. Spring sets properties and resolves dependencies as late as possible, when the bean is actually created. This means that a Spring container which has loaded correctly can later generate an exception when you request an object if there is a problem creating that object or one of its dependencies.
I'm not sure how this applies to my code. Can someone help me understand this better?
Thanks in advance.

Related

Parameter 0 of constructor in required a bean of type 'java.lang.String' that could not be found

I am working on spring batch with spring boot 2.X application, actually its existing code i am checked out from git. While running the application it fails due to below error only for me and same code is working for others.
s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'inputItemReader' defined in file [C:\Users\XYZ\git\main\batch\CBatchProcessing\target\classes\com\main\batchprocessing\batch\reader\InputItemReader.class]: Unsatisfied dependency expressed through **constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.lang.String' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations**: {}
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-10-16 23:23:37.411 ERROR 2384 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
**Parameter 0 of constructor in com.main.batchprocessing.batch.reader.InputItemReader required a bean of type 'java.lang.String' that could not be found.**
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.
I have checked below
All Spring components are correctly annotated with #Component, #Service, #Controller,#Repository, etc...
#ComponentScan & #EnableAutoCOnfiguration is also provided.
Tried giving "java.lang.String" in declarations.
Code:
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.JsonLineMapper;
import
org.springframework.batch.item.file.separator.JsonRecordSeparatorPolicy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Component;
#Component
public class InputItemReader extends FlatFileItemReader<Map<String,
Object>> implements StepExecutionListener {
#Autowired
private InputFileHeaderValidator inputFileHeaderValidator;
#Autowired
private FileAuditService fileAuditService;
private final Logger log =
LoggerFactory.getLogger(InputItemReader.class);
private java.lang.String inputFilePath;
public InputItemReader(String inputFilePath) {
setLineMapper(new JsonLineMapper());
setRecordSeparatorPolicy(new JsonRecordSeparatorPolicy());
setResource(new FileSystemResource(inputFilePath));
this.inputFilePath = inputFilePath;
}
}
Since you do not provide the public default constructor and you added your own non-default constructor the instantiation will fail. I would suggest you to define the input file path as property like #Value("${inputFilePath}").
If you need further initialization in your bean define a void method and annotate it with #PostConstruct and do the initialization within.
Add a public default constructor in your class. For example.
public User() {
}
Make sure you are using spring-boot-starter-data-jpa
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
You defined something like this:
#Component
public class InputItemReader{
public InputItemReader(String input){
...
}
}
The name of your class suggest that your object is not a bean, just a simple object. You should try to use it in classic way:
new InputItemReader(myString);
or to have a static method to process the input String.
Explanation: Spring IoC container will try to instantiate a new InputItemReader object like this :
new InputItemReader( -- WHAT TO PUT HERE? --)
and will fail to call your constructor, because it will not know what you do actually expect and input string.
UPDATE:
Your problem can be solved by removing #Component annotation and defining the bean in a configuration like this:
#Bean
public InputItemReader inputItemReader(InputFileHeaderValidator inputFileHeaderValidator, FileAuditService fileAuditService){
InputItemReader inputItemReader = new InputItemReader("--HERE SHOULD BE ACTUAL PATH---");
// set the required service, a cleaner approach would be to send them via constructor
inputItemReader.setFilteAuditService(fileAuditService);
inputItemReader.setInputFileHeaderValidator(inputFileHeaderValidator);
return inputItemReader;
}
For me, it was because of using #AllArgsConstructor annotation of the lombok. My code was like this:
#Service
#AllArgsConstructor
public class SampleService {
#Value("${search.page.size}")
private Integer pageSize;
private final SampleRepository sampleRepository;
And then, I removed #AllArgsConstructor and added #RequiredArgsConstructor annotation. The problem was solved.
#Service
#RequiredArgsConstructor
public class SampleService {
#Value("${search.page.size}")
private Integer pageSize;
private final BatchRepository batchRepository;
I had the same error but the error was generated by Feign Client. If you have this error using feign client you must add #EnableFeignClients on your main class:
#SpringCloudApplication
#EnableFeignClients
public class Application {
...
}
I also had the same error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field repository in com.example.controller.CampaignController required a bean of type 'com.example.data.CustomerRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.data.CustomerRepository' in your configuration.de here
I solved this issue by adding #EnableMongoRepositories annotation in the main class:
#SpringBootApplication
#EnableMongoRepositories(basePackageClasses = CustomerRepository.class)
public class CampaignAPI {
public static void main(String[] args) {
SpringApplication.run(CampaignAPI.class, args);
}
}
I was also having the same problem, for me following solution worked perfectly fine:
I had annotated my class as #AllArgsConstructor by importing import lombok.AllArgsConstructor
I had just removed this annotation and the code starts working.
Hope this may help someone.
The issue in my case was a redundant #Autowired,
I initially added a dependency using #Autowired, by eventually commented it out, however I forgot to comment the annotation, due to which the method next to #Autowired was considered as some sort of setter.
On removing the redundant annotation it is working fine.
Even after following the above solutions, if the problem still exists then please check your import statements.
In my case it was the wrong import of #service annotation.
in my case I missed the #Service annotation so once I placed it works.
in my case, the issue was way different.
#SpringBootApplication
#EnableNeo4jRepositories("com.digital.api.repositories") // <-- This was non-existent.
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Look at the #EnableNeo4jRepositories annotation. The package defined, was non-existent.
Try defining that package, and be careful that the Repository interfaces are based there.
Otherwise, Spring will not find the repositories classes that it should load up!
I was facing the same issue and it got fixed by removing Constructors from my Model class. Adding sample snippet below:
Map<String, ServiceDefinition> serviceDefinitionMapper = new HashMap<>();
A def;
B serviceCharacter;
#Autowired
public Scan(Map<String, ServiceDefinition> serviceDefinitionMapper, A def,
B serviceCharacter) {
super();
this.serviceDefinitionMapper = serviceDefinitionMapper;
this.def = def;
this.serviceCharacter = serviceCharacter;
}
Please Note: Do not keep any Constructor/#AllArgsConstructor in your Model class unless it is very much needed to be decalred.
In my case annotating fields with lombok #NonNull was causing the trouble.
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
#Service
#RequiredArgsConstructor
#Transactional
#Slf4j
public class UserServiceImp implements UserService, UserDetailsService {
....
}
I had this issue recently and it turned out I hadn't added a `#Component' annotation to the service file of my project. The effect was the class wasn't instantiated as a spring bean.
I faced same issue but with my own created interface repository that could not be found. The answer for me was that Spring 2.6.3 has bugs with Java 8. After i switched java to 11 version everything goes correct.
More info about this kind of problems i found here https://github.com/spring-projects/spring-boot/issues/6987
in my case i had this problem but it was because of i added an annotation of #Component in my Exception code which i shouldn't because we don't want it to be scanned really we're just using it to handle an exception in our service or i don't know try to delete the annotation in your built-in exception.
worked on my machine
I had this problem while testing with JUnit. It was that I forgot to create #MockBean annotation, that's why the bean was missing.
Not related to the above issue but if you have the same error of bean creation in a spring boot application and you are getting the same above error please check whether you have added the class path in #ComponentScan( of the main spring booth application launching class.
In my case , I defined a service's interface and forgot to implement it :) check this as well
For it to work I had to add this #RequiredArgsConstructor annotation to my class, and then added this #Value("${inputFilePath}") annotation to access the variable. See below:
#Component
#RequiredArgsConstructor
public class InputItemReader extends FlatFileItemReader<Map<String,
Object>> implements StepExecutionListener {
#Value("${inputFilePath}")
private inputFilePath;
//...
}
I met this error when trying to use record.
#Component
public record User(String Name, int Age){}
It turns out record needn't #Component. Fixed by simply removing it.
In my case, removing the #EnableJpaRepositories annotation from my config fixed the issue. The repositories are still wired correctly.
Before:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories
#EnableJpaAuditing
public class PersistenceConfig {
After:
#Configuration
#EnableTransactionManagement
#EnableJpaAuditing
public class PersistenceConfig {

Spring. Registering Aspect with #Bean

I need some help with creating Aspect bean.
I have module A, B... and module starter-x.
I have a couple of Aspects declared in module starter-x, and configurations for them(like spring data repository monitoring aspect with configuration annotated with #ConditionalOnClass(Repository.class) etc).
Here's how example configuration looks like
#Configuration
#ConditionalOnClass(Repository.class)
public class RepositoryMonitoringConfiguration {
#Bean
#Qualifier("RepositoryCallCounter")
public DatabaseCallCounter repositoryCounter(){
return new RepositoryCallCounter();
}
}
And i have class with #Aspect annotation(but not #Component, when i create bean with #Component not #Configuration, everything is ok)
So my question, is it possible to provide aspect bean in #Configuration class, without #Component annotation on it (I want to create cool starter with auto configuration)
It's possible, you just need to mark the configuration class like this:
#Configuration
#EnableAspectJAutoProxy
#ConditionalOnClass(Repository.class)
public class RepositoryMonitoringConfiguration {
#Bean
public DatabaseCallCounter repositoryCounter(){
return new RepositoryCallCounter();
}
}
I encountered same problem as you.
And I figured out the cause.
The solution is to exclude your Config class from #Around like following code.
package hello.hellospring.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Slf4j #Aspect
public class TimeTraceAop {
#Around("execution(* hello.hellospring..*(..)) && !target(your config class)")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
log.info("START: " + joinPoint.toString());
try {
return joinPoint.proceed();
} finally {
long finish = System.currentTimeMillis();
log.info("END: " + joinPoint + " " + (finish - start) + "ms");
}
}
}
You probably configured #Around to root package and its subpackages.
And that means #Around method is also applied to the configuration class.
So, when the spring tries to make a bean for not only AOP but also other else,
Spring finds AOP class in container that you want to make.
But, of course, there doesn't exist AOP class
if #Component is not applied to AOP class.
And Spring thinks AOP references AOP.
So Spring makes an error like following log.
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
memberController defined in file [/Users/macbook/Desktop/git/hello-spring/build/classes/java/main/hello/hellospring/controller/MemberController.class]
↓
memberService defined in class path resource [hello/hellospring/SpringConfig.class]
┌─────┐
| timeTraceAop defined in class path resource [hello/hellospring/SpringConfig.class]
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

Spring dependency injection - best design pattern of configuration

Right now i have a inherited project that is using annotation based spring dependency injection. So all classes are simply marked with #Component (or specific stereoTypes like #service, #Repository,#RestController, etc). This makes it a little hard to find where the dependency is located and i was thinking to change it so that each package has its own dependency configuration and then add each package to the #ComponentScan afterwards.
So for example if i had a package called com.mycoolpackage.login and mycoolpackage.networking
then i'd have a Spring configuration like this in first package:
#Configuration
public class LoginDIConfig {
#Bean
public LoginServiceImpl loginServiceImpl() {
return new LoginServiceImpl();
}
}
and in the second package i'd have the following:
#Configuration
public class NetworkDIConfig {
#Bean
public NetworkServiceImpl networkServiceImpl() {
return new NetworkServiceImpl();
}
}
and my#ComponentScan would look like this:
#ComponentScan(basePackages = {"com.mycoolpackage.login","com.mycoolpackage.network"})
So i have two questions about this approach.
How can i use a #Service annotation instead of bean here
Do you think this design is more easier as it tells you what your package dependencies are very easily instead of hunting them
down.
If you want to configure some been properties manually then you should go for above configuration else you should stick with exiting one.
This makes it a little hard to find where the dependency is located
#Autowire Or #Inject annotation will always lead you to dependency class.

How it works together these 2 Spring Java configuration classes?

I am studying for the Spring Core certification and I have the followind doubt with an exercice related to the beans configuration using the Java configuration way.
So I have the following RewardsConfig class that configure my beans (this class is into the application folder src/main/java):
package config;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import rewards.RewardNetwork;
import rewards.internal.RewardNetworkImpl;
import rewards.internal.account.AccountRepository;
import rewards.internal.account.JdbcAccountRepository;
import rewards.internal.restaurant.JdbcRestaurantRepository;
import rewards.internal.restaurant.RestaurantRepository;
import rewards.internal.reward.JdbcRewardRepository;
import rewards.internal.reward.RewardRepository;
#Configuration
public class RewardsConfig {
#Autowired
DataSource dataSource;
#Bean
public RewardNetwork rewardNetwork(){
return new RewardNetworkImpl(accountRepository(), restaurantRepository(), rewardRepository());
}
#Bean
public AccountRepository accountRepository(){
JdbcAccountRepository repository = new JdbcAccountRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RestaurantRepository restaurantRepository(){
JdbcRestaurantRepository repository = new JdbcRestaurantRepository();
repository.setDataSource(dataSource);
return repository;
}
#Bean
public RewardRepository rewardRepository(){
JdbcRewardRepository repository = new JdbcRewardRepository();
repository.setDataSource(dataSource);
return repository;
}
}
As you can see I declare 4 methods that are used to create 4 beans and that specify the dependency that occurs among these beans.
So I have a RewardNetwork bean that is implemented by RewardNetworkImpl class that depends from the following 3 beans: AccountRepository, RestaurantRepository and RewardRepository.
Is it the correct interpretation of the Java configuration is Spring?
Can I say for example that RewardNetwork is the declared bean and that RewardNetworkImpl its the current implementation of this bean?
All the 3beans (AccountRepository, RestaurantRepository and RewardRepository) depends by another bean dataSource that, as you can see in the previous code snippet, is declared as #Autowired:
#Autowired
DataSource dataSource;
This bean is not declared in this configuration class because it changes according to the environment (test, developt, production).
So, in my case it is declared into the unit test folder src/test/java:
package rewards;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
#Configuration
public class TestInfrastructureConfig {
/**
* Creates an in-memory "rewards" database populated
* with test data for fast testing
*/
#Bean
public DataSource dataSource(){
return
(new EmbeddedDatabaseBuilder())
.addScript("classpath:rewards/testdb/schema.sql")
.addScript("classpath:rewards/testdb/test-data.sql")
.build();
}
}
So the dataSource bean define a datasource that is valid only for the test environment (used when I perform a unit test).
Now my doubt is: I have 2 different configuration classes and the dataSource bean is not definied into the RewardsConfig configuration class that contains the 3 beans that use it. Why I can't not use the #Import annotation to use it into RewardsConfig?
Something like it:
#Import(TestInfrastructureConfig.class)
How it work exactly?
Tnx
You don't have to import beans to make them available for autowiring. #Import is used to add extra configuration classes.
You really don't want to hard-import a test configuration class, because then your production code is referring to test-only code (and, in this case, always activating it). Instead, think of your configuration class more like an abstract class: declare autowired beans, but don't worry about how they get there. The downstream (runtime) configuration will supply them, and you don't need to know how. Maybe you're supplying an in-memory H2 for testing and using Spring Cloud Connectors for actual runs, doesn't matter.

How to inject dependencies into a self-instantiated object in Spring?

Let's say we have a class:
public class MyClass {
#Autowired private AnotherBean anotherBean;
}
Then we created an object of this class (or some other framework have created the instance of this class).
MyClass obj = new MyClass();
Is it possible to still inject the dependencies? Something like:
applicationContext.injectDependencies(obj);
(I think Google Guice has something like this)
You can do this using the autowireBean() method of AutowireCapableBeanFactory. You pass it an arbitrary object, and Spring will treat it like something it created itself, and will apply the various autowiring bits and pieces.
To get hold of the AutowireCapableBeanFactory, just autowire that:
private #Autowired AutowireCapableBeanFactory beanFactory;
public void doStuff() {
MyBean obj = new MyBean();
beanFactory.autowireBean(obj);
// obj will now have its dependencies autowired.
}
You can also mark your MyClass with #Configurable annotation:
#Configurable
public class MyClass {
#Autowired private AnotherClass instance
}
Then at creation time it will automatically inject its dependencies. You also should have <context:spring-configured/> in your application context xml.
Just got the same need and in my case it was already the logic inside non Spring manageable java class which had access to ApplicationContext. Inspired by scaffman.
Solved by:
AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(manuallyCreatedInstance);
I used a different approach. I had spring loaded beans that I wanted to call from my extended classes of a third-party library that created its own threads.
I used approach I found here https://confluence.jaytaala.com/display/TKB/Super+simple+approach+to+accessing+Spring+beans+from+non-Spring+managed+classes+and+POJOs
In the non-managed class:
{
[...]
SomeBean bc = (SomeBean) SpringContext.getBean(SomeBean.class);
[...]
bc.someMethod(...)
}
And then as a helper class in the main application:
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class SpringContext implements ApplicationContextAware
{
private static ApplicationContext context;
public static <T extends Object> T getBean(Class<T> beanClass)
{
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException
{
SpringContext.context = context;
}
}
I wanted to share my solution that follows the #Configurable approach as briefly mentioned in #glaz666 answer because
The answer by #skaffman is nearly 10 years old, and that does not mean not good enough or does not work
The answer by #glaz666 is brief and didn't really help me solve my problem but, did point me in the right direction
My setup
Spring Boot 2.0.3 with Spring Neo4j & Aop starts (which is irrelevant anyway)
Instantiate a bean when Spring Boot is ready using #Configurable approach (using ApplicationRunner)
Gradle & Eclipse
Steps
I needed to follow the steps below in order to get it working
The #Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false) to be placed on top of your Bean that is to be manually instantiated. In my case the Bean that is to be manually instantiated have #Autowired services hence, the props to above annotation.
Annotate the Spring Boot's main XXXApplicaiton.java (or the file that is annotated with #SpringBootApplication) with the #EnableSpringConfigured and #EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
Add the dependencies in your build file (i.e. build.gradle or pom.xml depending on which one you use) compile('org.springframework.boot:spring-boot-starter-aop') and compile('org.springframework:spring-aspects:5.0.7.RELEASE')
New+up your Bean that is annotated with #Configurable anywhere and its dependencies should be autowired.
*In regards to point #3 above, I am aware that the org.springframework.boot:spring-boot-starter-aop transitively pulls the spring-aop (as shown here mavencentral) but, in my case the Eclipse failed to resolve the #EnableSpringConfigured annotations hence, why I explicitly added the spring-aop dependency in addition to the starter. Should you face the same issue, just declare the dependency or go on adventure of figuring out
Is there a version conflict
Why the org.springframework.context.annotation.aspect.* is not available
Is your IDE setup properly
Etc etc.
This worked for me:
#Configuration
public class AppConfig {
#Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
See more information: https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/creating-bean-definitions.html
Found the following way useful for my use case. Sharing here for reference, credit goes to the blogger entirely. This creates a static field and populates that from Spring and then provides a public static method which returns the field populated above.
https://sultanov.dev/blog/access-spring-beans-from-unmanaged-objects/

Categories

Resources