В этом пошаговом руководстве будет показано, как разработать простое веб-приложение с использованием Google Web Toolkit ( GWT ) для расширенного клиента и Spring в качестве серверной инфраструктуры на стороне сервера. Пример веб-приложения предоставит функции для выполнения операций CRUD (Create Retrieve Update Delete) с базой данных. Для слоя доступа к данным мы будем использовать JPA поверх Hibernate, а для базы данных мы будем использовать Hypersonic. Конечно, вы можете изменить конфигурацию и использовать все что угодно. Мы развернем веб-приложение на экземпляре Apache — Tomcat.
Maven поставляется в двух вариантах: автономный инструмент с поддержкой командной строки и как плагин интеграции IDE ( Eclipse и Netbeans). Нашей предпочтительной средой разработки является Eclipse , поэтому в качестве предварительного условия у вас должна быть установлена Eclipse с поддержкой Maven . Установка плагина Maven для Eclipse выходит за рамки данного руководства и не будет обсуждаться. Тем не менее вам понадобятся следующие компоненты:
Мы будем использовать Eclipse Galileo и «m2eclipse» Maven Integration для Eclipse Plugin версии 0.10.0, GWT версии 2.0.3, Spring версии 3.0.1, Hibernate версии 3.5.1, Hypersonic версии 1.8.0.10, slf4j-log4j12 версии 1.5. 8, версия 0.9.1.2 пула соединений c3p0 и версия 0.0.1 spring4gwt для этого руководства.
Хватит разговоров, давайте запачкаем руки!
- Создать новый проект Maven , перейти к файлу? Проект? Мавен? Maven Project
- На странице мастера «Выберите имя проекта и местоположение» убедитесь, что опция «Создать простой проект (пропустить выбор архетипа») не отмечена, нажмите «Далее», чтобы продолжить со значениями по умолчанию.
- На странице мастера «Выбор архетипа» выберите «Индексатор Nexus» в раскрывающемся списке «Каталог» и после обновления области выбора архетипов выберите архетип «gwt-maven-plugin» из «org.codehaus». Моджо «использовать. Вы можете использовать текстовое поле «фильтр», чтобы сузить результаты поиска. Нажмите «Далее», чтобы продолжить
- На странице мастера «Введите идентификатор артефакта» вы можете указать название и основной пакет вашего проекта. Мы установим переменную «Group Id» в «com.javacodegeeks», а переменную «Artifact Id» в «gwtspring». Вышеупомянутые выборы составляют основной пакет проекта как «com.javacodegeeks.gwtspring» и имя проекта как «gwtspring». Нажмите «Готово», чтобы выйти из мастера и создать свой проект.
Давайте вспомним несколько вещей о структуре проекта Maven GWT
- Папка / src / main / java содержит исходные файлы для динамического содержимого приложения
- Подпакет {main_package} .client содержит исходные файлы, доступные только для клиентской части приложения
- Подпакет {main_package} .server содержит исходные файлы, доступные только для серверной части приложения (этот подпакет не создается автоматически при создании проекта)
- Подпакет {main_package} .shared содержит исходные файлы, доступные как клиентской, так и серверной части приложения (этот подпакет не создается автоматически при создании проекта)
Чтобы правильно интегрировать Spring с GWT во время выполнения, мы должны предоставить все необходимые библиотеки для веб-приложения. Откройте графический редактор вашего «pom.xml» и внесите следующие изменения:
- Найдите раздел «Свойства» на странице «Обзор» редактора POM и внесите следующие изменения:
- Создайте новое свойство с именем org.springframework.version и значением 3.0.1.RELEASE
- Создайте новое свойство с именем org.hibernate.version и значением 3.5.1-Final.
- Измените значение свойства gwt.version на 2.0.3
- Измените значения свойств maven.compiler.source и maven.compiler.target в соответствии с версией среды выполнения Java, мы будем использовать 1.6
- Идентификатор группы: org.springframework Идентификатор артефакта: spring-orm Версия: $ {org.springframework.version}
- Идентификатор группы: org.springframework Идентификатор артефакта: spring-web Версия: $ {org.springframework.version}
- Идентификатор группы: org.hibernate Идентификатор артефакта: hibernate-core Версия: $ {org.hibernate.version}
- Идентификатор группы: org.hibernate Идентификатор артефакта: hibernate-annotations Версия: $ {org.hibernate.version}
- Идентификатор группы: org.hibernate Идентификатор артефакта: hibernate-entitymanager Версия: $ {org.hibernate.version}
- Идентификатор группы: org.hibernate.javax.persistence Идентификатор артефакта: hibernate-jpa-2.0-api Версия: 1.0.0.Final
- Идентификатор группы: org.slf4j Идентификатор артефакта: slf4j-log4j12 Версия: 1.5.8
- Идентификатор группы: org.hsqldb Идентификатор артефакта: hsqldb Версия: 1.8.0.10
- Идентификатор группы: c3p0 Идентификатор артефакта: c3p0 Версия: 0.9.1.2
- Идентификатор: JBoss URL: http://repository.jboss.org/maven2/
Как видите, Maven декларативно управляет библиотечными зависимостями. Создается локальный репозиторий (по умолчанию в папке {user_home} /. M2), и все необходимые библиотеки загружаются и помещаются туда из общедоступных репозиториев. Кроме того, внутрибиблиотечные зависимости автоматически разрешаются и обрабатываются. Тем не менее, не все библиотеки доступны для публичных репозиториев. В этом случае мы должны вручную загрузить необходимую библиотеку и использовать ее в нашем проекте. Такой случай — библиотека «spring4gwt». Чтобы использовать его, мы должны создать папку «lib» в папке / src / main / webapp / WEB-INF и поместить туда «spring4gwt-0.0.1.jar». В следующей статье мы обсудим, как создать собственный общий репозиторий Maven и установить библиотеки, которые не являются общедоступными, так что следите за обновлениями!
Следующим шагом является предоставление хуков для веб-приложения для загрузки контекста Spring при запуске и предоставления возможности «spring4gwt» перехватывать вызовы RPC между клиентом и сервером и преобразовывать их в вызовы службы Spring .
Найдите файл «web.xml» в / src / main / webapp / WEB-INF и добавьте следующее:
Для загрузки контекста Spring при запуске,
1
2
3
4
5
|
< listener > < listener-class > org.springframework.web.context.ContextLoaderListener </ listener-class > </ listener > |
В разделе сервлетов есть
1
2
3
4
5
|
< servlet > < servlet-name >springGwtRemoteServiceServlet</ servlet-name > < servlet-class >org.spring4gwt.server.SpringGwtRemoteServiceServlet </ servlet-class > </ servlet > |
В разделе отображения сервлетов включите, чтобы spring4gwt перехватывал вызовы RPC.
1
2
3
4
|
< servlet-mapping > < servlet-name >springGwtRemoteServiceServlet</ servlet-name > < url-pattern >/com.javacodegeeks.gwtspring.Application/springGwtServices/*</ url-pattern > </ servlet-mapping > |
Что следует отметить здесь:
- Дочерний элемент url-pattern элемента servlet-mapping для сервлета springGwtRemoteServiceServlet должен быть изменен на любое имя вашего модуля GWT , например, {module_name} / springGwtServices / *, имя модуля по умолчанию {main_package} .Application
- Вы можете изменить имя сервлета spring4gwt (здесь SpringGwtRemoteServiceServlet) на любое другое
Следующим шагом является создание файла «persistence.xml», чтобы описать соединение с базой данных с использованием JPA . Файл «pesistence.xml» должен находиться в каталоге META-INF, который, в свою очередь, должен быть доступен веб-приложению во время выполнения (в пути к классам). Чтобы выполнить вышеупомянутые требования, мы должны создать папку META-INF в папке проекта / src / main / resources. Наконец, создайте файл «persistence.xml» в папке / src / main / resources / META-INF. Пример persistence.xml представлен ниже
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
xsi:schemaLocation = "http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version = "2.0" > < persistence-unit name = "MyPersistenceUnit" transaction-type = "RESOURCE_LOCAL" > < provider >org.hibernate.ejb.HibernatePersistence</ provider > < properties > < property name = "hibernate.hbm2ddl.auto" value = "update" /> < property name = "hibernate.show_sql" value = "false" /> < property name = "hibernate.dialect" value = "org.hibernate.dialect.HSQLDialect" /> < property name = "hibernate.connection.driver_class" value = "org.hsqldb.jdbcDriver" /> < property name = "hibernate.connection.url" value = "jdbc:hsqldb:mem:javacodegeeks" /> < property name = "hibernate.connection.username" value = "sa" /> < property name = "hibernate.connection.password" value = "" /> < property name = "hibernate.c3p0.min_size" value = "5" /> < property name = "hibernate.c3p0.max_size" value = "20" /> < property name = "hibernate.c3p0.timeout" value = "300" /> < property name = "hibernate.c3p0.max_statements" value = "50" /> < property name = "hibernate.c3p0.idle_test_period" value = "3000" /> </ properties > </ persistence-unit > </ persistence > |
Теперь давайте создадим файл applicationContext.xml, который будет управлять контейнером Spring . Создайте файл в каталоге / src / main / webapp / WEB-INF. Пример «applicationContext.xml» представлен ниже
Что следует отметить здесь:
- Измените атрибут base-package элемента context: component-scan на тот, который является базовым пакетом вашего проекта, чтобы проверять его на наличие компонентов Spring (служб, DAO и т. Д.).
- Измените значение атрибута bean-компонента entityManagerFactory для свойства persistentUnitName на имя вашего постоянного модуля, как указано в файле «persistence.xml».
В последней части этого руководства мы собираемся представить объект передачи данных (DTO) для передачи данных между клиентом и сервером, объект доступа к данным (DAO), который используется для доступа к базе данных, и сервис Spring для предоставления функциональности. к веб-клиенту GWT .
DTO — это объект, который может использоваться как клиентом, так и сервером, поэтому вы должны создать подпакет «shared.dto» в вашем основном пакете (в нашем случае com.javacodegeeks.gwtspring) и поместить туда DTO. Мы собираемся создать EmployeeDTO, содержащий информацию для сотрудника, как показано ниже
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
package com.javacodegeeks.gwtspring.shared.dto; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table (name = "EMPLOYEE" ) public class EmployeeDTO implements java.io.Serializable { private static final long serialVersionUID = 7440297955003302414L; @Id @Column (name= "employee_id" ) private long employeeId; @Column (name= "employee_name" , nullable = false , length= 30 ) private String employeeName; @Column (name= "employee_surname" , nullable = false , length= 30 ) private String employeeSurname; @Column (name= "job" , length= 50 ) private String job; public EmployeeDTO() { } public EmployeeDTO( int employeeId) { this .employeeId = employeeId; } public EmployeeDTO( long employeeId, String employeeName, String employeeSurname, String job) { this .employeeId = employeeId; this .employeeName = employeeName; this .employeeSurname = employeeSurname; this .job = job; } public long getEmployeeId() { return employeeId; } public void setEmployeeId( long employeeId) { this .employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this .employeeName = employeeName; } public String getEmployeeSurname() { return employeeSurname; } public void setEmployeeSurname(String employeeSurname) { this .employeeSurname = employeeSurname; } public String getJob() { return job; } public void setJob(String job) { this .job = job; } } |
Чтобы объект DTO был доступен для клиента GWT, мы должны дать команду компилятору GWT проанализировать его. Для этого найдите основной файл модуля GWT , он должен называться «Application.gwt.xml» и находиться под вашим основным пакетом (в нашем случае com.javacodegeeks.gwtspring) и добавить следующие директивы:
1
2
3
|
<!-- Specify the paths for translatable code --> < source path = 'client' /> < source path = 'shared' /> |
Объект DAO будет использоваться для доступа к базе данных и выполнения операций CRUD (Create Retrieve Update Delete). Это серверный компонент, поэтому его следует поместить в подпакет «server» нашего проекта. Создайте подпакет «server.dao» под основным пакетом проекта (в нашем случае com.javacodegeeks.gwtspring) и поместите туда DAO. Пример DAO представлен ниже
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.javacodegeeks.gwtspring.server.dao; import javax.annotation.PostConstruct; import javax.persistence.EntityManagerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO; @Repository ( "employeeDAO" ) public class EmployeeDAO extends JpaDAO<Long, EmployeeDTO> { @Autowired EntityManagerFactory entityManagerFactory; @PostConstruct public void init() { super .setEntityManagerFactory(entityManagerFactory); } } |
Как видите, класс EmployeeDAO расширяет базовый класс DAO (JpaDAO). Класс EmployeeDAO может содержать конкретные запросы, касающиеся объекта EmployeeDTO, но все операции CRUD могут обрабатываться из базового класса DAO (JpaDAO). Разместите класс JpaDAO на том же уровне, что и класс EmployeeDAO, в подпакете «dao». Ниже мы представляем класс JpaDAO
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
package com.javacodegeeks.gwtspring.server.dao; import java.lang.reflect.ParameterizedType; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceException; import javax.persistence.Query; import org.springframework.orm.jpa.JpaCallback; import org.springframework.orm.jpa.support.JpaDaoSupport; public abstract class JpaDAO<K, E> extends JpaDaoSupport { protected Class<E> entityClass; @SuppressWarnings ( "unchecked" ) public JpaDAO() { ParameterizedType genericSuperclass = (ParameterizedType) getClass() .getGenericSuperclass(); this .entityClass = (Class<E>) genericSuperclass .getActualTypeArguments()[ 1 ]; } public void persist(E entity) { getJpaTemplate().persist(entity); } public void remove(E entity) { getJpaTemplate().remove(entity); } public E merge(E entity) { return getJpaTemplate().merge(entity); } public void refresh(E entity) { getJpaTemplate().refresh(entity); } public E findById(K id) { return getJpaTemplate().find(entityClass, id); } public E flush(E entity) { getJpaTemplate().flush(); return entity; } @SuppressWarnings ( "unchecked" ) public List<E> findAll() { Object res = getJpaTemplate().execute( new JpaCallback() { public Object doInJpa(EntityManager em) throws PersistenceException { Query q = em.createQuery( "SELECT h FROM " + entityClass.getName() + " h" ); return q.getResultList(); } }); return (List<E>) res; } @SuppressWarnings ( "unchecked" ) public Integer removeAll() { return (Integer) getJpaTemplate().execute( new JpaCallback() { public Object doInJpa(EntityManager em) throws PersistenceException { Query q = em.createQuery( "DELETE FROM " + entityClass.getName() + " h" ); return q.executeUpdate(); } }); } } |
Наконец, мы собираемся создать интерфейс службы и классы реализации для доступа клиента GWT . Интерфейс сервиса должен быть доступен как клиенту, так и серверу, поэтому его следует поместить в «общий» подпакет нашего проекта. Создайте подпакет «services» и разместите там интерфейс сервиса. Ниже приведен пример класса интерфейса
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
package com.javacodegeeks.gwtspring.shared.services; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO; @RemoteServiceRelativePath ( "springGwtServices/employeeService" ) public interface EmployeeService extends RemoteService { public EmployeeDTO findEmployee( long employeeId); public void saveEmployee( long employeeId, String name, String surname, String jobDescription) throws Exception; public void updateEmployee( long employeeId, String name, String surname, String jobDescription) throws Exception; public void saveOrUpdateEmployee( long employeeId, String name, String surname, String jobDescription) throws Exception; public void deleteEmployee( long employeeId) throws Exception; } |
Что следует отметить здесь:
- Клиент GWT должен иметь возможность выполнять асинхронные удаленные вызовы процедур (RPC) для службы на стороне сервера. Таким образом, интерфейс службы должен расширять интерфейс RemoteService. Maven автоматически создает асинхронный аналог указанного интерфейса перед компиляцией проекта и упаковкой граней
- Мы аннотируем интерфейс, чтобы определить URL, по которому будет доступен сервис. Поскольку сервис является сервисом Spring, мы хотим, чтобы «spring4gwt» перехватывал вызовы RPC и выполнял вызов сервиса Spring . Для этого мы определяем относительный путь, который будет обрабатываться «springGwtRemoteServiceServlet», объявленным в нашем «web.xml», как показано выше.
- Имя службы, объявленное в аннотации «RemoteServiceRelativePath», в данном случае «employeeService», должно соответствовать имени bean-компонента службы Spring . Мы определим имя бина службы Spring в классе реализации службы (см. Ниже)
Класс реализации сервиса является серверным компонентом, поэтому мы должны поместить его в подпакет «server» нашего проекта. Создайте подпакет «услуги» и разместите его там. Пример класса реализации сервиса представлен ниже
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
package com.javacodegeeks.gwtspring.server.services; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.javacodegeeks.gwtspring.server.dao.EmployeeDAO; import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO; import com.javacodegeeks.gwtspring.shared.services.EmployeeService; @Service ( "employeeService" ) public class EmployeeServiceImpl implements EmployeeService { @Autowired private EmployeeDAO employeeDAO; @PostConstruct public void init() throws Exception { } @PreDestroy public void destroy() { } public EmployeeDTO findEmployee( long employeeId) { return employeeDAO.findById(employeeId); } @Transactional (propagation=Propagation.REQUIRED, rollbackFor=Exception. class ) public void saveEmployee( long employeeId, String name, String surname, String jobDescription) throws Exception { EmployeeDTO employeeDTO = employeeDAO.findById(employeeId); if (employeeDTO == null ) { employeeDTO = new EmployeeDTO(employeeId, name,surname, jobDescription); employeeDAO.persist(employeeDTO); } } @Transactional (propagation=Propagation.REQUIRED, rollbackFor=Exception. class ) public void updateEmployee( long employeeId, String name, String surname, String jobDescription) throws Exception { EmployeeDTO employeeDTO = employeeDAO.findById(employeeId); if (employeeDTO != null ) { employeeDTO.setEmployeeName(name); employeeDTO.setEmployeeSurname(surname); employeeDTO.setJob(jobDescription); } } @Transactional (propagation=Propagation.REQUIRED, rollbackFor=Exception. class ) public void deleteEmployee( long employeeId) throws Exception { EmployeeDTO employeeDTO = employeeDAO.findById(employeeId); if (employeeDTO != null ) employeeDAO.remove(employeeDTO); } @Transactional (propagation=Propagation.REQUIRED, rollbackFor=Exception. class ) public void saveOrUpdateEmployee( long employeeId, String name, String surname, String jobDescription) throws Exception { EmployeeDTO employeeDTO = new EmployeeDTO(employeeId, name,surname, jobDescription); employeeDAO.merge(employeeDTO); } } |
Что следует отметить здесь:
- Мы используем аннотацию стереотипа @Service («employeeService»), чтобы объявить, что этот класс представляет службу Spring с именем «exampleService». Контейнер Spring будет запускать все службы при запуске
- Мы используем аннотацию @Autowire, чтобы внедрить экземпляр класса DAO в employeeService. Для правильного создания экземпляра службы контейнер Spring должен сначала разрешить все потенциальные ссылки между службами, поэтому он создает экземпляр класса DAO и внедряет экземпляр в соответствующее поле employeeService — поле employeeDAO. В случае, если вам интересно, внедрение зависимости выполняется в соответствии с типом (Class) и, если не выполняется в соответствии с именем, это означает, что если мы определили несколько служб одного и того же типа (Class), внедренная будет одна с тем же именем как обозначенное поле
- Мы используем аннотации Java @PostConstruct и @PreDestroy, чтобы объявить методы, которые будут вызываться контейнером Spring после инициализации (все внедрения зависимостей сделаны) и предварительного уничтожения службы
- Мы используем аннотацию @Transactional для всех методов, которым необходимо выполнить операцию обновления базы данных (INSERT, UPDATE, DELETE).
- Мы НЕ используем аннотацию @Transactional для методов, которые выполняют операции извлечения (FIND) над базой данных (за исключением объектов, которые содержат лениво инициализированные ссылки — см. Ниже), и / или не выполняют никаких операций с базой данных. Это связано с тем, что каждый раз, когда вы вызываете метод, аннотированный как транзакционный, контейнер Spring включает в себя диспетчер сущностей вызова JPA и, как следствие, диспетчер транзакций платформы, чтобы определить поведение транзакции, которое будет применяться, вводя заметное снижение производительности, особенно для приложений с низкой задержкой / высокой пропускной способностью
- Для методов, которые выполняют операции извлечения (FIND) для объектов, которые содержат лениво инициализированные ссылки, вы должны использовать аннотацию @Transactional, обозначая тип распространения «NESTED», чтобы Spring мог поддерживать сеанс Hibernate открытым для всего вызова метода
- Транзакционное поведение применяется только при обращениях клиентов к сервису. Транзакционное поведение не применяется к внутриоперационным вызовам. Например, если клиент вызывает операцию, которая не аннотирована как транзакционная, и реализация последней вводит вызов другой операции того же сервиса, которая аннотирована транзакционной, тогда для объединенных операций транзакционное поведение не будет применяться. Это связано с тем, что Spring использует прокси-классы AOP для обеспечения поведения транзакций
Мы почти закончили !, нам нужно разработать пользовательский интерфейс GWT для доступа к нашему сервису Spring .
Найдите точку входа вашего приложения GWT . Файл должен называться «Application.java» и находиться в подпакете «client» или в нашем основном пакете. Измените класс точки входа, как показано ниже
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
package com.javacodegeeks.gwtspring.client; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.javacodegeeks.gwtspring.shared.dto.EmployeeDTO; import com.javacodegeeks.gwtspring.shared.services.EmployeeServiceAsync; /** * Entry point classes define <code>onModuleLoad()</code>. */ public class Application implements EntryPoint { /** * The message displayed to the user when the server cannot be reached or * returns an error. */ private static final String SERVER_ERROR = "An error occurred while " + "attempting to contact the server. Please check your network " + "connection and try again. The error is : " ; /** * Create a remote service proxy to talk to the server-side Employee service. */ private final EmployeeServiceAsync employeeService = EmployeeServiceAsync.Util.getInstance(); /** * This is the entry point method. */ public void onModuleLoad() { final Button saveOrUpdateButton = new Button( "SaveOrUpdate" ); final Button retrieveButton = new Button( "Retrieve" ); final TextBox employeeInfoField = new TextBox(); employeeInfoField.setText( "Employee Info" ); final TextBox employeeIdField = new TextBox(); final Label errorLabel = new Label(); // We can add style names to widgets saveOrUpdateButton.addStyleName( "sendButton" ); retrieveButton.addStyleName( "sendButton" ); // Add the nameField and sendButton to the RootPanel // Use RootPanel.get() to get the entire body element RootPanel.get( "employeeInfoFieldContainer" ).add(employeeInfoField); RootPanel.get( "updateEmployeeButtonContainer" ).add(saveOrUpdateButton); RootPanel.get( "employeeIdFieldContainer" ).add(employeeIdField); RootPanel.get( "retrieveEmployeeButtonContainer" ).add(retrieveButton); RootPanel.get( "errorLabelContainer" ).add(errorLabel); // Focus the cursor on the name field when the app loads employeeInfoField.setFocus( true ); employeeInfoField.selectAll(); // Create the popup dialog box final DialogBox dialogBox = new DialogBox(); dialogBox.setText( "Remote Procedure Call" ); dialogBox.setAnimationEnabled( true ); final Button closeButton = new Button( "Close" ); // We can set the id of a widget by accessing its Element closeButton.getElement().setId( "closeButton" ); final Label textToServerLabel = new Label(); final HTML serverResponseLabel = new HTML(); VerticalPanel dialogVPanel = new VerticalPanel(); dialogVPanel.addStyleName( "dialogVPanel" ); dialogVPanel.add( new HTML( "<b>Sending request to the server:</b>" )); dialogVPanel.add(textToServerLabel); dialogVPanel.add( new HTML( "<br><b>Server replies:</b>" )); dialogVPanel.add(serverResponseLabel); dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT); dialogVPanel.add(closeButton); dialogBox.setWidget(dialogVPanel); // Add a handler to close the DialogBox closeButton.addClickHandler( new ClickHandler() { public void onClick(ClickEvent event) { dialogBox.hide(); saveOrUpdateButton.setEnabled( true ); saveOrUpdateButton.setFocus( true ); retrieveButton.setEnabled( true ); } }); // Create a handler for the saveOrUpdateButton and employeeInfoField class SaveOrUpdateEmployeeHandler implements ClickHandler, KeyUpHandler { /** * Fired when the user clicks on the saveOrUpdateButton. */ public void onClick(ClickEvent event) { sendEmployeeInfoToServer(); } /** * Fired when the user types in the employeeInfoField. */ public void onKeyUp(KeyUpEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { sendEmployeeInfoToServer(); } } /** * Send the employee info from the employeeInfoField to the server and wait for a response. */ private void sendEmployeeInfoToServer() { // First, we validate the input. errorLabel.setText( "" ); String textToServer = employeeInfoField.getText(); // Then, we send the input to the server. saveOrUpdateButton.setEnabled( false ); textToServerLabel.setText(textToServer); serverResponseLabel.setText( "" ); String[] employeeInfo = textToServer.split( " " ); long employeeId = Long.parseLong(employeeInfo[ 0 ]); String employeeName = employeeInfo[ 1 ]; String employeeSurname = employeeInfo[ 2 ]; String employeeJobTitle = employeeInfo[ 3 ]; employeeService.saveOrUpdateEmployee(employeeId, employeeName, employeeSurname, employeeJobTitle, new AsyncCallback<Void>() { public void onFailure(Throwable caught) { // Show the RPC error message to the user dialogBox .setText( "Remote Procedure Call - Failure" ); serverResponseLabel .addStyleName( "serverResponseLabelError" ); serverResponseLabel.setHTML(SERVER_ERROR + caught.toString()); dialogBox.center(); closeButton.setFocus( true ); } public void onSuccess(Void noAnswer) { dialogBox.setText( "Remote Procedure Call" ); serverResponseLabel .removeStyleName( "serverResponseLabelError" ); serverResponseLabel.setHTML( "OK" ); dialogBox.center(); closeButton.setFocus( true ); } }); } } // Create a handler for the retrieveButton and employeeIdField class RetrieveEmployeeHandler implements ClickHandler, KeyUpHandler { /** * Fired when the user clicks on the retrieveButton. */ public void onClick(ClickEvent event) { sendEmployeeIdToServer(); } /** * Fired when the user types in the employeeIdField. */ public void onKeyUp(KeyUpEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { sendEmployeeIdToServer(); } } /** * Send the id from the employeeIdField to the server and wait for a response. */ private void sendEmployeeIdToServer() { // First, we validate the input. errorLabel.setText( "" ); String textToServer = employeeIdField.getText(); // Then, we send the input to the server. retrieveButton.setEnabled( false ); textToServerLabel.setText(textToServer); serverResponseLabel.setText( "" ); employeeService.findEmployee(Long.parseLong(textToServer), new AsyncCallback<EmployeeDTO>() { public void onFailure(Throwable caught) { // Show the RPC error message to the user dialogBox .setText( "Remote Procedure Call - Failure" ); serverResponseLabel .addStyleName( "serverResponseLabelError" ); serverResponseLabel.setHTML(SERVER_ERROR + caught.toString()); dialogBox.center(); closeButton.setFocus( true ); } public void onSuccess(EmployeeDTO employeeDTO) { dialogBox.setText( "Remote Procedure Call" ); serverResponseLabel .removeStyleName( "serverResponseLabelError" ); if (employeeDTO != null ) serverResponseLabel.setHTML( "Employee Information <br>Id : " + employeeDTO.getEmployeeId() + "<br>Name : " + employeeDTO.getEmployeeName() + "<br>Surname : " + employeeDTO.getEmployeeSurname() + "<br>Job Title : " + employeeDTO.getJob()); else serverResponseLabel.setHTML( "No employee with the specified id found" ); dialogBox.center(); closeButton.setFocus( true ); } }); } } // Add a handler to send the employee info to the server SaveOrUpdateEmployeeHandler saveOrUpdateEmployeehandler = new SaveOrUpdateEmployeeHandler(); saveOrUpdateButton.addClickHandler(saveOrUpdateEmployeehandler); employeeInfoField.addKeyUpHandler(saveOrUpdateEmployeehandler); // Add a handler to send the employee id to the server RetrieveEmployeeHandler retrieveEmployeehandler = new RetrieveEmployeeHandler(); retrieveButton.addClickHandler(retrieveEmployeehandler); employeeIdField.addKeyUpHandler(retrieveEmployeehandler); } } |
Как видите, вызовы службы Spring выполняются так же, как и классические вызовы службы GWT , прозрачно для клиента.
Наконец найдите главную веб-страницу для вашего проекта. Файл должен называться «Application.html» и находиться в / src / main / resources / {main_package} / публичной папке вашего проекта (в нашем случае / src / main / resources / com / javacodegeeks / gwtspring / public). Измените главную веб-страницу, как показано ниже
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!-- The HTML 4.01 Transitional DOCTYPE declaration--> <!-- above set at the top of the file will set --> <!-- the browser's rendering engine into --> <!-- "Quirks Mode". Replacing this declaration --> <!-- with a "Standards Mode" doctype is supported, --> <!-- but may lead to some differences in layout. --> < html > < head > < meta http-equiv = "content-type" content = "text/html; charset=UTF-8" > <!-- --> <!-- Any title is fine --> <!-- --> < title >< a href = "http://www.springsource.org/" >Spring</ a > < a href = "https://code.google.com/webtoolkit/" >GWT</ a > Web Application Starter Project</ title > <!-- --> <!-- This script loads your compiled module. --> <!-- If you add any <a href="https://code.google.com/webtoolkit/">GWT</a> meta tags, they must --> <!-- be added before this line. --> <!-- --> < script type = "text/javascript" language = "javascript" src = "com.javacodegeeks.gwtspring.Application.nocache.js" ></ script > </ head > <!-- --> <!-- The body can have arbitrary html, or --> <!-- you can leave the body empty if you want --> <!-- to create a completely dynamic UI. --> <!-- --> < body > <!-- OPTIONAL: include this if you want history support --> < iframe src = "javascript:''" id = "__gwt_historyFrame" tabIndex = '-1' style = "position:absolute;width:0;height:0;border:0" ></ iframe > <!-- RECOMMENDED if your web app will not function without JavaScript enabled --> < noscript > < div style = "width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif" > Your web browser must have JavaScript enabled in order for this application to display correctly. </ div > </ noscript > < h1 align = "center" >< a href = "http://www.springsource.org/" >Spring</ a > < a href = "https://code.google.com/webtoolkit/" >GWT</ a > Web Application Starter Project</ h1 > < table align = "center" > < tr > < td colspan = "2" style = "font-weight:bold;" >Please enter employee info (id name surname job):</ td > </ tr > < tr > < td id = "employeeInfoFieldContainer" ></ td > < td id = "updateEmployeeButtonContainer" ></ td > </ tr > < tr > < tr > < td colspan = "2" style = "font-weight:bold;" >Please enter employee id:</ td > </ tr > < tr > < td id = "employeeIdFieldContainer" ></ td > < td id = "retrieveEmployeeButtonContainer" ></ td > </ tr > < tr > < td colspan = "2" style = "color:red;" id = "errorLabelContainer" ></ td > </ tr > </ table > </ body > </ html > |
Чтобы создать приложение, щелкните правой кнопкой мыши на вашем проекте? Беги как ? Maven пакет
Для развертывания веб-приложения просто скопируйте файл «.war» из каталога «target» в папку Apache — Tomcat «webapps».
Для запуска приложения укажите ваш браузер по следующему адресу
HTTP: // локальный: 8080 / {application_name} /
Если все прошло хорошо, вы должны увидеть свою главную веб-страницу. Должны отображаться два текстовых поля, за которыми следует кнопка. В первом текстовом поле вы можете сохранить или обновить сотрудника в базе данных. Введите в качестве входных данных идентификатор, имя, фамилию и описание задания, разделенные пробелом. При нажатии на кнопку «SaveOrUpdate» предоставленная информация будет сохранена в базе данных. Для существующих записей сотрудников (с тем же идентификатором) будет выполнено обновление. Второе текстовое поле используется для получения существующих записей сотрудников. Введите идентификатор сотрудника и нажмите кнопку «Получить». Если сотрудник существует, вы должны увидеть его идентификатор, имя, фамилию и описание работы.
Вы можете скачать проект здесь
Надеюсь, вам понравилось
Джастин
- GWT 2 Spring 3 JPA 2 Hibernate 3.5 Учебник
- GWT Spring и Hibernate входят в мир Data Grids
- Учебник по интеграции с Spring 3 HornetQ 2.1
- Spring 3 RESTful веб-сервисы
- JAX – WS с руководством по Spring и Maven