К сожалению, по Spring до сих пор никто не написал нормального вменяемого руководства, объясняющего все шаги. Перерыв кучу англоязычной документации, как официальное руководство, так и неофициальные статьи и форумы, мне всё-таки удалось вменяемо понять эту дивную технологию, чем спешу поделиться с вами, а в первую очередь – записать дабы не забыть в последствии самому. Мы будем использовать следующие технологии: Hibernate, Spring Framework, JSP (для вывода на фейс). И структура нашего проекта при этом будет следующей: Парочка объяснений: DAO самописное, поскольку все DAO-генераторы суют в классы кучу процедур, при этом абсолютно забывая даже всякие вещи первой необходимости. Плюс логика всего етого подхода в том чтоб с ORM работали только DAO-классы, а вся бизнес-логика была вынесена в Application Service, где 1-2 класса будут всем собственно рулить. Spring controllers отвечают за вывод данных неподсредственно в веб. Ну а JSP вместо любимых FreeMarker и прочих я в последнее время использую по довольно банальной причине: когда ЖОПА и надо срочный хотфикс прямо на живом сервере, JSP позволяет находу обратиться к ORM а то и к самой базе, без всяких выкрутасов, которые будут делать в последствии разработчики, когда ситуация «жопа» уже не будет подгонять. Шаг 1. Создаём базу данных Создаём обычным образом, желательно конечно чтоб база поддерживала вторичные ключи, потому никаких MyISAM не советую. Наша база будет выглядеть для примера чрезвычайно просто: одна единственная табличка User с полями Id (примари ключ) и Login(уникальное). Вот её ERD для тех кто совсем не имеет пространственного мышления: Шаг 2. Создаём маппинг классов. Я решил предпочитать на будущее EJB-style, если вы тоже решите – не забывайте кроме hibernate подключать еще hibernate-annotations, hibernate-commons-annotations и ejb3-persistance. Класс у нас один, тем более DBVA его сам генерит (generate->code from ERD->POJO), потому исключительно покажу его листинг без всяких объяснений: net.altertech.vtest.data.User package net.altertech.vtest.data; @Entity public class User implements Serializable { public User() { } @Column(name="Login", nullable=false, length=40, unique = true) private void setId(int value) { public int getId() { public void setLogin(String value) { public String getLogin() { public String toString() { } Парочка примечаний: Шаг 3. Создаём DAO для работы с нашими юзерами. Пойдём по кошерному пути и напишем для начала интерфейс net.altertech.vtest.UserDao package net.altertech.vtest; public interface UserDao { А затем уже создадим саму имплементацию класса net.altertech.vtest.UserDaoImpl package net.altertech.vtest; package net.altertech.vtest; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; public class UserDaoImpl extends HibernateDaoSupport implements UserDao { public List getUsers() { public User getUser(Integer id) { public void save(User user) { public void delete(User user) { } На что следует обратить внимание: наш класс наследует HibernateDaoSupport. А это значит, что в классе изначально будет доступна текущая HibernateSessionFactory и функции типа getSession() и getHibernateTemplate(). Подробней смотрите описание класса в документации по Spring Framework. Шаг 4. Создаём Application Service для бизнес-логики Мудрить особо не будем, да и объёмы не позволяют, потому создадим буквально пару функций. Интерфейс прилагается net.altertech.vtest.AppService package net.altertech.vtest; import org.springframework.transaction.annotation.Transactional; public @Transactional interface AppService { На что тут обратить внимание: на то что интерфейс @Transactional. Документация объясняет нам что при таком подходе все изменения в базе автоматом проходят через транзакцию и при ошибке на каком-то из уровней происходит полный rollback. Однако на практике не пробовал, ошибки предпочитаю обрабатывать сам потому данное определение опционально. Реализация интерфейса: net.altertech.vtest.AppServiceImpl package net.altertech.vtest; public class AppServiceImpl implements AppService { public void setUserDao(UserDao userDao) { private UserDao userDao; public List getUserList() { public void createUser(String login) { public void deleteUser(Integer id) { } Обратите внимание что методы для чтения начинаются с get – таким образом мы можем работать с этим классом как с bean и выводить данные из этих методов непосредственно на странице. Setter'ы делать врядли будет необходимость а вот «get»ter'ы для таких методов пригодятся. Класс так же имеет переменную UserDao – он получит ее автоматически при запуске Spring-приложения, точно так же как UserDao получит автоматически SessionFactory для работы с ORM. Шаг 4. Разработка простого контроллера Я не буду описывать контроллеры для добавления и удаления пользователей, впринципе контроллеры – самая лёгкая и понятная часть Spring Framework. Потому приведу листинг только контроллера для вывода списка юзеров. net.altertech.vtest.listUserController package net.altertech.vtest; import org.springframework.web.servlet.ModelAndView; public class listUserController implements Controller { public void setApplication(AppService application) { private AppService application; public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { } Обратите внимание на переменную application, она будет так же получена автоматически при запуске. Шаг 5. Вывод в веб Еще проще чем контроллер listuser.jsp <%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> Шаг 6. Маппинг ресурсов. Наиболее сложный этап для новичков, спешу обрадовать что этот этап – последний. Как вы помните, UserDao должен получить работающие ORM-ресурсы, AppService – работающие Dao а контроллеры – работающий AppService. Не говоря уже что необходимо обеспечить интерфейс к базе для ORM и объяснить какие именно объекты мы хотим построить. Ну и для веб не забыть объяснить какие урлы мапятся на какой контроллер. В результате должно получиться нечто типа а spring-маппинг этого безобразия выглядит так (далее читать комментарии в самом XML): <?xml version="1.0" encoding="UTF-8"?> <!— промапим веб-ресурсы --> <!— промапим контроллеры на классы, не забудем что каждый контроллер получает по application --> <!—вынесем некоторые параметры во внешний файл конфигурации для удобства --> <!—подключим базу. я использовал апачевский basicDataSource так как он используется почти во всех примерах. Какой datasource использовать в продакше – решать вам --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!—подключаем ORM (hibernate). Hibernate должен получить datasource и список наших POJO-объектов. Так как я описывал объекты в EJB-стиле, используется AnnotationSessionFactoryBean --> <property name="hibernateProperties"> <!—подключаем прокси транзакций. кошерно --> <!—подключаем главный класс бизнес-логики. Так же имеем autowire --> <!— подключаем менеджер транзакций. Объясняем что он должен получить SessionFactory от ORM --> Вот впринципе и всё. Приведу напоследок еще web.xml: <?xml version="1.0" encoding="UTF-8"?> Если вы всё сделали верно, всё должно красиво и быстро заработать. Кстате не пробовал на практике, как там с печально знаменитой thread-local сессией у Hibernate. Но вроде как на глаз, никаких дополнительных фильтров не требуется. Для создания примера использовались (лицензиённые) DBVA 4.1 и IDEA 7.0.1, за что им отдельное спасибо. Ни один бобёр не пострадал.
Пекедж вокруг базы нарисован т.к. я использую DB Visual Architect (DBVA), чего и вам советую.
import java.io.Serializable;
import javax.persistence.*;
@Table(name="User")
@org.hibernate.annotations.Proxy(lazy=false)
@Column(name="Id", nullable=false)
@Id
@GeneratedValue(generator="VAC100101115DE0F3CF801BCF")
@org.hibernate.annotations.GenericGenerator(name="VAC100101115DE0F3CF801BCF", strategy="native")
private int id;
private String login;
this.id = value;
}
return id;
}
this.login = value;
}
return login;
}
return String.valueOf(getId());
}
import net.altertech.vtest.data.User;
import java.util.List;
List getUsers();
User getUser(Integer id);
void save(User user);
void delete(User user);
}
import java.util.List;
import net.altertech.vtest.data.User;
return getHibernateTemplate().loadAll(User.class);
}
return (User) getHibernateTemplate().load(User.class, id);
}
getHibernateTemplate().save(user);
}
getHibernateTemplate().delete(user);
}
import java.util.List;
void setUserDao(UserDao userDao);
UserDao getUserDao();
List getUserList();
void createUser(String parameter);
void deleteUser(Integer id);
}
import net.altertech.vtest.data.User;
import java.util.List;
public UserDao getUserDao() {
return userDao;
}
this.userDao = userDao;
}
return userDao.getUsers();
}
User u = new User();
u.setLogin(login);
userDao.save(u);
}
User u = userDao.getUser(id);
userDao.delete(u);
}
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
this.application = application;
}
Map<String, Object> model = new HashMap<String, Object>();
model.put("application",application);
return new ModelAndView("/listuser.jsp",model);
}
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<c:forEach items="${application.userList}" var="user">
<c:out value="${user.login}" /><br />
</c:forEach>
</body>
</html>
<beans xmlns="http://www.springframework.org/schema/beans"
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-2.0.xsd">
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/listuser.html">listUser</prop>
</props>
</property>
</bean>
<bean id="listUser" class="net.altertech.vtest.listUserController">
<property name="application" ref="appService" />
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>WEB-INF/vtest.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="${database}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="annotatedPackages">
<list>
<value>net.altertech.vtest.data</value>
</list>
</property>
<property name="annotatedClasses">
<list>
<value>net.altertech.vtest.data.User</value>
<value>net.altertech.vtest.data.Video</value>
</list>
</property>
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
</props>
</property>
</bean>
<!—опишем userDao. Обратите внимание на autowire -->
<bean id="userDao" class="net.altertech.vtest.UserDaoImpl" autowire="byType" />
<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributeSource">
<bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/>
</property>
</bean>
<bean id="appService" parent="baseTransactionProxy">
<property name="target">
<bean class="net.altertech.vtest.AppServiceImpl" autowire="byType" />
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</beans>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<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>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>
Все о web программировании, разработке интернет сайтов, интернет магазинов, и не только.
понедельник, 7 апреля 2008 г.
Использование Spring Framework для Web. Полное руководство.
Подписаться на:
Комментарии к сообщению (Atom)

1 коммент.:
Отправить комментарий