Spring JPetStore总体架构属于三层结构. 分为业务服务层 表现层 数据访问层
业务服务层使用由POJO(java类)实现, 它们运行在轻量级的容器Spring中. 轻量级容器的功能在于, 一 管理业务对象的生命周期. 二 借助于Spring 的IOC(Inversion of Control, 控制反转)功能来完成对象之间的依赖关系, 而不用编程一显示完成. 三 借助于Spring的AOP(Aspect-Oriented Programming, 面向方面的程序设计) 为运行在容器中的对象提供一些企业服务, 比如声明式的事务管理. 业务层放弃了EJB而使用运行在轻量级容器的服务层, 会有以下好处:
1. 能够在Servlet引擎中运行. 不用EJB容器的服务器, tomcat就能搞定, 软件的费用低, 容易管理, 负载小.
2 . 容易在不同的应用服务器或serverlet引擎之间移植. 要想达到高可移植性, 对于EJB容器来说要比web容器困难, 比如, 就需要确保不同的EJB容器在启动时都会运行某一些代码.
3. 实现更为简单.
4. 不需要那么累赘的部署文件.
不好的地方:
1. 缺乏对远程调用的内置支持.
2. 缺乏一个标准的环境, 用于容纳, 管理业务对象.
3. 没有清晰的业务层.
4. 在不同的应用系统之间缺乏一致性, 第个系统都可能会有自己的一套做法: 怎么访问业务对象 怎么解决事务管理 怎么访问数据等.
虽然如此但现在我们有了spring一切就有了解决之道了.
表现层中, Spring JPetStore提供了两种不同的web层实现, 二者都要依靠同一个中间层, 一个是基于Structs的,另一个则是Spring的MVC框架. 二者都是基于JSTL的JSP视图.
数据访问层,使用了J2EE模式中的"数据访问对象"(Data Access Object, DAO), 他用一个DAO接口隐藏了持久化操作的细节, 这样使用这个模式的业务对象无需知道底层的持久化技术的细节. Spring JPetStore中使用了iBATIS框架.
下面让我们来看一下应用中的细节内容吧, 先在Eclipse下把应用加进来方便调试, 运行.
Eclipse可到(http://www.eclipse.org/downloads/上下载)他是压缩包解压后就可用(可以再去下MyEclipe http://www.myeclipse.com是Eclipese的开发插件方便开发, 不过要付费可以下载它的破解文件http://jinxinxin.bokee.com/inc/myeclipse_keygens.rar MyEclipse的安装如果不会去Google一下吧). 好了开发工具安装后新建一工程jpetstore, 然后用..\spring-jpetstore\samples\jpetstore下的src目录复盖你的eclipse工作空间下的\jpetstore下的src目录,然后再把..\spring-jpetstore\samples\jpetstore下的war目录下的全部内容拷到你的eclipse工作空间下的\jpetstore下的WebRoot目录下复盖WEB-INF.回到eclipse下刷新工程你可看到如下:
先从数据访问层说起吧, 他使用iBATIS框架来访问数据库,在..\spring-jpetstore\samples\jpetstore\db目录下有它的各种数据库的schema有hsql, mysql, oracle, postges你使用拿一种数据库是通过..\spring-jpetstore\samples\jpetstore\war\WEB-INF下的jdbc.properties来配置的.默认的是hsql数据库:
# Properties file with JDBC-related settings.
# Applied by PropertyPlaceholderConfigurer from "dataAccessContext-local.xml".
# Targeted at system administrators, to avoid touching the context XML files.
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://localhost:9002
jdbc.username=sa
jdbc.password=
要改为用mysql只要改为:
#jdbc.driverClassName=org.hsqldb.jdbcDriver
#jdbc.url=jdbc:hsqldb:hsql://localhost:9002
#jdbc.username=sa
#jdbc.password=
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/jpetstore
jdbc.username=root
jdbc.password=
即可.业务层能过...jpetstore.dao包下的接口来访问....jpetstore.dao.ibatis包从而通过iBATIS框架访问数据库中的数据的部份代码如下:
package org.springframework.samples.jpetstore.dao;
public interface AccountDao {
Account getAccount(String username, String password) throws DataAccessException;
...........
}
package org.springframework.samples.jpetstore.dao.ibatis;
public class SqlMapAccountDao extends SqlMapDaoSupport implements AccountDao {
public Account getAccount(String username, String password) throws DataAccessException {
Account account = new Account();
account.setUsername(username);
account.setPassword(password);
return (Account) getSqlMapTemplate().executeQueryForObject("getAccountByUsernameAndPassword", account);
}
..............
}
Account.xml
<mapped-statement name="getAccountByUsernameAndPassword" result-map="result">
select
SIGNON.USERNAME as USERID,
ACCOUNT.EMAIL,
ACCOUNT.FIRSTNAME,
ACCOUNT.LASTNAME,
ACCOUNT.STATUS,
ACCOUNT.ADDR1,
ACCOUNT.ADDR2,
ACCOUNT.CITY,
..
from ACCOUNT, PROFILE, SIGNON, BANNERDATA
where ACCOUNT.USERID = #username#
and SIGNON.PASSWORD = #password#
and SIGNON.USERNAME = ACCOUNT.USERID
and PROFILE.USERID = ACCOUNT.USERID
and PROFILE.FAVCATEGORY = BANNERDATA.FAVCATEGORY
</mapped-statement>
然后到了业务层, 业务层为表达层提供服务,操纵数据层来完成业务逻辑比如从数据库中读出客启信息传给表达层 向数据库中插入订单等.业务层还要完成对数据库操作的完整性,正确性即事务管理. 此应用是通过Spring的AOP来完成无需编程实现如下:
dataAccessContex-local.xml
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<!-- Transaction manager for a single JDBC DataSource -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource" />
</property>
<property name="mappingResources">
<list>
<value>org/springframework/samples/jpetstore/Hibernate/Account.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<!-- (see dataAccessContext-jta.xml for an alternative) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
<!-- SqlMap setup for iBATIS Database Layer -->
<bean id="sqlMap" class="org.springframework.orm.ibatis.SqlMapFactoryBean">
<property name="configLocation"><value>WEB-INF/sql-map-config.xml</value></property>
</bean>
<!-- ========================= DAO DEFINITIONS: IBATIS IMPLEMENTATIONS ========================= -->
<bean id="accountDao" class="org.springframework.samples.jpetstore.dao.hibdaoimp.AccountDaoImp">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>
................
</beans>
applicationContex.xml
<bean id="petStoreTarget" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">
<property name="accountDao"><ref bean="accountDao"/></property>
<property name="categoryDao"><ref bean="categoryDao"/></property>
<property name="productDao"><ref bean="productDao"/></property>
<property name="itemDao"><ref bean="itemDao"/></property>
<property name="orderDao"><ref bean="orderDao"/></property>
</bean>
<!-- Transactional proxy for the JPetStore primary business object -->
<bean id="petStore" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref local="petStoreTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
从上面的配置代码我们可看到通过Spring的IOC完成了org.springframework.samples.jpetstore.domain.logic.PetStoreImpl等类的注入. 通过AOP用org.springframework.transaction.interceptor.TransactionProxyFactoryBean来完成事务代理
业务层通过一个门面(facada)PetStoreFacade.java接口来为表达层提供服务:
package org.springframework.samples.jpetstore.domain.logic;
public interface PetStoreFacade {
Account getAccount(String username);
Account getAccount(String username, String password);
void insertAccount(Account account);
void updateAccount(Account account);
..........
}
用PetStoreImp.java来实现:
package org.springframework.samples.jpetstore.domain.logic;
public class PetStoreImpl implements PetStoreFacade, OrderService {
private AccountDao accountDao;
.........
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public Account getAccount(String username) {
return this.accountDao.getAccount(username);
}
public Account getAccount(String username, String password) {
return this.accountDao.getAccount(username, password);
}
public void insertAccount(Account account) {
this.accountDao.insertAccount(account);
}
.......
}
表达层这里介绍Structs,Structs中通过PetStoreFacade接口来访问业务层:
package org.springframework.samples.jpetstore.web.struts;
public abstract class BaseAction extends Action {
private
PetStoreFacade petStore;
public void setServlet(ActionServlet actionServlet) {
super.setServlet(actionServlet);
ServletContext servletContext = actionServlet.getServletContext();
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
this.petStore = (PetStoreFacade) wac.getBean("petStore");
}
protected PetStoreFacade getPetStore() {
return petStore;
}
}
public class SignonAction extends BaseAction {
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
AccountActionForm acctForm = (AccountActionForm) form;
String username = acctForm.getUsername();
String password = acctForm.getPassword();
Account account = getPetStore().getAccount(username, password);
.....
return mapping.findForward("success");
}
Structs框架通过struct-config.xml文来控制相关映射转发的:
struct-config.xml
<action path="/signon" type="org.springframework.samples.jpetstore.web.struts.SignonAction"
name="accountForm" scope="request"
validate="false">
<forward name="success" path="/index.jsp"/>
</action>
关于jsp就不在多说了下面再来看一下它的web.xml文件:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/dataAccessContext-local.xml /WEB-INF/applicationContext.xml
</param-value>
</context-param>
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
还有关于远程机制有Caucho的Hessian(一个借助HTTP的二进制协议) Burlap(一个基于XML的借助HTTP的传输协议), Apache Axis提供的JAX-RPC(基于SOAP的借助HTTP传输的web serverice), 还有基于RMI的远程调用.
想了解更多还是自已细看里面的代码吧!
请继续关注Spring JPetStore(三) 在其上实现自已的Sunlight Netstore