Spring系统学习笔记二

来源:互联网 发布:武威广电网络客服电话 编辑:程序博客网 时间:2024/05/18 03:38

Spring系统学习笔记二

@(Experience)[java|Spring]

本笔记学习自Spring in Action 第三版

Spring 应用程序核心组件,将介绍如下方面:
- 数据库部分
- 事务管理
- Spring MVC
- Spring Web Flow


5 数据库

内容
- 定义Spring对数据访问的支持
- 配置数据库资源
- 使用Spring的JDBC模板
- Spring与Hibernate和JPA集成使用

5.1 Spring的数据访问哲学

DAO框架

5.1.1 了解Spring的数据访问异常体系

可能导致抛出SQLException的常见问题包括:
- 应用程序无法连接数据库;
- 要执行的查询有语法错误;
- 查询中所使用的表或列不存在;
- 试图插入或更新的数据违反了数据库的完整性约束。

Spring的平台无关持久化异常
Spring提供了多个访问异常,但是他并没有与特定的持久化方式相关联。
这里写图片描述

所有的异常都继承自DataAccessException,并且DataAccessException是非检查型异常,所以没有必要捕获Spring所抛出的数据访问异常。

5.1.2 数据访问模板化

Spring使用模板模式来设计Spring的数据访问。他将数据访问过程中固定的和可变的部分明确划分为两个不同的类:模板(template)回调(callback)

Spring提供了多个可选的模板:
这里写图片描述

5.1.3 使用DAO支持类

这里写图片描述


5.2 配置数据源

Spring提供了在AppliactionContext中配置数据源Bean的多种方式:
- 通过JDBC驱动程序定义的数据源;
- 通过JNDI查找的数据源;
- 连接池的数据源。

5.2.1 使用JNDI数据源

样例:

<jee:jndi-lookup id="dataSource" jndi-name="/jdbc/SpitterDS" resource-ref="true"/>

5.2.2 使用数据源连接池

Spring并没有提供数据源连接池实现,DBCP项目是一个非常不错的选择。
DBCP包含了多个提供连接池功能的数据源,最常用的是BasicDataSource。

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>    <property name="url" value="jdbc:hsqldb:hsql://localhost/spitter/spitter"/>    <property name="username" value="sa"/>    <property name="password" value=""/>    <property name="initialSize" value="5"/>    <property name="maxActive" value="10"/></bean>

5.2.3 基于JDBC驱动的数据源

Spring提供了2种数据源对象:
DriverManagerDataSource :每个连接请求都会返回一个新建的连接。他没有进行池化管理。

SingleConnectionDataSource : 每个连接请求返回同一个连接。

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>    <property name="url" value="jdbc:hsqldb:hsql://localhost/spitter/spitter"/>    <property name="username" value="sa"/>    <property name="password" value=""/></bean>

5.3 在Spring中使用JDBC

5.3.1 使用JDBC模板

Spring为JDBC提供了3个模板类供使用。
- JdbcTemplate:最基本的JDBC模板,这个模板支持最简单的JDBC数据库访问功能以及简单的索引参数查询。
- NamedParameterJdbcTemplate:可以将查询值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数。
- SimpleJdbcTemplate:该模板利用自动装箱、泛型、可变参数列表来简化JDBC模板的使用。

使用SimpleJdbcTemplate访问数据

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">    <constructor-arg ref="dataSource"/></bean>

将template装配入Dao:

public class JdbcSpitterDAO implements SpitterDAO{...private SimpleJdbcTemplate jdbcTemplate;    public void setJdbcTemplate(SimpleJdbcTemplate jdbcTemplate){        this.jdbcTemplate = jdbcTemplate;    }}
<bean id="spitterDao" class="com.habuma.spitter.persistence.SimpleJdbcTemplateSpitterDao">    <property name="jdbcTemplate" ref="jdbcTemplate"/></bean>

基于template的增加:

public voidaddSpitter(Spitterspitter){    jdbcTemplate.update(SQL_INSERT_SPITTER,        spitter.getUsername(),        spitter.getPassword(),        spitter.getFullName(),        spitter.getEmail(),        spitter.isUpdateByEmail());    spitter.setId(queryForIdentity());}

命名参数方式不占篇幅解释。


5.4 在Spring中集成Hibernate

延迟加载、预先抓取、级联。这些超越JDBC能力的特性在ORM工具中得到服务。
(延迟加载,预先抓取也是iBATIS的重要概念)。

Spring对ORM提供了集成点,以及一些附加的服务:
- Spring声明式事务的集成支持;
- 透明的异常处理;
- 线程安全的、轻量级的模板类;
- DAO支持类;
- 资源管理。

5.4.1 Hibernate预览

HibernateTemplate的职责之一是管理Hibernate的Session。

5.4.2 声明Hibernate的Session工厂

获取Hibernate的Session接口的标准方式是借助于Hibernate的SessionFactory接口的实现类。
如果要使用XML来定义对象与数据之间的映射,那么需要在Spring中配置LocalSessionFactoryBean。

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    <property name="dataSource" ref="dataSource" />    <property name="mappingResources">    <list>        <value>Spitter.hbm.xml </value>    </list>    </property>        <property name="hibernateProperties">        <props>            <prop key="dialect">org.hibernate.dialect.HSQLDialect</prop>        </props>    </property></bean>

如果希望用注解的方式,那就需要使用AnnotationSessionFactoryBean来代替LocalSessionFactoryBean:

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">    <property name="dataSource" ref="dataSource"/>    <property name="packagesToScan" value="com.habuma.spitter.domain"/>    <property name="hibernateProperties">        <props>            <prop key="dialect">org.hibernate.dialect.HSQLDialect</prop>        </props>    </property></bean>

扫描packagesToScan指定的包中,含有JPA@Entity或@MappedSuperclass注解以及Hibernate的@Entity注解。

其他写法:

<property name="packagesToScan">    <list>        <value>com.habuma.spitter.domain</value>    </list></property><property name="annotatedClasses">    <list>        <value>com.habuma.spitter.domain.Spitter</value>        <value>com.habuma.spitter.domain.Spittle</value>    </list></property>

5.4.3 构建不依赖于Spring的Hibernate代码

package com.habuma.spitter.persistence;import java.util.List;import org.hibernate.SessionFactory;import org.hibernate.classic.Session;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Repository;import com.habuma.spitter.domain.Spitter;import com.habuma.spitter.domain.Spittle;@Repositorypublic class HibernateSpitterDao implements SpitterDao{    private SessionFactory sessionFactory;    @Autowired    public HibernateSpitterDao(SessionFactory sessionFactory){        this.sessionFactory = sessionFactory;    }    private Session currentSession(){        return sessionFactory.getCurrentSession();    }    public void addSpitter(Spitter spitter){        currentSession().save(spitter);    }    public Spitter getSpitterById(longid){        return(Spitter)currentSession().get(Spitter.class,id);    }    public void saveSpitter(Spitterspitter){        currentSession().update(spitter);    }    ...}

@Autowired注解将SessionFactory注入到sessionFactory中
@Repository注解一能被Spring的<context:component-scan>所扫描,二会在类上添加一个通知器,会捕获任何平台相关的异常并以Spring的非检查型数据访问异常重新抛出。


5.5 Spring与Java持久化API

Spring放弃了对EJB的支持,支持了JPA。
JPA诞生于EJB规范。

5.5.1 配置实体管理器工厂

基于JPA的应用程序使用EntityManagerFactory的实现类来获取EntityManager实例。JPA定义了两种类型的实体管理器:
- 应用程序管理类型
- 容器管理类型
这两种实体管理器工厂分别由对应的Spring工厂Bean创建的:
- LocationEntityManagerFactoryBean生成应用程序管理类型的Entity-ManagerFactory;
- LocalContainerEntityManagerFactoryBean生成容器管理类型的EntityManagerFactory。

使用应用程序管理类型的JPA

persistence.xml配置文件:

<persistencexmlns="http://java.sun.com/xml/ns/persistence" version="1.0">    <persistence-unit name="spitterPU">        <class>com.habuma.spitter.domain.Spitter</class>        <class>com.habuma.spitter.domain.Spittle</class>        <properties>            <property name="toplink.jdbc.driver" value="org.hsqldb.jdbcDriver"/>            <property name="toplink.jdbc.url" value="jdbc:hsqldb:hsql://localhost/spitter/spitter"/>            <propertyn ame="toplink.jdbc.user" value="sa" />            <propert yname="toplink.jdbc.password" value="" />        </properties>    </persistence-unit></persistence>

Spring bean配置:

<bean id="emf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">    <property name="persistenceUnitName" value="spitterPU"/></bean>

使用容器管理类型的JPA
我们可以在容器中生成EntityManagerFactory,即可以将数据源信息配置在Spring应用的上下文,而不是persistence.xml中

<bean id="emf" class= "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">    <property name="dataSource" ref="dataSource"/>    <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/></bean>

Spring提供了多个JPA厂商适配器:
- EclipseLinkJpaVendorAdapter
- HibernateJpaVendorAdapter
- OpenJpaVendorAdapter
- TopLinkJpaVendorAdapter

本例中,我们使用Hibernate作为JPA实现

<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">    <property name="database" value="HSQL"/>    <property name="showSql" value="true"/>    <property name="generateDdl" value="false"/>    <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect"/></bean>

从JNDI获取实体管理器工厂

<jee:jndi-lookup id="emf" jndi-name="persistence/spitterPU" />

5.5.2 编写基于JPA的DAO

一旦获取了EntityManagerFactory,就可以开始编写DAO了

package com.habuma.spitter.persistence;import java.util.List;import javax.persistence.EntityManager;import javax.persistence.PersistenceContext;import org.springframework.dao.DataAccessException;import org.springframework.stereotype.Repository;import org.springframework.transaction.annotation.Transactional;import com.habuma.spitter.domain.Spitter;import com.habuma.spitter.domain.Spittle;@Repository("spitterDao")@Transactionalpublic class JpaSpitterDao implements SpitterDao{    private static final String RECENT_SPITTLES="SELECTsFROMSpittles";    private static final String ALL_SPITTERS="SELECTsFROMSpitters";    private static final String SPITTER_FOR_USERNAME="SELECTsFROMSpittersWHEREs.username=:username";    private static final String SPITTLES_BY_USERNAME="SELECTsFROMSpittlesWHEREs.spitter.username=:username";    @PersistenceContext    private EntityManager em;    public void addSpitter(Spitter spitter){        em.persist(spitter);    }    public Spitter getSpitterById(long id){        returnem.find(Spitter.class,id);    }    public void saveSpitter(Spitter spitter){        em.merge(spitter);    }    ...}

6 事务管理

内容:
- 集成事务管理
- 编码方式管理事务
- 使用声明式事务
- 以注解的方式描述事务

在软件开发领域,全有或全无的操作被称为事务


6.1 理解事务

作为事务,这些过程将被视为一个操作,从而保证所有操作要么都成功要么全部回滚,就像这些操作从未发生过。

6.1.1 用4个词来表示事务

在传统的软件开发中,人们创建了一个术语来描述事务:A C I D
原子性 Atomic,一致性 Consistent,隔离性 Isolated,持久性 Durable。

6.1.2 理解Spring对事务管理的支持

Spring提供了编码式和声明式事务管理的支持。


6.2 选择事务管理器

Spring并不直接管理事务,而是提供了多种事务管理器。每个事务管理器都会充当某一特定平台的事务实现的门面。
这里写图片描述
这里写图片描述

6.2.1 JDBC事务

DataSourceTransactionManager

<bean id="transactionManager" class="org.springframework.jdbc.➥datasource.DataSourceTransactionManager">    <property name="dataSource" ref="dataSource"/></bean>

6.2.2 Hibernate事务

<bean id="transactionManager" class="org.springframework.➥orm.hibernate3.HibernateTransactionManager">    <property name="sessionFactory" ref="sessionFactory"/></bean>

6.2.3 持久化API事务

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">    <property name="entityManagerFactory" ref="entityManagerFactory"/></bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect"/>

6.4 声明式事务

Spring提供3种方式来声明事务式边界。以前,Spring只能使用Spring AOP和TransactionProxyFactoryBean的代理Bean来实现声明式事务。现在使用Spring的tx命名空间和@Transactional注解。

6.4.1 定义事务属性

在Spring中,声明式事务是通过事务属性(transaction attribute)来定义的。

传播行为
事务的第一个方面是传播行为(propagation behavior)。传播行为定义了客户端与被调用方法之间的事务边界。

隔离级别
隔离级别定义了一个事务可能受其他并发事务影响的程度。

只读
声明事务的第三个特性是否是只读事务。

事务超时
定义事务的执行时间

回滚规则
默认情况下,遇到运行期异常回滚,二遇到检查型异常不会回滚

6.4.2 在XML中定义事务

命名空间:

<beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:aop="http://www.springframework.org/schema/aop"    xmlns:tx="http://www.springframework.org/schema/tx"    xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/        spring-beans-3.0.xsd    http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd    http://www.springframework.org/schema/tx    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

tx配置事务:

<tx:advice id="txAdvice">    <tx:attributes>        <tx:method name="add*" propagation="REQUIRED"/>        <tx:method name="*" propagation="SUPPORTS" read-only="true"/>    </tx:attributes></tx:advice>

6.4.3 定义注解驱动的事务

<tx:annotation-driven/>

指定特定的事务管理器

<tx:annotation-driven transaction-manager="txManager"/>

通过<tx:annotation-driven>元素高速Spring检查上下文中所有的Bean并查找使用@Transactional注解的Bean。


7 使用Spring MVC构建Web应用程序

内容:
- 映射请求到Spring控制器
- 透明地绑定表单参数
- 校验表单提交
- 上传文件

7.1 Spring MVC起步

7.1.1 跟踪Spring MVC请求

这里写图片描述

7.1.2 搭建Spring MVC

Spring MVC的核心是DispatcherServlet,这个组件充当了Spring MVC的前段控制器,与其他Servlet一样,DispatcherServlet必须在Web应用程序的web.xml中进行配置。

<servlet>    <servlet-name>spitter</servlet-name>    <servlet-class>        org.springframework.web.servlet.DispatcherServlet    </servlet-class>    <load-on-startup>1</load-on-startup></servlet>

因为servlet的名字为spitter,DispatcherServlet将尝试从一个名为spitter-servlet.xml文件(在WEB-INF目录下)中加载应用上下文。

<servlet-mapping>    <servlet-name>spitter</servlet-name>    <url-pattern>/</url-pattern></servlet-mapping>

创建spitter-servlet.xml文件,用它来创建应用上下文

<?xml version="1.0"encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:mvc="http://www.springframework.org/schema/mvc"    xsi:schemaLocation="http://www.springframework.org/schema/mvc        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd        http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">    <mvc:resources mapping="/resources/**" location="/resources/"/></beans>

mvc:resources建立了一个服务于静态资源的处理器。根据以上配置,所有以/resources路径开头的请求都会自动由应用程序根目录下的/resources目录提供服务。所有图片,样式表,JavaScript以及其他静态资源都必须放在/resources。


7.2 编写基本的控制器

7.2.1配置注解驱动的Spring MVC

Spring自带了多个处理器映射实现供我们选择:
- BeanNameUrlHandlerMapping :根据控制器Bean的名字将控制器映射到URL。
- ControllerBeanNameHandlerMapping:与上类似,Bean的名字不需要遵守URL的约定
- ControllerClassNameHandlerMapping:使用控制器的类名作为URL基础,将控制器映射到URL
- DefaultAnnotationHandlerMapping:将请求映射给使用@RequestMapping注解的控制器和控制器方法
- SimpleUrlHandlerMapping:使用定义在Spring应用上下文的属性集合将控制器映射到URL

7.2.2 定义首页的控制器

HomeController是一个基本的Spring MVC控制器,用来处理首页的请求

package com.habuma.spitter.mvc;import javax.inject.Inject;import java.util.Map;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import com.habuma.spitter.service.SpitterService;@Controllerpublic class HomeController{    public static final int DEFAULT_SPITTLES_PER_PAGE=25;    private SpitterService spitterService;    @Inject    public HomeController(SpitterService spitterService){        this.spitterService=spitterService;    }    @RequestMapping({"/","/home"})    public String showHomePage(Map<String,Object>model){        model.put("spittles",spitterService.getRecentSpittles(DEFAULT_SPITTLES_PER_PAGE));        return "home";    }}

测试控制器:

public class HomeControllerTest{    @Test    public void shouldDisplayRecentSpittles(){        List<Spittle> expectedSpittles= asList(newSpittle(),newSpittle(),newSpittle());        SpitterService spitterService=mock(SpitterService.class);    when(spitterService.getRecentSpittles(DEFAULT_SPITTLES_PER_PAGE)).    thenReturn(expectedSpittles);    HomeControllercontroller=                    new HomeController(spitterService);    HashMap<String,Object> model =new HashMap<String,Object>();    String viewName=controller.showHomePage(model);    assertEquals("home",viewName);    assertSame(expectedSpittles,model.get("spittles"));    verify(spitterService).getRecentSpittles(DEFAULT_SPITTLES_PER_PAGE);}

7.2.3 解析视图

Spring自带了多个视图解析器实现供选择。

解析内部视图
InternalResourceViewResolver是一个面向约定的元素。它将逻辑视图名称解析为View对象。
这里写图片描述
它通过逻辑视图名称添加前缀和后缀来确定Web应用程序中的模板的路径。
比如:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">    <property name="prefix" value="/WEB-INF/views/"/>    <property name="suffix" value=".jsp"/></bean>

如果home.jsp使用JSTL标签:

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>    <property name="prefix" value="/WEB-INF/views/"/>    <property name="suffix" value=".jsp"/></bean>

解析Tiles视图
Apache Tiles是一个模板框架,它将页面分成片段并在运行时组装成完整的页面。

<bean class= "org.springframework.web.servlet.view.tiles2.TilesViewResolver"/>
<bean class= "org.springframework.web.servlet.view.tiles2.TilesConfigurer">    <property name="definitions">        <list>            <value>/WEB-INF/views/**/views.xml</value>        </list>    </property></bean>

7.2.4 定义首页的视图

<%@ taglibprefix="c"uri="http://java.sun.com/jsp/jstl/core"%><%@ taglibprefix="s"uri="http://www.springframework.org/tags"%><%@ taglibprefix="t"uri="http://tiles.apache.org/tags-tiles"%><%@ taglibprefix="fmt"uri="http://java.sun.com/jsp/jstl/fmt"%><div><h2>A globalcommunityoffriendsandstrangersspittingouttheirinner-mostandpersonalthoughtsonthewebforeveryoneelsetosee.</h2><h3>Lookatwhatthesepeoplearespittingrightnow...</h3><ol class="spittle-list"><c:forEach var="spittle" items="${spittles}">    <s:url value="/spitters/{spitterName}" var="spitter_url">        <s:param name="spitterName" value="${spittle.spitter.username}"/>    </s:url>    <li>        <span class="spittleListImage">            <img src="http://s3.amazonaws.com/spitterImages/${spittle.spitter.id}.jpg"            width="48"            border="0"            align="middle"            onError="this.src='<s:urlvalue="/resources/images"/>/spitter_avatar.png';"/>        </span>        <span class="spittleListText">            <a href="${spitter_url}">                <c:out value="${spittle.spitter.username}"/></a>                - <c:out value="${spittle.text}"/><br/>                <small><fmt:formatDate value="${spittle.when}"                            pattern="hh:mmaMMMd,yyyy"/></small>        </span>    </li></c:forEach></ol></div>

7.2.5 完成Spring应用上下文

Web.xml配置ContextLoaderListener

<listener>    <listener-class>        org.springframework.web.context.ContextLoaderListener    </listener-class></listener>

必须制定ContextLoaderListener需要加载哪些配置文件,如果没有规定就会加载/WEB-INF/applicationContext.xml这个配置文件。

servlet配置:

<context-param>    <param-name>contextConfigLocation</param-name>    <param-value>        /WEB-INF/spitter-security.xml        classpath:service-context.xml        classpath:persistence-context.xml        classpath:dataSource-context.xml    </param-value></context-param>
0 0
原创粉丝点击