JavaWeb学习笔记-part9-Spring(下)

目录

3 AOP面向切面编程

3.1 动态代理

3.2 AOP是什么东西

3.3 AOP的实现

4 Spring集成MyBatis

4.1 回顾一下MyBatis

4.2 我们需要spring做什么

4.3 整合创建项目

4.4 小贴士

5 Spring的事务处理

5.1 事务

5.2 Spring提供处理事务的统一模型

5.3 spring框架中提供的两种事务处理方案

总结

6 web项目中怎么使用容器对象


3 AOP面向切面编程

3.1 动态代理

3.1.1 代理模式:

  1. 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

  2. 假设你有a类,想要调用c类的方法,但是c类禁止a类调用它的方法,因此可以在a和c中间创建一个b代理,让b来访问c。此时,可以通过a访问b、b再访问c的过程,达到a类调用c方法的效果。

  3. 使用代理模式的作用:

    1. 功能增强:在你原有的功能上,增加了新的额外的功能

    2. 控制访问:代理类不让你直接访问目标

    3. 减少代码的重复

    4. 专注于业务逻辑代码

    5. 解耦合

  4. 实现代理的方式:

    1. 静态代理:

      代理类是自己手工实现的,自己创建一个java类,表示代理类。同时你所要代理的目标类是确定的。

      特点:实现简单、容易理解

      功能:目标方法调用、功能增强

      缺陷:当目标类增多,代理类也会成倍增加,代理类数量过多;接口中增加或修改功能,需要修改大量的目标类和代理类。

    2. 动态代理:

      在程序执行过程中,使用jdk的反射机制,创建代理对象,并动态的指定要代理的目标类。

      特点:

      在静态代理中目标类很多时,可以使用动态代理,避免静态代理的缺点。

      动态代理中目标类即使很多,代理类数量却可以很少,当你修改接口中的方式时,不会影响代理类。

  5. 动态代理的实现分类:

    1. jdk动态代理:使用java反射包中的类和接口实现动态代理的功能。必须实现接口。

    2. cglib动态代理:是第三方的工具库,创建代理对象。

      cglib的原理是继承,cglib通过继承目标类,创建它的子类,在子类中重写父类中的同名方法,实现功能的修改。因为cglib是继承,重写方法,所以要求目标类不能是final的,方法也不能是final的。

3.1.2 jdk动态代理:

  1. 反射:method类表示方法。通过Method可以执行某个方法。

  2. 实现:

    反射包java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy

    1. InvocationHandler接口(调用处理器):就一个方法invoke()

      invoke:表示代理对象要执行的功能代码。你的代理类要完成的功能就写在其中。

      怎么使用这个接口:创建类实现接口InvocationHandler;重写invoke方法,把原来静态代理中代理类要完成的功能,写在invoke中。

    2. Method类(方法):目标类中的方法。通过Method可以执行目标类方法。

    3. Proxy类:核心对象,作用是创建代理对象。

      方法:静态方法 newProxyInstance(),创建代理对象。它有三个参数

      1. ClassLoader loader类加载器,通过反射a.class,getClassLoader()获得

      2. Class<?>[] interfaces:接口,目标对象实现的接口,也用反射获得

      3. InvocationHandler h:自己写的,代理类要实现的代码

      返回值就是一个代理对象。

3.2 AOP是什么东西

3.2.1 概述

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。

AOP底层,就是采用动态代理模式实现的,采用了两种代理:JDK动态代理和CGLIB动态代理。

AOP就是动态代理的规范化,把动态代理的实现步骤、方法都定义好了,让开发人员可以使用统一的方式。

3.2.2 如何理解面向切面编程

  1. 需要分析项目功能时,找出切面

  2. 合理安排切面的执行时间(在目标方法前,还是目标方法后)

  3. 合理的安排切面执行的位置,在哪个类,哪个方法增加功能

关键术语:

  1. Aspect:切面,给你的目标类增加非业务的功能,就是切面。 特点:一般都是非业务方法,独立使用。 常见切面功能:日志,事务,统计信息,参数检查,权限验证。

  2. Advice 通知,通知表示切面功能执行的时间

  3. JoinPoint 连接点,连接业务方法和切面的位置。就是某类中的业务方法

  4. PointCut 切入点,指多个连接点方法的集合。多个方法

一个切面的三个关键要素:

  1. 切面的功能代码,切面干什么

  2. 切面的执行位置,使用PointCut表示切面执行的位置

  3. 切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后

3.3 AOP的实现

aop是一个规范,时动态的一个规范化,一个标准

aop的技术实现框架:

  1. spring:spring在内部实现了aop主要在事务处理时使用,我们项目开发中很少使用,因为比较笨重

  2. aspectJ:是eclipse的一个开源框架。spring中集成了aspectJ框架。

    aspectJ框架实现aop的两种方式:

    1. 使用xml的配置文件:配置全局事务

    2. 使用注解,我们在项目中要做aop功能,一般都使用注解。aspectJ有5种注解。

3.3.1 学习aspectJ框架

  1. 切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强) 在aspect框架中注解表示:

    1. @Before

    2. @AfterReturning

    3. @Around

    4. @AfterThrowing

    5. @After

  2. 切面执行的位置,使用的是切入点表达式。

     

    其中可以使用以下字符:

     

3.3.2 aspectJ使用步骤

  1. 新建maven项目:quick_start

  2. 加入依赖:

    1. spring依赖

    2. aspectJ依赖

    3. junit依赖

    <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
    ​
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-context</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
    ​
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
      </dependencies>
  3. 创建目标类

    package com.pjh.ba01;
    ​
    public interface SomeService {
        void doSome(String name, Integer age);
    }
    ​
    //上为接口,下为实现类
    ​
    package com.pjh.ba01;
    ​
    public class SomeServiceImpl implements SomeService{
        @Override
        public void doSome(String name, Integer age) {
            //给目标类增加一个执行时间的功能
    ​
            System.out.println("==== 目标方法doSome() ====");
        }
    }
  4. 创建切面类:普通类

    package com.pjh.ba01;
    ​
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    ​
    import java.util.Date;
    ​
    /**
     * @Aspect 是aspectJ框架中的注解。
     *         作用:表示当前类是切面类
     *         切面类:用来给业务方法增加功能的类,在这个类中有切面的功能代码
     *         位置:在类定义的上面
     */
    @Aspect
    public class MyAspect {
        /**
         * 定义方法:方式是实现切面功能的
         * 方法的定义要求:
         * 1. 公共方法public
         * 2. 方法没有返回值
         * 3. 方法名称自定义
         * 4. 方法可以有参数,也可以没有,如果有参数,参数是有规范的
         *
         * @Before 前置通知注解
         * 属性:value,是切入点表达式,表示切面的功能执行的位置。
         * 位置:在方法上面
         * 特点:
         * 1. 在目标方法之前先执行
         * 2. 不会改变目标方法的执行结果
         * 3. 不会影响目标方法的执行
         */
    ​
        @Before(value = "execution(void com.pjh.ba01.SomeServiceImpl.doSome(String,Integer))")
        public void myBefore() {
            System.out.println("==== 切面功能:输出执行时间 ====");
            System.out.println(new Date());
        }
    }
  5. 声明spring的配置文件:声明对象,将对象交给容器管理

    1. 声明目标对象

    2. 声明切面类对象

    3. 声明aspectJ框架中的自动代理生成器标签。

      自动代理生成器:用来完成代理对象的自动创建功能的。

    <?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:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
           https://www.springframework.org/schema/aop/spring-aop.xsd">
        <!-- 将对象交由spring容器统一管理 -->
        <!-- 声明目标对象 -->
        <bean id="someService" class="com.pjh.ba01.SomeServiceImpl"/>
        <!-- 声明切面对象 -->
        <bean id="myAspect" class="com.pjh.ba01.MyAspect"/>
        <!-- 声明自动代理生成器
             使用aspectJ框架内部功能,创建目标对象的代理对象。
             创建代理对象是在内存中实现的,修改目标对象的内存中的结构,创建为代理对象,
             所以目标对象就是修改后的代理对象
    ​
             aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象
         -->
        <aop:aspectj-autoproxy/>
    </beans>
  6. 创建测试类,从spring容器中获取目标对象(实际是代理对象)

    执行代理方法,实现aop的功能增强。

    @Test
    public void Test01() {
        String config = "applicationContext.xml";
        ApplicationContext context = new ClassPathXmlApplicationContext(config);
        
        //com.sun.proxy.$Proxy: jdk动态代理
        SomeService proxy = (SomeService) context.getBean("someService");
        proxy.doSome("lisi", 20);
    }
    /**
    * 输出结果如下:
    * 
    * ==== 切面功能:输出执行时间 ====
    * Sun Oct 03 16:41:02 CST 2021
    * ==== 目标方法doSome() ====
    */

3.3.3 JoinPoint参数

即通知方法的参数中可选的一项。

其表示一个要加入切面功能的业务方法。

作用是:可以在通知方法中获取方法执行时的信息,例如方法名称,方法的实参。

如果你的切面功能中需要用到方法的信息,就加入JoinPoint。

这个JoinPoint参数的值,是由框架赋予的,帮你叙事第一个位置的参数。

JoinPoint的几个方法:

  1. getSignature():获得方法的签名(定义)

  2. getArgs(): 获得方法的实参,其返回一个obj数组表示方法的所有实参。

3.3.4 五种注解

  1. @Before:前置通知

    定义要求:

    1. 公共方法

    2. 方法没有返回值

    3. 方法名称自定义

    4. 方法可以有参数,可以没有,如果有参数请见3.3.3

    属性:value,是切入点表达式,表示切面的功能执行的位置。 位置:在方法上面 特点:

    1. 在目标方法之前先执行

    2. 不会改变目标方法的执行结果

    3. 不会影响目标方法的执行

  2. @AfterReturning:后置通知

    定义要求:

    1. 公共方法

    2. 方法没有返回值

    3. 方法名称自定义

    4. 方法有参数,推荐Object,参数名自定义

    属性:

    1. value 切入点表达式

    2. returning 自定义变量,表示目标方法的返回值;自定义变量名必须和通知方法的形参名一样

    位置:在方法定义之上

    特点:

    1. 在目标方法之后执行

    2. 能够获取目标方法的返回值,可以根据这个返回值做不同的处理功能

    3. 可以修改这个返回值

  3. @Around:环绕通知

    定义格式:

    1. public

    2. 必须有一个返回值,推荐Object

    3. 方法名称自定义

    4. 方法有固定参数 ProceedingJoinPoint

    属性:value 切入点表达式

    位置:在方法定义上面

    特点:

    1. 它是功能最强的通知

    2. 它能在目标方法前后都能增强功能

    3. 控制目标方法是否被调用执行

    4. 修改原来的目标方法的执行结果,影响最后的调用结果

    相当于jdk的动态代理,InvocationHandler

    参数:ProceedingJoinPoint,继承于joinPoint,作用等同于method

    作用:执行目标方法

    返回值:就是目标方法的执行结果,可以被修改

  4. @AfterThrowing:异常通知

    定义格式:

    1. public

    2. 没有返回值

    3. 方法名称自定义

    4. 方法有一个Exception参数,如果还有就是JoinPoint

    属性:

    1. value 切入点表达式

    2. throwing 自定义变量,表示目标方法抛出的异常对象。变量名必须和方法的参数名一样

    特点:

    1. 在目标方法抛出异常时执行

    2. 可以做异常的监控程序,监控目标方法执行时是不是有异常

  5. @After:最终通知

    定义格式:

    1. public

    2. 没有返回值

    3. 方法名称自定义

    4. 方法没有参数,如果有就是JoinPoint

    属性:value 切入点表达式

    位置:在方法上面

    特点:

    1. 总是会执行

    2. 在目标方法之后执行

  6. @Pointcut:定义切入点

    五种注解有六个不是常识吗(X)

    作用:用来定义和管理切入点,如果你的项目中有多个切入点表达式时重复的,可以复用的,可以使用@Pointcut。

    属性:value 切入点表达式

    位置:在自定义方法的上面

    特点:当使用@Pointcut定义在一个方法的上面,此时这个方法的名称就是切入表达式的别名,其他通知中,value属性就可以使用这个方法名称,代替切入点表达式了,记得方法后面要加括号。

3.3.5 如果目标类不是接口的实现类(没有接口)

aspectJ会使用cglib,采用继承的方式实现动态代理。

当然有接口也可以使用cglib方式,只要目标类不是final。

方法是:

在spring的配置文件中,将<aop:aspectj-autoproxy/>改为<aop:aspectj-autoproxy proxy-target-class="true"/>

4 Spring集成MyBatis

使用IoC技术,它能把mybatis和spring集成在一起,像一个框架一样。

因为IoC能创建对象,可以把mybatis框架中的对象交给spring统一管理,开发人员从spring中获取对象。

开发人员就不用同时面对两个或者多个框架了。

4.1 回顾一下MyBatis

使用步骤:

  1. 定义dao接口,StudentDao

  2. 定义mapper文件,StudentDao.xml

  3. 定义mabatis主配置文件,mybatis.xml

  4. 创建dao的代理对象,StudentDao dao = SqlSession.getMapper(StudentDao.class);

  5. 查询数据库,List<Student> students = dao.selectStudents();

要使用dao对象,需要使用SqlSession的getMapper方法,因此我们需要:

  1. 获取SqlSession对象,需要使用SqlSessionFactory的openSession对象

  2. 创建SqlSessionFactory对象。通过读取mybatis的主配置文件,能创建SqlSessionFactory对象

需要SqlSessionFactory对象,使用Factory能获取SqlSession,有了SqlSession就能有dao,目的就是获取dao对象。Factory的创建需要读取主配置文件:

<!-- 数据库信息 -->
​
<environment id="test2">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.pwd}"/>
    </dataSource>
</environment>
​
<!-- mapper文件位置 -->
​
<mappers>
    <mapper resource="com/pjh/dao/StudentDao.xml"/>
</mappers>

由于mybatis自带的连接池不够强力,所以我们使用其他强力的连接池代替它默认的,把这个连接池也交给spring创建。

4.2 我们需要spring做什么

通过4.1的说明,我们需要让spring创建一下对象

  1. 独立的连接池类对象,使用阿里的druid连接池

  2. SqlSessionFactory对象

  3. 创建出dao对象

我们需要学习的就是创建这三个对象的语法,使用xml的bean标签。

4.3 整合创建项目

步骤:

  1. 新建maven项目

  2. 加入maven依赖

    1. spring依赖

    2. mybatis依赖

    3. mysql驱动

    4. spring事务依赖

    5. mybatis和spring集成依赖:mybatis官方提供,用来在spring项目中创建mybatis的SqlSessionFactory,dao对象的。

    <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
      </dependency>
        
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.5.RELEASE</version>
      </dependency>
    ​
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.2.5.RELEASE</version>
      </dependency>
        
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.19</version>
      </dependency>
        
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.1</version>
      </dependency>
    ​
      <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.1</version>
      </dependency>
    ​
      <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.12</version>
      </dependency>
    </dependencies>
  3. 创建实体类

  4. 创建dao接口和mapper文件

  5. 创建mybatis主配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
        <!-- 提示日志 -->
        <settings>
            <setting name="logImpl" value="STDOUT_LOGGING"/>
        </settings>
        <!-- 别名 -->
        <typeAliases>
            <!-- name:实体类所在包名 -->
            <package name="com.pjh.domain"/>
        </typeAliases>
        <!-- sql mapper的位置 -->
        <mappers>
            <package name="com.pjh.dao"/>
        </mappers>
    </configuration>
  6. 创建Service接口和实现类,属性是dao

  7. 创建spring的配置文件:声明mybatis的对象交给spring创建

    1. 数据源DataSource

    2. SqlSessionFactory

    3. Dao对象

    4. 声明自定义的service

    <?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:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    ​
        <!-- 读取配置文件 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
    ​
        <!-- 声明数据源DataSource对象,作用是连接数据库 -->
        <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
            <!-- set注入给DruidDataSource提供连接数据库的信息 -->
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
    ​
            <property name="maxActive" value="${jdbc.maxActive}"/>
            <!-- druid还有更多细分的设置项,但一般来说,设置这四项已经足够了,详情请查询druid的github wiki -->
        </bean>
    ​
        <!-- 声明mybatis提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- set注入给sqlSessionFactory提供数据源和mybatis主配置文件位置的信息 -->
            <property name="dataSource" ref="myDataSource"/>
            <property name="configLocation" value="classpath:mybatis.xml"/>
        </bean>
    ​
        <!-- 声明Dao对象,使用SqlSession的getMapper -->
        <!-- MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象 -->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!-- 指定SqlSessionFactory对象的id -->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <!-- 指定包名,包名是dao接口所在的包名 -->
            <!-- MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行一次getMapper方法,得到每个接口的dao对象 -->
            <!-- 创建好的dao对象放到spring的容器中,dao对象的默认名称为接口名首字母小写 -->
            <property name="basePackage" value="com.pjh.dao"/>
        </bean>
    ​
        <!-- 声明Service对象 -->
        <bean id="studentService" class="com.pjh.service.impl.StudentServiceImpl">
            <property name="studentDao" ref="studentDao"/>
        </bean>
    </beans>
  8. 创建测试类,获取sevice对象,通过service调用dao完成数据库的访问

4.4 小贴士

mybatis和spring整合使用时,事务自动提交,不需要再手动commit

5 Spring的事务处理

5.1 事务

什么是事务:

事务是指一组sql语句的集合,集合中有多条sql语句,可能是insert,update,select,delete,我们希望这些多个sql语句都能成功或者失败,这些sql语句的执行是一致的,作为一个整体执行。

什么时候使用事务:

当操作涉及多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作时符合要求的;在java代码中写程序,控制事务,应该放在service类的业务方法上,因为业务方法会调用多个dao方法,执行多个语句

jdbc和mybatis访问数据库怎样处理事务:

  1. jdbc:Connection.commit(); Connection.rollback();

  2. mybatis:SqlSession.commit(); SqlSession.rollback();

  3. 缺陷:

    1. 不同数据库访问技术,处理事务的对象,方法不同,需要了解不同数据库访问技术使用事务的原理

    2. 掌握多种数据库中事务的处理逻辑。什么时候提交,什么时候回滚

    3. 处理事务的多种方法

  4. 总结:多种数据库访问技术,有不同的事务处理的机制,对象,方法

怎么解决不足:

spring提供了一种处理事务的统一模型,能使用统一步骤、方法完成多种不同数据库访问技术的事务处理。

5.2 Spring提供处理事务的统一模型

声明式事务:把事务相关的资源和内容都提供给spring,spring就能处理事务提交和回滚了,几乎不用代码。

spring处理事务的模型,使用的步骤都是固定的,把事务使用的信息提供给spring就可以了

  1. 事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit和rollback

    事务管理器:是一个接口和他的众多实现类。

    接口:PlatformTransactionManager,定义了事务的重要方法commit、rollback

    实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。

    怎么使用:你需要告诉spring,你使用的哪种数据库的访问技术,在spring的配置文件种使用<bean>声明对应的实现类就可以了,例如mybaits就声明DataSourceTransactionManager。

  2. 你的事务方法需要什么样的事务,说明需要事务的类型

    说明方法需要的事务三个方面:

    1. 事务的隔离级别:4个值

      DEFAULT:MySql默认REPEATABLE_READ

      READ_UNCOMMITTED:读未提交。未解决并发问题

      READ_COMMITTED:读已提交,解决脏读,存在不可重复读与幻读

      REPEATABLE_READ:可重复读,解决脏读、不可重复读,存在幻读

      SERIALIZABLE:串行化。不存在并发问题

    2. 事务超时时间:表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。

      单位是秒,整数值,默认是-1(不设置超时时间)

    3. 事的传播行为:控制事务方式是不是有事务,是怎样的事务。

      7种传播行为,表示你的业务方法调用时,事务在方法之间时如何使用的。此处重点掌握前三个:

      PROPAGATION_REQUIRED

      指定的方法必须需要一个事务。如果当前存在事务,方法会自动加入到事务中(白嫖);如果不存在事务,方法会自己开启一个新的事务并加入其中(没白嫖到被迫用自己的,屑)。事务的启动和加入由spring控制。

      PROPAGATION_REQUIRES_NEW

      指定的方法必须需要一个事务,且必定创建一个新事务。如果当前已经存在事务,它会将旧的事务挂起,优先执行新的事务。

      PROPAGATION_SUPPORTS

      指定的方法非必须需要事务。如果当前存在事务,方法会自动加入到事务中;如果不存在事务,方法会以非事务方式执行。

      PROPAGATION_MANDATORY

      PROPAGATION_NESTED

      PROPAGATION_NEVER

      PROPAGATION_NOT_SUPPORTED

  3. 事务提交和回滚的时机:

    1. 当你的业务方法执行成功,且没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。

    2. 当你的业务方法抛出运行时异常或ERROR,spring执行回滚,调用事务管理器的rollback

    3. 当你的业务方法抛出非运行时异常,主要是受查异常时,提交事务

      受查异常:写代码时必须处理的异常。例如IOException,SQLException

5.3 spring框架中提供的两种事务处理方案

5.3.1 适合中小项目使用的注解方案

spring框架自己用aop实现给业务方法增加事务的功能,使用@Transactional注解增加事务。

@Transactional注解:

是spring框架自己的注解,放在public方法的上面,表示当前方法具有事务。可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等。

使用注解的步骤:

  1. 需要声明事务管理对象:<bean id="xx" class="DataSourceTransactionManager">

  2. 开启事务注解驱动,告诉spring框架,我要使用注解的方式管理事务

    spring使用aop机制,创建@Transactional所在类的代理对象,给方法加入事务功能

    spring给业务方法加入事务:

    在你的业务方法执行前,先开启事务,在业务方法结束后提交或者回滚事务吗,使用aop的环绕通知

    <!-- 使用spring的事务处理 -->
    <!-- 1. 声明事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 指定数据源 -->
        <property name="dataSource" ref="myDataSource"/>
    </bean>
    <!-- 2. 开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象 -->
    <!-- transaction-manager:事务管理器对象id -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
  3. 在方法上方加入@Transactional注解

    /**
    * rollback:表示发生指定的异常一定回滚
    * 处理逻辑:
    *     1. spring框架会首先检查方法抛出的异常是不是在rollbackfor的属性值中,如果在,不管是什么类型的*        异常,一定回滚
    *     2. 如果抛出异常不在rollbackfor列表中,spring会判断异常是不是RuntimeException,如果是一定
    *        回滚。
    */
    @Transactional(
            //传播方式
            propagation = Propagation.REQUIRED,
            //隔离级别
            isolation = Isolation.DEFAULT,
            //是否仅读
            readOnly = false,
            //指定异常回滚
            rollbackFor = {
                    NullPointerException.class,
                    NotEnoughException.class
            }
    )
    //或者直接使用@Transactional,使用注解的默认值
    @Override
    public void buy(Integer gid, Integer nums) {
        Sale sale = new Sale();
        sale.setGid(gid);
        sale.setNums(nums);
        saleDao.insertSale(sale);
    ​
        Goods goods = goodsDao.selectGoods(gid);
        if(goods == null) {
            throw new NullPointerException("编号 " + gid + "商品不存在");
        } else if(goods.getAmount() < nums) {
            throw new NotEnoughException("编号 " + gid + "商品不足");
        }
    ​
        Goods buyGoods = new Goods();
        buyGoods.setId(gid);
        buyGoods.setAmount(nums);
        goodsDao.updateGoods(buyGoods);
    }

5.3.2 适合大型项目使用aspectJ框架功能

在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务完全分离。

实现步骤:都是在xml配置文件中实现

  1. 加入aspectJ框架的依赖

  2. 声明事务管理器对象

  3. 声明方法需要的事务类型(配置方法的事务属性:隔离级别、传播行为、超时)

  4. 配置aop:指定哪些类需要创建代理

<!-- 使用spring的事务处理 -->
<!-- 1. 声明事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 指定数据源 -->
    <property name="dataSource" ref="myDataSource"/>
</bean>
<!-- 2. 声明业务方法的事务属性 -->
<!-- id:自定义名称,表示标签之间的配置内容的 -->
<!-- transaction-manager:事务管理器对象id -->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
    <!-- attributes:配置事务属性 -->
    <tx:attributes>
        <!-- method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性 -->
        <!--
            name:方法名称
            1. 完整方法名称,不带一包和类名
            2. 方法可以使用通配符,*表示任意字符
        -->
        <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.NullPointerException,com.pjh.exception.NotEnoughException"/>
    </tx:attributes>
</tx:advice>
<!-- 3. 配置aop -->
<aop:config>
    <!--
        配置切入点表达式:表达哪些包中的类要使用事务
        id:切入点表达式的名称,唯一值
        expression:切入点表达式,指定哪些类要使用事务,aspectJ会创建代理对象
    -->
    <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
    <!-- 
        配置增强器
        关联advice和pointcut
        advice-ref:通知
        pointcut-ref:切入点表达式的id 
    -->
    <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
</aop:config>

总结

spring的学习就暂时到此为止了,那是真的难顶,下一站springMVC

6 web项目中怎么使用容器对象

web项目在服务器上运行,tomcat一启动,项目一直运行,如果每次都要创建一次容器对象,是十分消耗资源的行为,因此spring容器对象存在一个即可。

我们将创建好的容器对象放到全局作用域ServletContext中。

实现:

使用监听器,当全局作用域对象被创建时,创建容器,存到ServletContext中

监听器的作用:

  1. 创建容器对象

  2. 将容器对象存到ServletContext

监听器可以自己创建,也可以使用框架提供好的ContextLoaderListener

使用步骤:

  1. 在web.xml中注册监听器

    <!-- 注册监听器 -->
    <!-- 监听器被创建对象后,会读取WEB-INF中的applicationContext.xml文件 -->
    <!-- 可以使用contest-param修改文件位置 -->
    <context-param>
        <!-- 需要配置的监听器对象 -->
        <param-name>contextConfigLocation</param-name>
        <!-- 指定文件路径 -->
        <param-value>classpath:spring.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
  2. 在servlet中获取容器对象

    WebApplicationContext context = null;
    //自己通过key找到全局作用域中的webApplicationContext
    //String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
    //context = (WebApplicationContext) request.getServletContext().getAttribute(key);
    ​
    //通过spring提供的工具类
    context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值