Why does EnableJpaRepositories registers beans twice? - spring

I have one abstract configuration:
#Configuration
#EnableJpaRepositories(basePackages = {
"x","y" }, entityManagerFactoryRef = "entityManager", transactionManagerRef = "transactionManager", repositoryBaseClass = BaseRepositoryImpl.class)
#EnableTransactionManagement
#EnableConfigurationProperties({ DataSourceProperties.class })
public abstract class AbstractBaseConfiguration {}
Then its implemented by the concrete Configuration class
#ComponentScan(basePackages = { "x", "z" })
public class BaseConfiguration extends AbstractBaseConfiguration {}
Combined in
#Configuration
#Import({ BaseConfiguration.class, RestConfiguration.class })
#ComponentScan(basePackages = { "x", "z" })
public class RestBaseConfiguration {}
So nested configurations, in the application at the end RestBaseConfiguration is used.
This was working before when using SpringBoot 2.0.X.
Now I upgraded SpringBoot to latest version (2.6.1). Suddenly I get exception:
org.springframework.beans.factory.support.BeanDefinitionOverrideException:
Invalid bean definition with name 'com.example.XRepository' defined in com.example.XRepository defined in #EnableJpaRepositories declared on AbstractBaseConfiguration
Cannot register bean definition JpaRepositoryFactoryBean defined in XRepository defined in #EnableJpaRepositories declared on AbstractBaseConfiguration
There is already JpaRepositoryFactoryBean defined in XRepository defined in #EnableJpaRepositories declared on BaseConfiguration
I guess underlying issue is because beans are being overwritten https://stackoverflow.com/a/53723731/978302.
The question is why? Based on the error message, is EnableJpaRepositories being also proxied for the abstract class and for this reason repositories will be instantiated twice? Or somehow basePackages parameter of the annotation is interfering with the scans and then scan are executed twice..

Related

ContextConfiguration(classes=...) in Test doesn't scan packages specified in the Configuration class

I have my application class defined as
#SpringBootApplication(scanBasePackages = {"com.binance.bot", "com.binance.api.client", "com.gateiobot"})
#Configuration
#EnableScheduling
public class BinancebotApplication implements CommandLineRunner {
And in my springboot test I tried to use the auto scan packages by specifying the above Application class, as:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
#EnableConfigurationProperties
public class MACDCalculationIntegrationTest {
Test fails with
Parameter 1 of constructor in com.gateiobot.macd.MACDCalculation required a bean of type 'com.binance.bot.common.Mailer' that could not be found.
Why isn't the autoscan working?

Adding #Conditional to an existing spring annotation in spring boot

I have an application which uses an existing spring annotation (#EnableResourceServer). I want this particular annotation to be enabled only when a particular property value is not false.
To do this, I created a meta-annotation and applied #ConditionalOnProperty on that :
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#ConditionalOnProperty(prefix = "custom.resource", name = "enabled", matchIfMissing = true)
#EnableResourceServer
public #interface EnableCustomResourceSecurity {
}
In my application I'm now using #EnableCustomResourceSecurity like :
#EnableCustomResourceSecurity
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
and it all works fine if the property is missing or true but when I change the property to custom.resource.enabled=false I get the following exception :
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
I tried putting this annotation in a couple of other places and noticed that when the conditional expression fails for this annotation, any annotation after this also stops getting processed.
What would be the correct way to achieve what I'm trying to do?
Your annotation #EnableCustomResourceSecurity has the meta annotation #ConditionalOnProperty. While it may seem as if it enables/disables the #EnableResourceServer annotation, it actually enables/disables your MyApplication bean as a whole. It is as if you would write:
#SpringBootApplication
#ConditionalOnProperty(...)
#EnableResourceServer
public class MyApplication {
To avoid this, simply create an empty SomeConfiguration class and annotate it with your custom annotation:
#Configuration
#EnableCustomResourceSecurity
public class SomeConfiguration {}
Instead of adding it to your MyApplication class.
I would recommend, you don't even need a custom annotation but just an empty configuration as mentioned by Michiel. This configuration, in turn, will also import the #EnableResourceServer annotation.
#Configuration
#EnableResourceServer
#ConditionalOnProperty(prefix = "custom.resource", name = "enabled", matchIfMissing = true)
public class ResourceServerConfig {
public ResourceServerConfig() {
System.out.println("initializing ResourceServerConfig ...");
}
}
If you want to control based on annotation, you can import the same configuration in the custom annotation as below:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Import(ResourceServerConfig.class)
public #interface EnableCustomResourceSecurity {
}

Spring: What happens when we move #ComponentScan to another class in the package?

I have the following classes:
#ComponentScan
public class CDPlayerConfig {
#Autowired
private static CompactDisc cd;
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
CompactDisc cd = context.getBean(CompactDisc.class);
System.out.println(cd);
}
}
public interface CompactDisc {
void play();
}
#Component
public class SgtPeppers implements CompactDisc {
public void play() {
System.out.println("play song....");
}
}
When I run the class CDPlayerConfig, the program runs successfully. However, if I remove the ComponentScan annotation to CompactDisc interface or SgtPeppers I get the following error:
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'CompactDisc' available
I think that ComponentScan annotation marks the package where Spring looks for beans. CDPlayerConfig, CompactDisc and SgtPeppers are all placed in the same package, so allegedly moving ComponentScan annotation from one class to another should not make a difference.
If so, why do I get an error?
For #ComponentScan to work you have to "tell" spring where to search, or it must find it with help of other, already loaded, #ComponentScan annotated class (your class must be then annotated also with #Component, #Configuration etc. so it could be found).
In your case, you register application context in the first line of main method - you have specified there to load CDPlayerConfig.class which is #ComponentScan annotated so now spring can automatically find other beans in the package:
ApplicationContext context =
new AnnotationConfigApplicationContext(CDPlayerConfig.class);
If you want to move #ComponentScan to another class, you have to change class registered in AnnotationConfigApplicationContext to some #ComponentScan annotated class:
SgtPeppers:
#Component
#ComponentScan
public class SgtPeppers implements CompactDisc {
(...)
Main in CDPlayerConfig:
ApplicationContext context =
new AnnotationConfigApplicationContext(SgtPeppers.class);
Note you should register context from concrete classes (not interfaces).
Also, above sample would work even without #ComponentScan annotation on SgtPeppers, but then beans defined in other classes from the package wouldn't be found.

spring boot - spring data jpa error

main class annotations -
#SpringBootApplication
#ComponentScan( basePackages = "com.webstar" )
#EntityScan(basePackages = "com.webstar.models" )
#EnableJpaRepositories(basePackages = "com.webstar.repository")
#EnableAutoConfiguration
public interface UserAccounts extends JpaRepository<Registration, Long>
{
}
my test class -
#RunWith( SpringJUnit4ClassRunner.class )
#WebAppConfiguration
public class RegistrationTest
{
#Autowired
private UserAccounts userRepo;
#Test
public void testRegistration()
{
Registration reg = new Registration("1212","1212","1212","121","1212",null,null);
userRepo.save(reg);
}
}
Error - Caused by the following:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
My repository and models are in different packages.
Let's say that you have the following configuration:
Your entity package com.other.model
Your repository package: com.other.repository
Your testing class that is in a totally different package: foo.test.mydemo.MyOtherAppTest
Go to foo.test.mydemo.MyOtherAppTest class and add the following annotation:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
#SpringBootConfiguration
#EnableAutoConfiguration
#EntityScan(basePackages = "com.other.model")
#EnableJpaRepositories(basePackages = "com.other.repository")
public class MyOtherAppTest {
on you RegistrationTest.class
add annotation:
#SpringApplicationConfiguration(class=classname.class)
//classname is you main class name
if you use springBootTest
use #SpringBootTest(class=classname.class)
//classname is you main class name
wish help you ~

No transactional EntityManager available;

My app using spring and jpa, i have two config file:
AppConfig:
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = { "com.test.api" })
#EnableJpaRepositories("com.test.api.repository")
#PropertySource("classpath:application.properties")
public class AppConfig {
}
WebAppConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.test.webservice" })
public static class WebAppConfig extends WebMvcConfigurerAdapter {
}
Almost work fine, but sometime it throw an exception: Caused by: java.lang.IllegalStateException: No transactional EntityManager available even I just select record (Not create, update or delete data.)
If I move annotation: #ComponentScan(basePackages = { "com.test.api" }) to Class WebAppConfig and remove: #ComponentScan(basePackages = { "com.test.webservice" }) then exception is disappear, but spring context load many time when server startup and spring beans are duplicate.
Or If I use entityManager.getEntityManagerFactory().createEntityManager().unwrap(Session.class) instead for entityManager.unwrap(Session.class) then exception also disappear.
How can I solve it?
Remove the ComponentScan on WebAppConfig and add the package in the ComponentScan annotation on AppConfig, that should solve your problem.

Resources