New in Spring: Load Application Context - spring

I am new with Spring.
So far in my application, every time I need to use a bean I load the XML.
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
So, in every class where I need to load a particular bean I use the above line.
In terms of efficciency or right use, I would like to know if this is the correct use (I suspect it is not) or if the context should be passed as parameter everytime one class need it.
Thanks

I assume that you are using Spring in non-web application.
If you are creating new application context each time you need to retrieve a bean, it is indeed not the correct solution. You should create application context once per application.
So the solution would be as you suggest passing the application context instance to classes that need it, or to otherwise ensure that you use the same instance in your application.
One of many issues you might run into with your current setup is problem with bean scoping. Spring has singleton beans, but those are singletons only within one application context. So if you retrieve a bean that is singleton from two different application contexts, they will not be the same instance. Other issues will involve performance, because application context creation will be expensive operation.

If you use spring, then you should use it everywhere. So instead of passing the application context around, put every bean in there and let Spring connect the dots for you.
In a nutshell, never call new yourself. Ask Spring for the bean instead. If a bean has dependencies, use constructor injection.
That way, Spring can create all the beans for you, wire them up and return fully working instances without you having to worry about where something comes form.
You should also read the articles about Java-based container configuration.
Related articles:
Spring by Example: Basic Constructor Injection
Spring Constructor-based Dependency Injection

Just Load the XML file and create the application context object. As long as XML is loaded, Spring will inject all the object dependencies.
So, You don't need to load or create application context object again and again. Just check your console with the below example, you will understand it.
Example: In main method you only write this line of code.
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
interface IParent {
public void whoAreYou();
}
class ChildA implements IParent {
public ChildA (IParent ChildB) {
super();
System.err.println("Constructor ChildA ");
ChildB.whoAreYou();
}
public ChildA (){
System.err.println("Constructor ChildA ");
}
#Override
public void whoAreYou() {
System.err.println("ChildA ");
}
}
class ChildB implements IParent {
public ChildB (){
System.err.println("Constructor ChildB");
}
#Override
public void whoAreYou() {
System.err.println("ChildB");
}
}
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-4.3.xsd">
<context:annotation-config/>
<bean id="childA" class="com.spring.xmlbased.config.ChildA">
<constructor-arg>
<bean id="ChildB" class="com.spring.xmlbased.config.ChildB"/>
</constructor-arg>
</bean>
</beans>
Please let me know, if u need further clarification.

Related

Log Axis Client Requests and Responses Using Spring bean

I have an integration set up with spring using Apache Axis as a SOAP client.
I am able to set up the client-config.wsdd to configure a handler as a class that is outside of the spring context. What I would like to do is configure the handler so that it is within the spring context. Is this possbile?
Here is my current client-config.wsdd
<?xml version="1.0" encoding="UTF-8"?>
<deployment name="defaultClientConfig"
xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<handler name="log" type="java:xxx.xxx.handler.SOAPLogHandler">
</handler>
<globalConfiguration>
<parameter name="disablePrettyXML" value="false"/>
<requestFlow>
<handler type="log"/>
</requestFlow>
<responseFlow>
<handler type="log"/>
</responseFlow>
</globalConfiguration>
<transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender"/>
</deployment>
I also have the service registered with spring:
<bean id="wsYPSoap" class="xxx.xxx.core.ws.WsYPSoapProxy">
<constructor-arg value="${xxx.service.url}" />
</bean>
I was looking for a way to use Spring bean directly as a handler for Axis client and exactly for the purposes of logging request/response details.
It seems that currently there is no easy and straightforward way to do this.
There is, however, a way to do it by accessing a Spring bean from Axis handler via application root context. There are few issues here:
Axis handlers are instantiated when they are needed as a simple classes (not beans)
handler instances can't rely on existence of any servlet (or, i.e. Faces) context at the time their invoke() method is called
Because of this, you have to place your Spring bean in the root application context. Problem is that you can't access the application context in the usual way (i.e. via ServletContext of FacesContext). Workaround for this is to create your own ApplicationContextAware bean which will store the reference to root application context on app startup for you to use in Axis handler.
Minimal ApplicationContextAware bean implementation example:
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext rootAppContext;
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
rootAppContext = ctx;
}
public static ApplicationContext getApplicationContext() {
return rootAppContext;
}
}
and in application-context.xml declare the bean:
<bean id="appContextProvider" lazy-init="false" class="package.where.the.class.is.ApplicationContextProvider" />
Notice the lazy-init="false" in declaration, this is important. Since this bean is not referenced (or auto-wired) anywhere, Spring will not create the instance of it ever, because Spring uses lazy strategy for bean creation. Setting lazy-init to false makes sure that the bean is created on app startup.
Provided that you have your DB logging bean (implemented by DBLogBean class) properly set up and loaded/created by Spring, you can access it in the Axis handler i.e. like this:
ApplicationContext ctx = ApplicationContextProvider.getApplicationContext();
if (ctx != null) {
DBLogBean bean = (DBLogBean) ctx.getBean("yourDBLogBeanId");
if( bean != null ) {
bean.doLogOrSomething();
}
}
Make sure you check if ApplicationContextProvider.getApplicationContext() returns null or not before fetching DB bean from context. Note now you also must check if ctx.getBean() returns null or not.
If this is not an option (i.e. for some reason you must have the DB bean whenever Axis handler is called), then you have to make sure Axis handler is only ever called after ApplicationContextProvider bean has been created. That topic, however, is out of scope here ;)
NOTE: Creating beans by using lazy-init="false" is not a
preferred way of creating bean instances. Beans should be
auto-wired/referenced by other beans/code and left to Spring to manage
their life cycle. For example, one downside of forcing bean creation
on startup is you can't be sure at which point they have been created
and become available, unless you take extra steps to handle that (or
make sure they are referenced by other code but then, why use
lazy-init="false" in the first place?).

How to override a Spring #Autowire annotation and set a field to null?

I am a Spring neophyte who is working on a large Spring-based project that has extensive coupling between Spring beans. I am trying to write some integration tests that exercise subsets of the total application functionality. To do so, I'd like to override some of the autowiring.
For example, suppose I have a class
public class MyDataServiceImpl implements MyDataService {
#Qualifier("notNeededForMyDataServiceTest")
#Autowired
private NotNeededForMyDataServiceTest notNeededForMyDataServiceTest;
//...
}
and a context file with:
<bean id="myDataService"
class="MyDataServiceImpl">
</bean>
In my test, I have no need to use the notNeededForMyDataServiceTest field. Is there some way I can override the #Autowired annotation and set notNeededForMyDataServiceTest to null, perhaps in the XML file? I don't want to modify any of the Java classes, but I do want to avoid the (problematic) configuration of notNeededForMyDataServiceTest.
I tried doing:
<bean id="myDataService"
class="MyDataServiceImpl">
<property name="notNeededForMyDataServiceTest"><null/></property>
</bean>
That doesn't work. IntelliJ informs me "Cannot resolve property 'notNeededForMyDataServiceTest'", apparently because there are no getters and setters for that field.
I'm using Spring Framework 3.1.3.
The following configuration should work, I took the liberty of mixing in Java configuration
#Configuration
//This will load your beans from whichever xml file you are using
#ImportResource("classpath:/path/beans.xml")
public class TestConfigLoader{
// This will declare the unused bean and inject MyDataServiceImpl with null.
public #Bean(name="notNeededForMyDataServiceTest") NotNeededForMyDataServiceTest getNotNeededForMyDataServiceTest(){
return null;
}
... any other configuration beans if required.
}
And annotate your test class like so:
// In your test class applicationContext will be loaded from TestConfigLoader
#ContextConfiguration(classes = {TestConfigLoader.class})
public class MyTest {
// class body...
}
These could help:
Context configuration with annotated classes
Testing with #Configuration Classes and Profiles
Spring TestContext Framework
and profiles:
beans profile="..."
Introducing #Profile
You could create different beans definition in the XML configuration and then activate them using the -Dspring.profiles.active="profile1,profile2" env.
You're using the #Autowired mechanism wrong. The qualifier is not a property that you need to set. That's actually the name of a bean, so that the container will be able to choose one particular instance in case multiple beans of the same type are defined in the same context.
So the container will look for a bean of type NotNeededForMyDataServiceTest and the name (which would actually be the bean id in XML): notNeededForMyDataServiceTest.
What I think you want is to instruct the container to not inject anything in that field if no bean of type NotNeededForMyDataServiceTest is defined in the application context. That could be achieved simply by setting the required attribute of the annotation to false:
#Autowired(required = false)
NotNeededForMyDataServiceTest someOptionalDependency;
The only drawback of this approach would be that the container will never complain at runtime if there's nothing to inject in that field (and perhaps you would want this sanity check when your code runs in production).
If you don't want to make that dependency optional (or you can't edit that code for some reason), you'll need to provide a mock / null value for that field by setting that explicitly in your context. One option to do that would be to use Java configuration instead of XML (like in #Abe's answer) and another approach would be to make use of a factory bean which returns null (like in this question).

Why is there a need to specify the class in both the xml file and in the getBean() method in Spring

This might be an obvious but I'm having a hard time understanding why we need to define the class of a bean in two places....
From the spring reference manual...
...
<bean id="petStore"
class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
// retrieve configured instance
PetStoreServiceImpl service = context.getBean("petStore", PetStoreServiceImpl.class);
Shouldn't the xml fine be enough for the container to know the class of petStore?
You can use the following method:
context.getBean("petStore")
However, as this returns a java.lang.Object, you'd still need to have a cast:
PetStoreServiceImpl petstore = (PetStoreServiceImpl)context.getBean("petStore");
However, this could lead to problems if your "petStore" bean is not actually a PetStoreServiceImpl, and to avoid casts (which since the advent of Generics are being seen as a bit dirty), you can use the above method to infer the type (and let's spring check whether the bean you're expecting is really of the right class, so hence you've got:
PetStoreServiceImpl service = context.getBean("petStore", PetStoreServiceImpl.class);
Hope that helps.
EDIT:
Personally, I would avoid calling context.getBean() to lookup methods as it goes against the idea of dependency injection. Really, the component that uses the petstore bean should have a property, which can then be injected with the correct component.
private PetStoreService petStoreService;
// setter omitted for brevity
public void someotherMethod() {
// no need for calling getBean()
petStoreService.somePetstoreMethod();
}
Then you can hook up the beans in the application context:
You could also do away with the configuration via XML and use annotation to wire up your beans:
#Autowired
private PetStoreService petStoreService;
As long as you've got
in your spring context, the "petStore" bean defined in your application context will automatically be injected. If you've got more than one bean with the type "PetStoreService", then you'd need to add a qualifier:
#Autowired
#Qualifier("petStore")
private PetStoreService petStoreService;
There's no requirement to specify the class in the getBean() method. It's just a question of safety. Note there's also a getBean() that takes only a class so that you can just look up beans by type instead of needing to know the name.

Is it possible to weave an aspect to dynamically instantiated class?

I use Spring and have an aspect that wraps around some class:
#Aspect
public class LoggingAspect{
#Around("execution(public * com.service.MyService.doStuff(..))")
public Object log(){
...
}
}
and in context xml:
<aop:aspectj-autoproxy/>
<bean id="loggingAspect" class="com.bla.bla.bla.LoggingAspect"/>
The problem is that instances of MyService are created at runtime so Spring knows nothing about this class during context initialization phase. Is it possible to use aspects in this case to wrap method calls of a class instantiated using new (not Spring)?
If my reading of the Spring docs is correct, you do it like this (for Spring proxy-based weaving):
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
Pojo pojo = (Pojo) factory.getProxy();
or like this (for AspectJ-style weaving):
AspectJProxyFactory factory = new AspectJProxyFactory(new SimplePojo());
factory.addAspect(new RetryAspect());
Pojo proxy = factory.getProxy();
(I drive all my AOP weaving through my bean configuration so I've not needed to use this sort of thing in practice.)
No, you can only use Spring AOP to advise Spring beans (because Spring creates a proxy object behind the scenes). You'll have to use full AspectJ, or create your MyService in the Spring container. See http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-choosing.

Accessing legacy out-of-container instantiated objects from Spring beans

We have a legacy system where something like a Service Locator is used to instantiate and provide all service objects:
class ServiceLocator {
ServiceA serviceA;
ServiceB serviceB;
public ServiceLocator () {
serviceA = ...;
serviceB = ...;
}
public ServiceA getServiceA() {
return serviceA;
}
public ServiceB getServiceB() {
return serviceB;
}
}
(imagine 70 more fields and getters...)
This object is then passed around from class to class to provide access to the service objects.
It is outside the scope of the project to change this design for existing code, but to at least not make things worse, we would like to introduce Spring to progressively instantiate future services with DI similar to Introducing an IoC Container to Legacy Code.
In contrast to the aforementioned situation, we already know how we will access the spring created spring bean objects from our legacy code. Our problem are objects we plan to create with spring, that need any of the service objects created outside of the spring context.
We came up with the following solution:
Create a static accessor for the ServiceLocator and set it in the constructor, load the spring application context object. In the spring configuration create a bean for the ServiceLocator with the static accessor as described in Section 3.3.2.2 in the Spring reference:
<bean id="serviceLocator"
class="ServiceLocator"
factory-method="getInstance"/>
for each Service create another bean using "instance factory method" as described in Section 3.3.2.3:
<bean id="serviceA"
factory-bean="serviceLocator"
factory-method="getServiceA"/>
Create other beans referencing these "dummy beans".
I guess this would work, but creates a lot of seamingly unnessessary pseudo configuration. What I'd rather like is something like this:
"If a bean is referenced and that bean is not explicitly defined, search for a method with the needed signature and name in the ServiceLocator class and use this object."
Is it possible to do so? Are there any entry points into the spring bean instantiation process that I am not aware of and that can be used here? Can I do this by subclassing the spring application context class?
Any help would be greatly appreciated!
You can define a BeanFactoryPostProcessor to populate your application context with beans from ServiceLocator.
In BeanFactoryPostProcessor, use beanFactory.registerSingleton(...) to add a fully instantiated bean, or ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(...) to add a definition (note that some application contexts may not implement BeanDefinitionRegistry, though all typical contexts implement it).

Resources