I'm using spring-data-neo4j in a spring-boot application. I did the configuration as recommended in spring.io guides and many other places by inheriting Neo4jConfiguration class. This works when the database location is hardcoded in the provided examples. However when I want to use a placeholder for retrieving the database location from a property file it's not retrieved and I get null. Here's the code
#Configuration
#EnableNeo4jRepositories(basePackageClasses = {MyRepository.class})
public class Neo4jConfig extends Neo4jConfiguration {
#Value("${neo4j.location}")
private String neo4jDatabaseLocation;
#Bean
public GraphDatabaseService graphDatabaseService() {
return new GraphDatabaseFactory()
.newEmbeddedDatabase(neo4jDatabaseLocation);
}
...
This normally works in any other config class but not in this one because of the Neo4jConfiguration class has some several methods marked with #Autowired. This causes circular reference and it's not initialized properly. This can be seen in the logs:
2014-09-06 20:59:45.168 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [c.m.f.Neo4jConfig$$EnhancerBySpringCGLIB$$7165d752]: AutowiredFieldElement for private javax.validation.Validator org.springframework.data.neo4j.config.Neo4jConfiguration.validator
2014-09-06 20:59:45.169 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [c.m.f.Neo4jConfig$$EnhancerBySpringCGLIB$$7165d752]: AutowiredMethodElement for public void org.springframework.data.neo4j.config.Neo4jConfiguration.setConversionService(org.springframework.core.convert.ConversionService)
2014-09-06 20:59:45.169 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [c.m.f.Neo4jConfig$$EnhancerBySpringCGLIB$$7165d752]: AutowiredMethodElement for public void org.springframework.data.neo4j.config.Neo4jConfiguration.setGraphDatabaseService(org.neo4j.graphdb.GraphDatabaseService)
2014-09-06 20:59:45.169 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Registered injected element on class [c.m.f.Neo4jConfig$$EnhancerBySpringCGLIB$$7165d752]: AutowiredFieldElement for private java.lang.String c.m.f.Neo4jConfig.neo4jDatabaseLocation
2014-09-06 20:59:45.169 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Eagerly caching bean 'c.m.f.Neo4jConfig' to allow for resolving potential circular references
2014-09-06 20:59:45.171 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Processing injected method of bean 'c.m.f.Neo4jConfig': AutowiredFieldElement for private javax.validation.Validator org.springframework.data.neo4j.config.Neo4jConfiguration.validator
2014-09-06 20:59:45.182 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Processing injected method of bean 'c.m.f.Neo4jConfig': AutowiredMethodElement for public void org.springframework.data.neo4j.config.Neo4jConfiguration.setConversionService(org.springframework.core.convert.ConversionService)
2014-09-06 20:59:45.183 DEBUG 4665 --- [ main] o.s.b.f.annotation.InjectionMetadata : Processing injected method of bean 'c.m.f.Neo4jConfig': AutowiredMethodElement for public void org.springframework.data.neo4j.config.Neo4jConfiguration.setGraphDatabaseService(org.neo4j.graphdb.GraphDatabaseService)
2014-09-06 20:59:45.184 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'graphDatabaseService'
2014-09-06 20:59:45.184 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Creating instance of bean 'graphDatabaseService'
2014-09-06 20:59:45.185 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2014-09-06 20:59:45.185 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
2014-09-06 20:59:45.188 DEBUG 4665 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Returning eagerly cached instance of singleton bean 'c.m.f.Neo4jConfig' that is not fully initialized yet - a consequence of a circular reference
As you can see what I'm trying to achieve here is not to hardcode the database location. Is there any workaround for this circular reference problem? Or maybe any other way of configuring it? As this is a spring-boot application, I don't have any Xml configuration and if it's possible I want to keep it that way.
Have you tried passing in the neo4jDatabaseLocation as a parameter:
#Configuration
#EnableNeo4jRepositories(basePackageClasses = {MyRepository.class})
public class Neo4jConfig extends Neo4jConfiguration {
#Bean
public GraphDatabaseService graphDatabaseService(#Value("${neo4j.location}") String neo4jDatabaseLocation) {
return new GraphDatabaseFactory()
.newEmbeddedDatabase(neo4jDatabaseLocation);
}
...
}
Proposed suggestions did not work for the spring framework version I am using. As a workaround, I defined the GraphDatabaseService bean in another configuration file which does not extend Neo4jConfiguration. Note that this Neo4jConfig class is needed regardless.
Related
I am trying to execute a couple of queries on different threads. There are 2 top level queries each executing on different tables at runtime. For executing the first set of queries (executeQuery1()), I spawn 2 different threads and they are processed well. From the output of these queries, I have to extract a list of ids and then fire another set of queries (executeQuery2()) on entirely different threads. As soon as the second set of queries are about to be submitted to the database, I see that EntityManager is closed and the application shutdown.
2022-04-28 22:34:29.529 TRACE 48403 --- [main] o.s.b.f.support.DisposableBeanAdapter : Invoking destroy() on bean with name 'springApplicationAdminRegistrar'
2022-04-28 22:34:29.529 TRACE 48403 --- [main] o.s.b.f.support.DisposableBeanAdapter : Invoking destroy() on bean with name 'mbeanExporter'
2022-04-28 22:34:29.529 DEBUG 48403 --- [main] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown
2022-04-28 22:34:29.529 DEBUG 48403 --- [main] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans
2022-04-28 22:34:29.529 TRACE 48403 --- [main] o.s.b.f.support.DisposableBeanAdapter : Invoking destroy() on bean with name 'defaultValidator'
2022-04-28 22:34:29.529 TRACE 48403 --- [main] o.s.b.f.support.DisposableBeanAdapter : Invoking destroy() on bean with name 'org.springframework.data.jpa.util.JpaMetamodelCacheCleanup'
2022-04-28 22:34:29.530 TRACE 48403 --- [main] o.s.b.f.support.DisposableBeanAdapter : Invoking destroy() on bean with name 'threadPoolTaskExecutor'
2022-04-28 22:34:29.530 DEBUG 48403 --- [main] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'threadPoolTaskExecutor'
2022-04-28 22:34:29.531 TRACE 48403 --- [main] o.s.b.f.s.DefaultListableBeanFactory : Retrieved dependent beans for bean 'verticaEntityManagerFactory': [verticaTransactionManager]
2022-04-28 22:34:29.531 TRACE 48403 --- [main] o.s.b.f.s.DefaultListableBeanFactory : Retrieved dependent beans for bean 'verticaTransactionManager': [transactionTemplate]
2022-04-28 22:34:29.531 TRACE 48403 --- [main] o.s.b.f.support.DisposableBeanAdapter : Invoking destroy() on bean with name 'verticaEntityManagerFactory'
2022-04-28 22:34:29.531 INFO 48403 --- [main] c.b.a.n.h.c.VerticaDataSourceConfig$1 : Closing JPA EntityManagerFactory for persistence unit 'vertica'
2022-04-28 22:34:29.531 DEBUG 48403 --- [main] o.hibernate.internal.SessionFactoryImpl : HHH000031: Closing
2022-04-28 22:34:29.531 TRACE 48403 --- [main] o.h.engine.query.spi.QueryPlanCache : Cleaning QueryPlan Cache
2022-04-28 22:34:29.531 TRACE 48403 --- [main] o.h.type.spi.TypeConfiguration$Scope : Handling #sessionFactoryClosed from [org.hibernate.internal.SessionFactoryImpl#77774571] for TypeConfiguration
2022-04-28 22:34:29.532 DEBUG 48403 --- [main] o.h.type.spi.TypeConfiguration$Scope : Un-scoping TypeConfiguration [org.hibernate.type.spi.TypeConfiguration$Scope#44af588b] from SessionFactory [org.hibernate.internal.SessionFactoryImpl#77774571]
2022-04-28 22:34:29.532 DEBUG 48403 --- [main] o.h.s.i.AbstractServiceRegistryImpl : Implicitly destroying ServiceRegistry on de-registration of all child ServiceRegistries
2022-04-28 22:34:29.532 DEBUG 48403 --- [main] o.h.b.r.i.BootstrapServiceRegistryImpl : Implicitly destroying Boot-strap registry on de-registration of all child ServiceRegistries
2022-04-28 22:34:29.532 TRACE 48403 --- [MyAsyncThread-4] j.i.AbstractLogicalConnectionImplementor : Preparing to begin transaction via JDBC Connection.setAutoCommit(false)
2022-04-28 22:34:29.532 TRACE 48403 --- [main] o.s.b.f.s.DefaultListableBeanFactory : Retrieved dependent beans for bean 'verticaDataSource': [dataSourceScriptDatabaseInitializer, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration, jdbcTemplate]
2022-04-28 22:34:29.532 TRACE 48403 --- [main] o.s.b.f.s.DefaultListableBeanFactory : Retrieved dependent beans for bean 'dataSourceScriptDatabaseInitializer': [jdbcTemplate, namedParameterJdbcTemplate]
2022-04-28 22:34:29.532 TRACE 48403 --- [main] o.s.b.f.s.DefaultListableBeanFactory : Retrieved dependent beans for bean 'jdbcTemplate': [namedParameterJdbcTemplate]
2022-04-28 22:34:29.532 TRACE 48403 --- [main] o.s.b.f.s.DefaultListableBeanFactory : Retrieved dependent beans for bean 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration': [jpaVendorAdapter, entityManagerFactoryBuilder]
2022-04-28 22:34:29.532 TRACE 48403 --- [main] o.s.b.f.s.DefaultListableBeanFactory : Retrieved dependent beans for bean 'jpaVendorAdapter': [entityManagerFactoryBuilder]
2022-04-28 22:34:29.532 TRACE 48403 --- [main] o.s.b.f.support.DisposableBeanAdapter : Invoking close() on bean with name 'verticaDataSource'
2022-04-28 22:34:29.533 INFO 48403 --- [main] com.zaxxer.hikari.HikariDataSource : vertica-db-pool - Shutdown initiated...
2022-04-28 22:34:29.533 DEBUG 48403 --- [main] com.zaxxer.hikari.pool.HikariPool : vertica-db-pool - Before shutdown stats (total=20, active=2, idle=18, waiting=0)
I have 2 datasources in my Spring Boot app and therefore have to configure datasources programmatically.
AsyncConfiguration.java
#EnableAsync
#Configuration
public class AsyncConfiguration implements AsyncConfigurer {
#Bean(name = "threadPoolTaskExecutor")
public Executor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("MyAsyncThread-");
executor.initialize();
return executor;
}
#Override
public Executor getAsyncExecutor() {
return new ThreadPoolTaskExecutor();
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
AsyncService.java
#Slf4j
#Service
public class AsyncService {
#Autowired VerticaRepository verticaRepo;
#Async("threadPoolTaskExecutor")
public CompletableFuture<List<Entity1>> execute1(String query) {
List<Entity1> result = verticaRepo.executeQuery1(query);
return CompletableFuture.completedFuture(result);
}
#Async("threadPoolTaskExecutor")
public CompletableFuture<List<Entity2>> execute2(List<BigInteger> ids, String query) {
List<Entity2> result = verticaRepo.executeQuery2(ids, query);
return CompletableFuture.completedFuture(result);
}
}
VerticaDataSourceConfig.java
#Configuration
#ConfigurationProperties("vertica.datasource")
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "verticaEntityManagerFactory",
transactionManagerRef = "verticaTransactionManager",
basePackages = { "mypackage.repository" }
)
public class VerticaDataSourceConfig /*extends HikariConfig*/ {
public final static String PERSISTENCE_UNIT_NAME = "vertica";
public final static String PACKAGES_TO_SCAN = "mypackage.entity";
#Autowired
private Environment env;
#Bean
public HikariDataSource verticaDataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(env.getProperty("vertica.datasource.jdbc-url"));
hikariConfig.setUsername(env.getProperty("vertica.datasource.username"));
hikariConfig.setPassword(env.getProperty("vertica.datasource.password"));
hikariConfig.setDriverClassName(env.getProperty("vertica.datasource.driver-class-name"));
hikariConfig.setConnectionTimeout(Long.parseLong(env.getProperty("vertica.datasource.hikari.connectionTimeout")));
hikariConfig.setIdleTimeout(Long.parseLong(env.getProperty("vertica.datasource.hikari.idleTimeout")));
hikariConfig.setMaxLifetime(Long.parseLong(env.getProperty("vertica.datasource.hikari.maxLifetime")));
hikariConfig.setKeepaliveTime(Long.parseLong(env.getProperty("vertica.datasource.hikari.keepaliveTime")));
hikariConfig.setMaximumPoolSize(Integer.parseInt(env.getProperty("vertica.datasource.hikari.maximumPoolSize")));
hikariConfig.setPoolName(env.getProperty("vertica.datasource.hikari.poolName"));
hikariConfig.setValidationTimeout(Integer.parseInt(env.getProperty("vertica.datasource.hikari.validationTimeout")));
return new HikariDataSource(hikariConfig);
}
#Bean
public LocalContainerEntityManagerFactoryBean verticaEntityManagerFactory(
final HikariDataSource verticaDataSource) {
return new LocalContainerEntityManagerFactoryBean() {{
setDataSource(verticaDataSource);
setPersistenceProviderClass(HibernatePersistenceProvider.class);
setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
setPackagesToScan(PACKAGES_TO_SCAN);
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.ddl-auto", env.getProperty("vertica.jpa.hibernate.ddl-auto"));
jpaProperties.put("hibernate.show-sql", env.getProperty("vertica.jpa.hibernate.show-sql"));
jpaProperties.put("hibernate.format_sql", env.getProperty("vertica.jpa.hibernate.format_sql"));
jpaProperties.put("hibernate.dialect", env.getProperty("vertica.jpa.properties.hibernate.dialect"));
setJpaProperties(jpaProperties);
afterPropertiesSet();;
}};
}
#Bean
public PlatformTransactionManager verticaTransactionManager(EntityManagerFactory verticaEntityManagerFactory) {
return new JpaTransactionManager(verticaEntityManagerFactory);
}
}
VerticaRepository.java
#Repository
public class VerticaRepository {
//#Autowired
#PersistenceContext(unitName = "vertica")
private EntityManager em;
#Transactional
public List<Entity1> executeQuery1(String queryStr) {
// query.setParameter() can only replace parameters in WHERE clause of a query;
// it cannot replace table or column names
String replacedQuery = // replace table name and column name
Query query = em.createNativeQuery(replacedQuery);
List<Object[]> result = query.getResultList();
List<Entity1> entities = new ArrayList<>();
// fill entities list with result
return entities;
}
#Transactional
public List<Entity2> executeQuery2(List<BigInteger> ids, String queryStr) {
String replacedQuery = // replace table name and column name; the table and col names are different from the ones in executeQWuery1()
Query query = em.createNativeQuery(replacedQuery);
List<Object[]> result = query.getResultList();
List<Entity2> entities = new ArrayList<>();
// fill entities list with result
return entities;
}
}
BusinessService.java
#Slf4j
#Component("businessService")
public class BusinessService {
#Autowired
private String query1;
#Autowired
private String query2;
#Autowired private AsyncService asyncService;
public Void serve() throws Exception {
List<CompletableFuture<List<Entity1>>> violationFutures = new ArrayList<>();
for (iterate over some list not shown here; this will loop 2 times with different table and col name substitutions in the query) {
violationFutures.add(asyncService.execute1(query1));
}
CompletableFuture<List<List<Entity1>>> vcf = sequence(violationFutures);
List<Entity1> aggregatedViolations = new ArrayList<>();
for (List<Entity1> list: vcf.get()) {
aggregatedViolations.addAll(list);
}
int numProcessors = Runtime.getRuntime().availableProcessors();
List<BigInteger> idList= //somehow get a list of ids from aggregatedViolations
List<List<BigInteger>> partitionedList = ListUtils.partition(idList, numProcessors);
List<CompletableFuture<List<Entity2>>> trendFutures = new ArrayList<>();
for (List<BigInteger> ids: partitionedList) {
for (iterate over some list not shown here; this will loop 2 times with different table and col name substitutions in the query) {
trendFutures.add(asyncService.execute2(getIds(devices), query2));
}
}
CompletableFuture<List<List<Entity2>>> tcf = sequence(trendFutures);
// rest of the business logic is dependent on the above queries execution
return null;
}
private static <T> CompletableFuture<List<List<T>>> sequence(List<CompletableFuture<List<T>>> futures) {
CompletableFuture<Void> allDoneFuture =
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
return allDoneFuture.thenApply(v ->
futures.stream().
map(future -> future.join()).
collect(Collectors.toList())
);
}
I believe that this has something to do with the EntityManagers being used in multi-threaded env. However, when I read the documentation, #Transactional will supply a new EM everytime. If the executeQuery1() was able to run 2 queries in parallel on different threads, why is executeQuery2() closing the EM?
I'm trying to configure my spring security application.
I want to create my own UserDetailsService.
For that i do something like this:
public class ApplicationUserService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return this.someUser();
}
}
I got 2 ways to add this UserService to Spring Security
Add it to configuration class. Something like this:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
#Override
protected UserDetailsService userDetailsService() {
return applicationUserService;
}
}
Or add annotaion #Component, or #Service on my class.
Everything is working fine when i chose only 1 way, but i got an question: why when i trying to use both variants (add #Service and add #Bean to config) nothing is working?
I got no exceptions, error or something like this in console:
2021-09-11 17:26:16.755 INFO 15819 --- [ main] com.example.test.TestApplication : Starting TestApplication using Java 16.0.2 on aleksander-MS-7A71 with PID 15819 (/home/aleksander/programming/java/4fun/test/target/classes started by aleksander in /home/aleksander/programming/java/4fun/test)
2021-09-11 17:26:16.756 INFO 15819 --- [ main] com.example.test.TestApplication : No active profile set, falling back to default profiles: default
2021-09-11 17:26:17.402 INFO 15819 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-09-11 17:26:17.409 INFO 15819 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-09-11 17:26:17.409 INFO 15819 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.52]
2021-09-11 17:26:17.442 INFO 15819 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-09-11 17:26:17.442 INFO 15819 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 630 ms
2021-09-11 17:26:17.555 INFO 15819 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter#6981f8f3, org.springframework.security.web.context.SecurityContextPersistenceFilter#38bb9d7a, org.springframework.security.web.header.HeaderWriterFilter#62db3891, org.springframework.security.web.authentication.logout.LogoutFilter#48528634, org.springframework.security.web.authentication.www.BasicAuthenticationFilter#80bfdc6, org.springframework.security.web.savedrequest.RequestCacheAwareFilter#78d6447a, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter#5e65afb6, org.springframework.security.web.authentication.AnonymousAuthenticationFilter#623dcf2a, org.springframework.security.web.session.SessionManagementFilter#2819c460, org.springframework.security.web.access.ExceptionTranslationFilter#6f49d153, org.springframework.security.web.access.intercept.FilterSecurityInterceptor#60bbacfc]
2021-09-11 17:26:17.676 INFO 15819 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-09-11 17:26:17.682 INFO 15819 --- [ main] com.example.test.TestApplication : Started TestApplication in 1.215 seconds (JVM running for 1.794)
The way you've described the question, the application will definitely throw an exception unless you've defined the bean preference.
First case:
Basically, UserDetailsService is an interface and you've provided the implementation of it by declaring the bean as
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
#Override
protected UserDetailsService userDetailsService() {
return new ApplicationUserService();
}
}
Second case: You want to check the behaviour by declaring another bean using #Service or #Component annotation as following
#Service
public class ApplicationUserService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new UserDetails();
}
}
If you try to use the above cases together, it won't work. The case is very simple you are providing two beans of type UserDetailsService to the spring container and hence it won't be able to identify which one it should use.
If you want to check the behaviour with both the cases you've to set the priority for beans, so in that case you can mark one of the bean with #Primary annotation.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Primary
#Bean
#Override
protected UserDetailsService userDetailsService() {
return new ApplicationUserService();
}
}
I am new to Spring and Spring Boot and I played around with different ways how to resolve Beans. In my example I've got a Bean that should always be a singleton. What surprises me is that there seems to be a way where this bean is resolved as, I assume, "prototype".
Could anyone explain to me why it's not a singleton when it is resolved in the signature of the method showSingletonBeans?
#SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args)
}
#Service("stackSingletonBean")
// #Scope("singleton")
class MySingletonBean {
init {
println("Created MySingletonBean " + this.hashCode())
}
}
#RestController
class MyController {
#Autowired
// #Qualifier("singletonBean")
lateinit var memberSingletonBean: MySingletonBean
#Autowired
lateinit var singeltonFactory: ObjectFactory<MySingletonBean>
fun buildSingleton() : MySingletonBean {
return singeltonFactory.`object`
}
#Lookup
fun getSingletonInstance() : MySingletonBean? {
return null
}
#GetMapping("/")
fun showSingletonBeans(#Autowired stackSingletonBean: MySingletonBean) {
println("member " + memberSingletonBean.hashCode() )
println("stack " + stackSingletonBean.hashCode())
println("lookup:" + getSingletonInstance().hashCode())
println("factory: " + buildSingleton().hashCode())
}
}
The log looks like that:
2020-08-13 18:44:32.604 INFO 172175 --- [ main] com.example.demo.DemoApplicationKt : No active profile set, falling back to default profiles: default
2020-08-13 18:44:33.118 INFO 172175 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-08-13 18:44:33.124 INFO 172175 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-08-13 18:44:33.124 INFO 172175 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.37]
2020-08-13 18:44:33.164 INFO 172175 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-08-13 18:44:33.164 INFO 172175 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 528 ms
Created MySingletonBean 1747702724
2020-08-13 18:44:33.286 INFO 172175 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-08-13 18:44:33.372 INFO 172175 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-08-13 18:44:33.379 INFO 172175 --- [ main] com.example.demo.DemoApplicationKt : Started DemoApplicationKt in 1.011 seconds (JVM running for 1.24)
2020-08-13 18:44:37.341 INFO 172175 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-08-13 18:44:37.341 INFO 172175 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-08-13 18:44:37.344 INFO 172175 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 3 ms
Created MySingletonBean 562566586
member 1747702724
stack 562566586
lookup:1747702724
factory: 1747702724
Created MySingletonBean 389331797
member 1747702724
stack 389331797
lookup:1747702724
factory: 1747702724
Resolving controller method parameters is actually quite different mechanism. It has nothing to do with dependency injection and the #Autowired annotation: the annotation can be removed and it won't change the behavior.
Although #Autowired can technically be declared on individual method or constructor parameters since Spring Framework 5.0, most parts of the framework ignore such declarations. The only part of the core Spring Framework that actively supports autowired parameters is the JUnit Jupiter support in the spring-test module (see the TestContext framework reference documentation for details).
https://docs.spring.io/
In your case, the stackSingletonBean is instantiated by the ModelAttributeMethodArgumentResolver. It's not aware of the #Service annotation nor of its scope: it simply uses the default constructor on each request.
Model attributes are sourced from the model, or created using a default constructor and then added to the model.
Note that use of #ModelAttribute is optional — for example, to set its attributes. By default, any argument that is not a simple value type( as determined by BeanUtils#isSimpleProperty) and is not resolved by any other argument resolver is treated as if it were annotated with #ModelAttribute. Web on Reactive Stack
I wanted to connect my spring boot app to 2 databases . So according to a tutorial i created 2 config classes.
Config Class 1
#Configuration
#EnableTransactionManagement
#PropertySource({ "classpath:database-configs.properties" })
#EnableJpaRepositories(
basePackages = {"com.dialog.pod.ideabiz_admin.data_access_objects"},
entityManagerFactoryRef = "adminEntityManagerFactory",
transactionManagerRef = "adminTransactionManager")
public class IdeabizAdminConfig {
#Autowired
private Environment env;
#Bean
PlatformTransactionManager adminTransactionManager() {
return new JpaTransactionManager(adminEntityManagerFactory().getObject());
}
#Bean
LocalContainerEntityManagerFactoryBean adminEntityManagerFactory() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
// jpaVendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(adminDataSource());
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
factoryBean.setPackagesToScan("com.dialog.pod.ideabiz_admin.models");
factoryBean.setJpaPropertyMap(jpaProperties());
factoryBean.setPersistenceUnitName("adminDataSource");
return factoryBean;
}
#Bean
DataSource adminDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("admin.jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("admin.jdbc.url"));
dataSource.setUsername(env.getProperty("admin.jdbc.username"));
dataSource.setPassword(env.getProperty("admin.jdbc.password"));
return dataSource;
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<>();
props.put("hibernate.implicit_naming_strategy","org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl");
props.put("hibernate.physical_naming_strategy","com.dialog.pod.PhysicalNamingStrategyImpl");
return props;
}
}
Config Class 2
#Configuration
#EnableTransactionManagement
#PropertySource({ "classpath:database-configs.properties" })
#EnableJpaRepositories(
basePackages = {"com.dialog.pod.ideabiz_log_summary.data_access_objects"},
entityManagerFactoryRef = "sumLogEntityManagerFactory",
transactionManagerRef = "sumLogTransactionManager")
public class IdeabizLogSummaryConfig {
#Autowired
private Environment env;
#Bean
PlatformTransactionManager sumLogTransactionManager() {
return new JpaTransactionManager(sumLogEntityManagerFactory().getObject());
}
#Bean
LocalContainerEntityManagerFactoryBean sumLogEntityManagerFactory() {
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
// jpaVendorAdapter.setGenerateDdl(true);
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(adminDataSource());
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
factoryBean.setPackagesToScan("com.dialog.pod.ideabiz_log_summary.models");
factoryBean.setJpaPropertyMap(jpaProperties());
factoryBean.setPersistenceUnitName("sumLogDataSource");
return factoryBean;
}
#Bean
DataSource adminDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("sumlog.jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("sumlog.jdbc.url"));
dataSource.setUsername(env.getProperty("sumlog.jdbc.username"));
dataSource.setPassword(env.getProperty("sumlog.jdbc.password"));
return dataSource;
}
private Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<>();
props.put("hibernate.implicit_naming_strategy","org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl");
props.put("hibernate.physical_naming_strategy","com.dialog.pod.PhysicalNamingStrategyImpl");
return props;
}
}
Application Class
#SpringBootApplication
#EnableAutoConfiguration (exclude = { DataSourceAutoConfiguration.class })
#Configuration
#ComponentScan
public class PodApiApplication {
public static void main(String[] args) {
SpringApplication.run(PodApiApplication.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/").allowedOrigins("*");
}
};
}
}
When i try to run the app i get following error.
***************************
APPLICATION FAILED TO START
***************************
Description:
Method requestMappingHandlerMapping in org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration required a single bean, but 2 were found:
- adminEntityManagerFactory: defined by method 'adminEntityManagerFactory' in class path resource [com/dialog/pod/ideabiz_admin/IdeabizAdminConfig.class]
- sumLogEntityManagerFactory: defined by method 'sumLogEntityManagerFactory' in class path resource [com/dialog/pod/ideabiz_log_summary/IdeabizLogSummaryConfig.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Process finished with exit code 1
I put #Primary to the first config class(according to another tutorial). When i do that datasource in the first config class works. Problem is when i do this first datasource also applied to all the jparepositories.
I'm new to Spring boot. I have been trying to solve this problem for over 5 hours :( . Thanks in advance for any help you are able to provide.
Full Log
2017-01-27 00:52:39.713 INFO 6704 --- [ main] com.dialog.pod.PodApiApplication : Starting PodApiApplication on DESKTOP-4B89ITN with PID 6704 (started by y2ksh in H:\Spring MVC Workspace\platform-overview-dashboard\PODApi)
2017-01-27 00:52:39.718 INFO 6704 --- [ main] com.dialog.pod.PodApiApplication : No active profile set, falling back to default profiles: default
2017-01-27 00:52:39.938 INFO 6704 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#6e171cd7: startup date [Fri Jan 27 00:52:39 IST 2017]; root of context hierarchy
2017-01-27 00:52:41.712 INFO 6704 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'adminDataSource' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=ideabizAdminConfig; factoryMethodName=adminDataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/dialog/pod/ideabiz_admin/IdeabizAdminConfig.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=ideabizLogSummaryConfig; factoryMethodName=adminDataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/dialog/pod/ideabiz_log_summary/IdeabizLogSummaryConfig.class]]
2017-01-27 00:52:42.804 INFO 6704 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [class org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$3cc0fc3] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-01-27 00:52:43.594 INFO 6704 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8081 (http)
2017-01-27 00:52:43.609 INFO 6704 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2017-01-27 00:52:43.609 INFO 6704 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.6
2017-01-27 00:52:43.810 INFO 6704 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2017-01-27 00:52:43.810 INFO 6704 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 3891 ms
2017-01-27 00:52:44.247 INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2017-01-27 00:52:44.253 INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'metricFilter' to: [/*]
2017-01-27 00:52:44.254 INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-01-27 00:52:44.254 INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-01-27 00:52:44.254 INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-01-27 00:52:44.254 INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2017-01-27 00:52:44.255 INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'webRequestLoggingFilter' to: [/*]
2017-01-27 00:52:44.255 INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'applicationContextIdFilter' to: [/*]
2017-01-27 00:52:44.367 INFO 6704 --- [ main] o.s.j.d.DriverManagerDataSource : Loaded JDBC driver: com.mysql.jdbc.Driver
2017-01-27 00:52:44.403 INFO 6704 --- [ main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'adminDataSource'
2017-01-27 00:52:44.435 INFO 6704 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [
name: adminDataSource
...]
2017-01-27 00:52:44.634 INFO 6704 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.0.11.Final}
2017-01-27 00:52:44.636 INFO 6704 --- [ main] org.hibernate.cfg.Environment : HHH000206: hibernate.properties not found
2017-01-27 00:52:44.641 INFO 6704 --- [ main] org.hibernate.cfg.Environment : HHH000021: Bytecode provider name : javassist
2017-01-27 00:52:44.725 INFO 6704 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
2017-01-27 00:52:45.302 INFO 6704 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2017-01-27 00:52:46.178 INFO 6704 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'adminDataSource'
2017-01-27 00:52:46.189 INFO 6704 --- [ main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'sumLogDataSource'
2017-01-27 00:52:46.190 INFO 6704 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [
name: sumLogDataSource
...]
2017-01-27 00:52:46.228 INFO 6704 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2017-01-27 00:52:46.291 INFO 6704 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'sumLogDataSource'
2017-01-27 00:52:46.695 INFO 6704 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
2017-01-27 00:52:46.947 INFO 6704 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory
2017-01-27 00:52:47.496 INFO 6704 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for #ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext#6e171cd7: startup date [Fri Jan 27 00:52:39 IST 2017]; root of context hierarchy
2017-01-27 00:52:47.560 WARN 6704 --- [ main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]: Factory method 'requestMappingHandlerMapping' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'openEntityManagerInViewInterceptor' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration$JpaWebConfiguration$JpaWebMvcConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManagerFactory' available: expected single matching bean but found 2: adminEntityManagerFactory,sumLogEntityManagerFactory
2017-01-27 00:52:47.562 INFO 6704 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'sumLogDataSource'
2017-01-27 00:52:47.563 INFO 6704 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'adminDataSource'
2017-01-27 00:52:47.566 INFO 6704 --- [ main] o.apache.catalina.core.StandardService : Stopping service Tomcat
2017-01-27 00:52:47.586 INFO 6704 --- [ main] utoConfigurationReportLoggingInitializer :
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2017-01-27 00:52:47.592 ERROR 6704 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Method requestMappingHandlerMapping in org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration required a single bean, but 2 were found:
- adminEntityManagerFactory: defined by method 'adminEntityManagerFactory' in class path resource [com/dialog/pod/ideabiz_admin/IdeabizAdminConfig.class]
- sumLogEntityManagerFactory: defined by method 'sumLogEntityManagerFactory' in class path resource [com/dialog/pod/ideabiz_log_summary/IdeabizLogSummaryConfig.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Process finished with exit code 1
Mark one of the beans as primary
#Primary
#Bean
public EntityManagerFactory entityManagerFactory() {
}
Your Spring Boot application implicitly activates Spring Web Mvc via #WebMvcAutoConfiguration which is triggered by having, among others, Servlet.class in your class path. This #WebMvcAutoConfiguration requires a bean of type EntityManagerFactory. However, you have registered two such beans adminEntityManagerFactory and sumLogEntityManagerFactory. So you have to tell Spring Web Mvc which is your preferred one via #Primary on top of the respective #Bean method. Alternatively if you don't need Web Mvc autoconfiguration switch it off by #EnableAutoConfiguration(exclude={WebMvcAutoConfiguration}) and configure Web Mvc manually.
It turns out i used same method name for both datasource beans . This was the reason why only one data source used for all jparepos. Thanks #Javatar81 for explaining how bean naming works
I have a basic API setup with Spring MVC Rest as following.
public abstract class AbstractApi implements InitializingBean {
#Autowired
protected ValidatorFactory validatorFactory;
/* ... */
#Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(validatorFactory);
}
}
#Controller
#RequestMapping("books")
public class BookApi extends AbstractApi {
private final BookRepository bookRepository;
#Autowired
public BookApi(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Book> getBooks() {
return new ResponseEntity<>(bookRepository.findAll(), HttpStatus.OK);
}
}
The server returns 404 - Not Found if I send GET /books request with above configuration.
But, if I make AbstractApi un-implement InitializingBean, it works fine. Also, annotating #PostConstruct to afterPropertiesSet() instead of implementing InitializingBean works.
Why is Spring #Controller API not working when implementing InitializingBean?
Your code looks correct. I tested in on my own and everything works as expected. What I'm suggesting is to remove #Autowired ValidatorFactory in AbstractApi class just for testing purpose. Implementing InitializingBean is not related to the request mapping handler mapping. My working code is:
public abstract class AbstractApi implements InitializingBean {
#Override
public void afterPropertiesSet() throws Exception {
System.out.println("after properties set");
}
}
and my controller
#Controller
#RequestMapping("books")
public class BooksController extends AbstractApi {
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> getBooks() {
return new ResponseEntity<>("", HttpStatus.OK);
}
}
and starting log from tomcat:
2016-01-28 16:48:03.141 INFO 2238 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2016-01-28 16:48:03.317 INFO 2238 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2016-01-28 16:48:03.329 INFO 2238 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.0.23
2016-01-28 16:48:03.405 INFO 2238 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2016-01-28 16:48:03.405 INFO 2238 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1815 ms
2016-01-28 16:48:03.512 INFO 2238 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2016-01-28 16:48:03.515 INFO 2238 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
after properties set
Your current #RequestMapping("books") path is incorrectly specified. When running locally on port 8080 looks like http://localhost:8080books
and should be #RequestMapping("/books") - http://localhost:8080/books
give that a try.