Earlier the configurations used to be in hard coded in the code, later it was externalized to .property files (for sake of avoiding hard coded values, avoiding changing code for the sake of changing configurations..etc) then it moved to XML (for sake of being more standardized, error free..etc)
Now, while reading about #Configuration in Spring 3 , looks like we are again moving back to the initial approach.
Why would we want to hard-code configurations in the code rather than
having it externalized ?
There are some advantages
Java is type safe. Compiler will report issues if you are
configuring right bean class qualifiers.
XML based on configuration can quickly grow big. [Yes we can split
and import but still]
Search is much simpler, refactoring will be bliss. Finding a bean
definition will be far easier.
There are still people who like XML configuration and continue to do it.
References:
Java configuration advantages
Some more reasons
Correct answer was given here, Other-than that answer, I give one more point
According to the Spring 5 reference
XML-based metadata is not the only allowed form of configuration
metadata. The Spring IoC container itself is totally decoupled from
the format in which this configuration metadata is actually written.
These days many developers choose Java-based configuration for their
Spring applications.
it means nowadays, people are moving towards java based config
for example: Spring web-mvc config in xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" 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-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="controller" />
<context:component-scan base-package="dao" />
<context:component-scan base-package="service" />
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
and same config in java based style
#Configuration
#EnableWebMvc
#ComponentScans({
#ComponentScan("controller"),
#ComponentScan("dao"),
#ComponentScan("service")
})
public class WebMVCConfig implements WebMvcConfigurer {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
what is easy to understand? Obviously, the Java-based config is easy to understand.
people who write codes and others can easily understand java code than XML. and you do not need to know about XML if you only know Java.
Related
To work via annotations in Spring need to define:
<?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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
xmlns:context="http://www.springframework.org/schema/context">
<context:annotation-config/>
</beans>
at
annotation-config.xml
But I created a simplest Spring boot Application (lets say I select lust Web in initialazr)
It works on annotations but there isn't any annotation-config.xml there and nor mentioning of ,
where is hidden?
You only need to add <context:annotation-config /> or <context:component-scan /> (which implies annotation driven config) when using an ApplicationContext implementation that doesn't support annotations out-of-the-box.
When only using XML based configuration you also use one of the XML enabled ApplicationContext implementations, generally that would be XmlWebApplicationContext. With these you would need to instruct the ApplicationContext to enable annotation processing.
When using Java based configuration you generally use an annotation based ApplicationContext, default would be AnnotationConfigWebApplicationContext. Due to its nature of processing Java configuration classes it has annotation processing enabled by default.
Spring Boot uses the latter (it actually uses a specialized subclass for this). Hence you don't need to explicitly enable it.
*-servlet.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">
<context:property-placeholder location="classpath*:spring.properties" />
<context:component-scan base-package="com.my.path" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
com.my.path.MyController
#Controller
public class MyController {
#Value("${my.property}")
private String myProperty;
#RequestMapping("/my/path")
public String default(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws Exception {
// Any reference to myProperty above is null...
return "view";
}
}
I do have other beans defined by ID in my servlet context that use the same system property I'm trying to add to my controller via the Value annotation. I know they are present and with the right values. So I'm not sure why I just can't get the annotation to work.
Take a look at this answer. Do you happen to have more XML Spring contexts? Is the component-scan defined on the same Spring config as the property-placeholder as shown above?
After reviewing just about every tutorial I could for a day I realized this has to be some sort of classloader issue. So I reviewed my packaging structure, which is an EAR containing a WAR. Sure enough I had two spring bean jars of identical versions, one in EAR/lib and the other in WAR/WEB-INF/lib. Apparently the properties were picked up in the EAR, but the post processing was happening in the WAR (or something like that). So by sorting that out, and getting duplicate JARs out of the WAR it is now fixed.
For WebApplicationContext, should I put #Transactional annotations in the controller or in services? The Spring docs have me a bit confused.
Here is my web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Alpha v0.02</display-name>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.json</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Here is my application-context.xml defining a spring dispatcher servlet:
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
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
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config />
<mvc:annotation-driven />
<tx:annotation-driven />
<context:component-scan base-package="com.visitrend" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="org.postgresql.Driver" />
<property name="jdbcUrl" value="jdbc:postgresql://localhost:5432/postgres" />
<property name="user" value="someuser" />
<property name="password" value="somepasswd" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:test.hibernate.cfg.xml" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="dataSource" ref="dataSource" />
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
Here's a service interface:
public interface LayerService {
public void createLayer(Integer layerListID, Layer layer);
}
Here's a service implementation:
#Service
public class LayerServiceImpl implements LayerService {
#Autowired
public LayerDAO layerDAO;
#Transactional
#Override
public void createLayer(Integer layerListID, Layer layer) {
layerDAO.createLayer(layerListID, layer);
}
}
And here's my controller:
#Controller
public class MainController {
#Autowired
private LayerService layerService;
#RequestMapping(value = "/addLayer.json", method = RequestMethod.POST)
public #ResponseBody
LayerListSetGroup addLayer(#RequestBody JSONLayerFactory request) {
layerService.createLayer(request.getListId(), request.buildLayer());
return layerService.readLayerListSetGroup(llsgID);
}
}
The Spring documentation has me a bit confused. It seems to indicate that using a WebApplicationContext means only controllers will be investigated for #Transactional annotations and not services. Meanwhile I see tons of recommendations to make services transactional and not controllers. I'm thinking that using <context:component-scan base-package="com..." /> in our spring-servlet.xml above so that it includes the services packages means the services are part of the context, and therefore will be "investigated" for transactional annotations. Is this accurate?
Here's the Spring documentation blurb that got me confused:
#EnableTransactionManagement and only looks
for #Transactional on beans in the same application context they are
defined in. This means that, if you put annotation driven
configuration in a WebApplicationContext for a DispatcherServlet, it
only checks for #Transactional beans in your controllers, and not your
services.
Further, is there any performance implications or "badness" if I define a controller method as transactional, and it calls a transactional method in a another class? My hunch is no, based on the documentation, but would love validation on that.
There is no requirement for whether the #Transactional annotation should go on a Controller or on a Service, but typically it would go on a Service that would perform the logic for one request that logically should be performed within one ACID transaction.
In a typical Spring MVC application, you would have, minimally, two contexts: the application context and the servlet context. A context is a sort of configuration. The application context holds the configuration that is relevant for your entire application, whereas the servlet context holds configuration relevant only to your servlets. As such, the servlet context is a child of the application context and can reference any entity in the application context. The reverse is not true.
In your quote,
#EnableTransactionManagement and only looks for #Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for #Transactional beans in your controllers, and not your services.
#EnableTransactionManagement looks for #Transactional in beans in packages declared in the #ComponentScan annotation but only in the context (#Configuration) they are defined in. So If you have a WebApplicationContext for your DispatcherServlet (this is a servlet context), then #EnableTransactionManagement will look for #Transactional in classes you told it to component scan in that context (#Configuration class).
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = "my.servlet.package")
public class ServletContextConfiguration {
// this will only find #Transactional annotations on classes in my.servlet.package package
}
Since your #Service classes are part of the Application context, if you want to make those transactional, then you need to annotate your #Configuration class for the Application Context with #EnableTransactionManagement.
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = "my.package.services")
public class ApplicationContextConfiguration {
// now this will scan your my.package.services package for #Transactional
}
Use your Application Context configuration with a ContextLoaderListener and your Servlet Context configuration when instantiating your DispatcherServlet. (See the javadoc for a full java based config, instead of xml, if you aren't doing it already.)
Addendum: #EnableTransactionManagement has the same behavior as <tx:annotation-driven /> in a java configuration. Check here for using ContextLoaderListener with XML.
The Service is the best place for putting transactional demarcations. The service should hold the detail-level use case behavior for a user interaction, meaning stuff that would logically go together in a transaction. Also that way a separation is maintained between web application glue code and business logic.
There are a lot of CRUD applications that don't have any significant business logic, for them having a service layer that just passes stuff through between the controllers and data access objects is not useful. In those cases you could get away with putting the transaction annotation on the data access objects.
Putting the transactional annotation on the controller can cause problems, see [the Spring MVC documentation][1], 17.3.2:
A common pitfall when working with annotated controller classes
happens when applying functionality that requires creating a proxy for
the controller object (e.g. #Transactional methods). Usually you will
introduce an interface for the controller in order to use JDK dynamic
proxies. To make this work you must move the #RequestMapping
annotations, as well as any other type and method-level annotations
(e.g. #ModelAttribute, #InitBinder) to the interface as well as the
mapping mechanism can only "see" the interface exposed by the proxy.
Alternatively, you could activate proxy-target-class="true" in the
configuration for the functionality applied to the controller (in our
transaction scenario in ). Doing so indicates
that CGLIB-based subclass proxies should be used instead of
interface-based JDK proxies. For more information on various proxying
mechanisms see Section 9.6, “Proxying mechanisms”.
The transaction propagation behaviors that you set up on the attributes decide what happens when a transactional method calls another transactional method. You can configure it so that the method called uses the same transaction, or so that it always uses a new transaction.
By having multiple calls to your service in the example code you're defeating the transactional purpose of the service. The different calls to your service will execute in different transactions if you put the transactional annotations on the service.
Sometimes it is very convenient to have #Transactional controller methods, especially when performing trivial operations using Hibernate. To enable this using XML configuration, add this to your dispatch-servlet.xml:
<beans ...
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="...
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
..
</beans>
The purpose of proxy-target-class is to use the CGLIB proxies which are required for AOP on controllers to work. If you don't add this, you'll get an error when starting up. Also, if you have any final methods in your controllers, note that they cannot be proxied (in particular, made transactional), and you will also get a warning from CGLIB for each of these methods, when starting up.
I recently started using hibernate and spring.
In the beginning I was told to use the sessionFactory and openSession together with beginTransaction to do db calls.
Later I heard about dao's so I started using it by creating an interface, implementing this interface in a class and then have this class extend HibernateDAOSupport.
I figured this was pretty solid until a colleague told me that this way is deprecated and that I should not use the HibernateDAOSupport class. But instead work with a SessionFactory instance in my dao implementation class and an instance of that class in a new seperate service class. Since this also seemed like a good way to do it, I decided to follow this path.
Just now I've read that this method is also deprecated with the new version of spring...
So my question is: What on earth is the correct up-to-date way to bring hibernate and spring together??
I've also heard talk about an entity manager, what is that about?
I'm looking for the general way to use them, if there are any exceptions to the general rule, please also provide an example of these exceptions.
Here's one of many ways to integrate Hibernate into Spring...
Your Service (or DAO) class should look as simple as this:-
// annotate this class so that Spring is aware of it
#Service
public class EmployeeServiceImpl implements EmployeeService {
#Autowired
private SessionFactory sessionFactory;
// this is straight-up HQL... really, no magic here
#Override
#SuppressWarnings("unchecked")
public Collection<Employee> getAllEmployees() {
return sessionFactory.getCurrentSession()
.createQuery("from Employee e order by e.lastName, e.firstName")
.list();
}
}
Now, you need to configure sessionFactory, otherwise the autowiring will fail. So, this is how you configure it:-
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<!--
When creating the session factory, point it to your existing hibernate.cfg.xml.
You can also port your entire Hibernate configuration and HBM mappings here, but
for simplicity sake, I'll reference the existing hibernate.cfg.xml here so that we
are not cluttering Spring configuration file with Hibernate-specific configuration.
-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:hibernate.cfg.xml"/>
</bean>
<!-- If you are running in production, you will want to use JNDI connection -->
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/myapp"/>
<!-- If you are running testcases, you might want to use JDBC instead -->
<!--
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver"/>
<property name="url" value="jdbc:jtds:sqlserver://machine/myapp"/>
<property name="username" value="myapp"/>
<property name="password" value="myapp"/>
</bean>
-->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:advisor pointcut="execution(* com.myapp..*.*(..))" advice-ref="txAdvice"/>
</aop:config>
</beans>
There are two ways to configure transaction: 1) use #Transactional and you can annotate which classes (or specific methods) that require proper transaction handling, and 2) use AOP and wrap all your code with transaction.
In my example above, I'm wrapping all my code with base package com.myapp with transaction. You can change (or add) the pointcuts to reduce the transaction wrapper.
Regarding Hibernate in Spring, as for 2013, HibernateDaoSupport is deprecated(and the HibernateTemplate too). It will not work in Hibernate 4.0.
I think that using #Transactional annotated classes with sessionFactory.getCurrentSession() for Hibernate specific and #PersistenceContext annotated EntityManager property to autowire persistence context for the JPA configuration is the current default choice.
JPA is a standard, while Hibernate is not, but most Hibernate specific things will work in JPA configuration(of course that would mean that you will not adhere to standards anymore and it will be harder to switch to another JPA implementation), only a few are available only in pure Hibernate, for example you could not detach standalone object from the session prior to JPA 2.0. It's simpler to implement some new feature in a proprietary framework rather than to change the standard, but I believe that JPA 2.0 is suitable for the most of the cases.
There are a lot of similar questions on StackOveflow 1 2 3 and so on.
A good argument to use JPA configuration is that it can work with Spring Data JPA - a framework that simplifies implementing repositories(you only need to declare interfaces instead of keeping parallel hierarchies between interfaces and generic-repository based classes).
In my Spring MVC webapplication I want to mix xml based configuration with annotations:
I use annotations like #Controller, #RequestMapping("bla.htm"), #RequestParam etc. to resolve HttpRequests to Controller Methods. Therefore I added
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<context:component-scan base-package="somePackage.controller"/>
to my dispatcher-servlet.xml.
But my controllers have attributes. Those attributes could be injected via #AutoWired annotation. But I also have do define Scopes. So i would have two annotations per attribute, which makes the code bad readable. So I want to inject dependencies in my applicationContext.xml file.
Is there a way I can keep the annotation-driven request-mapping but use context.xml files for Dependency Injection? Or is it only possible to use EITHER annotations OR xml configuration?
note: my beans for dependency injection are in a different xml file.
PS:
I should have mentioned, I use Spring 2.5 and can't upgrade it.
No, <mvc:annotation-driven> works fine with XML. But you'll need to get rid of the <context:component-scan>.
Update: with Spring 2.5, this should get you started:
<?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-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
<!-- now add some controllers -->
</beans>
Yes this is certainly possible.
To use the controller annotations such as #Controller and #RequestMapping make sure you put
<mvc:annotation-driven/>
in your <servletname>-servlet.xml
Then simple define your controllers using the normal XML bean notation such as:
<bean class="com.company.controllers.AController">
<property name="propertyName" ref="beanId" />
</bean>
These bean refs can come from any other applicationContext.xml defined in your web.xml too.