Autowire a bean within Spring's Java configuration - java

Is it possible to use Spring's #Autowired annotation within a Spring configuration written in Java?
For example:
#Configuration
public class SpringConfiguration{
#Autowired
DataSource datasource;
#Bean
public DataSource dataSource(){
return new dataSource();
}
// ...
}
Obviously a DataSource interface cannot be directly instantiated but I directly instantiated it here for simplification. Currently, when I try the above, the datasource object remains null and is not autowired by Spring.
I got #Autowired to work successfully with a Hibernate SessionFactory object by returning a FactoryBean<SessionFactory>.
So my question specifically: is there a way to do that with respect to a DataSource? Or more generally, what is the method to autowire a bean within a Spring Java Configuration?
I should note I am using Spring version 3.2.

If you need a reference to the DataSource bean within the same #Configuration file, just invoke the bean method.
#Bean
public OtherBean someOtherBean() {
return new OtherBean(dataSource());
}
or have it autowired into the #Bean method
#Bean
public OtherBean someOtherBean(DataSource dataSource) {
return new OtherBean(dataSource);
}
The lifecycle of a #Configuration class sometimes prevents autowiring like you are suggesting.

For completeness sake: Yes it is technically possible to use #Autowired annotation in spring #Configuration classes, and it works the same way as for other spring beans. But the accepted answer shows how the original problem should be solved.

maybe you can use constructor to inject some bean from other configuration file to your configuration,while #Autowired may not work correct in the configuration file
#Configuration
public class AppConfig {
private DataSource dataSource;
public AppConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
#Bean
public OtherBean otherBean(){
return new OtherBean(dataSource);
}
}

beans configured with #Configuration class can't be autowired ,
So you cannot use #Autowiring and #configuration together

Related

How do I unit-test the controller of a Spring Boot application with a DataSource dependency in a #ComponentScan #Configuration

Consider the following basic Spring Boot application:
#SpringBootApplication
#ComponentScan(basePackages = "webmvctestproblem.foo")
public class Application {
public static void main(String[] args) {
run(Application.class, args);
}
}
It contains only two other beans. One controller:
#RestController
class Greeter {
#GetMapping("/")
String greet() {
return "Hello, world!";
}
}
And one configuration in webmvctestproblem.foo containing a DataSource dependency:
#Configuration
class Bar {
#Autowired
private DataSource dataSource;
}
Running the application normally (through gradlew bootrun, e.g.) succeeds. Thus, confirming that the app is configured correctly under normal conditions.
However, running the following test causes a runtime error because Spring still attempts to resolve the data source bean dependency on the configuration class:
#RunWith(SpringRunner.class)
#WebMvcTest
public class GreeterTest {
#Test
public void test() throws Exception {
}
}
Of course, there isn't one to resolve because the test is a #WebMvcTest that is designed to create only MVC-related beans.
How do I get rid of the error? I have already tried excluding the configuration class using the excludeFilters attribute of the existing #WebMvcTest annotation and a new #ComponentScan annotation on the test itself. I don't want to resort to turning it into an integration test with #SpringBootTest.
(The project is also available on GitHub for convenience.)
If the DataSource is not mandatory for the test run, simply mock the DataSource with #MockBean in the test class.
#RunWith(SpringRunner.class)
#WebMvcTest
public class GreeterTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private DataSource dataSource;
#Test
public void shouldGreet() throws Exception {
mockMvc
.perform(get("/"))
.andExpect(content().string("Hello, world!"));
}
}
Spring will automatically create a Mock for DataSource and inject it into the running test application context.
Based on your source code it works.
(Btw: Your source code has a minor issue. The Greeter controller class is in the base package but the component scan only scans on the "foo" package. So there will be no Greeter controller on the test run if this isn't fixed.)
#WebMvcTest creates a "slice" of all the beans relevant to WebMvc Testing (Controllers, Json conversion related stuff and so forth).
You can examine the defaults in org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTypeExcludeFilter
In order to find which beans are actually supposed to be Run Spring must resolve them somehow, right?
So spring test tries to understand what should be loaded and what not by passing through these filters.
Now, if you mark anything with #Configuration spring "knows" that this is the place where the place should be found. So it will load the configuration and then will check which beans defined in this configuration must actually be loaded. However the object of configuration itself must be loaded anyway.
Now the process of loading the configuration object includes injecting stuff into these configurations - this is lifecycle of object creation of spring.
And this is a source of mistake here:
#Configuration
class Bar {
#Autowired
private DataSource dataSource;
}
Spring loads Bar and tries as a part of loading this object to autowire the data source. This fails since the DataSource Bean itself is excluded by filters.
Now in terms of solution:
First of all, why do you need this DataSource to be autowired in the Configuration object? Probably you have the bean that uses it, lets call it "MyDao", otherwise I don't see a point of such a construction, since #Configuration-s are basically a place to define bean and you shouldn't put business logic there (if you do - ask a separate question and me/our colleagues will try to help and suggest better implementation).
So I assume you have something like this:
public class MyDao {
private final DataSource dataSource;
public MyDao(DataSource dataSource) {
this.dataSource = dataSource;
}
}
#Configuration
class Bar {
#Autowired
private DataSource dataSource;
#Bean
public MyDao myDao() {
return new MyDao(dataSource);
}
}
In this case however you can rewrite the configuration in a different way:
#Configuration
class Bar {
// Note, that now there is no autowired datasource and I inject the parameter in the bean instead - so that the DataSource will be required only if Spring will have to create that MyDao bean (which it won't obviously)
#Bean
public MyDao myDao(DataSource dataSource) {
return new MyDao(dataSource);
}
}
Now the Bar object will still be created - as I've explained above, but it beans including MyDao of course won't be created, problem solved!
The solution with #Autowired(required=false) provided by #Anish B. should also work - spring will attempt to autowire but won't fail because the data source is unavailable, however you should think whether its an appropriate way to deal with this issue, your decision...
Before you can #Autowire the DataSource bean you need to define the DataSource in some config class or in the properties file. Something like this
spring.datasource.url = jdbc:mysql://localhost/abc
spring.datasource.name=testme
spring.datasource.username=xxxx
spring.datasource.password=xxxx
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
Or
#Configuration
public class JpaConfig {
#Bean
public DataSource getDataSource()
{
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:file:C:/temp/test");
dataSourceBuilder.username("sa");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
You should use
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#RunWith(SpringRunner.class)
on your test class, then you can inject
#Autowired
private MockMvc mockMvc;
and use it in test
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andReturn();
i suggest you read the documentation about testing. You can test a spring boot application in 100s of different ways.
WebMvcTest
as suggested by the documentation try, defining what controller class that you want to test in the #WebMvcTest annotation.
#RunWith(SpringRunner.class)
#WebMvcTest(Greeter.class)
public class GreeterTest {
#Autowired
private MockMvc mockMvc;
#Test
public void shouldGreet() throws Exception {
mockMvc
.perform(get("/"))
.andExpect(content().string("Hello, world!"));
}
}

DAO classes have the same #Qualifier

I have the source code of a website.
This website uses spring, and I'am confused about the Injection.
There are some DAO classes, on these DAO with a Qualifier :
#Repository("myDao")
#Qualifier("myTransactionManager")
public class MyCategoryDao {
}
But there is also another bean with the same name of the Qualifier.
And this bean is a JpaTransactionManager.
#Configuration
#EnableTransactionManagement(mode = AdviceMode.PROXY)
#ComponentScan(basePackages = "com.my.repository")
public class DatabaseConfig {
#Primary
#Bean(name = "myTransactionManager")
public JpaTransactionManager jpaTransactionManager(
#Named("myEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
}
}
I don't know why the bean and the DAOs have the same name/Qualifier.
Wouldn't it conflict the others?
Spring will check for type first, and if there is any ambiguity, then only it will check for the name specified in #Qualifier. In your case, types of both the beans are different, so that should work fine.

How to inject different services at runtime based on a property with Spring without XML

I am using Spring Boot for Java standalone application. I have a bean which makes use of a service. I want to inject different implementations of that service at runtime, based on a property in a properties file with Spring (4 for that matter).
This sounds like the Factory pattern, but Spring also allows using annotations to solve the problem, like this.
#Autowired #Qualifier("selectorProperty") private MyService myService;
Then in the beans.xml file I have an alias, so that I can use the property in the #Qualifier.
<alias name="${selector.property}" alias="selectorProperty" />
And in my different implementations I would have different qualifiers.
#Component("Selector1")
public class MyServiceImpl1
#Component("Selector2")
public class MyServiceImpl2
application.properties
selector.property = Selector1
selector.property = Selector2
Whereas regarding the factory pattern, in Spring you can use ServiceLocatorFactoryBean to create a factory that would give you the same functionality.
<bean
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean"
id="myServiceFactory">
<property
name="serviceLocatorInterface"
value="my.company.MyServiceFactory">
</property>
</bean>
public interface MyServiceFactory
{
MyService getMyService(String selector);
}
And then in your bean you can use something like this to get the right implementation at runtime depending on the value of the property.
#Value("${selector.property}") private String selectorProperty;
#Autowired private MyServiceFactory myServiceFactory;
private MyService myService;
#PostConstruct
public void postConstruct()
{
this.myService = myServiceFactory.getMyService(selectorProperty);
}
But the problem with this solution is that I could not find a way to avoid using XML to define the factory, and I would like to use only annotations.
So the question would be, is there a way to use the ServiceLocatorFactoryBean (or something equivalent) using only annotations, or am I forced to use the #Autowired #Qualifier way if I do not want to define beans in XML? Or is there any other way to inject different services at runtime based on a property with Spring 4 avoiding XML? If your answer is just use the #Autowired #Qualifier with the alias, please give a reason why that is better than using a well known factory pattern.
Using the extra XML is forcing me to use #ImportResource("classpath:beans.xml") in my Launcher class, which I'd rather not use either.
Thanks.
Actually, you can use ServiceLocatorFactory without XML by declaring it as a bean in your configuration file.
#Bean
public ServiceLocatorFactoryBean myFactoryServiceLocatorFactoryBean()
{
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(MyServiceFactory.class);
return bean;
}
#Bean
public MyServiceFactory myServiceFactory()
{
return (MyServiceFactory) myFactoryServiceLocatorFactoryBean().getObject();
}
Then you can still use the factory as usual, but no XML is involved.
#Value("${selector.property}") private String selectorProperty;
#Autowired #Qualifier("myServiceFactory") private MyServiceFactory myServiceFactory;
private MyService myService;
#PostConstruct
public void postConstruct()
{
this.myService = myServiceFactory.getMyService(selectorProperty);
}
I'm using Spring profiles
For example with dataSources
Using it you can define as many dataSources, as you like
#Configuration
#Profile("dev")
public class StandaloneDataConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
#Configuration
#Profile("cloud")
public class CloudDataConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
And in runtime, by specifying
-Dspring.profiles.active="myProfile"
you active one or another configuration (All of them must be imported in your main Configuration, they are just ignored based on active profile).
Here is a good article:
http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/

Spring batch 2.2 JavaConfig

I'm trying to get Spring Batch 2.2 working with JavaConfig.
Nowadays they have a #EnableBatchProcessing annotation that sets up a lot of things.
Default that annotation uses a datasource for its job data, but we don't want to save this data and don't want to create the table for it. The documentation says something about customizing but I have not been able to get it working:
The user has to provide a DataSource as a bean in the context, or else implement BatchConfigurer in the configuration class itself, e.g.:
public class AppConfig extends DefaultBatchConfigurer {
In our older version we've been able to use MapJobRepositoryFactoryBean class so it keeps all its data in memory. Is there anyway to use the full JavaConfig way and not define a DataSource? I've not been able to get it working.
Even if I define two data sources (one HSQL in-memory that never gets used) and our real Oracle datasource it does not work because it finds two data sources instead of one.
Anyone have an idea how to get this working? Or is the only solution going back to configuring this in the XML way?
Assuming that no other artifacts require a DataSource, you can use java config to create a context without a DataSource. To do that, your configuration will need to extend DefaultBatchConfigurer as you point out. In there, you'll override two methods, createJobRepository() and setDataSource(). Below is an example context (it doesn't define a job or steps, but it bootstraps all the related beans correctly).
#Configuration
#EnableBatchProcessing
public static class BatchConfiguration extends DefaultBatchConfigurer {
#Override
protected JobRepository createJobRepository() throws Exception {
MapJobRepositoryFactoryBean factory =
new MapJobRepositoryFactoryBean();
factory.afterPropertiesSet();
return (JobRepository) factory.getObject();
}
#Override
#Autowired
public void setDataSource(DataSource dataSource) {
if(dataSource != null) {
super.setDataSource(dataSource);
}
}
#Bean
public DataSource dataSource() {
return null;
}
}
I do think that simplifying this would be a useful feature and have added it to Jira. You can track it's progress here: https://jira.springsource.org/browse/BATCH-2048
Just define a dataSource() method in your BatchConfig Class
Here is how
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(driverUrl);
dataSource.setUsername(driverUsername);
dataSource.setPassword(driverPassword);
return dataSource;
}
This will automatically be invoked while setting up the TransactionManager

Can I create a prototype scoped bean with Spring Javaconfig?

The old docs for Spring Javaconfig say that I can use
#Bean(scope=DefaultScopes.PROTOTYPE)
to get a prototype bean, but Spring 3.0.5's #Bean doesn't seem to have this property.
Is there any way to control the scope of a bean in Javaconfig?
Use #Scope instead.
Also, DefaultScopes is not available in Spring core, but you can use BeanDefinition.SCOPE_PROTOTYPE and BeanDefinition.SCOPE_SINGLETON for convenience.
You can add #Scope("prototype") for example:
#Bean
#Scope("prototype")
public DemoDao getDao() {
DemoDao dao = new DemoDao();
dao.setAddress("annoted:address");
dao.setName("annoted:name");
return dao;
}
Use the following for Java config,
#Bean
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public SpringBean springBean(){
SpringBean bean = new SpringBean();
return bean;
}
Or simply,
#Scope(value = "prototype")
Refer #Scope annotation

Categories

Resources