While reading some advanced book on developing the enterprise applications, I constantly see the following pattern that could be illustrated by the following example:.
public interface Oracle {
String defineMeaningOfTheLife();
}
public class BookwormOracle implements Oracle {
public String defineMeaningOfTheLife() {
return "Use life, dude!";
}
}
And the main function:
public static void main(String[] args) {
XmlBeanDefinitionReader rdr = new XmlBeanDefinitionReader(factory);
rdr.loadBeanDefinitions(new ClassPathResource(
"META-INF/spring/xml-bean-factory-config.xml"));
Oracle oracle = (Oracle) factory.getBean("oracle");
System.out.println(oracle.defineMeaningOfTheLife());
}
And the xml config:
<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="oracle" name="wiseworm" class="BookwormOracle" />
As far as I understood, it is not possible to instantiate the interface. But, using Spring framework it seems to me that it is possible to do so. Since, how does the Spring framework manage to do it? From pure Java perspective the code
Oracle oracle = new Oracle();
is rather wrong.
Spring also needs an implementation of the interface to instanciate the bean and make it available to your application. It is actually what is defined in your context file (xml-bean-factory-config.xml):
<bean id="oracle" name="wiseworm" class="BookwormOracle" />
In the spring context, you define which implementation of the interface Oracle you want to create. When your main method call:
Oracle oracle = (Oracle) factory.getBean("oracle");
it asks to Spring to get the bean with id "oracle" which is an implementation of your Oracleinterface.
You shouldfirst understand about DI(Dependency Injection) and IoC(Inversion of Control) . Please google it .
I would recommend you this article from Martin Fowler on Ioc.
http://martinfowler.com/bliki/InversionOfControl.htmlenter link description here
Thanks
This line <bean id="oracle" name="wiseworm" class="BookwormOracle" /> is equal to the below java code.
BookwormOracle oracle = new BookwormOracle();
It just happened to have the name of the variable as oracle in the spring configuration, rather spring actually initializing the concrete class BookwormOracle . Later you are just asking the spring container to get that bean with the name oracle with below line.
Oracle oracle = (Oracle) factory.getBean("oracle");
Related
sometimes I have to add an object to camel registry (of course with java). In most cases it is a dataSource .
My problem is I can't figure out a general working way.
I always begin to acquire the registry:
getContext().getRegistry();
But "Registry" does not have any method to add an object. So I have to try (with debugger) what kind of registry is in use
getContext().getRegistry(some.class)<some method to add something>;
For example in one project (camel blueprint) I have to call
SimpleRegistry registry = new SimpleRegistry();
registry.put("some", bean);
getContext().getRegistry(CompositeRegistry.class).addRegistry(registry);
Now I created a project with same structure (also same maven parent) but now the code from above stops working because for some reason now camel uses a PropertyPlaceholderDelegateRegistry I am sure there will be code to add my bean but;
Is there code that works with every setup to add something to camels registry?
Here is one way of adding stuff to the registry in a RouteBuilder class. Below I am adding a TCPServerInitializerFactory which will be used later on. I always use the camel-blueprint archetype but create routes using java dsl. This works fine for me.
TCPServerInitializerFactory serverFactory = new TCPServerInitializerFactory(null);
final CamelContext camelContext = getContext();
final org.apache.camel.impl.SimpleRegistry registry = new org.apache.camel.impl.SimpleRegistry();
final org.apache.camel.impl.CompositeRegistry compositeRegistry = new org.apache.camel.impl.CompositeRegistry();
compositeRegistry.addRegistry(camelContext.getRegistry());
compositeRegistry.addRegistry(registry);
((org.apache.camel.impl.DefaultCamelContext) camelContext).setRegistry(compositeRegistry);
registry.put("spf", serverFactory);
For Camel 3.x use the code snippet for adding in registry at runtime
DefaultRegistry registry = (DefaultRegistry) camelContext.getRegistry();
registry.bind("key", <Object to bind>);
((org.apache.camel.impl.DefaultCamelContext) camelContext).setRegistry(registry);
This can be referred further,
enter link description here
If you are using Spring with camel you can register your bean in Application context and it will be get registered in ApplicationContextRegistry.
eg. ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext)
applicationContext).getBeanFactory();
beanFactory.registerSingleton("beantolookup", bean);
And you can look up
camelContext.getRegistry().lookupByName("beantolookup")
This is one way to solve:
Create a Camel Listener ( I have a standalone Java Camel App).
Add to beforeConfigure() method a section similar to below.
CamelContext mainCamelContext = main.getCamelContext();
DefaultRegistry defaultRegistry = mainCamelContext.getRegistry(DefaultRegistry.class);
defaultRegistry.bind("YouBeanName", new YourBeanClassName());
Once this is done - you can reference it in your XML DSL.
Tested using Camel 3.14.x, JDK 8.
If you're using the camel-spring package, you can use Spring to manage the registry. Here's an example of code I used to add a singleton bean in Java through Spring:
MyApplicationContext.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"
xmlns:camel="http://camel.apache.org/schema/spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="myRouteBuilder" class="com.example.MyRouteBuilder"/>
<camel:camelContext id="my-camel-context">
<camel:routeBuilder ref="myRouteBuilder"/>
</camel:camelContext>
</beans>
MyApplication.java
public class MyApplication {
public static void main(final String[] args) {
final FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("/path/to/MyApplicationContext.xml");
}
}
MyRouteBuilder.java
public void MyRouteBuilder extends RouteBuilder implements ApplicationContextAware {
#Override
public void configure() {
from("ftps://localhost:21/foo?pollStrategy=#myPollingStrategy")
.log("${body}");
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) {
final ConfigurableApplicationContext context = (ConfigurableApplicationContext)applicationContext;
final DefaultListableBeanFactory registry = (DefaultListableBeanFactory)context.getBeanFactory();
registry.registerSingleton("myPollingStrategy", new MyPollingStrategy());
}
}
The important part here is to implement the ApplicationContextAware interface in the class that you want to use to manually add beans to the registry. This interface can be implemented on any bean that Spring initializes; you don't have to use your RouteBuilder. In this case, the bean is created on startup during the setApplicationContext(...) execution before configure() is called, but you can also store the application context for use later (which is probably a more common usage).
This was tested with Camel 2.19.0 and Spring 4.3.10-RELEASE.
In the spring reference documentation
section 2.3 Usage scenarios, there is a paragraph that goes like this
Sometimes circumstances do not allow you to completely switch to a different framework. The Spring
Framework does
not
force you to use everything within it; it is not an
all-or-nothing
solution. Existing
front-ends built with Struts, Tapestry, JSF or other UI frameworks can be integrated with a Spring-
based middle-tier, which allows you to use Spring transaction features. You simply need to wire up your
business logic using an ApplicationContext
and use a WebApplicationContext
to integrate
your web layer.
Now I am not able to understand the last sentence. How can we wire up our business logic using an ApplicationContext and use a WebApplicationContext to integrate with web layer. How can we achieve this? And is the web-layer that they are talking about contains controllers and jsps?
As far as I remember if we needed any object in a class we simply autowire them and spring does the rest of the work.
Can someone please provide an explanation with examples. Please forgive my ignorances as I have just started to learn spring.
If a similar question is asked can someone please point me in the right direction
It is possible, even you can create more than one different context hierarchically.
I will give both answers, both hierarchic and non-hierarchic. I'll use java based configuration for both. I will give the answer for two context but you can implement this for many context.
1)Non-Hierarchic
Create two different context.xml, assume that context1.xml and context2.xml . context1.xml should be like this :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=..... some imports >
<context:annotation-config />
<context:component-scan base-package="desiredPackage1" />
<bean id="properties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>db.properties</value>
</list>
</property>
</bean>
<context:property-placeholder properties-ref="properties"/>
For context2.xml change only
<context:component-scan base-package="desiredPackage2" />
Then create a Configuration.java class like this:
public class Config {
public static void main(String[] args) throws Exception {
ApplicationContext desiredContext1 = new ClassPathXmlApplicationContext("file:////...path.../context1.xml");
ApplicationContext desiredContext2 = new ClassPathXmlApplicationContext("file:////...path.../context2.xml");
}
}
Now you have two different context, if you want to it hierarchically, change the main method like this :
2)Hierarchic
public class Config {
public static void main(String[] args) throws Exception {
ApplicationContext desiredContext1 = new ClassPathXmlApplicationContext("file:////...path.../context1.xml");
String[] congigPath = new String[1];
congigPath[0] = "file:////...path.../context2.xml";
ApplicationContext desiredContext2 = new ClassPathXmlApplicationContext(congigPath,desiredContext1);
}
}
In this case, desiredContext2 object could see desiredContext1 object but desiredContext1 object can not see desiredContext2 object.
If you want to use it when building your web-app use this annotations with you configuration class,
#Configuration
#ImportResource("context1.xml", "context2.xml")
public class Config { ....
I hope this will help to you.
You may setup two or even three different projects or modules each with their own context. For example a web project with WebApplicationContext which is rendering the views and calls business methods i.e. usinf restful web services from business tier. And setup a separate project or module to handle business which has its own context file and beans. And even a commons project to include shared beans between the web and business tier.
I'm trying to use spring data and spring config together in a small standalone application.
...
public static void main( String[] args )
{
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
...
}
1. My question is how can I discover the spring data repositories without using
<jpa:repositories base-package="foo.repositories" />
by spring config ?
2. If not, can I use 'ClassPathXmlApplicationContext' and 'AnnotationConfigApplicationContext' together somehow ?
You can now use the annotation #EnableJpaRepositories("some.root.package") .
For example:
#Configuration
#EnableTransactionManagement(proxyTargetClass = true)
#EnableJpaRepositories("some.root.package")
#ComponentScan(basePackages = { "maybe.another.root.package" })
public class SystemConfiguration {
...
}
(Spring Data's announcement)
For completeness' sake and to address your second question: Yes, you can combine Java and XML configurations. This way you don't have to wait for the next Spring Data JPA release.
Just annotate your configuration class with ImportResource, like this:
#Configuration
#ImportResource("classpath:jpa-config.xml")
public class AppConfig {
...
}
This answer is now out of date.
Currently there's no equivalent for <jpa:repositories … /> yet. Feel free tor track the according JIRA ticket. The feature will be a major one for the upcoming GA releases of the JPA module (1.1) as well as MongoDB (1.1).
I think you should have a look at context:component-scan
<context:component-scan base-package="com.myProject"/>
It autodetects components annotated with #Repository/#Service/#Component . Check here for this .
I'm trying to get a grip using MongoDB with the Spring Data MongoDB framework. I tried severeal approaches to connect to my local DB and insert + retrieve some collections and documents, using the official Spring Reference Documentation and some simple examples like this Hello-World-Demo.
Actually, for the beginning I'm going to use the MongoTemplate to keep it simple. But now I'run into the following problem.
When I use the Spring Configuration with annotations configure the setting needed to connect to my local DB, everything works fine.
Otherwise When I use XML for the configuration, I run into an java.lang.NullPointerException at com.mongodb.DBTCPConnector.getClusterDescription
Here are my configuration files and the example code for connecting to the DB:
Use Case 1 - Spring Configuration with annotations:
//package, imports etc. here
#Configuration
public class MongoConfiguration {
public #Bean MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(new MongoClient(), "Test1");
}
public #Bean MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoDbFactory());
}
}
Using the configuration class like this ...
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MongoConfiguration.class);
MongoOperations mongoOperation = (MongoOperations) ctx.getBean("mongoTemplate");
ctx.close();
for (String s : mongoOperation.getCollectionNames()) {
System.out.println(s);
}
.. creates this output:
documents
leute
system.indexes
system.users
Use Case 2 - XML configuration (SpringConfig2.xml):
<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"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/data/mongo
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<mongo:mongo host="127.0.0.1" port="27017" />
<mongo:db-factory dbname="Test1" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
</beans>
Using the configuration file like this ...
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext("SpringConfig2.xml");
MongoOperations mongoOperation = (MongoOperations) ctx.getBean("mongoTemplate");
ctx.close();
for (String s : mongoOperation.getCollectionNames()) {
System.out.println(s);
}
.. results in this error
java.lang.NullPointerException
at com.mongodb.DBTCPConnector.getClusterDescription(DBTCPConnector.java:404)
at com.mongodb.DBTCPConnector.getMaxBsonObjectSize(DBTCPConnector.java:653)
at com.mongodb.Mongo.getMaxBsonObjectSize(Mongo.java:641)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:81)
at com.mongodb.DBCollectionImpl.find(DBCollectionImpl.java:66)
at com.mongodb.DB.getCollectionNames(DB.java:510)
at org.springframework.data.mongodb.core.MongoTemplate$13.doInDB(MongoTemplate.java:1501)
at org.springframework.data.mongodb.core.MongoTemplate$13.doInDB(MongoTemplate.java:1499)
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:394)
at org.springframework.data.mongodb.core.MongoTemplate.getCollectionNames(MongoTemplate.java:1499)
at test.main(Test.java:28)
When debugging DBTCPConnector.getClusterDescription, it seems that in the second case the private class variable cluster is for some reason not instantiated, leading to the described error.
What I'd like to know is: am I doing anything wrong within my XML-configuration or when using this config / context? Why does using XML-configuration end in an error, while using annotation configuration just works fine?
Basically (in the end) I just "copy+paste"'d the code examples from the official references for Spring / Spring Data MongoDB.
I'd appreciate any help / suggestions :)
Thanks to the hint from Tushar Mishra in the comments I was able to track down the origin of the error and why it occurs in one case but not in the other.
I'll try to explain in short, maybe it'll save someone some research time (or remember me if I perhaps run into this or a similar error again).
When closing either the AnnotationConfigApplicationContext-object (UC1) or the AnnotationConfigApplicationContext-object (UC2) with ctx.close(), from somewhere
org.springframework.beans.factory.DisposableBean#destroy() is invoked. As far as I understood, within there used singleton beans get destroyed by invoking the destroy()-methods
of each of those beans.
In short: closing the XXApplicationContext-object also destroys the MongoFactoryBean and with it closes the connection to the MongoDB.
So using c**tx.close()** at the described position leads to using a MongoOperations-object on a closed connection at the next line, resulting in the described NullPointerException.
And for why the sample code with Annotations (UC1) runs fine, whereas the sample code configured with XML (UC2) just breaks with an NullPointerException:
In UC1, the org.springframework.beans.factory.DisposableBean#destroy()-call is interupted by some DisposableBeanMethodInterceptor, which tries to redirect to a dispose() method.
But there is no method implemented in MongoFactoryBean, so the Mongo-connection stays alive and can be used even after ctx.close() was invoked. I think the connection will get killed later by the garbage collector, leading to the same error.
I'm not sure what to make of this, yet. If I should implement a dispose()-method myself or something alike. But at least I figured out, why this code sample behave different, although supposed to do the same thing.
Maybe it helps someone. Thanks again, Tushar, for giving a hint to the right direction.
I'm trying to use spring data and spring config together in a small standalone application.
...
public static void main( String[] args )
{
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
...
}
1. My question is how can I discover the spring data repositories without using
<jpa:repositories base-package="foo.repositories" />
by spring config ?
2. If not, can I use 'ClassPathXmlApplicationContext' and 'AnnotationConfigApplicationContext' together somehow ?
You can now use the annotation #EnableJpaRepositories("some.root.package") .
For example:
#Configuration
#EnableTransactionManagement(proxyTargetClass = true)
#EnableJpaRepositories("some.root.package")
#ComponentScan(basePackages = { "maybe.another.root.package" })
public class SystemConfiguration {
...
}
(Spring Data's announcement)
For completeness' sake and to address your second question: Yes, you can combine Java and XML configurations. This way you don't have to wait for the next Spring Data JPA release.
Just annotate your configuration class with ImportResource, like this:
#Configuration
#ImportResource("classpath:jpa-config.xml")
public class AppConfig {
...
}
This answer is now out of date.
Currently there's no equivalent for <jpa:repositories … /> yet. Feel free tor track the according JIRA ticket. The feature will be a major one for the upcoming GA releases of the JPA module (1.1) as well as MongoDB (1.1).
I think you should have a look at context:component-scan
<context:component-scan base-package="com.myProject"/>
It autodetects components annotated with #Repository/#Service/#Component . Check here for this .