1. 1. Spring框架笔记
  2. 2. 开闭原则(Open Cloce Protocal, OCP):软件应对扩展开放,对修改关闭
  3. 3. 依赖倒转原则(Dependency Inversion Principle, DIP):
  4. 4. 控制反转 编程思想(Inversion of Control, IoC)
  5. 5. Spring框架
  6. 6. Spring的配置文件
  7. 7. Spring容器的使用
  8. 8. 启动Log4j2日志框架
  9. 9. set注入详解
  10. 10. 命名空间注入
  11. 11. util命名空间实现配置复用
  12. 12. 基于XML的自动装配
  13. 13. 引入外部properties文件
  14. 14. bean的作用域
  15. 15. bean的实例化
  16. 16. Bean的生命周期
  17. 17. Bean的循环依赖
    1. 17.0.1. Bean的三级缓存
  • 18. Spring IoC 注解式开发
    1. 18.0.1. 用于bean的注解
    2. 18.0.2. 如何在spring中使用注解
    3. 18.0.3. 选择性实例化bean
    4. 18.0.4. 负责注入的注解
    5. 18.0.5. 全注解式开发
  • 19. AOP-面向切面编程
    1. 19.0.1. AOP的常见术语
    2. 19.0.2. 切点表达式
      1. 19.0.2.1. 访问权限:
      2. 19.0.2.2. 返回值类型:
      3. 19.0.2.3. 全限定类型:
      4. 19.0.2.4. 方法名
      5. 19.0.2.5. 形式参数列表
      6. 19.0.2.6. 异常
    3. 19.0.3. 使用Spring的AOP
    4. 19.0.4. 使用注解实现AOP
    5. 19.0.5. 通用切点
      1. 19.0.5.1. 连接点
      2. 19.0.5.2. 全注解开发
      3. 19.0.5.3. 基于注解的实现
      4. 19.0.5.4. AOP编程式事务解决方案
  • 20. Spring对事务的支持(声明式事务)
    1. 20.1. Spring提供的事务管理API
    2. 20.2. 声明式事务-注解实现方式
    3. 20.3. 事务的传播(Propagation)行为
    4. 20.4. 事务的隔离级别
    5. 20.5. 事务超时
    6. 20.6. 只读事务
    7. 20.7. 异常回滚事务
    8. 20.8. 异常不回滚事务
  • Spring框架笔记

    开闭原则(Open Cloce Protocal, OCP):软件应对扩展开放,对修改关闭

    依赖倒转原则(Dependency Inversion Principle, DIP):

    * 高层模块不应该依赖低层模块,二者都应该依赖其抽象
    * 中心思想:面向接口编程,面向抽象编程
    * 目的:降低程序耦合,提高扩展性能
    

    控制反转 编程思想(Inversion of Control, IoC)

    * 不在程序中采用硬编码的方式来新建对象
    * 不在程序中采取硬编码的方式来维护对象关系
    * 由于出现较晚,没有被纳入GoF23种设计模式范围内
    * 实现方式较多,其中比较重要的有:依赖注入(Dependency Injection, DI)
    * IoC是思想,DI是实现
    * 注入方法:Set方法注入/构造方法注入
    * set注入
    
    1
    2
    3
    4
    <property name="set方法去掉set后首字母变成小写" ref="被注入的BeanID"/>
    <!--属性的首位一定要是大写,构造方法中set一定是小写-->
    <!--name后的内容仅指代setXXXXX方法中的XXXXX,并不代表任何其他意义,若name="qwq",则会调用setQwq方法-->

    * 构造方法注入
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <bean id="" class="">
    <constructor-arg index="0" ref="BeanID"/>
    <constructor-arg index="1" ref="BeanID"/>
    ......
    <!--index为第n个构造参数,从0开始计数-->
    <!--ref为已经创建的BeanID-->
    <constructor-arg name="参数名" ref=""/>
    <!--如果不写index和name,spring会尽力按照类型来进行注入-->
    </bean>

    Spring框架

    * 实现控制反转IoC,协助新建对象/维护对象关系
    * 实现IoC思想的容器
    * 八大模块:
        * Core: 基于IoC的核心,实现对Bean的管理,主要组件是BeanFactorty
        * AOP: 次核心,面向切面编程
        * Web MVC: Spring自己提供的一套MVC框架(暨SpringMVC)
        * WebFlux: 响应式Web框架
        * Web: 支持集成常见的Web框架(如struts,webwork)
        * DAO: 提供了单独的支持JDBC操作的API
        * ORM: 支持集成常见的ORM框架(如MyBatis,Hibernate)
        * Context: 提供扩展服务
    * 特点
        * 轻量
            * 非侵入式:Spring应用中对象不依赖Spring的特定类
            * 轻量:体积小
        * 控制反转:底层使用工厂模式(XML解析+工厂模式+反射机制)
        * 面向切面
        * 容器
        * 框架
    * pom.xml
        * packaging 打包方式
        * repositories 仓库地址
        * dependencies 依赖
    

    Spring的配置文件

    * 放在resources目录下的xml文件
    * 配置标签,让spring创造对象
    * bean的两个重要属性:
        * id bean的唯一标识符
        * 类的全限定类名(带包名)
    

    Spring容器的使用

    * 获取Spring容器对象:ApplicationContext ac=new ClassPathXmlApplicationContext("配置文件路径")
    * 注意:运行上一行句内代码即视为启动Spring容器,解析xml文件,并实例化其所有的Bean对象
    * 根据BeanID获取对象:ac.getBean("Bean的ID",bean类型.class); 参数2若留空,返回类型默认为Object
    * spring框架通过反射机制来创建对象→一定要保证无参构造方法存在
        * Class cz=Class.forName("完整类名")
        * Object obj=cz.newInstance()
    * 底层使用Map<String,Object存储>
    * 同一个xml文件内不可以有同id的bean,若同时加载多个xml后,存在同id的bean,后加载的bean的会覆盖先加载的bean,被覆盖的bean不会被实例化(猜测:也许是先加载完所有的bean然后再统一实例化?)
    * getBean的时候不可以虚空索敌,会报错
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!--启用Spring6-->
    <repository>
    <id>repository.spring.milestone</id>
    <name>Spring Milestone Repository</name>
    <url>https://repo.spring.io/milestone</url>
    </repository>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.0-M2</version>
    </dependency>

    启动Log4j2日志框架

    * 引入依赖
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!--引入依赖,引入impl后会自动引入core、api等-->
    <properties>
    <log4j.version>2.14.1</log4j.version>
    </properties>
    <dependencies>
    <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>${log4j.version}</version>
    </dependency>
    </dependencies>
    * 创建xml配置文件 * 在项目内记录日志信息 * 创建日志记录器对象
    1
    Logger logger=LoggerFactory.getLogger(被记录类名.class)
    * 根据不同级别输出日志
    
    1
    logger.级别("信息内容");

    set注入详解

    * 注入外部Bean:外部定义好bean,property中使用ref引用
    
    1
    2
    3
    4
    <bean id="exm1" class="bean"></bean>
    <bean id="Service" class="service">
    <property name="ServiceBean1" ref="exm1"/>
    </bean>
    * 注入内部Bean:在内部property中嵌套定义bean,不用写ref(少用)
    
    1
    2
    3
    4
    5
    <bean id="Service" class="service">
    <property name="ServiceBean1">
    <bean class="bean"></bean>
    </property>
    </bean>
    * 注入简单类型(如int,char,enum等):property中不指定ref,property指定value
    ~~Date虽然可以使用value赋值,但对于格式有着严格的要求,如Mon Feb 6 14:50:17 CST 2023,所以尽量还是用ref~~
    * 级联属性赋值:`\<property name=id.属性 value=""/\>`
    注意:如果想要使用级联属性赋值,需要编写对应id-bean的get方法
    * 注入数组:property内嵌array,使用value/ref填充
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <!--对于简单类型-->
    <bean id="array1" class="String">
    <property name="val">
    <array>
    <value>value1</value>
    <value>value2</value>
    </array>
    </property>
    </bean>

    <!--对于非简单类型,写好Set方法-->
    <bean id="r1" class="exam">
    <property name="e1" value="e11"/>
    </bean>
    <bean id="r2" class="exam">
    <property name="e2" value="e22"/>
    </bean>
    <bean id="r3" class="exam">
    <property name="e3" value="e33"/>
    </bean>
    <bean id="array2" class="notSimple">
    <property name="array">
    <array>
    <ref bean="r1"/>
    <ref bean="r2"/>
    <ref bean="r3"/>
    </array>
    </property>
    </bean>

    * 注入List/Set/Map/属性类
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    <bean id="array3" class="notSimple">
    <property name="List">
    <list>
    <value>val1</value>
    <value>val2</value>
    <value>val3</value>
    </list>
    </property>
    <!--注入Set或List,如果是简单类型,则用value,若不简单,则用bean ref-->
    <property name="Set">
    <set>
    <value>val1</value>
    <value>val2</value>
    <value>val3</value>
    </set>
    </property>
    <property name="Map">
    <map>
    <!--简单类型-->
    <entry key="" value=""/>
    <!--非简单类型-->
    <entry key-ref="" value-ref=""/>
    </map>
    </property>
    <!--Properties的key和value一定是String-->
    <property name="properties">
    <props>
    <prop key="key">value</prop>
    <prop key="pwd">pwd</prop>
    </props>
    </property>
    </bean>
    * 注入null和空字符串
    
    1
    2
    3
    4
    5
    6
    7
    8
    <property name="qwq">
    <null/> <!--此时,qwq为null-->
    </property>
    <property name="qwq" value="null"> <!--此时,qwq为"null"-->
    <property name="qwq">
    <value/> <!--此时,qwq为空字符串-->
    </property>
    <property name="qwq" value=""> <!--此时,qwq为空字符串-->
    • 注入值中存在特殊符号(<>?/)
    1. 使用实体符号代替特殊符号
      • |符号|转义字符|
      • |:-:|:-:|:-:|
      • |>|& gt;|
      • |<|& lt;|
      • |&apos;|& apos;|
      • |"|& quot;|
      • |&|& amp;|
      • |&iquest;|& iquest;|
    2. 将特殊符号放到<![CDATA[…]]>(只能用value标签,即双标记value)

    命名空间注入

    1. p命名空间注入
    * 目的:简化配置
    * 基于set注入
    * 配置文件头:xmlns:p="http://www.springframework.org/schema/p
    
    1
    <bean id="" class="" p:简单属性="" p:复杂属性-ref=""/>
    2. c命名空间注入
    * 目的:简化配置
    * 基于构造方法注入
    * 配置文件头:xmlns:p="http://www.springframework.org/schema/c
    
    1
    <bean id="" class="" c:下标="" c:下标-ref="" c:参数名=""/>

    util命名空间实现配置复用

    * 配置文件头:xmlns:p="http://www.springframework.org/schema/util
    * 更改schemaLocation:www.springframework.org/schema/util http://ww.springframework.org/schema/util/spring-util.xsd"
    
    1
    2
    3
    4
    5
    6
    7
    8
    <util:properties id="">
    <prop key="">...</prop>
    </util:properties>
    <!--在之后的property中-->
    <bean id="..." class="...">
    <property name="properties" ref="上面的id"/>
    <!--不用一条一条写里面的property了-->
    </bean>

    基于XML的自动装配

    1
    <bean id="" class="" autowire="byName"></bean>
    * 根据名字进行自动装配,底层基于set方法
    * 被自动注入的bean的id需要和set方法中的name一致(去掉set首字母小写)
    
    1
    2
    <bean class=""></bean>
    <bean id="" class="" autowire="byType"></bean>
    * 根据类型进行自动装配,底层基于set方法
    * 一个类型的对象只可以有一个
    

    引入外部properties文件

    * 配置文件头,引入context命名空间:xmlns:p="http://www.springframework.org/schema/context
    * 更改schemaLocation:www.springframework.org/schema/context http://ww.springframework.org/schema/util/spring-context.xsd"
    * 使用context:property-placeholder的location属性指定路径,从根目录开始寻址
    * resources相当于根目录
    * 使用${key}取值,保留双引号
    * 警惕系统变量乱入.
    

    bean的作用域

    * scope项:
        * Spring默认是单例的,即singleton,启动容器时即创建对象
        * 将bean的scope属性设置为prototype则为多例,此时每一次getBean则实例化一次
        * 若引用SpringMVC框架,可有request(一次请求一个bean)和session(一次会话一个值)
        * 还有其他少用scope项 如global session(portlet应用专用)\application(一个应用)\websocket(一个生命周期)\自定义
    

    bean的实例化

    * 构造方法实例化-自动调用无参构造方法
    * 简单工厂模式实例化
    
    1
    <bean id="" class="工厂类完整名" factory-method="创建的静态方法"/>
    * factory-bean实例化
    
    1
    2
    3
    4
    <bean id="" factory-bean="工厂类id" factory-method="创建的静态方法"/>
    <!--比如-->
    <bean id="factory" class="org.example.factory"/>
    <bean id="qwq" factory-bean="factory" factory-method="get"/>
    * FactoryBean接口实例化
    
    1
    2
    3
    4
    5
    6
    7
    8
    public class PersonFactoryBean implements FactoryBean<Person>{
    @override
    public Person getObject() throws Excrption(){
    return new Spring;
    }
    @override
    ......
    }
    1
    2
    <bean id="qwq" class="PersonFactoryBean"/>
    <!--通过工厂bean获取普通bean-->

    Bean的生命周期

    1. 实例化
    2. Bean属性赋值
    2.5 检查Bean是否实现了Aware相关接口,并设置相关依赖
    3. 执行“bean后处理器”的before方法
    3.5 检查Bean是否实现了InitializingBean接口,调用方法
    4. 初始化bean(xml中配置init-method="")
    5. 执行“bean后处理器”的after方法
    6. 使用bean
    6.5 检查Bean是否实现了DisposableBean接口并调用接口方法
    7. 销毁bean(xml中配置destroy-method="")
    * bean后处理器: implements BeanPostProcessor并重写其before和after方法,写完在xml中创建该bean即可
    * 一旦配置则对当前xml所有的bean都生效
    * 作用:检查Bean是否实现了某些特定的接口
    * Spring容器仅对singleton的bean进行完整的生命周期管理,其他scope只负责初始化完毕
    
    1
    2
    3
    DefaultListableBeanFactory fac=new DefaultListableBeanFactory();
    fac.registerSingleton(beanname(String),object)
    //关于如何将自己new的对象交给SpringNTR这件事

    Bean的循环依赖

    • spring仅可以自动处理set注入/singleton情况下的循环依赖问题
    • 构造注入/(singleton/prototype/Others)情况下__无法解决__
      原理:调用无参构造方法实例化对象(此时即曝光该Bean对象),然后使用set注入

    Bean的三级缓存

    private final Map<String,Object> singletonObjects 一级缓存
    private final Map<String,Object> earlysingletonObjects 二级缓存
    private final Map<String,ObjectFactory<?>> singletonFactories 三级缓存(该map集合即被称为缓存)
    key为bean的id
    value:
    一级缓存:经过注入后完整的bean对象
    二级缓存:已经被实例化,但是还没有赋值属性的bean对象
    三级缓存:单例工厂对象

    Spring IoC 注解式开发

    目的:简化XML配置
    使用方式:在配置文件中添加aop依赖,配置context命名空间,配置扫描,在类上只用注解

    1
    2
    <beans xmlns:context="http://www.springframeword.org/schema/context">
    <context:component-scan bace-package=""/>
    • 注解的开发
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      //标记注解的注解称为元注解
      //一般用于标记@Component出现的位置
      //下行表示Component注解可以出现在类和属性上
      //注解名是value时可以省略
      //参数是一个参数的数组时,大括号可以省略
      @Target(value={ElementType.TYPE, ElementType.FIELD})
      //下行表示@Component注解最终保留在class文件中,可以被反射机制读取
      @Retention(RetentionPolicy.RUNTIME)
      public @interface Component{
      //可以定义注解的参数,[传入类型 名称]
      }

    用于bean的注解

    • Component
    • Repository
    • Controller
    • Service

    如何在spring中使用注解

    • 加入aop的依赖
    • 添加context命名空间
    • 指定扫描包
    • 使用注解即可
      1
      2
      3
      <!--指定扫描包:在applicationContext.xml里写-->
      <context:component-scan base-package="被扫描包"/>
      <!--如果有多个包可以指定多个包的共同父包,但效率会下降-->

    选择性实例化bean

    • 方案1:<context:component-scan base-package=”被扫描包” use-default-filters=”false”/>
      false时让该包下所有的带有声明Bean的注解全部失效,Beans集体去世
      在此标签间可以加入(记得改为双标签):
      <context:include-filter type=”annotation” expression=”注解的完整类名,如org.springframework.stereotype.Repository”>
      让带有某注解的失效
    • 方案2:<context:component-scan base-package=”被扫描包” use-default-filters=”true”/>
      true时让该包下所有的带有声明Bean的注解全部生效
      <context:exclude-filter type=”annotation”,expression=”注解的完整类名”>
      让带有某注解的Bean生效

    负责注入的注解

    • @Value
      指定bean内参数值,只可以注入简单类型,可以在属性上,可以用在set方法上,也可以用在构造方法的形参上
    • @Autowired
      不需要指定任何属性,直接使用即可
      但对应类型的类只可以有一个实例对象,即便是接口实现/类的继承也会引起冲突
      默认为byType,可以使用在构造方法、方法、形参、属性、注解上
      UserDao->(UserDaoImplForPgSQL/UserDaoImplForMySQL),这种情况如果AutoWired注解一个UserDao对象就会引起注入冲突
      如果直接在代码中指定对象类型为xxxForMySQL这种会违反开闭原则哦
      可以注入非简单类型(ByName/ByType)
      解决方法:使用byName,@Autowired和Qualifier联合使用,在Qualifier中指定名字
    • @Qualifier
      指定注入类名字,和Autowired搭配使用
    • @Resource
      完成非简单类型注入,是JDK提供的注解
      默认byName,未指定时使用属性名作为name,如果还找不到就byType装配
      可以使用在属性和setter方法上
      一般推荐使用Resource替代Autowired
      引用注解:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      <dependency>
      <!--在spring6+中使用-->
      <groupId>jakarta.annotation</groupId>
      <artifactId>jakarta.annotation-api</artifactId>
      <version>2.1.1</version>
      <!--在spring5及以前使用-->
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
      </dependency>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public A{
      @Resource(name="")
      public String a1;
      @Resource(name="student1")
      public Student s1;
      //会注入spring容器中的student1对象
      @Resource
      public Student student2;
      //会注入student2对象,如果没有的话会byType装配
      }

    全注解式开发

    使用一个类代替配置文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Configuration
    @ComponentScan({packageA,packageB})
    public class ConfigClass{

    }
    //使用
    public void test{
    AnnotationConfigApplicationContext ac=new AnnotationConfigApplicatinContext("Configclass");
    //之后与xml注解无异处
    ac.getbean(...)

    }

    AOP-面向切面编程

    AOP:Aspect Orinted Programming,是OOP的补充和延申
    可以将和业务逻辑无关的、可复用的代码(即交叉业务)抽离出来,,形成组件
    在程序的特定地点(切面)插入
    如果不采用AOP,可能导致的问题有:

    • 部分交叉业务代码在多个业务中反复出现,并且和业务代码掺杂,修改难度较大
    • 代码者难以专注于核心代码的编写
      优点有:
    • 开发者可以专注于业务逻辑
    • 交叉业务易维护
    • 代码复用性更强

    AOP的常见术语

    • 连接点(JoinPoint):程序可以织入切面的位置
    • 切点(Pointcut):织入切面的方法
    • 通知(Advice):又被称作增强,就是具体要织入的代码体
      包括(
      前置:仅在Pointcut前 @Before,
      后置:仅在Pointcut后 @AfterReturning,
      环绕:在Pointcut前后 @Around,
      异常:Pointcut位于catch @AfterThrowing,
      最终:Pointcut位于finally语句块 @After
      )通知;
    • 切面(Aspect):切点+通知即切面
    • 织入(Weaving):把通知应用到目标位置的过程
    • 代理对象(Proxy):一个目标对象被织入通知后产生的新对象
    • 目标对象(Target):被织入通知的对象

    切点表达式

    用于定义通知往哪些方法上切入
    基本格式:

    1
    execution([访问权限] 返回值类型 [全限定类名][方法名](形参列表) [异常])
    • 必须精确到类

    访问权限:

    • 如果是public就只包括公开的方法

    返回值类型:

    • 必填,可以填*表示返回类型任意

    全限定类型:

    • 可选
    • 填写..表示当前包和子包下所有的类
    • 省略表示所有类

    方法名

    • 必填 表示所有方法 set表示所有以set开始的方法

    形式参数列表

    • 必填
    • (..,类型个数随意),(,没有参数的方法),(,有一个参数的方法),(,String)第一个参数随意,第二个String

    异常

    • 可选
    • 省略时表示所有类型

    例子:

    1
    2
    execution(public * org.example.*(..))
    //org.example类下所有的方法,参数随意

    使用Spring的AOP

    方式1:Spring+AspectJ-注解方式
    方式2:Spring+AspectJ-XML方式
    方式3:Spring-注解方式
    引入依赖:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!--依赖引入-->
    <!--context里有aop,但是无伤大雅-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.0-M2</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.0.0-M2</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.0-M2</version>
    </dependency>
    <!--xml中添加aop和context的命名空间-->

    使用注解实现AOP

    定义切面类,使用注解指定通知类型,括号内指定切点(可以使用切点表达式,也可以同一切点)

    • 将切面纳入Spring管理(通过注解/XML)
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      @Component("textAspect")
      @Aspect
      //通知spring容器这是一个切面类
      public class asp{
      @before(切点表达式)
      public void enhance(){
      System.out.println("qwq");
      }
      }

    • 开启自动代理
      1
      2
      3
      4
      5
      6
      7
      8
      <!--位于spring的配置文件中-->
      <aop:aspectj-autoproxy/>
      <!--
      有一参数,默认为false
      proxy-target-class="true" 强制所有类使用cglib动态代理
      proxy-target-class="false"接口使用jdk动态代理,反之使用cglib动态代理

      -->

    通用切点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Pointcut("切点表达式")
    //在之后就可以把所有的地方里写这个方法名了
    public void poincut(){
    //这里可以不用写代码
    }
    @After(poincut())
    public void afterPoincut(){
    //do something...
    }

    连接点

    在通知可以传入ProceedingJoinPoint类型的对象,即连接点,可以直接调用其成员方法

    • .proceed() 运行
    • .getSignature() 获取信息

    全注解开发

    之前提过一次,但是当时只有IoC全注解开发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Configuration
    @ComponentScan({packageA,packageB})
    @EnableAspectJAutoProxy(proxyTargetClass=true)
    public class ConfigClass{

    }
    //使用
    public void test{
    AnnotationConfigApplicationContext ac=new AnnotationConfigApplicatinContext("Configclass");
    //之后与xml注解无异处
    ac.getbean(...)

    }

    基于注解的实现

    1
    2
    3
    4
    5
    6
    <aop:config>
    <aop:pointcut id="?" expression="切点表达式">
    <aop:aspect ref="之前注册的切点bean名称">
    <aop:切点类型 method:"切面中的方法名称" pointcut-ref:"切点名称">
    </aop:aspect>
    </aop:config>

    AOP编程式事务解决方案

    ->满足ACID特性
    解决方案:利用AOP

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Aspect
    @Conponent("transactionAspect")
    public void Aspect{
    @Around("execution(* org.example.springtest..*(..))")
    public void aroundAdvice(ProceedingJoinPoint joinpoint){
    try{
    //开启事务
    joinpoint.proceed();
    //提交事务
    }catch(Throwable e){
    //回滚事务
    }
    }

    }

    Spring对事务的支持(声明式事务)

    前置知识:锁和隔离
    事务的四个特性(ACID)

    • Atomic 原子性 事务是最小的工作单元
    • Consistence 一致性 一个事务中的操作,要不全部执行,要不全不执行
    • Isolation 隔离性 事务和事务之间互不干扰
    • Duration 持久性 事务结束之后它带来的影响永远保存

    Spring提供的事务管理API

    接口:PlatformTransationManager
    接口的实现:

    • DataSourceTransationManager 支持JdbcTemplate,Mybatis等事务管理
    • JtaTransationManager 支持分布式事务管理

    声明式事务-注解实现方式

    • 配置xml
      命名空间:tx

      1
      2
      3
      4
      5
      6
      <!--配置事务管理器-->
      <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransationManager">
      <property name="dataSource" ref="dataSource">
      </bean>
      <!--开启事务注解-->
      <tx:annotation-driven transation-manager="txManager"/>
    • 在方法上/类上加入注解

      1
      2
      3
      4
      @Transational
      public class transferService{
      ...
      }

    事务的传播(Propagation)行为

    spring一共有七种传播行为

    • REQUIRED 支持当前事务,如果不存在就新建
    • SUPPORTS 支持当前事务,如果没有就非事务
    • MANDATORY 必须运行在一个事务中,如果没有事务正在发生,就异常
    • REQUIRES_NEW 开启新事务,挂起旧事务(若有)
    • NEVER 以非事务方式运行,如果有事务,异常
    • NESTED 如果有事务,嵌套运行事务,可以独立提交或回滚,如果外层没有,就相当于REQUIRED

    设置方法

    1
    2
    3
    4
    @Transational(propagation=Propagation.REQUIRED)
    public class transferService{
    ...
    }

    事务的隔离级别

    详细的参照这个->锁和隔离

    • 未提交读(READ_UNCOMMITTED)
      事务中的修改即使没有提交也对其他事务可见
    • 提交读(READ_COMMITTED)
      事务的修改只有提交后才对其他事务可见,这也是大多数数据库系统的默认隔离级别
    • 可重复读(REPEATABLE_READ)
      此级别是MySQL的默认事务隔离级别,采用多版本并发控制,避免了幻读的问题
    • 可串行化(SERIALIZABLE)
      强制事务串行执行,会在读取的每一行数据都上锁,因此会导致大量的超时和锁争用问题
      设置方法

    三大读级别

    • 脏读:读取到尚未提交到数据库里的书
    • 不可重复读:在同一次事务中,两次读取对于同一数据的结果不一样
    • 幻读:第一次和第二次读取的数据条数不一样,多出来的行即为幻行
      1
      2
      3
      4
      @Transational(isolation=Isolation.READ_UNCOMMITED)
      public class transferService{
      ...
      }

    事务超时

    1
    2
    3
    4
    5
    @Transational(timeout=10)
    //设置超时时间为十秒,如果10s内没有执行到最后一条DML语句,则回滚
    public class transferService{
    ...
    }

    只读事务

    1
    2
    3
    4
    5
    @Transational(readOnly=true)
    //只读事务只能执行select,可以启动spring的优化策略
    public class transferService{
    ...
    }

    异常回滚事务

    1
    2
    3
    4
    5
    @Transational(rollbackFor=RuntimeException.class)
    //发生RuntimeException时才回滚
    public class transferService{
    ...
    }

    异常不回滚事务

    1
    2
    3
    4
    5
    @Transational(noRollbackFor=RuntimeException.class)
    //发生RuntimeException时不回滚
    public class transferService{
    ...
    }