На мой взгляд спор не имеет смысла - один и тот же POJO может замечательно жить и под управлением EJB-контейнера и под управлением Spring Framework. Аргументы в стиле "EJB плохо тестируются" так же не состоятельны: при тестировании можно использовать Spring.
По сути, разница заключается лишь в том, что в Spring этот инъектируемый POJO по-умолчанию будет синглтоном и все его методы не являются потокобезопасными. В EJB контейнере же как правило создается пул объектов и спецификация гарантирует потокобезопасность бизнес-методов. При этом инъектируемый EntityManager является потокобезопасным в обоих случаях.
Позволю себе продемонстрировать данное мнение кодом, думаю так будет максимально наглядно. Данную заметку так же можно использовать в качестве пособия по интеграции JPA и Spring Framework.
IService:
Service:
Entity:
persistence.xml:
ejb-jar.xml:
EJB Client:
EJB Client's web.xml:
Результат работы:
applicationContext.xml:
Spring client:
Spring client's web.xml:
Результат работы:
Я думаю, идея понятна.
Про интеграцию Spring Framework и WebLogic можно так же прочитать здесь. В блоге Сурового челябинского программиста есть так же статья, описывающая совместное использование Hibernate со Spring Framework.
А вы что предпочитаете использовать, Spring Framework или EJB?
Понравилось сообщение - подпишитесь на блог и Twitter
По сути, разница заключается лишь в том, что в Spring этот инъектируемый POJO по-умолчанию будет синглтоном и все его методы не являются потокобезопасными. В EJB контейнере же как правило создается пул объектов и спецификация гарантирует потокобезопасность бизнес-методов. При этом инъектируемый EntityManager является потокобезопасным в обоих случаях.
Позволю себе продемонстрировать данное мнение кодом, думаю так будет максимально наглядно. Данную заметку так же можно использовать в качестве пособия по интеграции JPA и Spring Framework.
IService:
package name.samolisov.demo;
public interface IService {
public void transaction();
}
public interface IService {
public void transaction();
}
Service:
package name.samolisov.demo;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import name.samolisov.transactions.entity.Entity;
public class Service implements IService {
@PersistenceContext(unitName = "TransactionSA")
private EntityManager em;
public void transaction() {
em.persist(new Entity(Thread.currentThread().getName()));
}
}
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import name.samolisov.transactions.entity.Entity;
public class Service implements IService {
@PersistenceContext(unitName = "TransactionSA")
private EntityManager em;
public void transaction() {
em.persist(new Entity(Thread.currentThread().getName()));
}
}
Entity:
package name.samolisov.transactions.entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
@javax.persistence.Entity
public class Entity {
@Id
@SequenceGenerator(name = "EntitySeqGen", sequenceName = "id_seq", allocationSize = 10)
@GeneratedValue(generator = "EntitySeqGen")
private Long id;
private String message;
public Entity() {
}
public Entity(String message) {
this.message = message;
}
public Entity(Long id, String message) {
this.id = id;
this.message = message;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
@javax.persistence.Entity
public class Entity {
@Id
@SequenceGenerator(name = "EntitySeqGen", sequenceName = "id_seq", allocationSize = 10)
@GeneratedValue(generator = "EntitySeqGen")
private Long id;
private String message;
public Entity() {
}
public Entity(String message) {
this.message = message;
}
public Entity(Long id, String message) {
this.id = id;
this.message = message;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="TransactionSA" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/xa/sa</jta-data-source>
<class>name.samolisov.transactions.entity.Entity</class>
<properties>
<property name="eclipselink.target-server" value="WebLogic_10"/>
</properties>
</persistence-unit>
</persistence>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="TransactionSA" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/xa/sa</jta-data-source>
<class>name.samolisov.transactions.entity.Entity</class>
<properties>
<property name="eclipselink.target-server" value="WebLogic_10"/>
</properties>
</persistence-unit>
</persistence>
ejb-jar.xml:
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<enterprise-beans>
<session>
<ejb-name>ServiceBean</ejb-name>
<business-local>name.samolisov.demo.IService</business-local>
<ejb-class>name.samolisov.demo.Service</ejb-class>
<session-type>Stateless</session-type>
</session>
</enterprise-beans>
</ejb-jar>
version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<enterprise-beans>
<session>
<ejb-name>ServiceBean</ejb-name>
<business-local>name.samolisov.demo.IService</business-local>
<ejb-class>name.samolisov.demo.Service</ejb-class>
<session-type>Stateless</session-type>
</session>
</enterprise-beans>
</ejb-jar>
EJB Client:
package name.samolisov.transaction.web;
import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import name.samolisov.demo.IService;
public class TransactionServlet extends HttpServlet {
@EJB
private IService service;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
service.transaction();
}
}
import java.io.IOException;
import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import name.samolisov.demo.IService;
public class TransactionServlet extends HttpServlet {
@EJB
private IService service;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
service.transaction();
}
}
EJB Client's 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>TransactionWeb</display-name>
<servlet>
<servlet-name>tservlet</servlet-name>
<servlet-class>name.samolisov.transaction.web.TransactionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>tservlet</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
</web-app>
<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>TransactionWeb</display-name>
<servlet>
<servlet-name>tservlet</servlet-name>
<servlet-class>name.samolisov.transaction.web.TransactionServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>tservlet</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
</web-app>
Результат работы:
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<tx:annotation-driven />
<tx:jta-transaction-manager />
<jee:jndi-lookup id="TransactionSA" jndi-name="persistence/TransactionSA"/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<aop:config>
<aop:pointcut id="transactionMethod" expression="execution(* name.samolisov.demo.IService.transaction(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionMethod"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transaction" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<bean name="MyService" class="name.samolisov.demo.Service"/>
</beans>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<tx:annotation-driven />
<tx:jta-transaction-manager />
<jee:jndi-lookup id="TransactionSA" jndi-name="persistence/TransactionSA"/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<aop:config>
<aop:pointcut id="transactionMethod" expression="execution(* name.samolisov.demo.IService.transaction(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="transactionMethod"/>
</aop:config>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transaction" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<bean name="MyService" class="name.samolisov.demo.Service"/>
</beans>
Spring client:
package name.samolisov.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import name.samolisov.demo.IService;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
IService service = (IService) context.getBean("MyService");
service.transaction();
}
}
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import name.samolisov.demo.IService;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
IService service = (IService) context.getBean("MyService");
service.transaction();
}
}
Spring client's 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>SpringWeb</display-name>
<servlet>
<servlet-name>demo</servlet-name>
<servlet-class>name.samolisov.web.DemoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<persistence-unit-ref>
<description>
Persistence context for demo.
</description>
<persistence-unit-ref-name>
persistence/TransactionSA
</persistence-unit-ref-name>
<persistence-unit-name>
TransactionSA
</persistence-unit-name>
</persistence-unit-ref>
</web-app>
<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>SpringWeb</display-name>
<servlet>
<servlet-name>demo</servlet-name>
<servlet-class>name.samolisov.web.DemoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<persistence-unit-ref>
<description>
Persistence context for demo.
</description>
<persistence-unit-ref-name>
persistence/TransactionSA
</persistence-unit-ref-name>
<persistence-unit-name>
TransactionSA
</persistence-unit-name>
</persistence-unit-ref>
</web-app>
Результат работы:
Я думаю, идея понятна.
Про интеграцию Spring Framework и WebLogic можно так же прочитать здесь. В блоге Сурового челябинского программиста есть так же статья, описывающая совместное использование Hibernate со Spring Framework.
А вы что предпочитаете использовать, Spring Framework или EJB?
Понравилось сообщение - подпишитесь на блог и Twitter
9 комментариев:
Не понялю Можно пояснения?
Просто спор не имеет смысла - один и тот же POJO может замечательно жить и под управлением EJB-контейнера и под управлением Spring Framework. Аргументы в стиле "EJB плохо тестируются" так же не состоятельны: при тестировании можно использовать Spring.
По сути, разница заключается лишь в том, что в Spring этот инъектируемый POJO по-умолчанию будет синглтоном и все его методы не являются потокобезопасными. В EJB контейнере же как правило создается пул объектов и спецификация гарантирует потокобезопасность бизнес-методов. При этом инъектируемый EntityManager является потокобезопасным в обоих случаях.
EJB так же хорошо тестируется как и спринг, есть всякие тулзы типо arqullian.
В EJB3.1 можно так же как и в спринге использовать singleton. В свою очередь в спринге есть scope=prototype которое как бы похоже на steteless поведение, но врятли конечно для них контенейр создает пул.
Потокобезопасность контейнер EJB для методов stateless бина не гарантирует, и если например будет задействован какой либо общий объект (ну например заинжекшен singleton bean) который будет менятся в методе нашего stateless бина то будет по сути конкурентное изменение. Т.е. потокобезопасность должен реализовывать сам программист.
Спасибо за развернутый комментарий.
По поводу тестирования EJB, в 3.1 вообще появился встроенный контейнер, что снижает зависимость от внешних утилит.
Немножко уточню. Синглтон можно использовать и в некоторых серверах приложений, реализующих 3.0. Например в WebLogic объект-синглтон будет создан один на кластер и в случае падения сервера, на котором он - синглтон - работает, будет осуществлена его миграция на другой сервер.
По поводу потокобезопасности. Понятно, что с не очень большой аккуратности, скажем так, можно и кое что важное сломать. Но контейнер должен или сериализовывать вызовы бизнес-методов или использовать пул объектов. Естественно, что если заинъектить синглтон, то доступ к нему будет конкурентным, если же у каждого экземпляра компонента в пуле своя копия объекта (например, EntityManager'а), то все в порядке. Впрочем, соглашусь с Вами, разработчик должен понимать, что именно он передает в компоненты.
Сторонники Спринга в данном случае говорят, что у управляемого контейнером компонента вообще не должно быть в полях класса ссылок на объекты с изменяемым состоянием. Все такие объекты должны создаваться и использоваться как локальные переменные методов. Это действительно защищает от ошибок, но очень напоминает процедурное программирование.
Antonio SAN , для описанного вами случая с stateless и singleton в последнем надо использовать @Lock(LockType.READ) LockType.WRITE
на методах .
Павел, а вы можете провести тесты по времени что быстрее spring или ejb , есть мнение что ejb медленнее раза так в 1.5
Коллеги, я не тестировал сравнительную скорость приложения, написанного с одной стороны на Спринг, а с другой - на EJB 3. В любом случае, чтобы тесты были корректными, нужно, чтобы данные приложения делали одно и то же, а так же взаимодействовали с БД одинаковым способом, например через JPA. Получается, что нужен набор синтетических тестов, которые по сути проверяют лишь слой, управляющий транзакциями и создающий объекты (IoC), причем большинство объектов - синглетные и создаются один раз при старте приложения. Откуда здесь может взяться разница в производительности в 1.5 раза непонятно.
Позволю себе по некропостить. Я сделал бенчмарк, показывающий, что EJB немного, но быстрее, нежели Spring Framework (точнее, RESTификация в EJB быстрее нежели в Spring Web MVC).
Когда контейнер JBoss или Wildfly тогда выбор EJB3/Java EE, если под томкат то выбор Спринг, но Спринг как клей, для фронтенда JSF2.2 . EJB3 не нужна конфигурация, Спрингу нужно все, каждый раз когда Спринг обновляется может что то испортится, например под java 8 Спринг 3 не работает, а EJB3 как работал под JBoss 4.2.2 в 2008 году так и работает под wildfly 2016 + CDI . и потом стандартный java EE всегда на один шаг впереди Спринг.
Для стенделон без разницы что использовать. Может быть даже в Спринге есть по больше сахара чем в EJB, но для кластерных систем и для масштабирования мне кажется от EJB больший профит.
Отправить комментарий
Любой Ваш комментарий важен для меня, однако, помните, что действует предмодерация. Давайте уважать друг друга!