С выходом Java EE 7 я подумал, что пришло время обновить компонент Monster ! Несколько лет назад у Людовика Шампенуа возникла идея добавить как можно больше аннотаций Java EE в класс Java. Затем он использовался Алексисом Мусином Пушкиным на своих семинарах по Java EE. Пришло время возродить и обновить код, чтобы он соответствовал новинкам Java EE 7.
Что такое компонент монстр?
Компонент monster — это бесполезный кусок кода, в который вы добавляете несколько аннотаций Java EE, придающих ему различные аспекты. Java EE — это управляемая среда: возьмите класс Java, добавьте аннотацию @Stateless, и контейнер предоставит вам транзакции, безопасность, объединение в пул… возьмите другой класс, добавьте аннотацию @Path, и контейнер даст вам вызов REST. Таким образом, если вы добавляете @Stateless и @Path к одному и тому же классу, вы накапливаете службы EJB без состояния и веб-службы REST.
Монстр Компонент
Класс Book ниже представляет собой постоянный класс с методом (createAndListBooks), который сохраняется сам и извлекает все книги из базы данных. Это было превращено в Компонент Монстра, потому что это накапливает несколько услуг:
- Постоянство ( JPA ): класс Book помечается @Entity, @Table и объявляет @NamedQuery. Некоторые атрибуты Book настраивают отображение (с помощью @Column) или помечаются как @Transient (например, EntityManager).
- XML-привязка ( JAXB ): с помощью аннотаций @XmlRootElement и @XmlAccessorType класс Book может использовать маршализацию для получения XML-представления книги. Он настраивает привязку XML с помощью @XmlElement или @XmlTransient (для EntityManager)
- Ограничения (Bean Validation): некоторые атрибуты Книги имеют ограничения (@Size), а также параметры метода (@NotNull в createAndListBooks)
- EJB : класс Book является EJB без сохранения состояния (аннотирован @Stateless), поэтому он внедряет EntityManager и объявляет транзакционный метод createAndListBooks
- Сервлет : класс Book также является сервлетом, поскольку он расширяет HttpServlet, аннотируется @WebServlet и переопределяет doGetmethod. Этот метод использует внедренный Book EJB для сохранения себя.
- Веб-служба RESTful ( JAX-RS ): метод @Path и @GET позволяет вам вызывать метод createAndListBooks через HTTP GET. Он создает XML-представление всех книг благодаря аннотации JAXB @XmlRootElement
- Управление жизненным циклом : два частных метода имеют управление жизненным циклом (@PostConstruct и @PreDestroy)
- Перехват : класс Book определяет метод-перехватчик (метод logMethod, аннотированный @AroundInvoke), который регистрирует весь вызов метода
@Path("/MonsterRest")
@Stateless
@WebServlet(urlPatterns = "/MonsterServlet")
@Entity
@Table(name = "MonsterEntity")
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@NamedQuery(name = "findAll", query = "SELECT c FROM Book c")
public class Book extends HttpServlet {
// ======================================
// = Attributes =
// ======================================
@Id
@GeneratedValue
private Long id;
private String isbn;
private Integer nbOfPage;
private Boolean illustrations;
private String contentLanguage;
@Column(nullable = false)
@Size(min = 5, max = 50)
@XmlElement(nillable = false)
private String title;
private Float price;
@Column(length = 2000)
@Size(max = 2000)
private String description;
@ElementCollection
@CollectionTable(name = "tags")
private List<String> tags = new ArrayList<>();
// ======================================
// = Injected Resources =
// ======================================
@XmlTransient
@Transient
@EJB
private Book monsterEJB;
@XmlTransient
@Transient
@PersistenceContext(unitName = "monsterPU")
private EntityManager em;
// ======================================
// = Servlet Entry Point =
// ======================================
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String title = request.getParameter("title");
try {
response.getWriter().println("Servlet calling EJB " + monsterEJB.createAndListBooks(title));
} catch (Exception e) {
e.printStackTrace();
}
}
// ======================================
// = Business methods =
// ======================================
@GET
@Path("/{title}")
@Produces(MediaType.APPLICATION_XML)
public List<Book> createAndListBooks(@PathParam("title") @NotNull String title) {
// Sets data
this.id = null;
this.title = title + " " + new Date();
this.price = new Float(0.01);
this.description = "The hard-coded description";
this.isbn = "978-1-4302-1954-5";
this.nbOfPage = 210;
this.illustrations = Boolean.TRUE;
List<String> tags = new ArrayList<>();
tags.add("Monster");
tags.add("Component");
this.tags = tags;
// Persists the book
em.persist(this);
// Returns all books
TypedQuery<Book> query = em.createNamedQuery("findAll", Book.class);
List<Book> allBooks = query.getResultList();
return allBooks;
}
// ======================================
// = Interceptor =
// ======================================
@AroundInvoke
public Object logMethod(InvocationContext ic) throws Exception {
System.out.println(">>> " + ic.getTarget().getClass() + " - " + ic.getMethod().getName());
try {
return ic.proceed();
} finally {
System.out.println("<<< " + ic.getTarget().getClass() + " - " + ic.getMethod().getName());
}
}
@PostConstruct
private void prepare() {
System.out.println("\n=> PostConstruct");
System.out.println("================");
}
@PreDestroy
private void release() {
System.out.println("=============");
System.out.println("=> PreDestroy");
}
(...)
}
Выполнить компонент монстра
Прежде всего, если вы загрузите код и запустите интеграционный тест, вы заметите, что этот код действительно работает; o) я добавил простую веб-страницу JSF и вспомогательный компонент для вызова компонента Monster несколькими способами (EJB, Сервлет и веб-сервис REST). Таким образом, вы можете упаковать все в файл войны и развернуть его в GlassFish 4 . Существует также интеграционный тест, который проверяет, работает ли он во встроенном контейнере EJB.
Чего не хватает ?
Я мог бы добавить больше аннотаций … но вещи не всегда работают так, как мы хотим. Я хотел добавить возможности SOAP Web Service ( JAX-WS ) с аннотациями @WebService и @WebMethod. Прежде всего, если вы добавите аннотацию @WebService, это приведет к сбою интеграционного теста (веб-службы SOAP не являются частью встроенного контейнера EJB). Я также не смог сгенерировать WSDL, что-то не так с комбинацией аннотаций сопоставления JAX-WS и JPA. Также помните, что JAX-WS не является частью веб-профиля Java EE 7 , как JAX-RS.
Сервлет должен ввести EJB. Невозможно использовать аннотацию CDI @Inject, потому что это циклическая ссылка (книга внедряет себя). Но с @EJB это работает. Поэтому я не добавил CDI в этом примере.
Вы видите что-нибудь еще, что можно добавить?
Что это показывает?
Этот пример кода показывает, что вам не нужно чрезмерно проектировать свой код и добавлять несколько отделенных архитектурных слоев. Просто поместите все в один класс; o) Этот код немного шокирует, несколько концепций встроены в один класс (без разделения интересов), и большинство из вас (включая меня) сочтут это уродливым. С другой стороны, наличие нескольких уровней, абстракции, интерфейсов, DAO, DTO и т. Д. Также ужасно (но, похоже, мы привыкли к такой сложности). Не помещайте все в один класс, но также не распространяйте беспокойство на несколько артефактов. Найти правильный баланс и поцелуй.
Вывод
В этом посте я показал вам, что философия Java EE 7 сводится к добавлению метаданных в класс Java и оставлению контейнера для выполнения работы. Метаданные могут быть определены с помощью XML или аннотаций. Класс Book превратился в компонент Monster, добавив как можно больше аннотаций. Класс может сохраняться в транзакционном режиме благодаря JPA и EJB, а также вызываться как сервлет и веб-служба RESTful, перехватывая каждый вызов метода и проверяя ограничения.
