zoukankan      html  css  js  c++  java
  • 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);
        }
  • 相关阅读:
    Ubuntu 16.04出现:Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/...
    Ubuntu 16.04出现:Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/...
    Ubuntu 16.04出现:Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/...
    Ubuntu 16.04出现:Problem executing scripts APT::Update::Post-Invoke-Success 'if /usr/bin/test -w /var/...
    eclipse c++ 经常使用快捷键
    eclipse c++ 经常使用快捷键
    eclipse c++ 经常使用快捷键
    eclipse c++ 经常使用快捷键
    Java集合的遍历方式
    阿里五年老将的创业故事
  • 原文地址:https://www.cnblogs.com/toria/p/spring_study.html
Copyright © 2011-2022 走看看