Spring系统学习笔记二

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;
@Repository
public 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")
@Transactional
public 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;

@Controller
public 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 globalcommunityoffriendsandstrangersspittingouttheir
inner-mostandpersonalthoughtsonthewebforeveryoneelseto
see.</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>
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值