Начало J2EE На хоризонта - EJB 3.0
На хоризонта - EJB 3.0
Сряда, 26 Октомври 2005 15:49
Основен подход заложен във всички спецификации на J2EE е стремежът към ускоряване на процеса на разработка, чрез неговото опростяване и въвеждането на множество улеснения. Това основно преимущество допринесе за добрия прием на J2EE технологиите от страна на разработчиците и бързото навлизане на Java като предпочитана технология в множество компании, разработващи софтуерни решения.  
Въпреки това, за много разработчици, Enterprise JavaBeans (EJB) архитектурата – един от фундаменталните компоненти на J2EE, като че не пасваше в рамките на тази така харесвана стратегия. От самото начало, разработването на EJB компоненти беше сложно. Едно от големите очаквания – Enterprise JavaBeans да бъде технологията, която да замени сложната CORBA не се изпълни. Всъщност, с всяка следваща редакция на EJB спецификацията, нещата ставаха още по-заплетени.

Преди да преминем към обсъждане на новата спецификация EJB 3.0, нека посочим някои от основните недостатъци, с които разработчцитие се сблъскваха при разработката на Enterprise JavaBeans до момента:

  • EJB 2.1 моделът изисква създаването на няколко компонентни интерфейса и имплементацията на излишни callback методи.
  • Тези интерфейси трябва да имплементират EJBObject и EJBLocalObject и да обслужват множество ненужни изключения.
  • Дескрипторите за разполагане на EJB са сложни и в тях лесно се допускат грешки.
  • Управляваната от контейнера персистентност (CMP) е твърде сложна за разработка и управление. Липсват базови възможности, като дефиниция на първични ключове от sequence в база данни. EJBQL е твърде ограничен.
  • EJB компонентите не са напълно обектно-ориентирани (съществуват ограничения при наследяването и полиморфизма).
  • EJB компонентите не могат да се тестват извън контейнер, а дебъгването им в контейнера е изключително трудоемко.
  • Процесът по откриване и изикване на вече готовите EJB-та изисква доста писане и е свързан с нуждата от познаването на друга технология - JNDI.

Имайки предвид споменатите недостатъци, през втората половина на миналата година Sun Microsystems публикува чернова на спецификация EJB 3.0 [1] , която прави опит да се справи със сложността и директно атакува някои от от споменатите недостатъци.

Промените в новата спецификация могат условно да се разделят в две генерални направления:

  • Базиран на анотации програмен модел
  • Нов персистентен модел

Без излишни интерфейси


С появата на Java 5 (с кодово име Tiger) миналата година беше представено едно интересно разширение - модел за анотации. Тази възможност позволява дефинирането на собствени анотации, с които после да се бележат полета, методи, класове и т.н. и които да бъдат четими за външни инструменти[2].

Да погледнем как стои въпросът с разработката на Enterprise JavaBean в контекста на новата спецификация. Стандартният пример от повечето книги описващи EJB технологията е добре познатият Statless Session Bean – HelloWorld. Съгласно старата спецификация (EJB 2.1) разработчика на този bean трябва да създаде поне два интерфейса, един имплементиращ клас с няколко празни методи и да напише дексриптор. В EJB 3.0, същият bean се реализира така:

@Stateless
@Remote
public class HelloWorldBean {
     public String sayHelloWolrd() {
        return "Hello World!"
     }
}

Както е видно, интерфейси вече не са нужни при разработката на Statless bean-ове. Всъщност, те са генерират автоматично, но ако разработчикът държи да си ги напише ръчно, тази възможност е запазена. Нещо повече, за по-голямо улеснение ръчно написаните интерфейси вече не изискват да се имплементира EJBObject или EJBLocalObject. Ръчно написан интерфейс за горния пример ще изглежда така:

@Remote
public interface HelloWorld {
  public void sayHelloWorld(String name);
}

Така или иначе, в почти всички случаи, писането на такъв интерфейс вече не е нужно. Той се генерира автоматично при откриване на анотация @Remote в кода на самия bean, като включва всички public методи на класа.

Home интерфейсите са цялостно премахнати за всички видове EJB-та. Те никога не са имали голям смисъл при Stateless bean-овете, а ограниченото им използване при Stateful Session bean-овете не оправдава съществуването им.

Да забравим за дескрипторите

Използвайки възможностите за анотация, новата спецификация премахва с едно решение два сериозни проблема. Първо, както е видно и от предишния пример, в EJB 3.0 всички Enterprise JavaBeans не са нищо повече от добре познатите ни стандартни Java обекти, само че подходящо анотирани. Второ, чрез анотации могат да се зададат всички параметри и свързвания, които до сега се описваха в дексрипторите. По-този начин, самите дескриптори също стават напълно излишни.

Следният пример илюстрира декларация на метаданни в EJB съгласно новата спецификация:

@Stateful
public class ShoppingCartBean implements ShoppingCart {
  @Tx(TxType.REQUIRED) @MethodPermission({"customer"})
  public void purchase(Product product, int quantity) {...}
  @Remove void emptyCart() {...}}

Съвсем очевидно анотацията @Stateful маркира ShoppingCartBean като stateful bean. @Tx маркира транзакция. @MethodPermission се използва за задаване на познатата от дескрипторите политика на сигурност, базирана на роли. Всъщност, за всеки от познатите дескрипторни елементи съществува аналогична анотация. По този начин, дескрипторът може цялостно да се игнорира и върху приложния сървър могат да се разполагат стандартни JAR пакети.

Въпреки това дескрипторите остават като опционална възможност. Ако разработчикът е свикнал с тях или не иска допълнително да утежнява читаемостта на бизнес логиката с инфраструктурни метаданни, той може да продължи да ги ползва и съгласно новата спецификация.

Да забравим и за Callback методите

Съгласно спецификация 2.1 за всеки Enterprise JavaBean задължително трябваше да се имплементират някои методи, извиквани в различни фази на жизнения цикъл на компонента. Това бяха методи като ejbActivate, ejbPassivate, ejbLoad, ejbStore и т.н. Нещо повече, имплементацията на методите беше строго задължителна, дори в случаите когато е напълно ненужна (например ejbPassivate няма смисъл за Statless Session bean).

С новата спецификация, където EJB са вече стандартни класове, имплементацията на методите не е задължителна. Всъщност, най-вероятно те също ще бъдат заменени с подходящи анотации в някоя от следващите ревизии на спецификацията. Към момента съществува само една възможна анотация и тя е @Remove, с която всеки метод от даден Stateful Session bean може да се маркира и да играе ролята на добре познатия ejbRemove. По този начин, контейнерът получава инструкция да премахне bean-а след извикването на анотирания с @Remove метод. В момента са в ход дискусии на експертната група, дали подобна анотация трябва да се въведе еквивалентно и за метода ejbCreate. По-всичко изглежда, че към момента привържениците на идеята се увеличават.

Опростен CMP

CMP Entity bean-овете и изобщо целия персистентен фреймуърк са претърпели цялостна ревизия в EJB 3.0, като в тях са направени множество страхотни подобрения. Като начало веднага прави впечатление влиянието на вече добили популярност сред разботчиците персистентни модели като Hybernate и Oracle TopLink.

Съгласно новата спецификация, един примерен entity bean изглежда така:

@Entity
public class CustomerBean {
private Long customerId;
private String name;
private String company;
private String comment;
private Address address;
public CustomerBean() {}
@id(generate=AUTO) public Long getCustomerId() {
  return id;
}
public String setCustomerId(Long id) {
  this id=id;
}
public String getName() {
  return name;
}
public String setName() {
   this name= name;
}
...
@Dependent public Address getAddress() {
   return address;
}
public void setAddress(Address address) {
  this.address = address;
}
}

Както е видно, entity bean-овете вече също са стандартни Java обекти, а не класове, имплементиращи интерфейс като javax.ejb.EntityBean. Също така CustomerBean вече е конкретен, а не абстрактен клас. Член променливите са персистентните полета, които се достъпват с познатите аксесорни методи. Разликата е, че последните също вече не са абстрактни и могат да съдържат логика (например за валидизация на параметрите).

Анотацията @Id обозначава първичен ключ.Зададената в случая стратегия AUTO разчита на контейнера да избере механизъм за генерирането на стойностите за първичния ключ. Съществуват редица други стратегии, като например SEQUENCE, ENTITY, TABLE и т.н.

Новата спецификация дефинира и механизъм за пълно обектно-релационно свързване. По този начин може да се направи връзка между обект съдържащи агрегирани стойности в entity bean-а и представляващата го струткура на ниво база. В горния пример, подобно свързване е указано за обекта Address, чрез анотацията @Dependent. Самият Address може да изглежда така:

@DependentObject(access=AccessType.PROPERTY)
public class Address implements java.io.Serializable {
  private String street;
  private String state;
  private String city;
  public String getState() {
    return state;
  }
...
}

Подобрения в EJB QL

Сериозната преработка на спецификацията не е подминала и EJB QL. Вече спокойно можем да твърдим, че EJB QL е пълноправен език за операции с бази данни, който цялостно покрива функционалността на SQL. EJB QL вече поддържа GROUP BY, HAVING, вътрешен и външен JOIN, вложени заявки и др.

За разлика от спецификация 2.1, EJB 3.0 има пълна поддръжка за динамични заявки, като чрез EntityManager.createQuery() се конструира обекта, с който в последствие се взаимодейства за задаване на атрибути и извличане на резултати.

Query query = entityManager.createQuery("SELECT o FROM Orders o WHERE o.total > 10000");
query.setMaxResults(50);
Collection bigOrders = query.listResults();

Нов подход е възможността заявките да се дефинират предварително и да се съхраняват в entity bean-а чрез анотации. За целта се използва @NamedQuery, а извикването им става чрез метода createNamedQuery() на EntityManager:

@Entity
...
@NamedQuery(
  name = "getOrdersOver10000",
  queryString = "SELECT o FROM Orders o WHERE o.total > 10000"
)
public class OrderBean {
...
  Query query = entityManager.createNamedQuery("getOrdersOver10000");
  Collection bigOrders = query.listResults();
...
}

Едно от най-интересните нововъведения е възможността за проекция на резултатите от заявката върху произволен Java обект. Следната заявка е напълно валидна и илюстрира идеята:

SELECT new Report(c.state, SUM(o.grandTotal)) FROM Customer c JOIN Order.customer Customer GROUP BY c.state

В случая, Report е стандартен Java обект. При изпълнението на заявката, EntityManager ще съзадава нов обект за всеки ред в резултата и вместо в последствие да е нужно итериране с цел инициализация, EntityManager вече я е извършил чрез подаване на параметрите през конструктора на Report.

По-лесно откриване и извкиване

До сега използването на EJB (в частност, процесите по откриване и извикване) бяха сложни, дори в случаите когато EJB-то е част от самото приложение. Съгласно спецификация 2.1 използването на EJB изисква описването на ejb-ref или ejb-local-ref в дескриптора, динамично откриване (т.н. lookup) на EJB-то и неговото извикване. Един примерен дескриптор, опсиващ HelloWorld EJB би изглеждал така:

<ejb-ref>
<ejb-ref-name>HelloWorldEJB</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<home>HelloWolrdHome</home>
<remote>helloWorld.HelloWorld</remote>
</ejb-ref>

И съответното извикване в кода:

try {
  Context context = new InitialContext();
  HelloWorldHome helloHome = (HelloWorld) PortableRemoteObject.narrow(context.lookup ("HelloWorldEJB"),     HelloWorldHome.class);
  HelloWorld hello = helloHome.create();
....
} catch(RemoteException e) {
  // process RemoteException
} catch(NamingException e) {
  // process NamingException
} catch(CreateException e) {
  // process CreateException
}

В новата спецификация, всичко е много по-опростено. Представен е механизъм, който се нарича dependency injection и използването му има много предимства спрямо context.lookup(). Идеята на новия механизъм е обектите и услугите да декларират сами кои ресурси ще използват, а контейнерът ще има грижата да "инжектира" автоматично нужните стойности. Dependency injection се поддържа от всички видове session bean-ове, като има планове неговата поддръжка да се пренесе в по-голям обхват върху цялата J2EE спецификация (например при сървлетите). Използването на новия механизъм не изисква JNDI lookup. Извикването на HelloWolrd от друго EJB се свежда единствено до:

@EJB public HelloWolrd helloWorld;

Bean-ът анотиран по този начин, ще бъде "инжектиран" с инстанция на HelloWorld, която в последствие е изцяло достъпна за останалите методи. Чрез dependency injection могат да се "инжектират" всякакви ресурси, включително JMS, уеб услуги, DataSource и т.н. Инжектирането с DataSource именуван "DefaultDS" изглежда така:

@Resource(name="DefaultDS") private javax.sql.DataSource ds;

Страничен ефект от въвеждането на dependency injection е възможността, bean-овете да се тестват извън контекста на контейнера[4], което до момента също беше в списъка на съществените проблеми с предишната спецификация.

В заключение

EJB спецификацията определено има много път пред себе си, докато превърне разработването на Enterprise JavaBeans в нещо приятно. Стремежът към опростяване чрез анотации също има своята цена. Към момента анотациите по новата спецификация са над 50, като всяка от тях върви с определен набор параметри и условия. Въпреки това, избраният подход е безспорно крачка напред към подобрението на архитектурата. Фактът, че най-големите разработчици на приложни сървъри като Oracle, Sun и IBM също активно участват в работната група по спецификацията еднозначно показва, че скоро EJB 3.0 ще бъде стандартът за разработване на сървърни J2EE компоненти.

Коментари

Име
URL
Код   
Запис
 

КНИГАТА

Oracle Database Security Book
(c) 2004-2008 Николай Манчев. Освен ако изрично не е споменато нещо друго, всички материали публикувани тук се разпространяват под Creative Commons Attribution License. Материали, коментари и изображения, които не са създадени и подписани от мен са собственост на съответните им автори.