Is it possible to inject beans using an XML file in Spring? - spring

I am trying to inject a bean into an application using an XML file. The main function has
try(ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/spring/application.xml")) {
context.registerShutdownHook();
app.setResourceLoader(context);
app.run(args);
} catch (final Exception ex) {
ex.printStackTrace();
}
I also have a Person POJO and is set in the xml file.
The xml defination is as follows:
<context:annotation-config/>
<bean id="person" class="hello.service.Person" p:name="Ben" p:age="25" />
<bean class="hello.HelloBeanPostProcessor"/>
The link to my repo is:
https://bitbucket.org/rkc2015/gs-scheduling-tasks-complete
It is the default guide from Spring boot that does a scheduled task.
I'm trying to inject the Person POJO defined in the xml file into a scheduled task.
I am currently getting this error:
Error creating bean with name 'scheduledTasks': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private hello.service.Person
hello.service.ScheduledTasks.person; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [hello.service.Person] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for
this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Can anyone please help? I am new to Spring.

You can use #ImportResource annotation to import xml configurations.
Documentation link
#SpringBootApplication
#EnableScheduling
#ImportResource("/spring/application.xml")
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication app = new SpringApplication(Application.class);
app.run();
}
}

If this is through spring bean you should have used #component annotation for you bean definition or else i application.xml you should have defined scheduledTasks bean also and with it member variable of person so that both beans are created and can be autowired.

Related

Spring JPA not implementing/autowiring repository despite #EnableJpaRepositories annotation

I'm getting an exception when I start my application, where Spring complain about UnsatisfiedDependencyException:
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'applicationConfig': Unsatisfied dependency expressed through field 'controlRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean found for dependency [com.oak.api.finance.repository.ControlRepository]: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
My application is organized in this format:
I declared my repository interfaces, with the proper Spring JPA annotations:
#RepositoryRestResource(collectionResourceRel = "exchange", path = "exchanges")
public interface ControlRepository extends PagingAndSortingRepository<Control, Long> {
}
I annotated the EntryPoint class that contains the main method
#SpringBootApplication
#EntityScan(basePackages = {"com.oak.api.finance.model.dto"})
#EnableJpaRepositories(basePackages = {"com.oak.api.finance.repository"})
public class EntryPoint {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(EntryPoint.class);
logger.info("Starting application");
ApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class);
// SpringApplication.run(EntryPoint.class, args);
ctx.getBean(ApplicationServer.class).start();
}
I used #Autowired to inject my repository into my spring config (java based) ApplicationConfig class:
#Autowired
private ControlRepository controlRepository;
#Autowired
private CompanyRepository companyRepository;
#Autowired
private SectorRepository sectorRepository;
Essentially I want to control the dependency on Spring and limit it to a couple of packages, (the repositories, the java config, and the program entry point - EntryPoint)
I assumed that, by specifying #EnableJpaRepositories with the package where my repositories are located, spring would create a proxy for my repository and instantiate an instance of that, and that by the time I call :
ApplicationContext ctx = new AnnotationConfigApplicationContext(ApplicationConfig.class)
The repositories instances would be present in the beans poole and would be possible to autowire them into my ApplicationConfig context, and then inject them into my controller.
This is clearly not happening, and Spring is complaining about the missing Bean to autowire, but I'm not sure what am I missing.
Below a snapshot of my packages:
any ideas?
My guess is your repositories are not being scanned, so as a result beans are not getting created. Can you try removing these 2 annotations
#EntityScan(basePackages = {"com.oak.api.finance.model.dto"})
#EnableJpaRepositories(basePackages = {"com.oak.api.finance.repository"})
And keep only #SpringBootApplication. If this is not working, you might need to check the package structure (if possible paste a screenshot here)
Edit 1
replace #SpringBootApplication with
#Configuration
#EnableAutoConfiguration
#ComponentScan("com.oak")
Edit2
Use
new SpringApplicationBuilder()
.sources(SpringBootApp.class)
.web(false)
.run(args);
Or use CommandLineRunner after changing ComponentScan path to "com.oak" as mh-dev suggested

Instantiation of bean failed : Specified class is an interface

I am facing an issue while creating a bean for dependency injection. Here is the scenario.
I am dealing with MongoDB repository, I have also created a class which uses it. I am trying to instantiate bean instance of both.
MongoDB reporsitory:
#Repository
public interface ProductGlobalTrendRepository extends MongoRepository<ProductGlobalTrend,String>{
public ProductGlobalTrend findByPid(#Param("pId") String pId);
}
The class which is using it:
#Service
#Scope("singleton")
public class ProductTrendService {
#Autowired
#Qualifier("productGlobalTrendRepo")
ProductGlobalTrendRepository productGlobalTrendRepo;
public ProductTrendService() {
super();
}
public void setProductGlobalTrendRepo(
ProductGlobalTrendRepository productGlobalTrendRepo) {
this.productGlobalTrendRepo = productGlobalTrendRepo;
}
public ProductTrendService(ProductGlobalTrendRepository productGlobalTrendRepo) {
super();
this.productGlobalTrendRepo = productGlobalTrendRepo;
}
}
The spring's bean config xml has these entries:
<bean id="productTrendService" class="com.api.services.ProductTrendService"> </bean>
<bean id="productGlobalTrendRepo" class="com.mongodb.repository.ProductGlobalTrendRepository"> </bean>
Following is the error I am getting:
19428 [localhost-startStop-1] WARN
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
- Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'productTrendService': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: com.mongodb.repository.ProductGlobalTrendRepository
com.api.services.ProductTrendService.productGlobalTrendRepo; nested
exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'productGlobalTrendRepo' defined in
class path resource [com/vstore/conf/spring-security.xml]:
Instantiation of bean failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [com.mongodb.repository.ProductGlobalTrendRepository]:
Specified class is an interface
It complains that repository is a interface class.
Can somebody please suggest a fix/workaround for this bean dependency injection ?
The problem is with the following information in your context file
<bean id="productGlobalTrendRepo"
class="com.mongodb.repository.ProductGlobalTrendRepository">
</bean>
You should create a class com.mongodb.repository.ProductGlobalTrendRepositoryImpl which implements com.mongodb.repository.ProductGlobalTrendRepository and provides implementation of its methods.
then change your bean declaration info as
<bean id="productGlobalTrendRepo"
class="com.mongodb.repository.ProductGlobalTrendRepositoryImpl">
</bean>
Behind the scene the object is created which is not possible with the interface.

Spring beans GeoModule bean injection to RepositoryRestMvcConfiguration

I'm currently testing spring data rest, and I want to expose the primary keys (ids) of my entities through the REST interface.
I have found that the proper (?) way to do this is:
public class IdExporterConfiguration extends RepositoryRestMvcConfiguration {
#Override
protected void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config) {
super.configureRepositoryRestConfiguration(config);
config.exposeIdsFor(User.class);
}
}
The problem is, that if I change my bean definition to this:
<bean class="test.project.util.IdExporterConfiguration"/>
From this:
<bean class="org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration"/>
my application fails to start...
The error is:
Could not autowire field: org.springframework.data.geo.GeoModule org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration.geoModule;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.data.geo.GeoModule] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Basically it says that it does not find a GeoModule bean, so it can't autowire it for the RepositoryRestMvcConfiguration base...
Now the fun part is, that is I define the bean:
<bean class="org.springframework.data.geo.GeoModule"/>
The error changes to:
Could not autowire field: org.springframework.data.geo.GeoModule org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration.geoModule;
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.data.geo.GeoModule] is defined:
expected single matching bean but found 2: jacksonGeoModule,org.springframework.data.geo.GeoModule#0
So if I don't define a bean, there is 0, but if I define one, there is 2?
I still don't know why, but if I use the #Configuration annotation, then it works...
No GeoModule bean needed, but how can it be, that with the original config as XML bean definition it works, but with the subclass, it does not?

Spring-boot with Liquibase Overloading Property

I am using Spring boot and Liquibase.
Using this url as guidelines
http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/
In pom.xml, the below entry is present so that spring boot knows about liquibase.
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
and put the changelog file in resources folder.
db.changelog-master.xml
Now Spring boot first tring to find db.changelog-master.yaml in classpath and throwing the exception like this.
Cannot find changelog location: class path resource [db/changelog/db.changelog-master.yaml
To Fix the Issue, I have added the bean like below in my class and tried to set changeLog proprty.
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class SampleDataJpaApplication {
#Autowired
LiquibaseProperties properties;
#Autowired
private DataSource dataSource;
#Bean
public SpringLiquibase liquibase() {
SpringLiquibase liquibase = new SpringLiquibase();
properties.setChangeLog("classpath:/db/changelog/db.changelog-master.xml");
liquibase.setChangeLog(this.properties.getChangeLog());
liquibase.setContexts(this.properties.getContexts());
liquibase.setDataSource(this.dataSource);
liquibase.setDefaultSchema(this.properties.getDefaultSchema());
liquibase.setDropFirst(this.properties.isDropFirst());
liquibase.setShouldRun(this.properties.isEnabled());
return liquibase;
}
public static void main(String[] args) throws Exception {
Logger logger = LoggerFactory.getLogger("SampleDataJpaApplication");
SpringApplication springApplication = new SpringApplication();
springApplication.run(SampleDataJpaApplication.class, args);
}
}
but it is failing with the message.
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'sampleDataJpaApplication': Injection of
autowired dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field:
org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties
sample.data.jpa.SampleDataJpaApplication.properties; nested exception
is org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
[org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties]
found for dependency: expected at least 1 bean which qualifies as
autowire candidate for this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Caused by: org.springframework.beans.factory.BeanCreationException:
Could not autowire field:
org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties
sample.data.jpa.SampleDataJpaApplication.properties; nested exception
is org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
[org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties]
found for dependency: expected at least 1 bean which qualifies as
autowire candidate for this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Please provide the inputs here, why i am getting this exception or Is there any any other available way to override the same class so that i can change the changeLog property of liquibase properties.
I'm not entirely sure what the exact runtime path to your change log is, but why don't you just use the "liquibase.*" properties in application.properties? You should be able to leave out the Liquibase #Bean and let Boot do it for you.
If you prefer to add you own Liquibase #Bean then take the hint and make sure you define a LiquibaseProperties bean as well (e.g. by declaring #EnableConfigurationProperties(LiquibaseProperties.class)).

Failed to load ApplicationContext in JUnit with JNDI datasource

I have some troubles testing my application, whereas it works well in normal execution.
I think it comes from JNDI resources which are not found, but I don't understand why and how to fix it.
When I start my Junit test, I got this error:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99)
at ...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'DAOImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.sql.DataSource com.sample.DAOImpl.myDatasource; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=myDatasource)}
Related cause: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myDatasource' defined in URL [file:src/test/resources/spring/test-dao-config.xml]: Invocation of init method failed; nested exception is javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:288)
at ...
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.sql.DataSource com.sample.DAOImpl.myDatasource; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=myDatasource)}
at ...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=myDatasource)}
at ..
Here is my configuration:
Context.xml
<Resource name="jdbc/myDatasource" auth="Container" type="javax.sql.DataSource"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:#database:99999:instance"
username="user"
password="password"
validationQuery="select 1 from dual"
testOnBorrow ="true"
maxActive="5"
maxIdle="1"
maxWait="-1" />
test-dao-config.xml
<bean id="myDatasource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/myDatasource" />
</bean>
DaoImpl
#Repository
public class DacsDAOImpl implements DacsDAO
{
private final static Logger LOGGER = LoggerFactory.getLogger(DAOImpl.class);
#Autowired
#Qualifier("myDatasource")
private DataSource myDatasource;
....
}
And my tests
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/test/resources/spring/test-dao-config.xml" })
public class MyDAOImplTest
{
private MyDAO dao;
#BeforeClass
public static void initJndi() throws IllegalStateException, NamingException
{
//some test, but doesn't work
// SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
// builder.bind("java:comp/env/jdbc/myDatasource", "myDatasource");
// builder.activate();
}
#Before
public void setUp() throws IllegalStateException, NamingException
{
dao = new MyDAOImpl();
}
#Test
public void testTotalUser()
{
int result = dao.getTotalUser();
Assert.assertEquals(0, result);
}
}
Thanks
You are running in a test case so everything in your Context.xml isn't available as that is only available on tomcat. Why do you need a jndi lookup in your test case anyway? If you want to test your dao use an in-memory database like hsql, h2 or derby and use that instead. Spring has some nice tags to make it easy for you.
<jdbc:embedded-database id="myDataSource" type="H2">
// Add some init scripts here.
</jdbc:embedded-database>
If you really need to do a JNDI lookup you are almost there in your test case. However you have to register a DataSource not a String. So you still will need to construct some (in-memory) datasource and bind that to the mock jndi location
#BeforeClass
public static void initJndi() throws IllegalStateException, NamingException
{
//some test, but doesn't work
// Construct in-memory database
SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
builder.bind("java:comp/env/jdbc/myDatasource", myDatasource); //Actual datasource not a String!
builder.activate();
}
And finally your test is also flawed, you are loading your context but aren't doing anything with it. You are constructing a MyDAOImpl in your #Before method. Why even bother loading the context as you are doing nothing with.

Resources