Spring

Spring

IOC

IOC的理解

参考链接

Inversion of Control,控制反转、依赖注入。

  • 控制什么?

    控制对象的创建及销毁(生命周期)。

  • 反转什么?

    没有IOC容器的话,如果对象之间有依赖关系,那么被依赖的对象是在依赖他的对象在使用它的时候创建的,也就是说,被依赖的对象的控制权在依赖他的对象的手里。

    有了IOC后将对象的控制权交给IOC容器。

//奥迪车类
public class Audi {
    //使用奥迪车
    public void start() {
        System.out.println("start");
    }
    //停车
    public void stop() {
        System.out.println("stop");
    }
}
public class ZhangSan {
    //张三开奥迪车回家
    public void goHome(){
        //创建奥迪车
        Audi audi = new Audi();
        //启动
        audi.start();
        //停车
        audi.stop();
    }
    //张三开车去购物
    public void goShop() {
        //创建奥迪车
        Audi audi = new Audi();
        //启动
        audi.start();
        //停车
        audi.stop();
    }
}

张三开自己的奥迪车出行,需要自己主动创建并销毁一辆车。若是更换一辆其他的车,需要去修改代码,工程量巨大。

而且对于面向对象编程来说,有两个问题:

  • 张三需要的只是辆车,不管是不是奥迪。
  • 车辆并不应该由张三创建。

于是,做出如下修改:

  • 抽象出一个车的接口。
  • 使用构造函数创建车,让别人来创建车,比如说IOC容器。
public interface Car {
    //启动
    public void start();
    //停止
    public void stop();
}
//奥迪车类
public class Audi implements Car{
    //启动奥迪车
    @Override
    public void start() {
        System.out.println("start");
    }
    //停车
    @Override
    public void stop() {
        System.out.println("stop");
    }
}
public class ZhangSan {
    private Car car;
    public ZhangSan(Car car) {
        this.car = car;
    }
    //张三开车回家
    public void goHome() {
        //启动
        car.start();
        //停车
        car.stop();
    }
    //张三开车去购物
    public void goShop() {
        //启动
        car.start();
        //停车
        car.stop();
    }
}

IOC的实现

以下实现遵循三个约定:

  • 所有Bean的生命周期交由IOC容器管理(IOC的作用)
  • 所有被依赖的Bean通过构造方法执行注入(简化实现,具体情况要依据具体项目而定)
  • 被依赖的Bean需要优先创建

类图如下

public interface Car {
    //启动
    public void start();
    //停止
    public void stop();
}
//奥迪车类
public class Audi implements Car{
    //启动奥迪车
    @Override
    public void start() {
        System.out.println("start");
    }
    //停车
    @Override
    public void stop() {
        System.out.println("stop");
    }
}
public interface Human {
    public void goHome();
}
public abstract class HumanWithCar implements Human{    
    protected Car car;    
    public HumanWithCar(Car car) {        
        this.car = car;    
    }    
    @Override    
    public abstract void goHome();
}
public class ZhangSan extends HumanWithCar{
    public ZhangSan(Car car) {
        super(car);
    }
    //张三开车回家
    public void goHome() {
        //启动
        car.start();
        //停车
        car.stop();
    }
    //张三开车去购物
    public void goShop() {
        //启动
        car.start();
        //停车
        car.stop();
    }
}
public class LiSi extends HumanWithCar{
    public LiSi(Car car) {
        super(car);
    }

    @Override
    public void goHome() {
        car.start();
        car.stop();
    }
}
/*
1.实例化bean
2.保存bean
3.提供bean

4.每一个bean要产生一个唯一的id与之相对应
 */
public class IOCContainer {
    private Map<String, Object> beans = new ConcurrentHashMap<String, Object>();
    /**
     * 根据bean_id,获取一个Bean
     * @param bean_id
     * @return 返回bean
     */
    public Object getBean(String bean_id) {
        return beans.get(bean_id);
    }
    /**
     * 委托IOC容器创建一个Bean
     * @param clazz 要创建Bean的class
     * @param bean_id
     * @param param_bean_ids 要创建的Bean的class的构造方法所需要的参数的所有bean_id
     */
    public void setBean(Class<?> clazz, String bean_id, String... param_bean_ids) {
        //1.组装构造方法所需要的参数值
        Object[] param_values = new Object[param_bean_ids.length];
        for (int i = 0; i < param_bean_ids.length; i++) {
            param_values[i] = beans.get(param_bean_ids[i]);
        }
        //2.用反射调用构造方法实例化bean
        Object bean = null;
        for (Constructor<?> constructor : clazz.getConstructors()) {
            try {
                bean = constructor.newInstance(param_values);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        if(bean == null) {
            throw new RuntimeException("找不到一个合适的构造方法去实例化bean");
        }
        //3.将实例化的bean放入beans
        beans.put(bean_id, bean);
    }
}
public class ClassTest {
    private IOCContainer iocContainer = new IOCContainer();

    @Before
    public void before() {
        iocContainer.setBean(Audi.class, "audi");
        iocContainer.setBean(ZhangSan.class, "zhangsan", "audi");
        iocContainer.setBean(LiSi.class, "lisi", "audi");
    }

    @Test
    public void test() {
        Human zhangsan = (Human) iocContainer.getBean("zhangsan");
        zhangsan.goHome();
        Human lisi = (Human) iocContainer.getBean("lisi");
        lisi.goHome();
    }
}

Spring实例化Bean

用IDEA写Spring要注意依赖的导入!

Spring实例化Bean有三种方法:

  1. 通过构造方法实例化Bean

    • 创建一个Bean类,并写一个无参构造

      public class Bean {
          public Bean() {
              System.out.println("Bean.Bean");
          }
      }
      
    • 在resources下创建spring.xml

      <!-- 创建一个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"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
          <!-- 将一个Bean交由Spring创建并管理 -->
          <bean id="bean" class="com.ucar.Bean"></bean>
      </beans>
      
    • 编写测试函数,通过构造方法实例化Bean

      public class test {
          @Test
          public void test() {
              //获取ClassPath下的xml格式的spring上下文
              ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
              //获取Bean
              Bean bean = context.getBean("bean", Bean.class);
              System.out.println("bean = " + bean);
          }
      }
      
  2. 通过静态方法实例化Bean(工厂模式)

    • 创建一个Bean2类,并写一个无参构造

      public class Bean2 {
          public Bean2() {
              System.out.println("Bean2.Bean2");
          }
      }
      
    • 新增Bean2的工厂

      public class Bean2Factory {
          public static Bean2 getBean2() {
              return new Bean2();
          }
      }
      
    • 在spring.xml中将Bean2的工厂交由spring创建并管理

      <!-- 创建一个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"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
          <!-- 将一个Bean交由Spring创建并管理 -->
          <bean id="bean" class="com.ucar.Bean"></bean>
      
          <!-- 将Bean2的工厂交由Spring创建并管理 -->
          <bean id="bean2" class="com.ucar.Bean2Factory" factory-method="getBean2"></bean>
      </beans>
      
    • 编写测试函数,通过静态方法实例化Bean2

      public class test {
          @Test
          public void test() {
              //获取ClassPath下的xml格式的spring上下文
              ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
              //获取Bean
              Bean bean = context.getBean("bean", Bean.class);
              System.out.println("bean = " + bean);
      
              //若不用spring的话该这样实现Bean2 bean2 = Bean2Factory.getBean2();
              //若用spring则用一下方法实现(要在spring.xml中修改)
              Bean2 bean2 = context.getBean("bean2", Bean2.class);
              System.out.println("bean2 = " + bean2);
          }
      }
      
  3. 通过实例方法实例化Bean(工厂模式)

    • 创建一个Bean3类,并写一个无参构造

      public class Bean3 {
          public Bean3() {
              System.out.println("Bean3.Bean3");
          }
      }
      
    • 新增Bean3的工厂,将获取Bean3的方法改成非静态

      public class Bean3Factory {
          //取消静态声明
          public Bean3 getBean3() {
              return new Bean3();
          }
      }
      
    • 在spring.xml中实例化Bean3Factory,用工厂来创建Bean3

      <?xml version="1.0" encoding="UTF-8"?>
      <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.xsd">
          <!-- 将一个Bean交由Spring创建并管理 -->
          <bean id="bean" class="com.ucar.Bean"></bean>
      
          <!-- 将Bean2的工厂交由Spring创建并管理 -->
          <bean id="bean2" class="com.ucar.Bean2Factory" factory-method="getBean2"></bean>
      
          <!-- 实例化Bean3Factory,用工厂来创建Bean3 -->
          <bean id="bean3factory" class="com.ucar.Bean3Factory"></bean>
          <bean id="bean3" class="com.ucar.Bean3" factory-bean="bean3factory" factory-method="getBean3"></bean>
      </beans>
      
    • 编写测试函数,通过实例方法实例化Bean3

      public class test {
          @Test
          public void test() {
              //获取ClassPath下的xml格式的spring上下文
              ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
              //获取Bean
              Bean bean = context.getBean("bean", Bean.class);
              System.out.println("bean = " + bean);
      
              //若不用spring的话该这样实现Bean2 bean2 = Bean2Factory.getBean2();
              //若用spring则用一下方法实现(要在spring.xml中修改)
              Bean2 bean2 = context.getBean("bean2", Bean2.class);
              System.out.println("bean2 = " + bean2);
      
              //若不用spring的话该这样实现
              //Bean3Factory bean3Factory = new Bean3Factory();
              //Bean3 bean3 = bean3Factory.getBean3();
              //若用spring则用一下方法实现(要在spring.xml中修改)
              Bean3 bean3 = context.getBean("bean3", Bean3.class);
              System.out.println("bean3 = " + bean3);
          }
      }
      

Spring注入Bean

  1. 通过构造函数注入Bean

    • 创建一个空的AnotherBean类

      public class AnotherBean {
      }
      
    • 创建一个含两个属性的Bean类,并实现有参构造

      public class Bean {
          private AnotherBean anotherBean;
          private String string;
      
          public AnotherBean getAnotherBean() {
              return anotherBean;
          }
      
          public void setAnotherBean(AnotherBean anotherBean) {
              this.anotherBean = anotherBean;
          }
      
          public String getString() {
              return string;
          }
      
          public void setString(String string) {
              this.string = string;
          }
      
          @Override
          public String toString() {
              return "Bean{" +
                      "anotherBean=" + anotherBean +
                      ", string='" + string + '\'' +
                      '}';
          }
      
          public Bean(AnotherBean anotherBean, String string) {
              this.anotherBean = anotherBean;
              this.string = string;
          }
      }
      
    • 编写spring.xml

      由于Bean没有无参构造,所以需要添加constructor-arg,这个标签有以下几个参数:

      • index:当前这个参数是构造方法中的第几个参数
      • name:当前参数的参数名是什么
      • type:当前参数的类型是什么
      • value:适用于简单的数据类型的赋值(String,Integer,Double,Boolean)
      • ref:适用于复杂的数据类型的赋值,ref的值要听从对应的参数类型Bean的BeanId
      • index,name,type不用全写,只要通过三者组合能明确的表示出这是哪个参数就行了
      <?xml version="1.0" encoding="UTF-8"?>
      <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.xsd">
      
          <bean class="com.ucar.AnotherBean" id="anotherBean"></bean>
          <bean class="com.ucar.Bean" id="bean">
              <!-- 这个bean没有无参构造,所以需要添加constructor-arg,这个标签有以下几个参数
                   index:当前这个参数是构造方法中的第几个参数
                   name:当前参数的参数名是什么
                   type:当前参数的类型是什么
                   value:适用于简单的数据类型的赋值(String,Integer,Double,Boolean)
                   ref:适用于复杂的数据类型的赋值,ref的值要听从对应的参数类型Bean的BeanId
                   index,name,type不用全写,只要通过三者组合能明确的表示出这是哪个参数就行了-->
              <constructor-arg index="0" name="anotherBean" type="com.ucar.AnotherBean" ref="anotherBean"></constructor-arg>
              <constructor-arg index="1" name="string" type="java.lang.String" value="aaaaa"></constructor-arg>
          </bean>
      
      </beans>
      
    • 编写测试函数,通过构造函数注入Bean

      public class test {
          @Test
          public void test() {
              //获取ClassPath下的xml格式的spring上下文
              ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
              Bean bean = context.getBean("bean", Bean.class);
              System.out.println("Bean = " + bean);
          }
      }
      
  2. 通过set方法注入Bean

    通过set方法注入Bean首先在Bean类里面要有set方法

    • 创建一个空的AnotherBean类

      public class AnotherBean {
      }
      
    • 在上面的Bean中新增两个属性anotherBean1、string1,并实现set方法

      public class Bean {
          private AnotherBean anotherBean;
          private String string;
          private AnotherBean anotherBean1;
          private String string1;
      
          public AnotherBean getAnotherBean() {
              return anotherBean;
          }
      
          public void setAnotherBean(AnotherBean anotherBean) {
              this.anotherBean = anotherBean;
          }
      
          public String getString() {
              return string;
          }
      
          public void setString(String string) {
              this.string = string;
          }
      
          public AnotherBean getAnotherBean1() {
              return anotherBean1;
          }
      
          public void setAnotherBean1(AnotherBean anotherBean1) {
              this.anotherBean1 = anotherBean1;
          }
      
          public String getString1() {
              return string1;
          }
      
          public void setString1(String string1) {
              this.string1 = string1;
          }
      
          @Override
          public String toString() {
              return "Bean{" +
                      "anotherBean=" + anotherBean +
                      ", string='" + string + '\'' +
                      ", anotherBean1=" + anotherBean1 +
                      ", string1='" + string1 + '\'' +
                      '}';
          }
      
          public Bean(AnotherBean anotherBean, String string) {
              this.anotherBean = anotherBean;
              this.string = string;
          }
      }
      
    • 在之前spring.xml上用set注入两个新的属性

      <?xml version="1.0" encoding="UTF-8"?>
      <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.xsd">
      
          <bean class="com.ucar.AnotherBean" id="anotherBean"></bean>
          <bean class="com.ucar.Bean" id="bean">
              <!-- 这个bean没有无参构造,所以需要添加constructor-arg,这个标签有以下几个参数
                   index:当前这个参数是构造方法中的第几个参数
                   name:当前参数的参数名是什么
                   type:当前参数的类型是什么
                   value:适用于简单的数据类型的赋值(String,Integer,Double,Boolean)
                   ref:适用于复杂的数据类型的赋值,ref的值要听从对应的参数类型Bean的BeanId
                   index,name,type不用全写,只要通过三者组合能明确的表示出这是哪个参数就行了-->
              <constructor-arg index="0" name="anotherBean" type="com.ucar.AnotherBean" ref="anotherBean"></constructor-arg>
              <constructor-arg index="1" name="string" type="java.lang.String" value="aaaaa"></constructor-arg>
      
      		<!-- 用set方法注入两个新的属性 -->
              <property name="anotherBean1" ref="anotherBean"></property>
              <property name="string1" value="bbbbb"></property>
          </bean>
      </beans>
      
    • 编写测试函数,通过构造函数注入Bean

      public class test {
          @Test
          public void test() {
              //获取ClassPath下的xml格式的spring上下文
              ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
              Bean bean = context.getBean("bean", Bean.class);
              System.out.println("Bean = " + bean);
          }
      }
      
  3. 构造函数注入和set注入的简便写法

    简便写法是将spring.xml中的constructor-arg缩写为c,property缩写为p,要在头新增

    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:p="http://www.springframework.org/schema/p"
    

    spring.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:c="http://www.springframework.org/schema/c"
           xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean class="com.ucar.AnotherBean" id="anotherBean"></bean>
    <!--    <bean class="com.ucar.Bean" id="bean">-->
    <!--        &lt;!&ndash; 这个bean没有无参构造,所以需要添加constructor-arg,这个标签有以下几个参数-->
    <!--             index:当前这个参数是构造方法中的第几个参数-->
    <!--             name:当前参数的参数名是什么-->
    <!--             type:当前参数的类型是什么-->
    <!--             value:适用于简单的数据类型的赋值(String,Integer,Double,Boolean)-->
    <!--             ref:适用于复杂的数据类型的赋值,ref的值要听从对应的参数类型Bean的BeanId-->
    <!--             index,name,type不用全写,只要通过三者组合能明确的表示出这是哪个参数就行了&ndash;&gt;-->
    <!--        <constructor-arg index="0" name="anotherBean" type="com.ucar.AnotherBean" ref="anotherBean"></constructor-arg>-->
    <!--        <constructor-arg index="1" name="string" type="java.lang.String" value="aaaaa"></constructor-arg>-->
    
    <!--        &lt;!&ndash; 用set方法注入两个新的属性 &ndash;&gt;-->
    <!--        <property name="anotherBean1" ref="anotherBean"></property>-->
    <!--        <property name="string1" value="bbbbb"></property>-->
    <!--    </bean>-->
    
        <bean class="com.ucar.Bean" id="bean"
              c:_0-ref="anotherBean" c:string="ccccc"
              p:anotherBean1-ref="anotherBean" p:string1="ddddd"></bean>
    
    </beans>
    
  4. 集合类Bean的注入

    • list
    • set
    • map
    • properties
  5. null值注入

  6. 注入时创建内部Bean

Bean作用域

不规定的话,默认为Singleton

  1. Singleton作用域(单例模式)

    每次像spring上下文去请求Bean实例的时候,spring都会返回同一个实例,在spring的整个生命周期中都只存在一个实例,不用的spring上下文有不同的实例。

  2. prototype作用域(多例模式)

    每次像spring上下文去请求Bean实例的时候,spring都会返回不同的实例。不用的spring上下文有不同的实例。

  3. Web环境作用域

    • request作用域

      每个request请求都会创建一个单独的实例。

    • session作用域

      每个session请求都会创建一个单独的实例

    • application作用域

      每个application请求都会创建一个单独的实例

    • websocket作用域

  4. 自定义作用域

AOP

AOP的理解

  • AOP:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • 主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等
  • 切面:一个关注点的模块化,这个关注点可能会横切多个对象
  • 连接点:程序执行过程中的某个特定的点(方法)
  • 通知:在切面的某个特定的连接点上执行的动作
  • 切入点:匹配连接点的断言,在AOP中通知和一个切入点表达式关联
  • 引入:在不修改类代码的前提下,为类添加新的方法和属性
  • 目标对象:被一个或多个切面所通知的对象
  • AOP代理:AOP框架创建的对象,用来实现切面契约(包括通知方法执行等功能)
  • 织入:把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入

Advice的类型

  • 前置通知:在某连接点之前执行的通知,但不能阻止连接点前的执行(除非它抛出一个异常)
  • 返回后通知:在某连接点正常完成后执行的通知
  • 抛出异常后通知:在方法抛出异常退出时执行的通知
  • 后通知:当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
  • 环绕通知:包围一个连接点的通知

Spring框架中AOP的用途

  • 提供了声明式的企业服务,特别是EJB的替代服务的声明
  • 允许用户定制自己的方面,以完成OOP与AOP的互补使用

Spring事务管理

事务的特性

  • 原子性

    事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生

  • 一致性

    事务前后数据的完整性必须保持一致

  • 隔离性

    多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离

  • 持久性

    一个事务一旦被提交,它对数据库中数据的改变就是永久性的,即使数据库发生故障也不应该对其有任何影响

Spring事务管理的一组API

  • 事务管理器(PlatformTransactionManager)

    Spring为不同的持久化框架提供了不同的PlatformTransactionManager接口实现

  • 事务定义信息(TransactionDefinition):隔离、传播、超时、只读
    • 隔离
      • 脏读

        一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的

      • 不可重复读

        在同一事务中,多次读取同一数据返回的结果有所不同

      • 幻读

        一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。

      • DEFAULT

        使用后端数据库默认的隔离级别(Spring中的选择项)

      • READ_UNCOMMITED

        允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读

      • READ_COMMITED

        允许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可发生

      • REPEATABLE_READ

        对相同字段的多次读取时一致的,除非数据被事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生

      • SERIALIZABLE

        完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的

    • 传播

      • PROPAGATION_REQUIRED

        支持当前事务,如果不存在,就新建一个

      • PROPAGATION_SUPPORTS

        支持当前事务,如果不存在,就不使用事务

      • PROPAGATION_MANDATORY

        支持当前事务,如果不存在,抛出异常

      • PROPAGATION_REQUIRES_NEW

        如果有事务存在,挂起当前事务,创建一个新的事务

      • PROPAGATION_NOT_SUPPORTED

        以非事务方式运行,如果有事务存在,挂起当前事务

      • PROPAGATION_NEVER

        以非事务方式运行,如果有事务存在,抛出异常

      • PROPAGATION_NESTED

        如果当前事务存在,则嵌套事务执行

  • 事务具体运行状态(TransactionStatus)

Spring的编程式事务管理

<!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" file-encoding="utf-8"/>
    <!-- 配置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>
<!-- 配置DAO -->
    <bean id="accountDao" class="com.fsz.AccountDaoTmpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
<!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

<!-- 编程式事务管理 -->
<!-- 配置业务层 -->
    <bean id="accountService" class="com.fsz.AccountServiceTmpl">
        <property name="accountDao" ref="accountDao"/>
        <!-- 注入事务管理器模板 -->
        <property name="transactionTemplate" ref="transactionTemplate"/>
    </bean>

    <!-- 配置事务管理的模板 -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
    </bean>
public class AccountServiceTmpl implements AccountService{
    //注入转账DAO
    private AccountDao accountDao;

    //注入事务管理的模板
    private TransactionTemplate transactionTemplate;

    public AccountDao getAccountDao() {
        return accountDao;
    }

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

    public TransactionTemplate getTransactionTemplate() {
        return transactionTemplate;
    }
    
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    @Override
    public void transfer(String out, String in, Double money) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                accountDao.outMoney(out, money);
                int i = 1/0;
                accountDao.inMoney(in, money);
            }
        });
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class Test2 {

    @Resource(name = "accountService")
    private AccountService accountService;

    @Test
    public void Test2() {
        accountService.transfer("aaa", "bbb", 200d);
    }
}

Spring的声明式事务管理

基于AOP的思想

  • 基于TransactionProxyFactoryBean

需要为每个进行事务管理的类,配置一个TransactionProxyFactoryBean进行增强

<!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" file-encoding="utf-8"/>
    <!-- 配置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>
<!-- 配置DAO -->
    <bean id="accountDao" class="com.fsz.AccountDaoTmpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

<!-- 声明式事务管理 -->
    <!-- 配置业务层的代理 -->
    <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 配置目标对象 -->
        <property name="target" ref="accountService"/>
        <!-- 注入事务管理器 -->
        <property name="transactionManager" ref="transactionManager"/>
        <!-- 注入事务属性 -->
        <property name="transactionAttributes">
            <props>
                <!-- prop的格式:
                        PROPAGATION :事务的传播行为
                        ISOLATION   :事务的隔离级别
                        readOnly    :只读
                        -Exception  :发生哪些异常回滚事务
                        +Exception  :发生哪些异常不会滚事务-->
                <prop key="transfer">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
public class AccountServiceTmpl implements AccountService{
    //注入转账DAO
    private AccountDao accountDao;

    public AccountDao getAccountDao() {
        return accountDao;
    }

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

    @Override
    public void transfer(String out, String in, Double money) {
        accountDao.outMoney(out, money);
        //int i = 1/0;
        accountDao.inMoney(in, money);
    }
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class Test2 {

    //注入代理类,因为代理类进行增强的操作
    @Resource(name = "accountServiceProxy")
    private AccountService accountService;

    @Test
    public void Test2() {
        accountService.transfer("aaa", "bbb", 200d);
    }
}
  • 基于AspectJ的XML

    xml里面代码更加清晰,一旦配置好后,业务层类上不需要增加代码

  • 基于注解

    用注解做事务管理只要在xml里面开启注解事务,然后在需要用到事务的类上打上@Transactional注解就行了
    在使用的时候配置特别简单,但是要在业务层的类上增加注解

<!-- 引入外部属性文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" file-encoding="utf-8"/>
    <!-- 配置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="accountService" class="com.fsz.AccountServiceTmpl">
        <property name="accountDao" ref="accountDao"/>
<!--        <property name="transactionTemplate" ref="transactionTemplate"/>-->
    </bean>

    <!-- 配置DAO -->
    <bean id="accountDao" class="com.fsz.AccountDaoTmpl">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 开启注解事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>


//事务注解
@Transactional
public class AccountServiceTmpl implements AccountService{
    //注入转账DAO
    private AccountDao accountDao;

    public AccountDao getAccountDao() {
        return accountDao;
    }

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

    @Override
    public void transfer(String out, String in, Double money) {
        accountDao.outMoney(out, money);
        int i = 1/0;
        accountDao.inMoney(in, money);
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值