spring框架

spring的概述

spring是什么?

  • spring是全栈轻量级开源框架,以IOC(Inverse of Control:反转控制)和AOP(aspect Oriented Programming:面向切面编程)为内核,提供了表现层Spring MVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的java EE企业应用开源框架。

spring的两大核心

IOC

​ IOC是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

AOP

​ 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

spring的发展历程和优势

  • Spring的发展历程

  • Spring的优势
    • 方便解耦,简化开发
      • 通过Spring提供的IOC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单调模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用
    • AOP编程的支持
      • 通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付
    • 声明式事务的支持
      • 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量
    • 方便程序的测试
      • 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情
    • 方便集成各种优秀的框架
      • Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts,Hibernate,Hessian,Quartz等)的直接使用
    • 降低JavaEE API的使用难度
      • Spring对JavaEE API(如JDBC,JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
    • Java源码是经典学习案例
      • Spring的源代码设计精妙,结构清晰,匠心独用,处处体现着大师对java设计模式灵活运用以及对java技术的高深造诣。它的源代码无意是java技术的最佳实践的范例。

spring的体系结构

程序的耦合以及解耦

之前案例中的问题

  • 程序的耦合
  •  耦合:程序间的依赖关系
      *      包括:
              *              类之间的依赖
              *              方法之间的依赖
    
  •  解耦:
      *          降低程序间的依赖关系
    
  •  实际开发中:
      *          应该做到:编译期不依赖,运行期才依赖
    
  •  解耦的思路
      *          1.使用反射来创建对象,而避免使用new关键字
      *          2.通过读取配置文件来获取要创建的对象全限定类名
    

工厂模式解耦

  • 一个创建Bean对象的工厂
  • bean:在计算机英语中,有可重用组件的含义
  • javabean:用java语言写的可重用组件
    • javabean > 实体类
    • 它就是创建我们的service和dao对象
    • 1.需要一个配置文件来配置我们的service和dao
    • 配置的内容:
      • 唯一标识=全限定类名(key=value)
    • 2.通过读取配置文件中配置的内容反射创建对象
    • 配置文件:
      •          xml获取properties
        

IOC的概念和soring中的IOC

IOC的概念

  • 传统应用程序示意图

img

  • 有IoC/DI容器后程序结构示意图
  • 由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了

spring中的IOC

获取spring的IOC容器,并根据id获取对象

//1.获取核心容器对象
ApplicationContext ac =new ClassPathXmlApplicationContext("bean.xml");
//2.根据id获取bean对象
IAccountService as = (IAccountService) ac.getBean("accountService");
IAccountDao adao=ac.getBean("accountDao",IAccountDao.class);

ApplicationContext的三个常用实现类

  • ClassPathXmlApplicationContext
    • 它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(xml更常用)
  • FileSystemXmlApplicationContext
    • 它可以加载磁盘任意路径下的配置文件(必须有访问权限)
  • AnntationConfigApplicationContext
    • 它是用于读取注解创建容器的

核心容器的两个接口引发出的问题

  • ApplicationContext 单例对象适用
    • 它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只有一读取完配置文件马上就创建配置文件中配置的对象。
  • BeanFactory 多例对象适用
    • 它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。

spring对bean的管理细节

     #### 创建bean的三种方式
  • 第一种:使用默认构造函数创建。

    • 在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。

    • 采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。

    • <bean id="accountService" class="service.impl.AccountServiceImpl"></bean>
      
  • 第二种:使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并入spring'容器)

    • <bean id="instanceFactory"  class="factory.InstanceFactory"></bean>
      <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
      
  • 第三种:使用工厂中的静态方法创建对象(使用某个类中静态方法创建对象,存入spring容器)

    • <bean id="accountService" class="factory.StaticFactory" factory-method="getAccountService"></bean>
      
       #### bean对象的作用范围
      
  • bean标签scope属性:

    • 作用:用于指定bean的作用范围

      • 取值:常用的就是单例的和多例的

        • singleton:单例的(默认值)

        • prototype:多例的

        • request:作用与web应用的请求范围

        • session:作用于web应用的会话范围

        • global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session

          bean对象的作用周期

  • 单例对象

    • 出生:当容器创建时对象出生
    • 活着:只要容器还在对象一直活着
    • 死亡:容器销毁对象消亡
    • 总结:单例对象生命周期和容器相关
  • 多例对象

    • 出生:当我们使用对象时,spring框架为我们创建
    • 活着:对象在使用过程中就一直活着。
    • 死亡:当对象长时间不用且没有别的对象引用时由java的垃圾回收器回收

spring中基于XML的IOC环境搭建

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">

java代码

	//1.获取核心容器对象
     ApplicationContext ac =new ClassPathXmlApplicationContext("bean.xml");
    //2.根据id获取bean对象
     IAccountService as = (IAccountService) ac.getBean("accountService");

依赖注入(Dependency Injection)

依赖注入(Dependency Injection)

IOC的作用

降低程序间的耦合(依赖关系)

依赖关系的管理

  • 以后都交给spring来维护
  • 在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明依赖关系的维护,就称之为依赖注入。

依赖注入

能注入的数据有三类

  • 基本类型和string

  • 其他bean类型(在配置文件中或者注解配置过的bean)

  • 复杂类型/集合类型

    注入的方式有三种

  • 使用构造函数提供

    • 使用的标签:constructor-arg
    • 标签出现的位置:bean标签的内部
    • 标签中的属性:
      • type:用于指定要注入的数据和数据类型,该数据类型也是构造函数中某个或某些参数的类型
      • index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。参数索引的位置是从0开始
      • name:用于指定给构造函数中指定名称的参数赋值 (常用)
        以上三个用于指定给构造函数中那个参数赋值===
      • value:用于提供基本类型和string类型的数据
      • ref:用于指定其他的bean类型数据。它指的就是在spring的IOC核心容器中出现过的bean对象
    • 优势:
      • 在获取bean对象时,注入数据时必须的操作,否则对象无法创建成功
    • 弊端
      • 改变了bean对象的实例化方式,使我们在创建对象时如果用不到这些数据,也必须提供
<bean id="accountService" class="service.impl.AccountServiceImpl">
    <constructor-arg name="name" value="刘某人"></constructor-arg>
    <constructor-arg name="age" value="18"></constructor-arg>
    <constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>


<!--配置一个日期对象-->
<bean id="now" class="java.util.Date">

</bean>
  • 使用set方法提供
    • 涉及的标签:property
    • 出现的位置:bean标签内部
    • 标签的属性:
      • name:用于指定注入时所调用的set方法名称
      • value:用于提供基本类型和string类型的数据
      • ref:用于指定其他的bean类型数据。它指的就是在spring的IOC核心容器中出现过的bean
    • 优势
      • 创建对象时没有明确的限制,可以直接使用默认构造函数
    • 弊端
      • 如果有某个必须有值,则获取对象时set方法没有执行
<bean id="accountService2" class="service.impl.AccountServiceImpl2">
    <property name="age" value="18"></property>
    <property name="name" value="矻矻"></property>
    <property name="birthday" ref="now"></property>
</bean>
  • 复杂类型的注入/集合类型的注入
    • 用于给list结构集合注入的标签:
      • list array set
    • 用于给map结构集合注入的标签:
      • map props
    • 结构相同,标签可以通用
<bean id="accountService3" class="service.impl.AccountServiceImpl3">
    <property name="myStrs">
        <array>
            <value>aaa</value>
            <value>bb</value>
            <value>c</value>
        </array>
    </property>
    <property name="myList">
        <list>
            <value>aaa</value>
            <value>bb</value>
            <value>c</value>
        </list>
    </property>
    <property name="myMap">
        <props>
            <prop key="testc">ccc</prop>
        </props>
    </property>
    <property name="mySet">
        <set>
            <value>aaa</value>
            <value>bb</value>
            <value>c</value>
        </set>
    </property>
    <property name="myProps">
        <map>
            <entry key="testa" value="aaa"></entry>
            <entry key="testb">
                <value>bbb</value>
            </entry>
        </map>
    </property>
</bean>
  • 使用注解提供

IOC注解

  • 用于创建对象的
    • 他们的作用就和在xml配置文件中编写一个标签实现的功能是一样的
    • Component:
      • 作用:用于把当前类对象存入spring容器中
      • 属性:
        • value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改为小写
    • Controller:一般用在表现层
    • Service:一般用在业务层
    • Repository;一般用在持久层
    • 以上三个注解,他们的作用和属性于Component是一模一样的。
    • 他们三个是spring框架为我们提供明确的三层使用的,使我们的三层对象更加清晰
  • 用于注入数据的
    • 他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的
    • Autowired:
      • 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配
        • 就可以注入成功,如果容器中没有任何bean的类型和要注入的类型匹配则报错
        • 如果容器中有多个类型匹配时:
        • 则先找出类型一致的,再根据名称来找
        • 如果没有一样的名称择报错
      • 出现位置:
        • 可以是变量上,也可以是方法上
      • 细节:
        • 在使用注解注入时,set方法就不是必须的了
    • Qualifier:
      • 作用:在按照类型注入的基础之上再按照名称注入。它在给类成员注入时,不能单独使用
        • 但是在给方法参数注入时可以
      • 属性:
        • value:用于指定注入bean的名称
    • Resource:
      • 作用:直接按照bean的名称注入。它可以独立使用
      • 属性:
        • name:用于指定bean的名称
    • 以上三个注解都只能注入其他bean类型的数据,而基本类型和string类型无法使用上述注解实现。
    • 另外,集合类型的注入只能通过xml来实现
    • value:
      • 作用:用于注入基本类型和string类型的数据
      • 属性:
        • value:用于指定数据的值。它还可以使用spring中SpEL(也就是spring的el表达式)
          • SpEL的写法:$
  • 用于改变作用范围的
    • 他们的作用就和在bean标签中使用scope属性实现的功能是一样的
    • Scope
      • 作用:用于指定bean的作用范围
      • 属性:
        • value:指定范围的取值。常用取值:singleton prototype
  • 生命周期相关的
    • 他们的作用就和在bean标签中使用init-method和destroy-method的作用是一样的
    • PreDestroy
      • 作用:用于指定销毁方法
    • PostConstruct
      • 作用:用于指定初始化方法

spring中的新注解:

  •  configuration
      *          作用:指定当前类是一个配置类
      *          细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可用不写
    
  •  componentScan
      *          作用:用于通过注解指定spring在创建容器时要扫描的包
      *          属性:
                  *              value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
                  *              我们使用此注解就等同于在xml中配置了:
                  *              <context:component-scan base-package="com.lai"></context:component-scan>
    
  •  bean
      *          作用:用于把当前方法的返回值作为bean对象存入spring的IOC容器中
      *          属性:
                  *              name:用于指定bean的id。当不写时默认值是当前方法的名称
      *          细节:
      *          当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象
      *          查找的方式和Autowired注解的作用是一样的
    
  •  Import
      *          作用:用于导入其他的配置类
      *          属性:
                  *              value:用于指定其他配置类的字节码
                  *              当我们使用import注解之后,有import注解的类就是父配置类,而导入的就是子配置类
    
  •  propertySource
      *          作用:用于指定properties文件的位置
      *          属性:
                  *      value:指定文件的名称和路径。
                          *      关键字:classpath,表示类路径下
    
  •  EnableTransactionManagement
      *      作用:开启spring注解事务支持
    

在junit中,在变量上写Autowired注解为什么没用

  • 应用程序入口
    • main方法
  • junit单元测试中,没用main方法也能执行
    • junit集成了一个main方法
    • 该方法就会判断当前测试类中那些方法有@Test注解
    • junit就让有Test注解的方法执行
  • junit不会管我们是否采用了spring框架
    • 在执行测试方法时,junit根本不知道我们是否使用了spring 框架
    • 所有也就不会为我们读取配置文件/配置类创建spring核心容器

所有由以上三点可知

​ 当测试方法执行时,没有IOC容器,就算写了Autowired注解,也无法实现注入

spring整合junit的配置

  •  1.导入spring整合junit的jar包(坐标)
    
  •  2.使用junit提供的一个注解把原有的main方法替换了,替换成spring提供的
      *         注解: Runwith
    
  •  3.告知spring的运行器,spring的IOC创建时基于xml还是注解的,并且说明位置
      *          注解:ContextConfiguration
      *          属性:Locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
                  *                      classes:指定注解类所在的位置
    
  •  当我们使用spring5.x版本的时候,要求junit的jar包必须是4.12及以上
    

AOP概念

动态代理

  • 特点:字节码随用随创建,随用随加载
  • 作用:不修改源码的基础上对方法增强
  • 分类:
    • 基于接口的动态代理
    • 基于子类的动态代理

基于接口的动态代理:

  • 涉及的类:proxy

  • 提供者:JDK官方

  • 如何创建代理对象:

    • 使用proxy类中的newProxyInstance方法
  • 创建代理对象的要求:

    • 被代理类最少实现一个接口,如果没有则不能使用
  • newProxyInstance方法的参数:

    • ClassLoader:类加载器
      • 它是用于加载代理对象字节码,和代理对象使用相同的类加载器。固定写法
    • Class[]:字节码数组
      • 它是用于代理对象和被代理对象有相同的方法,固定写法
    • InvocationHandler:用于提供增强的代码
      • 它是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类但不是必须的
      • 此接口的实现类都是谁用谁写
  • 作用:执行被代理对象的任何接口方法都会经过该方法

  • 方法参数的含义:

    • proxy:代理对象的引用
    • method:当前执行的方法
    • args:当前执行方法所需的参数
    • 返回值:和被代理对象方法有相同的返回值

基于子类的动态代理:

  • 涉及的类:Enhancer
  • 提供者:第三方cglib库
  • 如何创建代理对象:
    • 如何创建代理对象:
  • 创建代理对象的要求:
    • 被代理类不能是最终类
  • create方法的参数:
    • Class:指定字节码
      • 它是用于指定被代理对象的字节码
    • Callback:用于提供增强的代码
      • 它是让我们写如何代理,我们一般都是写一个该接口的实现类,通常情况下都是匿名内部类但不是必须的
      • 我们一般写的都是该接口的子接口实现类:MethodInterceptor
  • 执行被代理对象的方法都会经过该方法
    • 方法参数的含义:
      • proxy:代理对象的引用
      • method:当前执行的方法
      • args:当前执行方法所需的参数
      • methodProxy:当前执行方法的代理对象

什么事AOP

​ 简单来说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强

AOP的作用和优势

  • 作用
    • 在程序运行期间,不修改源码对己有方法进行增强
  • 优势
    • 减少重复代码
    • 提供开发效率
    • 维护方便

AOP的实现方式

使用动态代理技术

spring中的AOP

aop相关术语

  • Joinpoint(连接点):
    • 所谓连接点是指那些被拦截到的点,在spring中,这些点指的是方法,因为spring只支持方法类型的连接点
    • 连接点就是连接业务和增强方法中的那个点(就是业务层的那些方法)
  • Pointcut(切入点):
    • 所谓切入点是指我们要对那些Joinpoint进行拦截定义
    • 被增强的方法就是切入点

所有的切入点都是连接点,但不是所有的连接点都是切入点

  • Advise(通知/增强):
    • 所谓通知就是指拦截到Joinpoint之后所要做的事情就是通知
    • 通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
    • 前置通知:在切入点方法调用前的语句
    • 后置通知:在切入点方法调用后的语句
    • 异常通知:在catch中的语句
    • 最终通知:在finally中的语句
    • 环绕通知:整个invoke方法在执行就是环绕通知,在环绕通知中有明确的切入点方法调用
      • 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式
  • Introduction(引介)
    • 引介是一种特殊的通知,在不修改代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field
  • Target(目标对象):
    • 代理的目标对象
  • Weaving(织入):
    • 是指把增强应用到目标对象来创建新的代理对象的过程
    • spring采取动态代理织入,而Aspectj采用编译期织入和类装载期织入。
  • Proxy(代理):
    • 一个类被AOP织入增强后,就产生一个结果代理类
  • Aspect(切面):
    • 是切入点和通知(引介)的结合

学习spring中的AOP要明确的事

  • 开发阶段(我们做的)
    • 编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务需求。
    • 把公用代码抽取出来,制作成通知。(开发阶段最后再做);AOP编程人员来做
    • 配置文件中,声明切入点与通知间的关系,即切面:AOP编程人员来做
  • 运行阶段(spring框架完成的)
    • Spring框架监控切入点方法的执行阶段。一旦监控到切入点方法被运行,使用代理机制,动态控制创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对象的功能织入,完成完整的代码逻辑运行

Spring中基于xml的AOP配置步骤

​ 1.把通知的bean也交给spring来管理
​ 2.使用AOP:config标签表示开始AOP的配置
​ 3.使用AOP:aspect标签表明开始配置切面
​ id属性:给切面提供一个唯一标志
​ ref属性:是指定通知类bean的id
​ 4.在aop:aspect标签的内部使用对应的标签来配置通知的类型
​ 我们现在示例是让printLogger方法在切入点方法执行之前执行:所有是前置通知
​ aop:before:表示配置前置通知
​ method属性:用于指定Logger类中那个方法是前置通知
​ pointcut属性:用于执行切入点表达式,该表达式的含义指的是对业务层中那些方法增强
​ 切入点表达式的写法:
​ 关键字:execution(表达式)
​ 表达式:访问修饰符 返回值 包名.包名.包名....类名.方法名(参数列表)
​ 标准的表达式写法:public void com.lai.service.impl.AccountServiceImpl.saveAccount()
​ 访问修饰符可以省略:void com.lai.service.impl.AccountServiceImpl.saveAccount()
​ 返回值可以使用通配符,表示任意返回值
​ * com.lai.service.impl.AccountServiceImpl.saveAccount()
​ 包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*
​ * ....AccountServiceImpl.saveAccount()
​ 包名可以使用..表示当前包及其子包
​ * ..AccountServiceImpl.saveAccount()
​ 类型和方法名都可以使用
号实现通配
​ * ...()
​ 参数列表:
​ 可以直接写数据类型:
​ 基本类型直接写名称
​ 引用类型写包名.类名的方式 java.lang.String
​ * ...
(int)
​ 可以使用通配符,表示任意类型,但是必须有参数
​ * ...()
​ 可以使用..表示有无 参数均可,有参数可以是任意类型
​ * ...(..)
​ 全通配写法:
...(..)
​ 实际开发中切入表达式的通常写法
​ 切到业务层实现类下的所有方法
​ * com.lai.service.impl.
.*(..)

通知

<!-- 配置前置通知 -->
<aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>
<!-- 配置后置通知 -->
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1" ></aop:after-returning>
<!-- 配置异常通知 -->
<aop:after-throwing method="afterThorwingPrintLog" pointcut-ref="pt1"></aop:after-throwing>
<!-- 配置最终通知 -->
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>

<!-- 配置切入点表达式 ,id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容
    此标签写在aop:aspect标签内部只能当做切面使用
    它还可以写在aop:aspect标签外面,此时就变成了所有切面可用,但是它必须在aop:aspect标签之前
 -->
<aop:pointcut id="pt1" expression="execution(public void com.lai.service.impl.AccountServiceImpl.saveAccount())"/>

注解

package com.lai.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;

/**
 * 用于记录日志的工具类,它里面提供了公共的代码
 	当我们使用前置,后置,异常,最终通知注解来实现时最终通知会比后置通知先执行,所以如果要用注解最好使用环绕通知
 */
@Component("logger")
@Aspect//表示当前类是个切面类
public class Logger {
    @Pointcut("execution(* com.lai.service.impl.*.*(..))")
    private void pt1(){}

    /**
     * 前置通知
     */
    //@Before("pt1()")
    public void beforePrintLog(){
        System.out.println("前置通知:Logger类中的beforePrintLog开始记录日志了。。。");
    }
 /**
     * 后置通知
     */
    //@AfterReturning("pt1()")
    public void afterReturningPrintLog(){
        System.out.println("后置通知:Logger类中的afterReturningPrintLog开始记录日志了。。。");
    }
 /**
     * 异常通知
     */
    //@AfterThrowing("pt1()")
    public void afterThorwingPrintLog(){
        System.out.println("异常通知:Logger类中的afterThorwingPrintLog开始记录日志了。。。");
    }
 /**
     * 最终通知
     */
    //@After("pt1()")
    public void afterPrintLog(){
        System.out.println("最终通知:Logger类中的afterPrintLog开始记录日志了。。。");
    }

    /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理中的环绕通知有明确的切入点方法调用,而我们的代码中没有
     * 解决:
     *      spring框架为我们提供了一个接口:ProceedingJoinPoint 该接口有一个方法proceed。此方法就相当于明确调用切入点方法
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
     */
    @Around("pt1()")
    public Object aroundPrintLog(ProceedingJoinPoint pjd){
        Object rtValue = null;
        try {
            Object[] args = pjd.getArgs();//得到方法执行所需的参数
            System.out.println("前置");
            rtValue=pjd.proceed(args);//明确调用业务层方法(也叫切入点方法)
            System.out.println("后置");
            return rtValue;
        }catch (Throwable t){
            System.out.println("异常");
            throw new RuntimeException(t);
        }finally {
            System.out.println("最终");
        }


    }
}

事务控制

spring中基于xml的声明式事务控制配置步骤

<!--spring中基于xml的声明式事务控制配置步骤
        1.配置事务管理器
        2.配置事务的通知
            此时我们需要导入事务的约束 tx名称空间和约束,同时也需要AOP的
            使用tx:advice标签配置事务通知
                属性:
                    id;给事务通知起一个唯一标志
                    transaction-manager:给事务通知提供一个事务管理器阴影
        3.配置AOP中的通用切入点表达式
        4.建立事务通知和切入点表达式的对应关系
        5.配置事务的属性
            在事务的通知tx:advice标签的内部
    -->
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>

    </bean>

    <!--配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--配置事务的属性
        propagation:用于知道事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择,查询方法可以选择SUPPORTS
        isolation:用于指定事务隔离的级别。默认值是DEFAULT,表示使用数据库的默认隔离级别
        read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值为false,表示读写
        timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值以秒为单位
        no-rollback-for:用于指定一个异常,当产生当异常时,事务不回滚,产生其他异常时,事务回滚,没有默认值时,表示任何异常都回滚
        rollback-for:用于指定一个异常,当产生当异常时,事务回滚,产生其他异常时,事务不回滚,没有默认值,表示任何异常都回滚
        -->
        <tx:attributes>
            <tx:method name="transfer" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
        </tx:attributes>
    </tx:advice>

spring中基于注解的声明式事务控制配置步骤

<!--spring中基于注解的声明式事务控制配置步骤
     1.配置事务管理器
     2.开启spring对注解事务的支持
     3.在需要事务支持的地方使用@Transactional注解
 -->
 <!--配置事务管理器-->
 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <property name="dataSource" ref="dataSource"></property>

 </bean>
<!--开启spring对注解事务的支持-->
 <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
posted on 2021-04-13 09:13  吃草的青牛  阅读(86)  评论(0编辑  收藏  举报