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.
Related
So I've been learning Spring for a few weeks. I am trying to make a simple project involving a Controller -> Service -> Repository -> Database pattern.
I started having this problem, but could not find the solution to it. I stumbled upon some similar problems online with the same error, but none of them gave my solution, everything seems normal in my project.
This is the error on output:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field repository in com.Library.services.LibraryService required a bean named 'entityManagerFactory' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.
This are the files of my project (file tree):
This is my code:
Main class:
package com.Library;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#EntityScan("com.Library.services")
#EnableJpaRepositories("com.Library.repositories")
public class LibraryApplication {
public static void main(String[] args) {
SpringApplication.run(LibraryApplication.class, args);
}
}
Model:
package com.Library.models.dtos;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
#Getter
#Setter
#Entity
public class BookCategory {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
private String Name;
}
Repository:
package com.Library.repositories;
import com.Library.models.dtos.BookCategory;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
#Repository
public interface BookCategoryRepository extends JpaRepository<BookCategory,Long> {
List<BookCategory> findAll();
}
Service:
package com.Library.services;
import com.Library.models.dtos.BookCategory;
import com.Library.repositories.BookCategoryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class LibraryService {
#Autowired
BookCategoryRepository repository;
public List<BookCategory> getAllCategories() {
return repository.findAll();
}
}
Controller:
package com.Library.controllers;
import com.Library.models.dtos.BookCategory;
import com.Library.services.LibraryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RestController
public class LibraryController{
#Autowired
LibraryService libraryService;
public List<BookCategory> getAllCategories() {
return libraryService.getAllCategories();
}
}
What could be the issue?
This bean definition is usually provided automatically by Spring Boot Auto-Configuration. The spring reference manual explains how to diagnose such issues:
The Spring Boot auto-configuration tries its best to “do the right thing”, but sometimes things fail, and it can be hard to tell why.
There is a really useful ConditionEvaluationReport available in any Spring Boot ApplicationContext. You can see it if you enable DEBUG logging output. If you use the spring-boot-actuator (see the Actuator chapter), there is also a conditions endpoint that renders the report in JSON. Use that endpoint to debug the application and see what features have been added (and which have not been added) by Spring Boot at runtime.
Many more questions can be answered by looking at the source code and the Javadoc. When reading the code, remember the following rules of thumb:
Look for classes called *AutoConfiguration and read their sources. Pay special attention to the #Conditional* annotations to find out what features they enable and when. Add --debug to the command line or a System property -Ddebug to get a log on the console of all the auto-configuration decisions that were made in your app. In a running application with actuator enabled, look at the conditions endpoint (/actuator/conditions or the JMX equivalent) for the same information.
In your case, a simple full text search finds that Hibernate is auto-configured by org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, which is declared as follows:
#AutoConfiguration(after = { DataSourceAutoConfiguration.class })
#ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionImplementor.class })
#EnableConfigurationProperties(JpaProperties.class)
#Import(HibernateJpaConfiguration.class)
public class HibernateJpaAutoConfiguration {
}
As you can tell from the ConditionalOnClass annotation, this configuration is only applied if your classpath contains the classes LocalContainerEntityManagerFactoryBean from spring-orm-jpa, EntityManager from the JPA spec, and SessionImplementor from the hibernate jar.
Most likely, you are missing one of these JAR files (maven dependencies), or have the wrong version of one. The ConditionEvaluationReport should tell you which, and the precise package names to check for.
This question already has answers here:
How can I #Autowire a spring bean that was created from an external jar?
(5 answers)
Closed 3 years ago.
I have a Spring Boot project and I can't get components from an external jar to be autowired. When I try to, I got a org.springframework.beans.factory.NoSuchBeanDefinitionException saying that can't find a bean with that name available.
I tried some solutions found in similar questions, like these ones:
How to autowire #service from external Jar in Spring
Spring Boot #autowired does not work, classes in different package
How can I #Autowire a spring bean that was created from an external jar?
..but still can't managed it to work.
Here is an example of what I'm trying to accomplish:
Here is boot class in the Spring Boot project spring-project-example
package com.springdi.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import com.dependency.example.DependencyBasePackageClass;
import com.dependency.example.somepackage.SomeBean;
#SpringBootApplication
#ComponentScan(basePackages = {"com.springdi.example"}, basePackageClasses = DependencyBasePackageClass.class)
public class SpringProjectExampleApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringProjectExampleApplication.class, args);
String beanName = SomeBean.class.getName();
System.out.printf("%s can be autowired: %s\n", beanName, String.valueOf(context.containsBean(beanName)).toUpperCase());
}
}
It's just a simple Spring Boot project checking if it is possible to autowire a component present in the dependency jar.
Here is the component in the jar (dependency-example-1.0.0.jar)
package com.dependency.example.somepackage;
import org.springframework.stereotype.Component;
#Component
public class SomeBean {
public void someMethod() {
System.out.println("Some process...");
}
}
And here is the base package class of this same jar
package com.dependency.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Just a class to serve as the root for component
* scanning in "com.dependency.example" and its sub-packages
*/
#Configuration
#ComponentScan
public class DependencyBasePackageClass {
}
I've already tried #Import(DependencyBasePackageClass.class) in SpringProjectExampleApplication and #ComponentScan with basePackages and basePackageClasses, but no success.
I also tried using #SpringBootApplication(scanBasePackageClasses = {SpringProjectExampleApplication.class, DependencyBasePackageClass.class})
and the not type safe #SpringBootApplication(scanBasePackages = {"com.springdi.example", "com.dependency.example"}).
#Configuration #ComponentScan({"com.dependency.example"}) also fails, context.containsBean("com.dependency.example.somepackage.SomeBean") still returns false.
This jar is included in classpath and in the pom.xml as a dependency
<dependencies>
<!-- other dependencies -->
<dependency>
<groupId>com.rbaggio</groupId>
<artifactId>dependency-example</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/dependency-example-1.0.0.jar</systemPath>
</dependency>
</dependencies>
Could it be the location of the jar, the way it is included or some extra configuration needed?
I'd appreciate any help! Thanks in advance.
Okey some basic things, you have mixed up your packages a bit.
#SpringBootApplication will scan all classes in packages below the class this is annotated on. This annotation is an alias for #EnableAutoConfiguration, #Configuration and #ComponentScan means that #ComponentScan(basePackages = {"com.springdi.example"}, basePackageClasses = DependencyBasePackageClass.class) is not needed.
com.springdi.example // class with #SpringBootApplication annotation
|
|
|
com.springdi.example.* // Will find all #Service, #Component, #Configuration
// in subpackages below the #SpringBootApplication
// annotation
You can read more about the annotation here SpringBootApplication
Since your other annotated classes are NOT in the same package structure as the #SpringBootApplication you need to define all the places you want to scan for annotations.
#SpringBootApplication(scanBasePackages = {"com.springdi.example", "com.dependency.example"})
will probably include all the packages that you want to scan through.
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.
I have a hard time loading properties from my properties file. I'm trying to load a property into my configuration class which is listed below.
package dk.fitfit.budget.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
#Configuration
#PropertySource("classpath:application.properties")
public class ApplicationConfig {
private static final Logger logger = LoggerFactory.getLogger(ApplicationConfig.class);
#Autowired
private Environment env;
#Value("${snot:test}")
private String snot;
public ApplicationConfig() {
logger.info("Application config loaded!"); // Displays as expected
logger.info("snot: {}", snot); // snot: null
logger.info("env: {}", env); // env: null
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
If I change the name of the file (application.properties) or "#PropertySource("classpath:application.properties")" I get an error about the file not being present. So clearly it's being loaded (to some extend anyway).
I was at least expecting to see the default string "test" injected into the variable snot. But not even that is done. I'm not able to autowire Environment either... not sure if there is a relation.
The content of my application.properties is as below.
snot=snog
My application.properties file is placed in src/main/resources/.
Anyone got a clue about what I'm doing wrong?
The purpose of a constructor is to initialize a class instance. You have to construct and initialize the class instance before Spring can do anything to it. As such, you cannot expect the env and snot fields to have anything other than the default null value within the constructor.
Spring (and all dependency-injection tools) can't inject fields before the instance is created with new, which is one reason why constructor injection should be preferred to field injection. Generally, you can use #Value on constructor parameters, but there are certain issues with #Configuration classes that make constructor injection impractical.
However, there's a solution meant for exactly this situation: #PostConstruct. This method will be called after the DI framework has done all its magic to the bean:
#PostConstruct
public void log() {
logger.info("Application config loaded!"); // Displays as expected
logger.info("snot: {}", snot); // snot: null
logger.info("env: {}", env); // env: null
}
I'm #Autowireing a org.springframework.core.io.ResourceLoader into one of my #Service classes.
During the tests, I'd like to get access to an instance of a ResourceLoader, so that it can be injected into the service being tested. What's the best way to get a fully functional instance of ResourceLoader during your tests?
If that's not possible, is there an alternative to ResourceLoader? Essentially, I need to have my service read some static files from the project.
Update:
Started using #RunWith(SpringJUnit4ClassRunner.class) + #ContextConfiguration on my test; however, the ResourceLoader that is now being injected via #Autowire into my service behaves differently than usual (ie. when it's not in the test context). In the test, ResourceLoader#getResource returns a Resource pointing to bad relative path, rather than the proper absolute path which appears during a regular execution.
Some more information discovered while debugging:
During tests, the #Autowired resourceLoader is an instanceof org.springframework.context.support.GenericApplicationContext
During regular execution, resourceLoader is an instance of org.springframework.web.context.support.XmlWebApplicationContext
You can use a DefaultResourceLoader. It handles URL and classpath resources which should be enough for simple tests.
DefaultResourceLoader doesn't need any special setup. Just create a new instance and pass it into your test.
What kind of test do you want to write?
If it's a unit test, you should probably mock ResourceLoader and inject that mock into the service instance. (Use mockito for example)
If it's an integration test, you would be better off using the Spring TestContext framework. Create a spring context that contains all components needed for the test, then annotate your test class with #RunWith(SpringJUnit4ClassRunner.class) +
#ContextConfiguration, which will make it possible to autowire fully configured beans (e.g. the service instance to be tested) in the test class.
I suppose you have your service defined as something like this:
public class ResourceService {
#Autowired
ResourceLoader loader;
}
Now, when you write your test for ResourceService :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-config.xml" })
public class ResourceServiceTest {
#Autowired
ResourceService resourceService;
#Test
public void test() {
...
}
}
The Spring TestContext Framework configures instances of your test classes via Dependency Injection. So, when you autowire ResourceService in your test class, Spring will inject the autowired ResourceLoader property into ResourceService.
ENVIRONMENT:
Java: 17
Springboot: 2.6.2
Junit-jupiter:5.8.2
Generally integration tests will need to start up a container to execute the test cases. This can be achieved using the following code:
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ResourceLoader;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.jupiter.api.Assertions.assertTrue;
#RunWith(SpringRunner.class)
#SpringBootTest
#TestPropertySource(locations = "classpath:application-integration-test.properties")
class TemplateKeyValidatorIT {
#Autowired
ResourceLoader resourceLoader;
#Autowired
private ResourceLoaderService resourceLoaderService;
#Test
void testResourceLoading() {
// Given
String fileName = "myTestFile.txt";
// When
File file = resourceLoaderService.load(fileName);
// Then
assertTrue(file.exists());
}
}
#RunWith(SpringRunner.class): SpringRunner is the base Spring framework Runner. It extends SpringJUnit4ClassRunner, but it’s just an alias to that class.
#SpringBootTest: Annotation is needed to bootstrap the entire container; it creates an ApplicationContext that will be used by the integration tests.
#TestPropertySource(locations = "classpath:application-integration-test.properties"): Loads integration specific application properties.
Note: this will boot strap the real application context but you can separate test configuration class using:
#TestConfiguration
public class MyTestContextConfiguration {
#Bean
public ResourceLoaderService resourceLoaderService() {
return new ResourceLoaderService() {
// implement methods
};
}
}
And this can then be impoerted by your test class using:
#Import(MyTestContextConfiguration.class)