Unused Bean Get Called in Spring App - java

I have some strange behavior in my Spring App, here is my Java Spring Boot application structure:
in package com.somethingsomething.packageA, I have 2 files
First is ParentA.java
package com.somethingsomething.packageA;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class ParentA {
#Autowired
private ChildA childA;
public ChildA getChildA() {
return childA;
}
#PostConstruct
public void ParentAPostConstruct() {
System.out.println("ParentA PostConstruct were called");
}
}
Second is ChildA.java
package com.somethingsomething.packageA;
import org.springframework.stereotype.Component;
#Component
public class ChildA {
public ChildA() {
System.out.println("ChildA were called");
}
}
and then under package com.somethingsomething.packageB, I also have two similar files.
First is ParentB.java
package com.somethingsomething.packageB;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class ParentB {
#Autowired
private ChildB childB;
public ChildB getChildB() {
return childB;
}
#PostConstruct
public void ParentBPostConstruct() {
System.out.println("ParentB PostConstruct were called");
}
}
Second is ChildB.java
package com.somethingsomething.packageB;
import org.springframework.stereotype.Component;
#Component
public class ChildB {
public ChildB() {
System.out.println("ChildB were called");
}
}
Both of packageA and packageB have similar structure. Then under com.somethingsomething I have two main function for both package:
ForPackageA.java
package com.somethingsomething;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.somethingsomething.packageA.ParentA;
#SpringBootApplication
public class ForPackageA {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(ForPackageA.class, args);
ParentA parentA =
applicationContext.getBean(ParentA.class);
System.out.println(parentA);
System.out.println(parentA.getChildA());
}
}
and ForPackageB.java
package com.somethingsomething;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import com.somethingsomething.packageB.ParentB;
#SpringBootApplication
public class ForPackageB {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(ForPackageB.class, args);
ParentB parentB =
applicationContext.getBean(ParentB.class);
System.out.println(parentB);
System.out.println(parentB.getChildB());
}
}
When i run ForPackageA.java, this will appear in log:
ChildA were called
ParentA PostConstruct were called
ChildB were called
ParentB PostConstruct were called
com.somethingsomething.packageA.ParentA#88d6f9b
com.somethingsomething.packageA.ChildA#47d93e0d
The ChildB were called and ParentB PostConstruct were called were not suppose to be there, since ForPackageA.java doesn't depend on those beans. Why is this happening?
The same thing also happening when i run ForPackageB.java, which will log following:
ChildA were called
ParentA PostConstruct were called
ChildB were called
ParentB PostConstruct were called
com.somethingsomething.packageB.ParentB#610db97e
com.somethingsomething.packageB.ChildB#6f0628de
But in this case ChildA were called and ParentA PostConstruct were called were not suppose to be logged.
So, why is this peculiar behavior is happening? Is this default behavior of Spring?
Edit:
If let say i add following line
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) to ParentB.java
(after #Component)
It will not make ParentB PostConstruct were called appear when i run
ForPackageA.java.
and also, if i add those line to ChildB.java it will also not
make ChildB were called appear when i run ForPackageA.java
Why are the beans act differently in prototype mode, i.e. they are not getting called like when they are in singleton mode?

Yes, this is default behaviour of Spring. At application start all Beans with a #Component annotation get created, no matter if you use them or not.
The call applicationContext.getBean(ParentB.class) then simply returns the already created Bean.
To answer your edit:
Spring Beans are by default Singletons, so theres always only one instance of the bean per applicationContext. This is Inversion of Control, meaning that Spring handles object instantiation, not you.
Beans with the Prototype scope can have multiple object instances and can, in a way, be instantiated by you. (By calling applicationContext.getBean(ParentA.class)). This is similar to doing something like ParentA a = new ParentA().
I suggest you read this to get a deeper understanding of scopes.

The bean object creation doesn't depends on which object you are trying to get. Whenever spring application gets launched, its all components object are created automatically and you are getting already created component object using applicationContext.getBean(componentClass) method. Hope this will help you to understand why you are getting logs for every object.

Why is this happening?
When you start your Spring app the ApplicationContext is initialised by component scanning your application and registering all Spring annotated beans in the context. This allows them to be injected as required.
Is this default behaviour of Spring?
Yes. You can change this behaviour by configuring the component scanning to only look at specified packages if you wish (although the use cases for this are few and far between).

Related

How to fix "Field ... required a bean of type ... that could not be found" exception Spring Boot

I am working with spring boot tutorial from javabrains and everything was clear until putting CrudRepository inside project. Below you can find my main class:
package pl.springBootStarter.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class CourseApiDataApplication
{
public static void main(String args[])
{
SpringApplication.run(CourseApiDataApplication.class,args);
}
}
Service class:
package pl.springBootStarter.app.topic;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
#Service
public class TopicService
{
#Autowired
private TopicRepository topicRepository;
private List<Topic> topics = new ArrayList<>(Arrays.asList(
new Topic("spring","spring framework", "spring framework dectription"),
new Topic("sprin","spring framework", "spring framework dectription"),
new Topic("spri","spring framework", "spring framework dectription")));
public List<Topic> getAllTopics()
{
// return topics;
List<Topic> t = new ArrayList<Topic>();
topicRepository.findAll().forEach(t::add);
return t;
}
public Topic getTopic (String id)
{
return topics.stream().filter( t -> t.getId().equals(id)).findFirst().get();
}
public void addTopic(Topic topic) {
topicRepository.save(topic);
}
public void updateTopic(Topic topic, String id)
{
topics.set(topics.indexOf(topics.stream().filter(t-> t.getId().equals(id)).findFirst().get()), topic);
}
public void deleteTopic(String id)
{
topics.remove(topics.stream().filter(t -> t.getId().equals(id)).findFirst().get());
}
}
And Repository interface:
package pl.springBootStarter.app.topic;
import org.springframework.data.repository.CrudRepository;
public interface TopicRepository extends CrudRepository<Topic,String>
{
}
When I run the app there is a problem with injection of TopicRepository into topicRepository field in TopicService class. I get following error:
Error starting ApplicationContext. To display the conditions report re- run your application with 'debug' enabled.
2019-05-01 10:33:52.206 ERROR 6972 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Field topicRepository in pl.springBootStarter.app.topic.TopicService required a bean of type 'pl.springBootStarter.app.topic.TopicRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
What could be the reason that Spring cannot do the autowiring?
Be sure the class is scanned by spring!
(this may help if that's the problem:
Intellij Springboot problems on startup).
Optionally you may want to annotate TopicRepository as a #Repository.
#Repository
public interface TopicRepository extends CrudRepository<Topic,String>
{
}
See a demo code here: https://github.com/lealceldeiro/repository-demo
Spring cannot inject bean because it has not been created.
You have to instruct Spring to generate implementation of declared repository interfaces by using #EnableJpaRepositories(basePackages={"pl.springBootStarter.app"}) annotation on any of your configuration classes or class annotated with #SpringBootApplication. That should fix your problem.
I got a similar message and I was missing the #Service annotation in the Service class. Simple mistake, posting in case it helps anyone else.
For anybody who was brought here by googling the generic bean error message, but who is actually trying to add a feign client to their Spring Boot application via the #FeignClient annotation on your client interface, none of the above solutions will work for you.
To fix the problem, you need to add the #EnableFeignClients annotation to your Application class, like so:
#SpringBootApplication
// ... (other pre-existing annotations) ...
#EnableFeignClients // <------- THE IMPORTANT ONE
public class Application {
I got a similar message.
the thing was my main package was com.example
and the package for other classes was com.xyz
so when I Changed the name of the package of other class to com.example.topic
i.e.
finally
The main package was com.example
and the package for the other class was com.example.topic
A simple mistake, posting in case it helps anyone else.
In my cases, the necessary configuration from org.springframework.boot.autoconfigure.jdbc. has been excluded at SpringBootApplication, causing relevant bean not added properly. Check your main application java file and see if you can find following configuration in the exclusion list
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
#SpringBootApplication(
exclude = {
DataSourceAutoConfiguration.class, // REMOVE THIS
DataSourceTransactionManagerAutoConfiguration.class, // REMOVE THIS
}
)
and remove them from exclusion list.
in service class
do:
#Autowired(required=false)

SpringBoot error : No bean named 'myController' available

I am building a basic program of "hello world" in SpringBoot
Code
MyController.java
package controllers;
import org.springframework.stereotype.Controller;
#Controller
public class MyController {
public String hello() {
System.out.println("Hello World");
return "foo";
}
}
DemoApplication.java
package di.prac;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import controllers.MyController;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext ctx=SpringApplication.run(DemoApplication.class, args);
MyController m = (MyController)ctx.getBean("myController");
m.hello();
System.out.println("*******"+Arrays.asList(ctx.getBeanDefinitionNames()));
}
}
I am using eclipse and created this project from http://start.spring.io/ without any dependencies.
I learned that Spring create the bean of MyController class with name myController ,but Spring is not able to find myController bean
ERROR
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'myController' available at
org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:686)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1210)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at
org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089)
at di.prac.DemoApplication.main(DemoApplication.java:16)
Please find and explain the error in the Project
Place your controller under sub package of di.prac like di.prac.controllers or use #ComponentScan on your controller. By default, Spring scans the current and sub packages where your main application is present. If you want to scan other packages too, then you can specify the packages in #SpringBootApplication as an argument like.
#SpringBootApplication(scanBasePackages = {"com.xyz.controllers", "com.abc.models""})
We should avoid putting the #Configuration class in the default package (i.e. by not specifying the package at all). In this case, Spring scans all the classes in all jars in a classpath. That causes errors and the application probably doesn't start.
For your controller to be available in the context of Spring, you need to define that it is managed by the Spring container. Only the #Controller annotation is not enough, it indicates only the stereotype of your bean, as well as the annotations #Repository and #Service.
In cases where the beans have these annotations and are managed by Spring, it is because their packages that the spring is scanning to search for them has been specified programmatically or per xml. In your case, you should annotate your DemoApplication class with 2 other annotations:
#Configuration - Allows access to spring context
#ComponentScan - Packages to be scanned by Spring
#Configuration
#ComponentScan (basePackages = {"controllers"})
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext ctx=SpringApplication.run(DemoApplication.class, args);
MyController m = (MyController)ctx.getBean("myController");
m.hello();
System.out.println(Arrays.asList(ctx.getBeanDefinitionNames()));
}
}
Just encountered same problem, solution is simple.You just (me also) created package "controllers" on the wrong place. It should be created not in java folder but under folder with name of your project. Simple but deadly mistake. Your code is written perfectly fine.

Spring #Configuration behavior with #Autowired field

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.

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.

JAX-RS: How to extend Application class to scan packages?

Currently, I do something similar to
import javax.annotation.Nonnull;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
#ApplicationPath("oauth")
public class OAuthApplication extends Application {
final Set<Class<?>> classes = new HashSet<>();
#Nonnull
#Override
public Set<Class<?>> getClasses() {
classes.add(RegisterResource.class);
return Collections.unmodifiableSet(classes);
}
}
No if I add ten new Resources on the ApplicationPath, I need to do
classes.add(<ClassName>.class);
ten times, it is tedious and sometimes forgetful as well.
Does JAX-RS or RESTEasy provide the way so that I can mention the package name and classes are scanned under it?
I know Jersey has something as
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("org.foo.rest;org.bar.rest");
}
}
Reference
Any thoughts/ideas?
UPDATE
Seems we can do following in web.xml
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
Is there a specific Java equivalent?
"Is there a specific Java equivalent?"
Simply leave the class empty, meaning do not override getClasses() or getSingletons(). Per the spec - 2.3.2:
[...]
In either of the latter two cases, if both Application.getClasses and Application.getSingletons return an empty list then all root resource classes and providers packaged in the web application MUST be included in the published JAX-RS application. If either getClasses or getSingletons return a non-empty list then only those classes or singletons returned MUST be included in the published JAX-RS application.
So you can simply do
#ApplicationPath("oauth")
public class OAuthApplication extends Application {}
and your classpath will get scanned for #Path and #Provider classes. Override either method (returning a non-empty set), and only those classes will be added.
It should also be noted that public Map<String, Object> getProperties() can be safely overridden. You can use this to add arbitrary properties (even to register classes/features), as seen here, or if you need to configure a provider, you can do so in a Feature or DynamicFeature as seen here
Is a combination of all Application class, ResourceClass and #ApplicationPath
in agreement with the docs and the class ResourceConfig implements by jersey the way to do is:
#ApplicationPath(value = "/resource")
public class ApplicationREST extends ResourceConfig {
public ApplicationREST() {
//add single resources
register(SimpleResource1.class);
register(SimpleResource2.class);
...
//enable multipar feature
register(MultiPartFeature.class);
...
//enable scan package and recursive
packages(true, "com.example.rest");
}
}
I hope this helps you
It is very simple:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/webapi")
public class MyApp extends Application {
}
This will scan all the classes annotated with #Provider and #Path.

Categories

Resources