#Repository
public interface MyRepository extends CrudRepository<Custom, Long> {}
#Service
#RequiredArgsConstructor
public class MyServiceImpl implements MyServiceInterface {
private final MyRepository repository;
}
I use test configuration with instructions for bean construction for testing.
How to create #Bean for MyRepository interface?
#TestConfiguration
#EnableJpaRepositories(basePackages = "com.example.app")
public class TestBeans {
#Bean
MyServiceInterface getMyService() {
return new MyServiceImpl(getMyRepository());
}
#Bean
MyRepository getMyRepository() {
return null; // what should be here?
}
}
Just use #Autowire, spring will take care of bean creation if you had given #Repository on JPA interface.
If you look at the #Repository you noticed that this annotation is a stereotype of #Component. So when you use annotation #Repository this class will be treat as a bean (of course, if you enable jpa repositories).
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Repository {
...
}
So if you want inject you repository into your bean, you can do like this:
#Bean
MyServiceInterface getMyService(MyRepository myRepository) {
return new MyServiceImpl(myRepository);
}
Related
I'm new to Java development so sorry in advance if I'm not using the appropriate terms.
Whenever I run a test on a class that needs to save something in my database, I face a NullPointerException on the Autowired repository.
I use Junit4, here are code snippets :
application-test.properties
spring.datasource.url=jdbc:tc:mysql:8.0.29://localhost:3306/MYSERVICE
MyService.java
#Component
class MyService {
#Autowired MyRepository myRepository;
public void mainFunction() {
myRepository.saveSomething();
}
}
MyRepository.java
#Repository
public interface MyRepository extends JpaRepository<T, Long> {
void saveSomething();
}
MyServiceTest.java
public class myServiceTest extends TestConfiguration {
#Rule
public MySQLContainer mysql = new MySQLContainer();
#InjectMocks MyService myService;
#Test
public void mainFunctionTest() {
myService.mainFunction()
}
}
MyServiceTestApplication.java
#SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class
})
public class MyServiceTestApplication{
}
TestConfiguration.java
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyServiceTestApplication.class)
#ActiveProfiles("test")
public abstract class TestConfiguration {
}
When I run the test in debug mode, I can see that myRepository is null
Any help would be highly appreciated
Thanks :)
Edit 01/08/2022 : Add #Component on MyService
Edit 01/08/2022 (2) : Add MyServiceTestApplication.java and TestConfiguration.java
It seems, you forgot to annotate the class MyService with #Service.
With this annotation being made at that class, the Spring framework will recognize it:
This annotation serves as a specialization of #Component, allowing for
implementation classes to be autodetected through classpath scanning.
Given that the rest of the configuration is working, the #Autowired dependency injection mechanism will hereby provide you with an instance of the #Repository you requested, at runtime, here your test setup.
I have a pojo class which i need to inject in component. how to inject pojo object in spring?
For example
RestClass is under Module(like one microservice). Pojo Value should be injected here only. service class and pojo is different module.
#Controller
public class RestClass {
#Autowired
private ServiceClass serviceClass;
// How to apply MyConfig pojo object into ServiceClass
//like MyConfig.Builder().limit(100).build();
#PostMapping("/business")
private void callDoBusiness(){
serviceClass.doBusiness();
}
}
//// ServiceClass and Pojo class is under one module. Pojo object should not construct here. should be passed from another module
#Service
public class ServiceClass {
#Autowired
private MyConfig myConfig;
public void doBusiness(){
System.out.println(myConfig.getLimit())
}
}
#Builder
#Getter
public class MyConfig {
private int limit;
.
.
}
#Bean annotation is used in your case. For example:
Create some configuration file with #Configuration annotation. It doesn't matter where you create your Config configuration class or how you name it. You can even have multiple #Configuration classes across your project.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
class Config {
// configure your MyConfig class here as a bean
#Bean
public MyConfig myConfig() {
return MyConfig.Builder().limit(100).build();
}
}
Now you can autowire it wherever you use it as in you example.
You can use #Configuration and #Bean. Something like this
AppConfig.java
#Configuration
public class AppConfig {
#Bean
public PojoClass getBean(){ return new PojoClass();}
}
You can use #Bean
#Configuration
public class Config {
#Bean
public MyConfig MyConfig() {
return new MyConfig();
}
}
I have my custom starter. Inside it, I define a repository. How should I define it in the configuration? This is how I did the usual bean before.
#Bean
#ConditionalOnMissingBean(HelloWorldController.class)
public HelloWorldController helloWorldController() {
return new HelloWorldController();
}
Repository:
#Repository
public interface CarRepository extends JpaRepository<Car, Long> {
}
And configuration
#Configuration
#EnableJpaRepositories
public class DomainConfiguration {
}
If you use this starter, context will not see the repository bean. Because I did not declare it in the configuration.I don't know how to declare it.
This is my Spring repository interface.
#Repository
public interface WebappRepository extends CrudRepository<myModel, Long> {
}
In my controller, I can instantiate the WebappRepository even though it's an interface, because of Spring annotation magic.
public class controller{
#Autowire
WebappRepository repo;
public controller(){
}
}
But this variant, using the constructor, does not work, and rightly so, because WebappRepository is an interface.
public class controller{
WebappRepository repo;
public controller(){
this.repo = new WebappRepository();
}
}
Olivier Gierke himself advocates to avoid #Autowire fields at all costs. How can I "instantiate" the repository interface in my Spring application while avoiding the #Autowire?
Inject dependencies in constructor:
#Component
public class Controller{
WebappRepository repo;
#Autowire
public Controller(WebappRepository repo){
this.repo = repo;
}
}
If you're on Spring 4.3+ and your target class has just one constructor, you can omit autowired annotation. Spring will inject all the needed dependencies for it.
So writing just below constructor would suffice:
public controller(WebappRepository repo){
this.repo = repo;
}
Reference: https://docs.spring.io/spring/docs/4.3.x/spring-framework-reference/htmlsingle/#beans-autowired-annotation
I have a #Service annotated class which provides core functionality which I can use in all my projects:
#Service
public class MyService {}
and another one which extends it to implement project specific stuff:
#Service
public class ExtendedMyService extends MyService {}
Now I would like to configure a bean alias to be able to use #Qualifier("MyServiceAlias") when autowiring it using a property:
# MyService qualifier (default: myService)
myService.qualifier=extendedMyService
In XML it would look like:
<alias name="${myService.qualifier}" alias="MyServiceAlias" />
It is also discussed here, but I need to do it w/o XML, JavaConfig only.
Is it possible and how to realize?
There is an open Jira for this: https://jira.spring.io/browse/SPR-6736
The workaround is to use #Bean in #Configuration class:
#Configuration
public class AppConfig {
#Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
public MyService myService() {}
}
If you want to use the placeholder, another workaround is to use #Bean in a #Configuration class using #Value and the Spring applicationContext.
#Configuration
public class AppConfig {
#Autowired
private ApplicationContext context;
#Bean
public MyService myService(#Value("${myService.qualifier}") String qualifier) {
return (MyService) context.getBean(qualifier);
}
}
NB : special consideration must be taken for the placeholder bean which must be loaded at the beginning (cf javadoc)
With small amount of configuration and one ImportBeanDefinitionRegistrar you can configure bean aliases via Java configuration. You can check bean-alias library project for reference - developed for the needs of my projects. Feel free to modify and/or copy the source into your own project in case the spring version used in it does not work with your setup.
Once you have the library on your path, you declare an alias through the annotation:
#Configuration
#BeanAlias(name = "fromName", alias = "toName")
public class ExampleConfiguration {
}
That's it.
How it works is that with the annotation we import a ImportBeanDefinitionRegistrar implementation
#Import(BeanAliasBeanRegistrar.class)
public #interface BeanAlias {
}
which registers the alias in the BeanDefinitionRegistry
class BeanAliasBeanRegistrar implements ImportBeanDefinitionRegistrar, PriorityOrdered {
#Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
...
registerAlias(registry, metadata.getAnnotationAttributes(BeanAlias.class.getName()));
}
private void registerAlias(BeanDefinitionRegistry registry, Map<String, Object> attributes) {
...
registry.registerAlias(name, alias);
}
}