Spring学习笔记

1.什么是spring?

Spring是 SE/EE 开发的一站式框架。(一站式框架:有EE开发的每一层解决方案。)

  • WEB层             :SpringMVC
  • Service层         :Spring的Bean管理,Spring声明式事务
  • DAO层              :Spring的Jdbc模板,Spring的ORM模块

Spring的核心是控制反转(IOC)面向切面(AOP)

2. 什么是控制反转(IOC)和依赖注入(DI)? 

什么是控制反转(IOC)

  将对象的创建权反转给(交给)Spring。就是将原本在程序中手动创建对象的控制权,交由Spring框架管理

什么是依赖注入(DI)

  是指在Spring创建对象的过程中,将这个对象所依赖的属性注入(设置)进来

3.Spring的工厂类  

BeanFactory(老版本的工厂类):调用getBean的时候,才会生成类的实例

ApplicationContext (新版本的工厂类):加载配置文件的时候,就会将Spring管理的类都实例化(ApplicationContext 接口继承BeanFactory接口)

ApplicationContext有两个实现类:

    ClassPathXmlApplicationContext :加载类路径下的配置文件

    FileSystemXmlApplicationContext    :加载文件系统下的配置文件

代码:

在ApplicationContext.xml中配置bean:

<beans>
    <!-- Spring的入门的配置 -->
    <bean name="userDAO" class="com.ztt.spring.demo1.UserDAOImpl" >
      <property name="name" value="李东"/>
    </bean>
</beans>

【注】:<bean>标签的 idname 的配置:

  • id  :使用了约束中的唯一约束。里面不能出现特殊字符
  • name :没有使用约束中的唯一约束(理论上可以出现重复的,但是实际开发不能出现的)。里面可以出现特殊字符。

【注】:Bean的作用范围的配置 (scope:Bean的作用范围)

  • singleton          :默认scope的值是singleton,即产生的对象是单例的,singleton对象会在容器启动时被创建
  • prototype        :多例模式。spring容器启动的时候并不会创建对象,而是在调用getBean()方法时才会创建对象。
  • request             :应用在web项目中,Spring创建这个类以后,将这个类存入到request范围中。
  • session              :应用在web项目中,Spring创建这个类以后,将这个类存入到session范围中。
  • globalsession   :应用在web项目中,必须在porlet环境下使用。但是如果没有这种环境,相对于session。

测试:

public void demo(){
        // 创建Spring的工厂
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean对象
        UserDAO userDAO = (UserDAO) applicationContext.getBean("userDAO");
        userDAO.save();
    }

4.请介绍一下Spring框架中Bean的生命周期和作用域  

(1)bean定义

    在配置文件里面用<bean></bean>来进行定义。

(2)bean初始化

  • 在配置文件中通过指定init-method属性来完成

(3)bean调用

    有三种方式可以得到bean实例,并进行调用

(4)bean销毁

使用配置文件指定的destroy-method属性来完成。

需要注意的是

  1)destroy-method 只对 scope="singleton" (单例)有效

  2)销毁方法,必须关闭ApplicationContext对象(手动调用),才会被调用

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
applicationContext.close();

## 作用域                                        

singleton

  单例模式。当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例。并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。

prototype

  多例模式。每次通过容器的geuBean方法获取prototype定义的beans时都会产生一个新的bean实例

  根据经验,对所有有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用 singleton作用域。

request

  对于每次HTTP请求中,使用request定义的bean都会产生一个新的实例。该作用 域仅在基于web的Spring ApplicationContext情形下有效。

session

  对于每次HTTP Session,使用session定义的都将产生一个新的实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

global session

  每个全局的HTTP Session,使用session定义的bean都将产生一个新的实例。该作用域仅在基于 web的Spring ApplicationContext情形下有效

5.IOC   

5.1 spring配置bean 实例化有几种方式?(3种)

 (1)无参构造方法方式

编写类:

public class Bean1 {

    //无参构造方法
    public Bean1() {
        。。。。。。
    }

}

在 applicationContext.xml 中编写配置:

<!-- 无参数构造方法 -->
<bean id="bean1" class="com.ztt.spring.Bean1"></bean>

(2)使用静态工厂方法实例化

编写类Bean2的静态工厂

public class Bean2Factory {

    public static Bean2 createBean2(){
        return new Bean2();
    }
}

在 applicationContext.xml 中编写配置:

<!-- 静态工厂实例化 -->
<bean id="bean2" class="com.ztt.spring.Bean2Factory" factory-method="createBean2"/>

(3)使用实例工厂方法实例化

编写Bean3的实例工厂:

public class Bean3Factory {

    public Bean3 createBean3(){
        return new Bean3();
    }
}

在 applicationContext.xml 中编写配置:

<!-- 实例工厂实例化 -->
<bean id="bean3Factory" class="com.ztt.spring.Bean3Factory"></bean>
<bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"></bean>

5.2  Java中实现依赖注入的三种方式?Spring的属性注入方式有哪几种(2种)? 

Java中实现依赖注入有3种方式:构造方法方式、set方法方式、接口注入方式

spring支持构造方法的属性注入set方法的属性注入

(1)构造方法的属性注入  (通过 <constructor-arg> 完成注入)

  <!-- 构造方法的方式 -->
    <bean id="car" class="com.ztt.spring.Car">
        <constructor-argname="name" value="宝马"/>
        <constructor-arg name="price" value="800000"/>
    </bean>

(2)set方法的属性注入  (通过<property> 完成注入)

<!-- set方法的方式 -->
<bean id="car2" class="com.ztt.spring.Car2">
     <property name="name" value="奔驰"/>
     <property name="price" value="1000000"/>
</bean>

【注】如果注入属性不是普通类型,而是类的话,不用value,用ref。 (value:设置普通类型的值,ref:设置其他的类的id或name

<!-- set方法注入对象类型的属性 -->
<bean id="employee" class="com.ztt.spring.Employee">
     <!-- value:设置普通类型的值,ref:设置其他的类的id或name -->
     <property name="name" value="涛哥"/>
     <property name="car2" ref="car2"/>
</bean>

(3)P名称空间的属性注入(Spring2.5以后)

通过引入p名称空间完成属性的注入:

写法

  普通属性         p:属性名=”值”

  对象属性         p:属性名-ref=”值”

首先引入P名称空间:

 使用p名称空间:

<!-- p名称空间的方式 -->
<bean id="car2" class="com.ztt.spring.Car2" p:name="奇瑞QQ" p:price="30000"></bean>
    
<bean id="employee" class="com.ztt.spring.Employee" p:name="王东" p:car2-ref="car2"></bean>

 5.3 Spring基于注解的配置  

5.3.1 入门演示:  

要想在类上使用注解,首先需要在Spring配置文件中applicationContext.xml中配置组件扫描。

<!-- 使用IOC的注解开发,配置组件扫描(哪些包下的类使用IOC注解) -->
<!-- 注意:扫描是为了扫描类上的注解 -->
<context:component-scan base-package="com.ztt.spring"></context:component-scan>

然后在类上添加注解:

利用注解方式设置属性值:

  • 属性如果有set方法,需要将属性注入的注解加到set方法上;
  • 属性如果没有set方法,需要将属性注入的注解加到该属性上。
//@Component("UserDAO") //相当于<bean id="UserDAO" class="com.ztt.spring.dao.impl.UserDAOImpl"/>
@Repository("UserDAO")  //因为修饰DAO层的类,所以使用@Component的衍生类@Repository,代替@Component
public class UserDAOImpl implements UserDAO {
    
    //利用注解设置属性的值:如果属性有set方法,将注解加到set方法上;如果没有set方法,将注解加到该属性上
    @Value("zhang")
    private String name;
    
    /*@Value("zhang")
    public void setName(String name) {
        this.name = name;
    }*/

    @Override
    public void save() {
        System.out.println("UserDao的save方法执行了。。。" + name);
    }
}

测试:

@Test
    //使用spring的注解方式
    public void test1(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDAO userDAO = (UserDAO) applicationContext.getBean("UserDAO");
        userDAO.save();
    }

5.3.2 Spring的IOC的注解:  

1、@Component :组件。  修饰一个类,将这个类交给Spring管理。

  这个注解有3个衍生注解(功能类似):

    @Controller  :修饰web层的类

    @Service      :修饰service层的类

    @Repository:修饰DAO层的类

2、属性注入的注解

  普通属性:

    @Value:设置普通属性的值

  对象类型属性:

    @Autowired:设置对象类型的属性的值。但是按照类型完成属性注入。

      我们习惯按照名称完成属性注入:必须让@Autowired和@Qualifier一起使用完成按照名称属性注入,

    @Resource:完成对象类型的属性的注入,按照名称完成属性注入

@Service("UserService")  //相当于<bean id="UserService" class="com.ztt.spring.demo1.UserServiceImpl"/>
public class UserServiceImpl implements UserService {
    
    //注入DAO
    //*@Autowired @Qualifier(value="UserDAO")  //联合使用@Resource(name="UserDAO")
private UserDAO userDAO;
    
    @Override
    public void save() {
        System.out.println("UserService的save方法执行了。。。");
        userDAO.save();
    }
}

3、Bean的其他注解

  生命周期相关的注解:

    @PostConstruct :初始化方法

    @PreDestroy      :销毁方法

  Bean作用范围的注解:

    @Scope           :作用范围

      singleton   :默认单例

      prototype  :多例

      request

      session

      globalsession

@Service("CustomerService")  //<bean id="CustomerService" init-method="init" destroy-method="destroy"/>
@Scope("prototype")
public class CustomerService {
    @PostConstruct  //相当于init-method="init"
    public void init(){
        System.out.println("CustomerService被初始化了");
    }
    
    public void save(){
        System.out.println("Service的save方法执行了。。。");
    }
    @PreDestroy   //相当于destroy-method="destroy"
    public void destroy(){
        System.out.println("CustomerService被销毁了");
    }
}

【注】有时会用xml与注解整合开发,即用xml管理Bean,注解完成属性注入。

这时类上不再加注解了,只在属性上加注解。applicationContext.xml里面不再配置组件扫描而改为<context:annotation-config/>:

   <!-- xml中只管理Bean,属性注入交给注解 -->
<!-- 上面的扫描context:component-scan可以关闭,只打开属性上的注解 --> <!-- 在没有扫描的情况下,使用属性注入的注解 --> <context:annotation-config/>
<bean id="productService" class="com.ztt.spring.demo3.ProductService"/> <bean id="productDAO" class="com.ztt.spring.demo3.ProductDAO"/>

6. AOP    

6.1 什么是AOP? 

  面向切面编程。AOP是OOP(面向对象编程)的扩展和延伸,解决OOP开发遇到的问题。当我们需要为分散的对象引入公共的行为时,OOP则显得无能为力,她会引入大量重复代码。也就是说,OOP允许定义从上到下的关系,但不适合定义从左到右的关系。

  AOP利用一种成为“横切”的技术,将那些影响了多个类的公共行为封装到一个可重用模块,将其命名为“Aspect”(方面),便于减少系统的重复代码,降低模块间的耦合度。

  使用“横切”技术,AOP将系统分为两部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,横切关注点的一个特点是疆场发生在核心关注点的多处,并且各处都基本相似。比如权限认证、日志、事务处理。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

  AOP解决的问题往往可以用代理模式来解决。代理模式分静态代理和动态代理,静态代理的代理类需要手工编写,在编译器生成;而动态代理的代理类是在运行时才生成的,动态代理有JDK和cglib两种实现方式。AOP的实现原理是动态代理

  • JDK动态代理:只能对实现了接口的类产生代理。
  • cglib动态代理:对没有实现接口的类产生代理对象。生成子类对象。

Spring底层,如果实现了接口,自动用JDK动态代理;如果没有实现接口,自动用cglib动态代理。

6.2 AOP的相关术语:   

Joinpoint(连接点) :指那些可以被拦截到的点(方法)。 在Spring 中,这些点指的是方法,应为spring只支持方法类型的连接点。

Pointcut(切入点)   :指我们要对哪些 Jointpoint 进行拦截的定义。

Advice(通知/增强) :指拦截到 Jointpoint 之后要做的动作就是Advice。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知。

  • 前置通知 :在目标方法执行之前执行的通知。
  • 后置通知 :在目标方法执行之后执行的通知。如果方法没有正常返回(例如抛出异常),则后置通知不会执行
  • 环绕通知 :在目标方法执行之前和之后都可以执行的通知。  【注】1)环绕通知需要返回返回值,否则真正调用者将拿不到返回值,只能得到一个nul;2)只有环绕通知可以接收ProceedingJoinPoint,而其他通知只能接收JoinPoint。
  • 异常通知:在目标方法抛出异常时执行的通知。
  • 最终通知:是在目标方法执行之后执行的通知。无论代码是否有异常都会执行

详细点https://www.cnblogs.com/chuijingjing/p/9806651.html

Introduction(引介:是一种特殊的通知,在不修改类代码的前提下,Introduction 可以在运行期为类动态的添加一些方法或Field。。

Target(目标对象)   : 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 

Weaving(织入)      :把通知(Advice)应用到目标对象来创建新的代理对象的过程。 Spring采用动态代理织入,而AspectJ采用编译器织入和类装载期织入。

Proxy(代理)          :一个类被AOP织入增强后,就产生一个结果代理类。

Aspect(切面)        :切入点和通知的结合。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。

6.3 Spring的AOP基于xml开发    

功能:我们想在类 ProductDAOImpl 中的 save() 方法前加入权限验证

(1)目标对象(被增强的对象)

public class ProductDAOImpl implements ProductDAO {

    @Override
    public void save() {
        System.out.println("保存商品。。。");
    }
    @Override
    public void update() {
        System.out.println("修改商品。。。");
    }
    @Override
    public void find() {
        System.out.println("查找商品。。。");
    }
    @Override
    public String delete() {
        System.out.println("删除商品。。。");
        return "zhangsan";
    }
}

(2)切面类MyAspectXML:

public class MyAspectXML {
    
    /**
     * 前置通知
     */
    public void checkPre(JoinPoint joinPoint){
        //传入此参数可以打印切入点信息
        System.out.println("权限校验=======" + joinPoint);
    }
    
    /**
     * 后置通知
     */
    public void writeLog(Object result){
        //传入此参数可以打印切入点信息
        System.out.println("日志记录=======" + result);
    }
    
    /**
     * 性能监控
     * @throws Throwable 
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        //传入此参数可以打印切入点信息
        System.out.println("环绕前通知=======");
        //相当于执行目标程序,并得到一个返回值
        Object object = joinPoint.proceed();
        System.out.println("环绕后通知=======");
        return object;
    }
    
    /**
     * 异常抛出通知
     */
    public void afterThrowing(Throwable ex){
        //传参的目的是获得异常抛出信息
        System.out.println("异常抛出通知========" + ex);
    }
    
    /**
     * 最终通知
     */
    public void after(){
        //传参的目的是获得异常抛出信息
        System.out.println("最终通知========");
    }
}

(3)applicationContext.xml:

  <!-- 配置目标对象:被增强的对象 -->
    <bean id="ProductDAO" class="com.ztt.spring.demo1.ProductDAOImpl"/>
    
    <!-- 将切面类交给Spring管理 -->
    <bean id="MyAspectXML" class="com.ztt.spring.demo1.MyAspectXML"/>
    
    <!-- 通过AOP的配置完成对目标类产生代理 -->
    <aop:config>
        <!-- 切入点,通过表达式配置哪些类的哪些方法需要增强 -->
        <aop:pointcutexpression="execution(* com.ztt.spring.demo1.ProductDAOImpl.save(..))" id="pointCut1"/>
        <aop:pointcut expression="execution(* com.ztt.spring.demo1.ProductDAOImpl.delete(..))" id="pointCut2"/>
        <aop:pointcut expression="execution(* com.ztt.spring.demo1.ProductDAOImpl.update(..))" id="pointCut3"/>
        <aop:pointcut expression="execution(* com.ztt.spring.demo1.ProductDAOImpl.find(..))" id="pointCut4"/>
        <!-- 配置切面 -->
        <aop:aspectref="MyAspectXML">
            <!-- 前置通知=========== -->
            <aop:beforemethod="checkPre" pointcut-ref="pointCut1"/>
            <!-- 后置通知=========== -->
            <aop:after-returningmethod="writeLog" pointcut-ref="pointCut2" returning="result"/>
            <!-- 环绕通知=========== -->
            <aop:around method="around" pointcut-ref="pointCut3"/>
            <!-- 异常抛出通知========= -->
            <aop:after-throwingmethod="afterThrowing" pointcut-ref="pointCut4" throwing="ex"/>
            <!-- 最终通知 -->
            <aop:aftermethod="after" pointcut-ref="pointCut4"/>
        </aop:aspect>
    </aop:config>

 总结:

  1)使用<aop:config>进行配置:proxy-target-class=“true”时使用cglib代理,如果不声明,Spring会自动选择JDK代理。

  2)<aop:pointcut>切入点,配置哪些类的哪些方法需要增强。

  3)<aop:advisor>特殊的切面,只有一个通知和一个切入点。

  4)切入点表达式:基于execution 函数完成。   

          语法:[访问修饰符]  方法返回值  包名.类名.方法名(参数)                 

          【注】访问修饰符可不写,*表示任意返回值/包名/类名等,..表示任意返回值

          例    : public  void com.ztt.spring.ProductDAOImpl.save(..)

          例    : * *.*.*.*DAO.save(..)

  

  5)Spring AOP的具体加载步骤

  1. 当 spring 容器启动的时候,加载了 spring 的配置文件。

  2. 为配置文件中的所有bean创建对象。

  3. Spring容器会解析aop:config的配置。

    解析切入点表达式,用切入点表达式和纳入Spring容器中的bean做匹配,如果匹配成功,则会为该bean创建代理对象,代理对象的方法=目标方法+通知;如果匹配不成功,则不会创建代理对象。

  4. 在客户端利用context.getBean()获取对象时,如果该对象有代理对象,则返回代理对象,否则返回目标对象。

6.3 Spring的AOP基于注解开发   

配置文件中只需要声明bean,不用进行AOP声明。配置文件中需要配置扫描注解类。配置文件中还需要配置AOP注解识别。

 (1)目标对象还是上面的ProductDAOImpl。

(2)切面类MyAspectAnno:

@Aspect
public class MyAspectAnno {

    @Before(value = "MyAspectAnno.pointcut_save()")
    public void checkPre(){
        System.out.println("前置权限校验=======");
    }

    @AfterReturning(value="MyAspectAnno.pointcut_delete()",returning="result")
    public void writeLog(Object result){
        //传入此参数可以打印切入点信息
        System.out.println("后置日志记录=======" + result);
    }

    @Around(value="MyAspectAnno.pointcut_update()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
        //传入此参数可以打印切入点信息
        System.out.println("环绕前通知=======");
        //相当于执行目标程序,并得到一个返回值
        Object object = joinPoint.proceed();
        System.out.println("环绕后通知=======");
        return object;
    }

    @AfterThrowing(value="MyAspectAnno.pointcut_find()",throwing="ex")
    public void afterThrowing(Throwable ex){
        //传参的目的是获得异常抛出信息
        System.out.println("异常抛出通知========" + ex);
    }

    @After(value="MyAspectAnno.pointcut_find()")
    public void after(){
        System.out.println("最终通知========");
    }
    
    //切入点注解:
    @Pointcut(value="execution(* com.ztt.spring.demo1.ProductDAO.find(..))")
    private void pointcut_find(){}
    @Pointcut(value="execution(* com.ztt.spring.demo1.ProductDAO.update(..))")
    private void pointcut_update(){}
    @Pointcut(value="execution(* com.ztt.spring.demo1.ProductDAO.delete(..))")
    private void pointcut_delete(){}
    @Pointcut(value="execution(* com.ztt.spring.demo1.ProductDAO.save(..))")
private void pointcut_save(){}
}

(2)applicationContext.xml: 配置文件中只需配置AOP注解识别<aop:aspectj-autoproxy/>  和扫描类。

    <!-- 开启AOP注解的自动代理 -->
    <aop:aspectj-autoproxy/>
    
    <!-- 配置目标类 -->
    <bean id="ProductDAO" class="com.ztt.spring.demo1.ProductDAO"></bean>
    
    <!-- 配置切面类 -->
    <bean id="MyAspectAnno" class="com.ztt.spring.demo1.MyAspectAnno"></bean>

7.  Spring的事务管理    

 7.1 事务的概念和特性    

 什么是事务?

  事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态。

  比如向数据库插入多条数据,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。这样可以防止出现脏数据,防止数据库数据出现问题。

事务的四大特征?

  原子性:事务不可分割。事务的原子性确保动作要么全部完成,要么完全不起作用。

  一致性:事务执行前后数据完整性保持一致。一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。

  隔离性:一个事务的执行不应该受到其他事务的干扰。(可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。)

  持久性:一旦事务结束,数据就持久化到数据库。(一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。)

如果不考虑隔离性引发安全性问题?

1、 读问题

  • 脏读                :一个事务读到另一个事务未提交的数据。
  • 不可重复读     :一个事务读到另一个事务已经提交的 update 的数据,导致一个事务中多次查询结果不一致。
  • 虚读、幻读     :一个事务读到另一个事务已经提交的 insert 的数据,导致一个事务中多次查询结果不一致。

2、写问题

  • 丢失更新

解决读问题:设置事务的隔离级别:

  • Read uncommitted :未提交读,任何读问题解决不了。
  • Read committed      :已提交读,解决脏读,但是不可重复读和虚读有可能发生。
  • Repeatable read      :重复读,解决脏读和不可重复读,但是虚读有可能发生
  • Serializable                :解决所有读问题

7.2 介绍Spring的事务管理    

Spring的事务管理的API:

1、PlatformTransactionManager:平台事务管理器

  平台事务管理器:接口,是Spring用于管理事务的真正的对象。

    DataSourceTransactionManager  :底层使用JDBC管理事务

    HibernateTransactionManager       :底层使用Hibernate管理事务

2、TransactionDefinition   :事务定义信息

  事务定义:用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读

3、TransactionStatus:事务的状态

  事务状态:用于记录在事务管理过程中,事务的状态的对象。

事务管理的API的关系:

  Spring进行事务管理的时候,首先平台事务管理器(PlatformTransactionManager根据事务定义信息(TransactionDefinition)进行事务的管理,在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态(TranscationStatus)的对象中。

 

 

l  Spring中提供了七种事务的传播行为:

n  保证多个操作在同一个事务中

u  PROPAGATION_REQUIRED   默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来

u  PROPAGATION_SUPPORTS   支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务

u  PROPAGATION_MANDATORY  如果A中有事务,使用A中的事务。如果A没有事务,抛出异常

n  保证多个操作不在同一个事务中

u  PROPAGATION_REQUIRES_NEW  如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作

u  PROPAGATION_NOT_SUPPORTED 如果A中有事务,将A的事务挂起。不使用事务管理

u  PROPAGATION_NEVER     如果A中有事务,报异常

n  嵌套式事务

 

u  PROPAGATION_NESTED     嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。

spring的事务管理机制,一般是使用TransactionMananger进行管理,可以通过Spring的注入来完成此功能。spring提供了几个关于事务处理的类:

  TransactionDefinition      :事务属性定义

  TranscationStatus            :代表了当前的事务,可以提交,回滚。

  PlatformTransactionManager :这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类 AbstractPlatformTransactionManager,我们使用的事务管理类例如 DataSourceTransactionManager等都是这个类的子类。

一般事务定义步骤:

TransactionDefinition td = new TransactionDefinition();
TransactionStatus ts = transactionManager.getTransaction(td);
try{ 
    //do something
    transactionManager.commit(ts);
}catch(Exception e){
    transactionManager.rollback(ts);
}

 7.2 Spring的两类事务管理(编程式、声明式) 

 所谓编程式事务指的是通过编码方式实现事务,允许用户在代码中精确定义事务的边界。即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate

声明式事务管理是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

 7.2.1 编程式事务         (需手动写代码,了解)            

(1)applicationContext.xml

 <!-- 配置Service============= -->
   <bean id="accountService" class="com.ztt.tx.demo1.AccountServiceImpl">
     <property name="accountDao" ref="accountDao"/>
     <!-- 第三步:在业务层注入事务管理的模板 -->
     <property name="trsactionTemplate" ref="transactionTemplate"/>
   </bean>

   <!-- 配置DAO================= -->
   <bean id="accountDao" class="com.ztt.tx.demo1.AccountDaoImpl">
     <property name="dataSource" ref="dataSource"/>
   </bean>

    <!-- 配置连接池和JDBC的模板 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>    
    <!-- 配置C3P0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    
    <!-- 第一步:配置平台事务管理器============================= -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入连接池,获得连接 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!-- 第二步:配置事务管理的模板 -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>

(2)编写事务管理的代码:

public class AccountServiceImpl implements AccountService {

    // 注入DAO:
    private AccountDao accountDao;
    
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    // 注入事务管理的模板
    private TransactionTemplate trsactionTemplate;

    public void setTrsactionTemplate(TransactionTemplate trsactionTemplate) {
        this.trsactionTemplate = trsactionTemplate;
    }

    @Override
    /**
     * from:转出账号
     * to:转入账号
     * money:转账金额
     */
    public void transfer(final String from, final String to, final Double money) {
        //编写事务管理代码
        trsactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                accountDao.outMoney(from, money);
                accountDao.inMoney(to, money);
            }
        });        
    }
}

7.2.2 声明式事务管理               (通过配置实现,AOP)

(1)xml方式的声明式事务管理   

applicationContext.xml

  <!-- 第一步:配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 第二步:配置事务的增强 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 事务管理的规则 -->
            <!-- <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="find*" read-only="true"/> -->
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    
    <!-- 第三步:AOP的配置 -->
    <aop:config>
        <aop:pointcut expression="execution(* com.ztt.tx.demo2.AccountServiceImpl.*(..)))" id="pointcut1"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
    </aop:config>

(2)注解方式的声明式事务管理        

applicationContext.xml:

  <!-- 第一步:配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 第二步:开启注解事务 -->
    <tx:annotation-driventransaction-manager="transactionManager"/>

第三步:在业务层添加注解:

@Transactional
public class AccountServiceImpl implements AccountService {
    // 注入DAO:
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    /**
     * from:转出账号
     * to:转入账号
     * money:转账金额
     */
    public void transfer(String from, String to, Double money) {
        accountDao.outMoney(from, money);
        accountDao.inMoney(to, money);
    }
原文地址:https://www.cnblogs.com/toria/p/spring_study.html