I want to reference a java config class in my Spring config xml file, but failed, please see below my code:
Bean1 class :
package c2_5_2.ref.javaconfig.from.xml;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component("bean1")
public class Bean1 {
#Autowired
public Bean1(Bean2 bean2)
{
this.bean2 = bean2;
}
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
}
Bean2 class :
package c2_5_2.ref.javaconfig.from.xml;
import java.util.List;
public class Bean2 {
private List<String> nameList;
public List<String> getNameList() {
return nameList;
}
public void setNameList(List<String> nameList) {
this.nameList = nameList;
}
public void displayNames()
{
for(String name : nameList)
{
System.out.println(name);
}
}
}
Bean3 Class :
package c2_5_2.ref.javaconfig.from.xml;
public class Bean3 {
private Bean1 b1;
public Bean1 getB1() {
return b1;
}
public void setB1(Bean1 b1) {
this.b1 = b1;
}
public void introduce()
{
System.out.println("this is bean 3");
}
}
Java Config Class :
package c2_5_2.ref.javaconfig.from.xml;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages= {"c2_5_2.ref.javaconfig.from.xml"})
public class BeanConfig {
#Bean(name="bean2")
public Bean2 getBean2()
{
Bean2 b2 = new Bean2();
List<String> nameList = new ArrayList<String>();
nameList.add("Bitt");
nameList.add("Rock");
nameList.add("Lucas");
nameList.add("Crius");
b2.setNameList(nameList);
return b2;
}
}
BeanConfig.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="c2_5_2.ref.javaconfig.from.xml.BeanConfig"></bean>
<bean id="bean3" class="c2_5_2.ref.javaconfig.from.xml.Bean3"></bean>
</beans>
Run Test method :
private static void test3()
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("c2_5_2/ref/javaconfig/from/xml/BeanConfig.xml");
Bean3 b3 = ctx.getBean(Bean3.class);
b3.introduce();
Bean1 b1 = ctx.getBean(Bean1.class);
}
Get following output and error message:
Oct 13, 2017 9:55:16 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext#6d9c638: startup date [Fri Oct 13 09:55:16 CST 2017]; root of context hierarchy
Oct 13, 2017 9:55:16 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [c2_5_2/ref/javaconfig/from/xml/BeanConfig.xml]
**this is bean 3**
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'c2_5_2.ref.javaconfig.from.xml.Bean1' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:348)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:335)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)
at c2_5_2.ref.javaconfig.from.xml.Test_Driven.test3(Test_Driven.java:33)
at c2_5_2.ref.javaconfig.from.xml.Test_Driven.main(Test_Driven.java:12)
So from the output we can see the spring configuration xml file is found by spring framework because we got output "This is bean 3", but other 2 beans, "bean1" and "bean2" cannot be found even though I put the qualified name of BeanConfig class as a value to class property of the bean element in BeanConfig.xml file.
My concern is why spring cannot find the Bean definition, how to fix it?
if you use xml as config file to load the context you need to add <context:component-scan base-package="c2_5_2.ref.javaconfig.from.xml" /> which will fix your problem. since you code just create the BeanConfig class as a bean but it doesn't know that it is a configuration and trigger the scan the package. because you just create a bean.
once you add context-scan-package in ur xml, it will scan and get the BeanConfig file as config class which will create the bean2 and scan the package (redundant package)
Via component scanning
#Configuration is meta-annotated with #Component, therefore
#Configuration classes are candidates for component scanning
(typically using Spring XML's element) and
therefore may also take advantage of #Autowired/#Inject like any
regular #Component. In particular, if a single constructor is present
autowiring semantics will be applied transparently:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="c2_5_2.ref.javaconfig.from.xml.BeanConfig"></bean>
<bean id="bean3" class="c2_5_2.ref.javaconfig.from.xml.Bean3"></bean>
<context:component-scan base-package="c2_5_2.ref.javaconfig.from.xml" />
</beans>
update: if you want to create the bean as configuration file you can do:
Via Spring XML
<beans>
<context:annotation-config/>
<bean class="c2_5_2.ref.javaconfig.from.xml.BeanConfig"/>
</beans>
see detail : how spring look for #Configuration
Related
I am trying to use the properties file and inject it to my fields of a class, but I am getting null .
I have created a properties file called fortune.properties:
fortune1=Have a good day
fortune2=Have a bad day
fortune3=Have a medioccure day
Then I am loading it in the applicationContext.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Enable Component Scanning -->
<context:component-scan base-package="com.luv2code.springdemo"></context:component-scan>
<context:property-placeholder location="classpath:fortune.properties" />
</beans>
In the RandomFortuneClass, I am using value injection using #Value() annotation:
package com.luv2code.springdemo;
import java.util.Random;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component()
public class RandomFortuneService implements FortuneService {
#Value("${fortune1}")
private String fortune1;
#Value("${fortune2}")
private String fortune2;
#Value("${fortune3}")
private String fortune3;
String[] fortunes = new String[] {fortune1, fortune2, fortune3};
private Random random = new Random();
#Override
public String getFortune() {
int index = random.nextInt(fortunes.length);
return fortunes[index];
}
}
Then I am declaring fortuneService in SwimCoach class using constructor injection:
package com.luv2code.springdemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component()
public class SwimCoach implements Coach {
private FortuneService fortuneService;
#Autowired
SwimCoach(#Qualifier("randomFortuneService") FortuneService fs) {
fortuneService = fs;
}
#Override
public String getDailyWorkout() {
System.out.println();
return "Swim 5m everyday";
}
#Override
public String getDailyFortune() {
return fortuneService.getFortune();
}
}
Now when I try to call the method getDailyFortune in the AnnotationDemoApp class, I always get null as output.
package com.luv2code.springdemo;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationDemoApp {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Coach theCoach = context.getBean("swimCoach", Coach.class);
System.out.println(theCoach.getDailyFortune());
context.close();
}
}
What is the error here?
Kindly comment if more information is needed.
Here is my file hierarchy:
PS: I am following the spring & hibernate course for beginners by Chad Darby.
You simply need to move fortune.properties into /src/main/resources, but let me suggest a cleaner way...
First, drop the xml configuration, it's outdated by several years and everything you can do there can be done via java annotations.
Second, add your property files to src/main/resources.
Third, tell Spring where to resolve your properties from, you can do this by adding #PropertySources({#PropertySource("fortune.properties")}) to a configuration class like this (also note that I've added the #ComponentScan and #Configuration annotations here):
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
#Configuration
#ComponentScan
#PropertySource("fortune.properties")
public class Config {
}
Fourth, in your main method, use the AnnotationConfigApplicationContext as opposed to the ClassPathXmlApplicationContext. I think you'll find it's much cleaner.
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Coach theCoach = context.getBean("swimCoach", Coach.class);
System.out.println(theCoach.getDailyFortune());
}
Can anyone tell me what is the error in this program and how to correct the error? I have given proper annotation and still getting errors.
I need help fixing this error I get when trying to deploy . Why isn't the Car bean being defined? Am I missing something in my web.xml or do I have to map the customerService somehow? I am using annotations for mapping. any help would be much appreciated. Here is the error log entry from the localhost log:
App.java
package om.venkatesh.omshakthi;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
public static void main( String[] args )
{
ApplicationContext context=new ClassPathXmlApplicationContext("Spring.xml");
Car obj=(Car)context.getBean("car");
obj.drive();
}
}
Car.java
package om.venkatesh.omshakthi;
import org.springframework.stereotype.Component;
#Component
public class Car implements Vehicle{
public void drive()
{
System.out.println("Car");
}
}
Vehicle.java
package om.venkatesh.omshakthi;
public interface Vehicle {
void drive();
}
Spring.xml file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context = "http://www.springframework.org/schema/context"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<bean id="tyre" class="om.venkatesh.omshakthi.Tyre">
</bean>
</beans>
Without xml you can do it like this:
Create config class:
#Configuration
public class AppConfig {
#Bean
public Car car() {
return new Car();
}
}
Main class:
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
Car car = (Car) ctx.getBean("car");
car.drive();
}
}
Spring.xml has no bean with id "car" (although there is one for "tyre")
You need to define your bean in XML
<bean id="car" class="om.venkatesh.omshakthi.Car">
You don`t need #Component annotation when you use ClassPathXmlApplicationContext.
Just define a Car bean in your xml:
<bean id="car" class="om.venkatesh.omshakthi.Car">
I want to test a class that simple shutdown the application:
#Component
public class ShutdownManager {
#Autowired
private ApplicationContext applicationcontext;
public int shutdown(int returnCode) {
return SpringApplication.exit(applicationcontext, () -> returnCode);
}
}
The test case I created is this:
public class ShutdownManagerTest {
#Test
public void should_shutdown_gracefully() {
SpringApplication app = new SpringApplication(TestClass.class);
ConfigurableApplicationContext context = app.run();
ShutdownManager shutdownManager = (ShutdownManager)context.getBean("shutdownManager");
int result = shutdownManager.shutdown(10);
assertThat(result).isEqualTo(10);
}
#SpringBootApplication
#ImportResource("classpath:/core/shutdownmanagertest.xml")
private static class TestClass {
public TestClass() {
}
public static void main(String[] args) {
}
}
}
My shutdownmanagertest.xml contains only one bean:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="shutdownManager" class="com.mycodebase.ShutdownManager" />
</beans>
However, when I run this, it complains that:
Field myOtherService in com.mycodebase.MyTask required a bean of type 'com.mycodebase.MyOtherService ' that could not be found.
The class MyTask is in the same package of the ShutdownManager which contains a field myOtherService which has an #Autowire annocation. But it is not defined in my test xml which contains just one bean.
Can someone help me understand why it tries to resolve a bean that is not in the context?
It's doing that because that's how #SpringBootApplication works.
#SpringBootApplication:
This is a convenience annotation that is equivalent to declaring #Configuration, #EnableAutoConfiguration and #ComponentScan.
#ComponentScan
If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.
So everything in the same or a subpackage of ShutdownManagerTest is being picked up.
I am getting
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'springTest': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: com.learn.stackoverflow.general.Person
com.learn.stackoverflow.general.SpringTest.person; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [com.learn.stackoverflow.general.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)}
even after using all the annotations, I have annotated all my classes with #Component and adding component scan but still #Autowired gives me error.
Below are my classes:
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Component;
#Component
public class SpringTest {
#Autowired
Person person;
public static void main(String[] args) {
testApplicationContext();
}
private static void testApplicationContext() {
// here static and instance initializers of Person class will be invoked right away, even when we are not calling getBean method
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("C:\\E_Drive\\Projects\\Workspace\\Test\\CS101\\src\\com\\learn\\stackoverflow\\general\\bean.xml");
SpringTest springTest = (SpringTest) applicationContext.getBean("springTest");
}
}
Person class:
import org.springframework.context.annotation.Scope;
import com.bea.core.repackaged.springframework.stereotype.Component;
#Component
#Scope(value="singleton")
public class Person {
static{
System.out.println("1");
}
{
System.out.println("2");
}
}
XML file:
<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.learn.stackoverflow.general"/>
<!-- <bean id = "person" class = "com.learn.stackoverflow.general.Person" scope="singleton">
</bean> -->
<bean id = "springTest" class = "com.learn.stackoverflow.general.SpringTest" scope="singleton">
</bean>
</beans>
Use the right import in the Person.class. Instead of
import com.bea.core.repackaged.springframework.stereotype.Component;
you have to use
import org.springframework.stereotype.Component;
BTW you can avoid using
#Scope(value="singleton")
since singleton is a default scope for Spring beans.
I am writing a class that implements the following method:
public void run(javax.sql.DataSource dataSource);
Within this method, I wish to construct a Spring application context using a configuration file similar to the following:
<bean id="dataSource" abstract="true" />
<bean id="dao" class="my.Dao">
<property name="dataSource" ref="dataSource" />
</bean>
Is it possible to force Spring to use the DataSource object passed to my method wherever the "dataSource" bean is referenced in the configuration file?
I have been in the exact same situation. As nobody proposed my solution (and I think my solution is more elegant), I will add it here for future generations :-)
The solution consists of two steps:
create parent ApplicationContext and register your existing bean in it.
create child ApplicationContext (pass in parent context) and load beans from XML file
Step #1:
//create parent BeanFactory
DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory();
//register your pre-fabricated object in it
parentBeanFactory.registerSingleton("dataSource", dataSource);
//wrap BeanFactory inside ApplicationContext
GenericApplicationContext parentContext =
new GenericApplicationContext(parentBeanFactory);
parentContext.refresh(); //as suggested "itzgeoff", to overcome a warning about events
Step #2:
//create your "child" ApplicationContext that contains the beans from "beans.xml"
//note that we are passing previously made parent ApplicationContext as parent
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[] {"beans.xml"}, parentContext);
I discovered two Spring interfaces can be used to implement what I need. The BeanNameAware interface allows Spring to tell an object its name within an application context by calling the setBeanName(String) method. The FactoryBean interface tells Spring to not use the object itself, but rather the object returned when the getObject() method is invoked. Put them together and you get:
public class PlaceholderBean implements BeanNameAware, FactoryBean {
public static Map<String, Object> beansByName = new HashMap<String, Object>();
private String beanName;
#Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
#Override
public Object getObject() {
return beansByName.get(beanName);
}
#Override
public Class<?> getObjectType() {
return beansByName.get(beanName).getClass();
}
#Override
public boolean isSingleton() {
return true;
}
}
The bean definition is now reduced to:
<bean id="dataSource" class="PlaceholderBean" />
The placeholder receives its value before creating the application context.
public void run(DataSource externalDataSource) {
PlaceholderBean.beansByName.put("dataSource", externalDataSource);
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
assert externalDataSource == context.getBean("dataSource");
}
Things appear to be working successfully!
The second solution causes an exception because of a refresh problem. A more elegant way will be adding objects to the context and then loading xml definitions using the xmlreader.
Thus:
ObjectToBeAddedDynamically objectInst = new ObjectToBeAddedDynamically();
DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory();
parentBeanFactory.registerSingleton("parameterObject", objectInst);
GenericApplicationContext parentContext = new GenericApplicationContext(parentBeanFactory);
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(parentContext);
xmlReader.loadBeanDefinitions(new FileSystemResource("beandefinitions.xml"));
parentContext.refresh();
ObjectUsingDynamicallyAddedObject userObjectInst= (ObjectUsingDynamicallyAddedObject )parentContext.getBean("userObject");
and
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userObject" class="com.beanwiring.ObjectUsingDynamicallyAddedObject"
>
<constructor-arg ref="parameterObject" />
</bean>
</beans>
works perfect!
You can create a wrapper class for a DataSource that simply delegates to a contained DataSource
public class DataSourceWrapper implements DataSource {
DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
#Override
public Connection getConnection(String username, String password)
throws SQLException {
return dataSource.getConnection(username, password);
}
//delegate to all the other DataSource methods
}
Then in you Spring context file you declare DataSourceWrapper and wire it into all your beans. Then in your method you get a reference to DataSourceWrapper and set the wrapped DataSource to the one passed in to your method.
This all working is highly depended on what happens in your Spring context file when its being loaded. If a bean requires the DataSource to already be available when the context loads then you may have to write a BeanFactoryPostProcessor that alters the Spring context file as it loads, rather then doing things after the load (though perhaps a lazy-init could solve this issue).
If you create an object by calling "new", it's not under the control of the Spring factory.
Why not have Spring inject the DataSource into the object instead of passing it into run()?
There's a more elegant way in which you can use an external xml file and load it with file system resource then inject beans configured in it into your application context. Thus:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;
#Service
#Order(-100)
public class XmlBeanInitializationService implements ApplicationContextAware, InitializingBean {
private ApplicationContext applicationContext;
#Value("${xmlConfigFileLocation}")
private String xmlConfigFileLocation;
#Override
public void afterPropertiesSet() throws Exception {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry)applicationContext);
reader.loadBeanDefinitions(new FileSystemResource(xmlConfigFileLocation));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
where ${xmlConfigFileLocation} is a property specified in your application.properties file which points to the file location in your system thus:
xmlConfigFileLocation="your-file-path-anywhere-in-your-system"
and your xml file may contain:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.yourpackage.YourBean1Class"></bean>
<bean class="com.yourpackage.YourBean2Class"></bean>
<bean class="com.yourpackage.YourBean3Class"></bean>
</beans>
thus when your application starts spring loads the class and loads the bean into application context.
Hope this helps someone.