Bean creation is supposed to be a Singleton in the spring container, correct? I am migrating a Spring configuration file to Spring Boot with Annotations. I have the below code, but it seems to call the "mySingletonBean()" every time that it is used within another bean creation. It is my understanding that the #Bean annotation is supposed to be Singleton by default. Am I creating my Beans correctly?
#Bean
public SomeBean mySingletonBean() {
SomeBean mybean = new SomeBean();
mybean.setName = "Name";
return mybean;
}
#Bean
public Bean1 bean1() {
Bean1 bean1 = new Bean1();
bean1.setBean(mySingletonBean());
return bean1;
}
#Bean
public Bean2 bean2() {
Bean2 bean2 = new Bean2();
bean2.setBean(mySingletonBean());
return bean2;
}
Spring is proxying your application context class, and takes care of all the context related stuff like instantiating, caching etc. of the beans.
Run this small test, feel free to debug to see what class the configuration class became:
package stackoverflow;
import java.util.Arrays;
import java.util.Date;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertTrue;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Test.MyContext.class)
public class Test {
#Autowired
private ApplicationContext applicationContext;
#Autowired
#Qualifier("myStringBean")
private String myStringBean;
#Autowired
private SomeBean someBean;
#Configuration
static class MyContext {
#Bean
public String myStringBean() {
return String.valueOf(new Date().getTime());
}
#Bean
public SomeBean mySomeBean() {
return new SomeBean(myStringBean());
}
}
#org.junit.Test
public void test() {
assertTrue(myStringBean == applicationContext.getBean("myStringBean"));
assertTrue(myStringBean == someBean.getValue());
System.out.println(Arrays.asList(applicationContext.getBean("myStringBean"), myStringBean, someBean.getValue()));
}
static class SomeBean {
private String value;
public SomeBean(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}
Spring Boot is smart framework and method mySingletonBean() will starts only once time, don't worry about it. U can start debug mode and see .
Related
When I create 2 beans with #StepScope and apply #Order then the order for both beans still gets resolved to Ordered.LOWEST_PRECEDENCE when trying to autowire them as a List.
#Bean
#Order(1)
#StepScope
public MyBean bean1(
return new MyBean("1");
}
#Bean
#Order(2)
#StepScope
public MyBean bean2(
return new MyBean("2");
}
Looking in OrderComparator I see the beans are resolved to a source of ScopedProxyFactoryBean which returns a null order value.
Wondering if Im doing something wrong here as I would expect the ordering to work correctly.
So the aim in to autowire an ordered list into another bean eg
#Component
public class OuterBean {
private List<MyBean> beans;
public OuterBean(List<MyBean> beans) {
this.beans = beans;
}
}
And I would expect the list to contain {bean1,bean2}
Step scoped beans need to be used in the scope of a running step. Here is an example:
import java.util.List;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
#Configuration
public class MyJob {
#Bean
#Order(1)
#StepScope
public MyBean bean1() {
return new MyBean("1");
}
#Bean
#Order(2)
#StepScope
public MyBean bean2() {
return new MyBean("2");
}
#Bean
public OuterBean outerBean(List<MyBean> beans) {
return new OuterBean(beans);
}
#Bean
public Tasklet tasklet(OuterBean outerBean) {
return (contribution, chunkContext) -> {
outerBean.sayHello();
return RepeatStatus.FINISHED;
};
}
#Bean
public Job job(JobBuilderFactory jobs, StepBuilderFactory steps) {
return jobs.get("job")
.start(steps.get("step")
.tasklet(tasklet(null))
.build())
.build();
}
}
The complete code can be found here. This example prints:
bean = 1
bean = 2
which means step scoped beans have been injected in the right order.
I am trying to understand the behaviour of #Primary in #Profile from this video
Dependency Injection using profile.
The active profile in file application.properties is english and running it gives error
expected single matching bean but found 2: helloWorldServiceEnglish,helloWorldServiceSpanish
Adding #Primary annotation in helloConfig.java resolves the error:
#Bean
#Profile("english")
#Primary
public HelloWorldService helloWorldServiceEnglish(HelloWorldFactory factory) {
return factory.createHelloWorldService("en");
}
When I am Autowiring using Profile and there is only one single Profile named english then why it is searching for other beans which do not have #Profile annotation? And how adding #Primary is changing this behaviour?
Does Spring internally first scans for Autowire by type and completely ignore #Profile because of which it throws error expected single matching bean but found 2.
helloConfig.java
package com.spring.config;
import com.spring.services.HelloWorldFactory;
import com.spring.services.HelloWorldService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
#Configuration
public class HelloConfig {
#Bean
public HelloWorldFactory helloWorldFactory() {
return new HelloWorldFactory();
}
#Bean
#Profile("english")
#Primary
public HelloWorldService helloWorldServiceEnglish(HelloWorldFactory factory) {
return factory.createHelloWorldService("en");
}
#Bean
#Qualifier("spanish")
public HelloWorldService helloWorldServiceSpanish(HelloWorldFactory factory) {
return factory.createHelloWorldService("es");
}
#Bean
#Profile("french")
public HelloWorldService helloWorldServiceFrench(HelloWorldFactory factory) {
return factory.createHelloWorldService("fr");
}
#Bean
#Profile("german")
public HelloWorldService helloWorldServiceGerman(HelloWorldFactory factory) {
return factory.createHelloWorldService("de");
}
#Bean
#Profile("polish")
public HelloWorldService helloWorldServicePolish(HelloWorldFactory factory) {
return factory.createHelloWorldService("pl");
}
#Bean
#Profile("russian")
public HelloWorldService helloWorldServiceRussian(HelloWorldFactory factory) {
return factory.createHelloWorldService("ru");
}
}
DependencyInjectionApplication.java
package com.spring.componentScan;
import com.spring.controllers.GreetingController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan("com.spring")
public class DependencyInjectionApplication {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(DependencyInjectionApplication.class, args);
GreetingController controller = (GreetingController) ctx.getBean("greetingController");
controller.sayHello();
}
}
GreetingController.java
package com.spring.controllers;
import com.spring.services.HelloWorldService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
#Controller
public class GreetingController {
private HelloWorldService helloWorldService;
private HelloWorldService helloWorldServiceSpanish;
#Autowired
public void setHelloWorldService(HelloWorldService helloWorldService) {
this.helloWorldService = helloWorldService;
}
#Autowired
#Qualifier("spanish")
public void setHelloWorldServiceFrench(HelloWorldService helloWorldServiceSpanish) {
this.helloWorldServiceSpanish = helloWorldServiceSpanish;
}
public String sayHello() {
String greeting = helloWorldService.getGreeting();
System.out.println(helloWorldServiceSpanish.getGreeting());
System.out.println(greeting);
return greeting;
}
}
Application.properties
spring.profiles.active=english
Complete Source code:
If you consider this source code
#Bean(name = "french")
public HelloWorldService helloWorldServiceFrench(HelloWorldFactory factory) {
return factory.createHelloWorldService("fr");
}
#Bean
public HelloWorldService helloWorldServiceGerman(HelloWorldFactory factory) {
return factory.createHelloWorldService("de");
}
#Bean
public HelloWorldService helloWorldServicePolish(HelloWorldFactory factory) {
return factory.createHelloWorldService("pl");
}
#Bean
public HelloWorldService helloWorldServiceRussian(HelloWorldFactory factory) {
return factory.createHelloWorldService("ru");
}
here is no #Profile annotation, and thats why Spring creating multiple beans of same type, if you want them to recognized differently, try giving them well qualified explicit name by #Bean(name="polish") (or Spring anyway assign them by looking at #Bean method name) and then autowire using #Qualifier("polish")
Okay, so your example is not updated with the latest code. But I assume that you want to create multiple instances of the same bean type and use them for different languages. It's easy to achieve and you don't need to have #Profile and #Primary for that.
What you need is just assign qualifier for the bean instance (or use the one that spring assigns by default). And the inject bean by this qualifier.
#Bean
public HelloWorldService helloWorldServiceFrench(HelloWorldFactory factory) {
return factory.createHelloWorldService("fr");
}
#Bean
public HelloWorldService helloWorldServiceGerman(HelloWorldFactory factory) {
return factory.createHelloWorldService("de");
}
#Bean
public HelloWorldService helloWorldServicePolish(HelloWorldFactory factory) {
return factory.createHelloWorldService("pl");
}
#Bean
public HelloWorldService helloWorldServiceRussian(HelloWorldFactory factory) {
return factory.createHelloWorldService("ru");
}
Controller:
#Controller
public class GreetingController {
#Qualifier("helloWorldServiceGerman")
#Autowired
private HelloWorldService helloWorldServiceGerman;
#Qualifier("helloWorldServiceFrench")
#Autowired
private HelloWorldService helloWorldServiceFrench;
#Qualifier("helloWorldServicePolish")
#Autowired
private HelloWorldService helloWorldServicePolish;
#Qualifier("helloWorldServiceRussian")
#Autowired
private HelloWorldService helloWorldServiceRussian;
. . .
}
Update
You usually mark a bean as #Primary when you want to have one bean instance as a priority option when there are multiple injection candidates. Official doc with good example.
#Profile just narrows bean search, but still if you have multiple beans of the same type in the same profile - #Primary to the rescue (if you autowire by type, autowire by qualifier still works fine though).
Developers usually do this to avoid NoUniqueBeanDefinitionException that you had initially.
I have this ApplicationContextProvider class defined along with the MyApplication.java (entry point where the application is run):
package com.company.my.app;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
Have the package restapi with two classes in it (Greeting is just a class to hold data):
package com.company.my.app.restapi;
import com.company.my.app.ApplicationContextProvider;
import io.micrometer.core.instrument.Counter;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class GreetingController {
private static final Logger LOG = LoggerFactory.getLogger(GreetingController.class);
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
ApplicationContextProvider acp = new ApplicationContextProvider();
ApplicationContext context = acp.getContext();
if (context == null) LOG.info("app context is NULL");
Counter bean = context.getBean(Counter.class);
bean.increment();
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Finally the MyApplication class is:
package com.company.my.app;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.binder.MeterBinder;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Counter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#SpringBootApplication
public class MyApplication {
#Bean
public MeterBinder exampleMeterBinder() {
return (meterRegistry) -> Counter.builder("my.counter")
.description("my simple counter")
.register(meterRegistry);
}
#Configuration
public class CounterConfig {
#Bean
public Counter simpleCounter(MeterRegistry registry) {
return registry.counter("my.counter");
}
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
When I run the app and call http://localhost:8081/greeting in my browser, it crashes printing app context is NULL. How do I get the application context? I need it to retrieve the simple counter bean.
tl;dr: You don't need the context; there's a better way.
ApplicationContextAware is an artifact from much older versions of Spring, before many of the now-standard features were available. In modern Spring, if you need the ApplicationContext, just inject it like any other bean. However, you almost certainly shouldn't interact with it directly, especially for getBean, which should be replaced with injecting whatever you were getting.
In general, when you need a Spring bean, you should declare it as a constructor parameter. (If you have multiple constructors, you need to annotate one with #Autowired, but if there's only a single constructor, Spring is smart enough to know to use it.) If you're using Lombok, you can use #Value to automatically write the constructor, and Groovy and Kotlin have similar features.
In the specific case of Micrometer, which you're showing here, it is not conventional to declare individual metrics as beans because they are fine-grained tools intended to apply to specific code paths. (Some services might have 10 separate metrics to track various possible scenarios.) Instead, you inject the MeterRegistry and select the counters or other metrics that you need as part of your constructor. Here, your controller class should look like this. (I've eliminated the duplicate AtomicLong, but you could add it back in as you showed if there's a specific reason you need it.)
#RestController
public class GreetingController {
private static final Logger LOG = LoggerFactory.getLogger(GreetingController.class);
private static final String template = "Hello, %s!";
private final Counter counter;
public GreetingController(MeterRegistry meterRegistry) {
counter = meterRegistry.counter("my.counter");
}
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
counter.increment();
long count = (long) counter.count();
return new Greeting(count, String.format(template, name));
}
}
I'm building JPA configuration with multiple persistence units using different in-memory datasources, but the configuration fails resolving the qualified datasource for entity manager factory bean with the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method emfb in datasources.Application$PersistenceConfiguration required a single bean, but 2 were found:
- ds1: defined by method 'ds1' in class path resource [datasources/Application$PersistenceConfiguration.class]
- ds2: defined by method 'ds2' in class path resource [datasources/Application$PersistenceConfiguration.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
Here is the sample application
package datasources;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.sql.DataSource;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.apache.log4j.Logger;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.stereotype.Component;
#Configuration
#EnableAutoConfiguration(exclude = {
// HibernateJpaAutoConfiguration.class,
// DataSourceAutoConfiguration.class
JtaAutoConfiguration.class
})
#ComponentScan
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.build()
.run(args);
}
#Component
#Path("/ds")
public static class DsApi {
private final static Logger logger = Logger.getLogger(DsApi.class);
#Autowired(required = false)
#Qualifier("ds1")
private DataSource ds;
#GET
public String ds() {
logger.info("ds");
return ds.toString();
}
}
#Component
#Path("/em")
public static class EmApi {
private final static Logger logger = Logger.getLogger(EmApi.class);
#PersistenceContext(unitName = "ds2", type = PersistenceContextType.TRANSACTION)
private EntityManager em;
#GET
public String em() {
logger.info("em");
return em.toString();
}
}
#Configuration
#ApplicationPath("/jersey")
public static class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(DsApi.class);
register(EmApi.class);
}
}
#Configuration
public static class PersistenceConfiguration {
#Bean
#Qualifier("ds1")
public DataSource ds1() {
return new EmbeddedDatabaseBuilder().build();
}
#Bean
#Qualifier("ds2")
public DataSource ds2() {
return new EmbeddedDatabaseBuilder().build();
}
#Bean
#Primary
#Autowired
public LocalContainerEntityManagerFactoryBean emfb(#Qualifier("ds1") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(Application.class)
.persistenceUnit("ds1")
.build();
}
#Bean
#Autowired
public LocalContainerEntityManagerFactoryBean emfb2(#Qualifier("ds2") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(Application.class)
.persistenceUnit("ds2")
.build();
}
}
}
The error is indicating that at some point in the application, a bean is being injected by the type DataSource and not being qualified by name at that point.
It does not matter that you have added #Qualifier in one location. The injection is failing in some other location that has not been qualified. It's not your fault though because that location is in Spring Boot's DataSourceAutoConfiguration which you should be able to see in your stack trace, below the piece that you have posted.
I would recommend excluding DataSourceAutoConfiguration i.e. #SpringBootApplication(exclude = DataSourceAutoConfiguration.class). Otherwise, this configuration is only being applied to the bean you have made #Primary. Unless you know exactly what that is, it is likely to result in subtle and unexpected differences in behaviour between your DataSources.
Declare one of your DataSource as #Primary.
Also you have 2 beans of same type - LocalContainerEntityManagerFactoryBean, declare one of them #Primary as well, as follows:
#Configuration
public static class PersistenceConfiguration {
#Bean
#Primary
public DataSource ds1() {
return new EmbeddedDatabaseBuilder().build();
}
#Bean
public DataSource ds2() {
return new EmbeddedDatabaseBuilder().build();
}
#Bean
#Primary
#Autowired
public LocalContainerEntityManagerFactoryBean emfb(#Qualifier("ds1") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(DemoApplication.class)
.persistenceUnit("ds1")
.build();
}
#Bean
#Autowired
public LocalContainerEntityManagerFactoryBean emfb2(#Qualifier("ds2") DataSource ds, EntityManagerFactoryBuilder emfb) {
return emfb.dataSource(ds)
.packages(DemoApplication.class)
.persistenceUnit("ds2")
.build();
}
}
Try declaring the datasource beans outside the static class . I.e directly in Application.java
I'm writing a DAL with Spring Data and Hibernate but I'm running into a IllegalArgumentException exception which is stopping my work.
Here is the DALConf.java class which contains DataSource and persistence exception translation processor configurations
package my.dal.config;
import java.util.Properties;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
#Configuration
#ComponentScan(basePackages = { "my.dal" })
#PropertySource("classpath:dbconnection.properties")
public class DALConfig {
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_POOL_INITIAL_SIZE = "pool.initialsize";
private static final String PROPERTY_NAME_POOL_MAX_IDLE = "pool.maxidle";
#Resource
private Environment environment;
#Bean
public DataSource dataSource() throws Exception
{
Properties props = new Properties();
props.put("driverClassName", environment.getProperty(PROPERTY_NAME_DATABASE_DRIVER));
props.put("url", environment.getProperty(PROPERTY_NAME_DATABASE_URL));
props.put("username", environment.getProperty(PROPERTY_NAME_DATABASE_USERNAME));
props.put("password", environment.getProperty(PROPERTY_NAME_DATABASE_PASSWORD));
props.put("initialSize", environment.getProperty(PROPERTY_NAME_POOL_INITIAL_SIZE));
props.put("maxIdle", environment.getProperty(PROPERTY_NAME_POOL_MAX_IDLE));
BasicDataSource bds = BasicDataSourceFactory.createDataSource(props);
return bds;
}
#Bean
public PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor()
{
PersistenceExceptionTranslationPostProcessor b = new PersistenceExceptionTranslationPostProcessor();
return b;
}
}
Then here is the HibernateConfig.class which contains Hibernate configurations
package my.dal.config;
import java.util.Properties;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#ComponentScan(basePackages = { "my.dal" })
#PropertySource("classpath:hibernate.properties")
#EnableTransactionManagement
public class HibernateConfig {
private static final String PROPERTY_NAME_DAL_CLASSES_PACKAGE = "hibernate.dal.package";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.showsql";
#Resource
private Environment environment;
#Autowired
DataSource dataSource;
#Bean
public SessionFactory sessionFactory()
{
LocalSessionFactoryBean lsfb = new LocalSessionFactoryBean();
lsfb.setPackagesToScan(environment.getProperty(PROPERTY_NAME_DAL_CLASSES_PACKAGE));
Properties hibernateProperties = new Properties();
hibernateProperties.put("dialect", environment.getProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
hibernateProperties.put("show_sql", environment.getProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
lsfb.setHibernateProperties(hibernateProperties);
lsfb.setDataSource(dataSource);
return lsfb.getObject();
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator(){
return new HibernateExceptionTranslator();
}
#Bean
public HibernateTransactionManager transactionManager()
{
// HERE THE EXCEPTION IS THROWN
HibernateTransactionManager htm = new HibernateTransactionManager(sessionFactory());
return htm;
}
}
This is the DAO UserDAO.java
package my.dal.dao;
import my.models.dal.User;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
#Repository
public class UserDAO
{
private SessionFactory sessionFactory;
#Autowired
public UserDAO(SessionFactory sessionFactory) {
this.sessionFactory=sessionFactory;
}
#Transactional
public int insert(User user) {
return (Integer) sessionFactory.getCurrentSession().save(user);
}
#Transactional
public User getByUsername(String username) {
return (User) sessionFactory.getCurrentSession().get(User.class, username);
}
#Transactional
public void update(User user) {
sessionFactory.getCurrentSession().merge(user);
}
#Transactional
public void delete(String username) {
User u = getByUsername(username);
sessionFactory.getCurrentSession().delete(u);
}
}
Finally, this is the test class DALTest.java
package my.dal.tests;
import static org.junit.Assert.assertTrue;
import my.dal.config.DALConfig;
import my.dal.config.HibernateConfig;
import my.dal.dao.UserDAO;
import my.models.dal.User;
import org.hibernate.SessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#ContextConfiguration(classes = { DALConfig.class, HibernateConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class DALTest {
#Autowired
SessionFactory sessionFactory;
#Test
public void testGetUser() {
UserDAO userDAO = new UserDAO(sessionFactory);
User user = null;
user = userDAO.getByUsername("mrossi");
assertTrue(null != user);
}
}
The execution of the test ends with the following exception
...
Caused by: java.lang.IllegalArgumentException: Property 'sessionFactory' is required
at org.springframework.orm.hibernate4.HibernateTransactionManager.afterPropertiesSet(HibernateTransactionManager.java:247)
at org.springframework.orm.hibernate4.HibernateTransactionManager.<init>(HibernateTransactionManager.java:130)
at my.dal.config.HibernateConfig.transactionManager(HibernateConfig.java:66)
...
It is thrown at this line
HibernateTransactionManager htm = new HibernateTransactionManager(sessionFactory());
It seems like Spring cannot instantiate the sessionFactory bean but I don't know what could be the problem...
What do you think about that?
Thank you
You forgot to call
lsfb.afterPropertiesSet()
before getting the object from lsfb. afterPropertiesSet() is the method that builds and exposes the session factory.
One way to fix that is that you are using Constructor Injection for the sessionFactory which is not working with annotation exposed bean well. (Not sure how Spring 4 makes any improvement on that. I only used Spring 3.5 and below)
I would recommend to use getter/setter method injection for that in UserDAO.java
private SessionFactory sessionFactory;
#Autowired
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
So annotation can get the bean.
As JB Nizet suggested, you're missing the call to afterPropertiesSet()which is needed if you handle the object lifecycle yourself. I'd like to propose a slightly better version of your config to avoid this issue, which you might run into in other cases as well.
Whenever you configure a FactoryBean in JavaConfig, return the factory and refer to the actual object to be produced on the client methods. In your example this would look something like this:
#Configuration
class YourConfig {
#Bean
public LocalSessionFactoryBean sessionFactory() {
// setup factory
}
#Bean
public HibernateTransactionManager transactionManager(SessionFactory factory) {
return new HibernateTransactionManager(factory);
}
}
As you can see, we don't manually invoke any lifecycle callbacks on the factory as we have to return it as is. Spring will do that for us. It will also invoke ….getObject() on it to create the actual SessionFactory and hand it into the #Bean method to create the transaction manager.