С выходом 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, перехватывая каждый вызов метода и проверяя ограничения.