I'm trying to setup a TransactionManager in my Web Application (powered by Spring MVC 3), as I need to have one method of a component annotated as #Transactional.
This is my situation:
web.xml: load 2 xml files for the Spring ContextLoaderListener (applicationContext.xml and database.xml)
applicationContext.xml: contains some beans which I can't define via annotation, plus the tags for annotation, plus the usual context:annotation-config and context:component-scan (this component-scan includes the package that contains the #Transactional method)
database.xml: contains the datasource (I'm using BasicDataSource from commons-dbcp), the transaction manager definition and tx:annotation-driven.
I've got a #Component (DeleteComponent) which has an interface and an implementation (DeleteComponentImpl). The implementation class is annotated with #Component and has one public method annotated with #Transactional (I annotated the concrete class and not the interface, as Spring documentation states). For #Transactional I didn't put any arguments, as defaults are fine. This class has some DAOs (annotated with #Repository) being injected via #Autowired. I'm using only plain JDBC (no Hibernate or other ORM). This #Component is injected into a #Controller (which is defined in spring-servlet.xml).
If the method annotated as #Transactional throws an exception (unchecked, as RuntimeException), however, nothing is rolled back. The database retains the changes did before the exception. I'm using Jetty web server for testing locally my application. The thing I noticed is actually that seems like no transaction manager is being set up. Actually, my transaction manager is named transactionManager. The xml line to set up the annotation-driven transaction is
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
If I change it to use a non-existing bean name like
<tx:annotation-driven transaction-manager="fake"/>
The application still deploys correctly and doesn't complain.
Any tips on what should I check to make it working?
Thanks.
I solved by using #Transactional(rollbackFor = RuntimeException.class) and switching from a BasicDataSource to a ComboPooled that's present in c3p0 library. Thanks for the suggestions though.
To get rollback when an exception is thrown add this:
#Transactional(rollbackFor=Exception.class)
You will also need to set up the transactionManger bean (here is mine, using hibernate) :
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory" />
I found this tutorial informative.
I believe that #Autowired and #Resource aren't scanned by Spring on a #Component.
Try to use a ContextHolder class to get the context and the DAO
#Component
public class ContextHolder implements ApplicationContextAware {
/**
* Spring context which will directly be injected by Spring itself
*/
private static ApplicationContext context = null;
/**
* Overridden method of ApplicationContextAware, which will automatically be called by the container
*/
public void setApplicationContext(ApplicationContext context) throws BeansException {
this.context = context;
}
/**
/**
* Static method used to get the context
*/
public static ApplicationContext getApplicationContext() {
return context;
}
}
and call your dao with :
ContextHolder.getApplicationContext().getBean("MyDAO");
Related
I have a class called EmployeeService and which is annotated with #service annotation from spring framework
package com.sample.EmployeeService
#Service
public class EmployeeService {
}
and I also have entry in context.xml
<bean id="empSer" class ="com.sample.EmployeeService"
May I know how many bean has been created in psring container.
In here
Spring will scan all the classes with #Service annotation, register it as a bean, then it will inject the dependencies that have #Autowired annotation.
#Service
public class EmployeeService {
}
In here
The context.xml is Spring's advanced container. Similar to BeanFactory, it can load bean definitions, wire beans together
<bean id="empSer" class ="com.sample.EmployeeService"
Actually you get one single bean - whether context.xml it is loaded first or classes with #Service annotation
And
If first loaded context.xml and second loaded #Service annotation class, Spring Application overwrite by default as the beans are being loaded up into the factory
Taking as reference the post Spring #Autowired and #Qualifier
We have this example to fix the autowiring conflict :
public interface Vehicle {
public void start();
public void stop();
}
There are two beans, Car and Bike implements Vehicle interface.
#Component(value="car")
public class Car implements Vehicle {
#Override
public void start() {
System.out.println("Car started");
}
#Override
public void stop() {
System.out.println("Car stopped");
}
}
#Component(value="bike")
public class Bike implements Vehicle {
#Override
public void start() {
System.out.println("Bike started");
}
#Override
public void stop() {
System.out.println("Bike stopped");
}
}
#Component
public class VehicleService {
#Autowired
#Qualifier("bike")
private Vehicle vehicle;
public void service() {
vehicle.start();
vehicle.stop();
}
}
That's a very good example to fix this problem.
But when I have the same problem but without those balises in the application context:
<context:component-scan></context:component-scan>
<context:annotation-config></context:annotation-config>
All the issues are solved by using the #Qualifier annotation, but in my case we don't use the balise that permit to use annotation.
The question is :
How can I fix this issue just using the configuration in application context, that's it, without using annotations?
I searched a lot and I found people talking about autowire attribute in the bean declaration <bean id="dao" class="package.IDao" autowire="byName"></bean> and I need more explanation about it.
How can I fix this issue just using the configuration in application
context?
You could use the qualifier tag like below (see https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-autowired-annotation-qualifiers)
<context:annotation-config/>
<beans>
<bean class="your_pkg_route.Vehicle">
<qualifier value="bike"/>
</bean>
</beans>
</context:annotation-config>
I found people talking about autowire attribute in the bean
declaration and I need more explanation about it
Using Annotation
#Autowired used on a bean declaration method injects the defined dependencies by (another) declared beans. Now, if your dependencies are in the same context of your application, you don't need to use the #Autowired annotation at all because Spring is able to figure them out by itself. So, if your dependencies are outside your applicatoin context then you can use it.
For example, take as reference the below code:
#Autowired
#Bean
public MyBean getMybean(Dependency1 depdency1, Dependency2 depdency2) {
return new MyBean(depdency1.getSomeStuff(), depdency2.getSomeOtherStuff());
}
Here, #Autowired will find an instance of Dependency1 and Dependency2 and will provide them for the creation of an instance of MyBean.
Using xml configuration
From Pro Spring 5... Spring supports five modes for autowiring.
byName: When using byName autowiring, Spring attempts to wire each property to a bean of the same name. So, if the target bean has a property named foo and a foo bean is defined in ApplicationContext, the foo bean is assigned to the foo property of the target.
byType: When using byType autowiring, Spring attempts to wire each of the
properties on the target bean by automatically using a bean of the same type in
ApplicationContext.
constructor: This functions just like byType wiring, except that it uses constructors rather than setters to perform the injection. Spring attempts to match the greatest numbers of arguments it can in the constructor. So, if your bean has two constructors, one that accepts a String and one that accepts String and an Integer, and you have both a String and an Integer bean in your ApplicationContext, Spring uses the two-argument constructor.
default: Spring will choose between the constructor and byType modes
automatically. If your bean has a default (no-arguments) constructor, Spring uses
byType; otherwise, it uses constructor.
no: This is the default
So, in your case you would need to do something like this (BUT, I would NOT recommend it. Why?, you would need to declare Vehicle class as a bean and a component which is not correct, see Spring: #Component versus #Bean. On the other hand I'm not sure if you could use it just declaring it as a bean):
// xml config
<context:annotation-config/>
<beans>
// use the primary tag here too! in order to say this the primary bean
// this only works when there are only two implementations of the same interface
<bean id="bike" primary="true" class="your_pkg_route.Bike"/>
<bean id="car" class="your_pkg_route.Car"/>
<bean autowire="byName" class="your_pkg_route.VehicleService"/>
<beans>
</context:annotation-config>
// VehicleService
#Component
public class VehicleService {
private Vehicle bike; // call attribute 'bike' so it is autowired by its name
public void service() {
//...
}
}
As you can see there is a lot of complications trying to do this using xml config, so I would recommend you to use the annotation option if possible.
Related posts:
Why do I not need #Autowired on #Bean methods in a Spring configuration class?
Difference between #Bean and #Autowired
PS: I have not tested any of the posted codes.
You can use #Primary instead of #Qualifier
#Primary
#Component(value="bike")
public class Bike implements Vehicle {
we use #Primary to give higher preference to a bean when there are multiple beans of the same type.
We can use #Primary directly on the beans
You can also set primary attribute in XML:
property has primary attribute:
<bean primary="true|false"/>
If a #Primary-annotated class is declared via XML, #Primary annotation metadata is ignored, and is respected instead.
I'm using Spring and am trying to autowire (using annotations) a DAO into a Service, which is then wired into a controller. Having
#Autowired
Movie movieDao;
on its own doesn't work, as I think the new method gets called, so that DAO isn't managed by Spring. The following does work, but it will look messy if I have to copy and paste that context configuration into each method
#Autowired
MovieDao movieDao;
#Override
public List<Movie> findAll() {
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
context.load("classpath:app-context.xml");
context.refresh();
MovieDao movieDao = (MovieDao) context.getBean("movieDao", MovieDao.class);
return movieDao.findAll();
}
where this code is in my Service class. Is there a more elegant way to ensure that my DAO is initialised properly, rather than copying and pasting the first 4 lines of that method into each Service method?
[edit] The class that contains the code above is a class called MovieServiceImpl, and it essentially corresponds to the DataServicesImpl class in the architecture described on this page. (I'll add a summary/description of that architecture and what I'm trying to do soon). This is the code: http://pastebin.com/EiTC3bkj
I think that the main problem is that you want to instantiate your service directly (with new) and not with Spring:
MovieService movieService = new MovieServiceImpl();
When you do this, your MovieServiceImpl instance is constructed but not initialised (the field #Autowired MovieDao is null).
If you want to instantiate properly your object with field injection, you need to use Spring. As explained in the documentation or in this example, you can automatically detect all your annotated beans and initialize them in your context with the component scanning.
Example
In your case, using annotiations on (#Component, #Service, etc) and in (#Autowired, #Inject, etc) your beans, your project could look like this:
Spring configuration app-context.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">
<!-- Use component scanning to auto-discover your beans (by annotation) and initialize them -->
<context:component-scan base-package="com.se325.a01" />
<!-- No need to declare manually your beans, because beans are auto-discovered thanks to <context:component-scan/> -->
</beans>
Entry point of your application App.java
package com.se325.a01;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.se325.a01.model.Movie;
import com.se325.a01.service.MovieService;
public class App {
public static void main(String[] args) {
// Let's create the Spring context based on your app-context.xml
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"app-context.xml"});
// Now your context is ready. All beans are initialised.
// You can retrieve and use your MovieService
MovieService movieService = context.getBean("movieService");
Movie matrixMovie = new Movie("Matrix");
movieService.create(matrixMovie);
}
}
In fact, when you are using Spring, it is really important to understand how the context is initialized. In the example above, it can be sum up as:
Your entry point App#main is called.
The configuration app-context.xml is loaded by ClassPathXmlApplicationContext.
The package com.se325.a01 is scanned thanks to the line <context:component-scan base-package="com.se325.a01" />. All annotated beans (#Component, #Service, etc) are contructed but not yet initialised.
When all the beans are constructed, Spring initialises them by injecting dependencies. In the example, the #Autowired annotations which mark the dependencies are also discovered thanks to the line <context:component-scan ... \>.
The context is ready with all beans :)
Notes
All this answer explains how you can use component scanning and annotations to use Spring in a main entry point. However, if you are developing a server application, the entry point is the WEB-INF/web.xml.
As #chrylis said, field injection is error prone. Prefer using constructor-based injection.
What it does is pretty simple:
#Inject
private Provider<ProductService> productService;
The Product service is available through productService.get() and .get() will resolve the instance from the Spring context on each call.
But when should I use it? And where?
My main use case is pretty simple: When I get circular dependencies, the provider helps to resolve the dependency at runtime. But it looks a bit random if you throw it in just when you can't create your context caused by a circular dependency.
Are there any known patterns about the usage of Providers?
In cdi, Providers are used to inject objects of narrower scope into a more broadly-scoped bean, e.g., if a session-scoped bean needs access to a request scoped object it injects a provider and then a method, which is running in a request, calls provider.get() to obtain a local variable reference to the appropriate request-scoped object.
Given the following:
#RequestScoped
public class Bean1 {
void doSomething();
}
The following will use the Bean1 instance associated with the first request in the session to use Bean2 regardless of which request is calling Bean2.doSomething():
#SessionScoped
public class Bean2 {
#Inject Bean1 bean;
public void doSomething() {
bean.doSomething();
}
}
The following will use the instance of Bean associated with the particular request that is currently calling Bean3.doSomething() i.e. a different bean for each request:
#SessionScoped
public class Bean3 {
#Inject Provider<Bean1> bean;
public void doSomething() {
bean.get().doSomething();
}
}
This interface is equivalent to org.springframework.beans.factory.ObjectFactory<T> that is typically used to avoid BeanFactory.getBean() calls in client code when looking for prototype instances. Often used with ObjectFactoryCreatingFactoryBean to get prototypes beans sourced by the BeanFactory.
example from ObjectFactoryCreatingFactoryBean javadocs:
<beans>
<!-- Prototype bean since we have state -->
<bean id="myService" class="a.b.c.MyService" scope="prototype"/>
<bean id="myServiceFactory"
class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
<property name="targetBeanName"><idref local="myService"/></property>
</bean>
<bean id="clientBean" class="a.b.c.MyClientBean">
<property name="myServiceFactory" ref="myServiceFactory"/>
</bean>
</beans>
With Providers, you can use the ProviderCreatingFactoryBean instead.
Other option to solve the same problem, (using inheritance instead composition) is the
lookup method injection
I have defined the following interceptor:
#Aspect
public class OpenSessionInRequestInterceptor {
private Log log = LogFactory.getLog(getClass());
#Autowired
private SessionFactory sessionFactory;
public OpenSessionInRequestInterceptor() {
}
#Around("#annotation(com.sc2.master.aop.hibernate.OpenSession)")
public Object processAround(ProceedingJoinPoint pjp) throws Throwable {
log.info("Opening Hibernate Session in method "+pjp.getSignature());
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
Object ret = pjp.proceed();
session.close();
TransactionSynchronizationManager.unbindResource(sessionFactory);
log.info("Closing Hibernate Session in method "+pjp.getSignature());
return ret;
}
}
When I execute the following piece of code in a spring test
#OpenSession
public void call() {
BusinessCustomer customer = (BusinessCustomer) this.customerDao.loadAll().get(0);
System.out.println(customer.getContacts().size());
}
the aspect method is called. To start the test my test case class looks as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"file:WebContent/WEB-INF/applicationContext.xml"})
#Transactional
However, when I have a method annotated with #OpenSession and deploy the application on my Tomcat server, the interceptor method is not called.
The application context definition looks as follows:
<aop:aspectj-autoproxy proxy-target-class="true">
</aop:aspectj-autoproxy>
<bean id="openSessionInRequestInterceptor" class="OpenSessionInRequestInterceptor"></bean>
I can absolutely not figure out, why AOP does not work when deployed on the tomcat. I hope you have some ideas.
Solution I found the solution. I places my aop configuration in the applicationContext.xml, but this will not work. I placed the configuration in the application-servlet.xml and now everything is fine. Has someone an idea why?
I admit I didn't have to make it work using a marker annotation, but I needed the annotation as argument, so this worked:
#Around("#annotation(foo)")
public Object invoke(ProceedingJoinPoint invocation, Foo foo) throws Throwable
But.. note that #Transactional also starts a session if one isn't started, so perhaps you don't really need that.
Update: if your beans are defined in the child context, then the aop configuration of the parent context does not affect them. The parent context does not see the child context, and your x-servlet.xml is a child context.
To answer why you have to put configuration in the servlet XML to get to work:
I assume you are using <context:component-scan ...> tag and this is placed in the servlet XML. That is the reason why you need to have them both in servlet XML, otherwise they don't "see" each other. As a result, the connection is not properly established.