Spring总结

重构JavaEE三层结构

面向接口编程

使用者只看接口,不管实现类,实现类交给容器工厂去创建

使用properties或者xml配置文件指定类的全路径,防止修改源代码

IOC/DI

控制反转是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理

将对象之间的相互依赖关系交给IoC容器来管理,并由IoC容器完成对象的注入

IoC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的

控制反转 (Inversion Of Control)和依赖注入(Dependency Injection)是同一行为的两种表达,只是描述的角度不同而已

控制反转的描述: IoC/DI容器反过来控制应用程序,控制应用程序所需要的外部资源

依赖注入的描述: 应用程序依赖IoC/DI容器,依赖它注入所需要的外部资源

具体的讲:当某个角色需要另外一个角色协助的时候,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但在 spring 中 创建被调用者的工作不再由调用者来完成,因此称为控制反转。创建被调用者的工作由 spring 来完成,然后注入调用者,因此也称为依赖注入。 IOC 侧重于原理,DI 侧重于实现。

依赖倒置原则/面向接口编程:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。

Spring容器中的Bean

创建Bean的三种方式

1、使用构造器创建实例

根据是否采用构造注入来选择指定的构造器创建对象

2、使用静态工厂方法创建

<bean…/>需要指定以下属性

  • class:该属性的值为静态工厂类的全类名

  • factory-method:该属性指定静态工厂方法来生产Bean实例

  • 如果静态工厂方法需要参数,则使用<constructor-arg…/>元素指定静态工厂方法的参数

3、使用实例工厂方法创建

<bean…/>需要指定以下属性

  • factory-bean:工厂Bean的id

  • factory-method:指定实例工厂的工厂方法

  • 如果工厂方法需要参数,则使用<constructor-arg…/>元素指定工厂方法的参数

依赖注入方式

  1. 构造器注入

    配置<constructor-arg…/>时可指定一个index属性,用于指定该参数值将作为第几个构造参数值,type属性指定value值要转化为哪种数据类型

    <bean id="role" class="com.hzb.model.Role"> 
    	<constructor-arg name="name" value=" 张三 " index="0"></constructor-arg>  
        <constructor-arg name="age" value="18" index="1"></constructor-arg> 
     	<constructor-arg name="birthday" ref="now" index="2"></constructor-arg> 
    </bean> 
    
  2. 使用set方法注入

    <bean id="role" class="com.hzb.model.Role">   
        <property name="name" value="test"></property> 
     	<property name="age" value="21"></property>   
        <property name="birthday" ref="now"></property> 
    </bean>
    

Bean的作用域

bean 默认是单例的,可以使用 scope 配置作用域

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

spring支持6种作用域,常用的是:singleton,prototype(每次获取都创建一个新的 bean)

XML配置

设置普通属性值:<property value.../>

配置合作者Bean:<property ref.../>

使用自动装配注入合作者Bean

  • 指定<bean…/>的autowire属性:no、byName、byType、constructor、autodetect

装配集合

<!-- 给数组注入数据 -->  
<property name="myArray"> 
  <array>    
      <value>AAA</value>    
      <value>BBB</value>    
      <value>CCC</value>   
  </array> 
</property> 
 <!-- 注入 list 集合数据 -->  
<property name="myList">   
    <list>    
        <value>AAA</value>    
        <value>BBB</value>    
        <value>CCC</value>   
    </list>  
</property> 
 <!-- 注入 set 集合数据 -->  
<property name="mySet"> 
    <set>    
      <value>AAA</value>    
      <value>BBB</value>    
      <value>CCC</value>   
    </set>  
</property> 
 <!-- 注入 map 数据 -->
<property name="myMap"> 
    <map>    
      <entry key="testA" value="aaa"></entry> 
   	  <entry key="testB">     
       	<value>bbb</value> 
   	  </entry>   
  </map> 
</property> 
<!-- 注入 properties 数据 --> 
<property name="myProps"> 
   <props> 
   		<prop key="testA">aaa</prop>    
        <prop key="testB">bbb</prop>   
    </props> 
</property> 

基于XML Schema的简化配置方式

1、使用p:命名空间(用于简化set方式注入)

<bean id="accountService"  
    class="com.hzb.service.impl.AccountServiceImpl4"     
    p:name="test" p:age="21" p:birthday-ref="now"/> 

2、使用c:命名空间(用于简化构造注入)

c:构造器参数名="值"或c:构造器参数名-ref=“其他Bean的id”

c:_N=“值”(N代表第几个构造器参数)

<bean class="spring.bean.Source" id="source" c_0="apple" c_1="big" c_2="little"/>

3、使用util:命名空间

<util:list id="list">
    <ref bean="role1"/>
    <ref bean="role2"/>
</util:list>
<util:map id="map">
    <entry key="" value-ref=""/>
    <entry key="" value-ref=""/>
</util:map>

xml和注解混合使用

@ImportResource
在使用注解的配置类上,可以使用@ImportResource引入Spring的配置文件,让配置文件里面的内容生效

抽象Bean与子Bean

抽象Bean的<bean…/>指定abstract=“true”

子Bean的<bean…/>的parent属性指定父Bean的id

容器中的工厂Bean

实现FactoryBean接口

使用FactoryBean向容器中注册bean

  • 当配置文件中标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法
  • 通过boolean isSingleton()方法的返回值来决定FactoryBean创建的bean实例的作用域是singleton还是prototype
  • 如果要从容器中获取FactoryBean对象本身,只需要在获取工厂Bean本身时,在id前面加上&符号即可

获取Bean本身的id

实现BeanNameAware接口,Spring容器调用该接口中的方法时,把部署该Bean的id属性作为参数传入该方法

强制初始化Bean

指定<bean…/>的depends-on属性,该属性可以在初始化该bean之前,强制初始化指定的bean

协调作用域不同步的Bean

Spring容器在初始化singleton作用域的Bean之前,先创建被依赖的prototype bean,然后才初始化singleton bean,并将prototype bean注入singleton bean,这会导致以后无论何时通过singleton bean去访问prototype bean时,得到的永远是最初那个prototype bean

解决办法:

  • 将调用者Bean的实现类定义为抽象类,并定义一个抽象方法来获取被依赖的Bean

  • 在<bean…/>中添加<lookuo-method name=“获取被依赖的Bean的抽象方法名” bean=“prototype bean的id”…/>子元素让Spring为调用者Bean的实现类实现指定的抽象方法

容器中Bean的生命周期

初始化——>依赖注入——>setBeanName方法(接口BeanNameAware)——>setBeanFactory方法(接口BeanFactoryAware)——>

setApplicationContext(接口ApplicationContextAware)——>

postProcessBeforeInitialization方法(BeanPostProcessor的预初始化方法,注意:它是对所有Bean而言的)——>

afterPropertiesSet方法(接口InitializingBean)——>自定义初始化方法(除了XML配置init-method外,也可以用注解@PostConstruct标注方法)——>

postProcessorAfterInitialization方法(BeanPostProcessor的后初始化方法,注意:它是对所有Bean而言的)——>

生存期——>destroy方法(接口DisposableBean)——>自定义销毁方法(除了XML配置destroy-method外,也可以用注解@PreDestroy标注方法)

public class JuiceMaker implements BeanNameAware, BeanFactoryAware,ApplicationContextAware, InitializingBean, DisposableBean {
    private String shop;
    private Source source;
    public String getShop() {
        return shop;
    }
    public void setShop(String shop) {
        this.shop = shop;
    }
    public Source getSource() {
        return source;
    }
    public void setSource(Source source) {
        this.source = source;
    }
    public String makeJuice(){
        return "这是由"+shop+"提供的"+source.toString();
    }

   @PostConstruct
   public void init(){
       System.out.println(this.getClass().getSimpleName()+"执行自定义init()");
   }

   @PreDestroy
   public void myDestroy(){
       System.out.println(this.getClass().getSimpleName()+"执行自定义destroy()");
   }

    private BeanFactory beanFactory;
    private ApplicationContext applicationContext;
    private String beanName;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.out.println(beanFactory);
        System.out.println(this.getClass().getSimpleName()+"执行setBeanFactory(BeanFactory beanFactory) ");
    }

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println(beanName);
        System.out.println(this.getClass().getSimpleName()+"执行setBeanName ");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println(this.getClass().getSimpleName()+"执行destroy() ");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println(this.getClass().getSimpleName()+"执行afterPropertiesSet()");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        System.out.println(applicationContext);
        System.out.println(this.getClass().getSimpleName()+"执行setApplicationContext");
    }
}

bean后置处理器

public class BeanPostProcessorImpl implements BeanPostProcessor {
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(bean.getClass().getSimpleName()+"结束实例化");
        return bean;
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(bean.getClass().getSimpleName()+"开始实例化");
        return bean;
    }
}

配置

<context:component-scan base-package="spring.bean"></context:component-scan>
<bean class="spring.bean.JuiceMaker" id="juiceMaker" init-method="init" destroy-method="myDestroy">
    <property name="source" ref="source"/>
    <property name="shop" value="喜茶"/>
</bean>

<bean class="spring.bean.Source" id="source">
    <property name="fruit" value="apple"></property>
    <property name="size" value="big"></property>
    <property name="sugar" value="little"></property>
</bean>

<bean class="spring.bean.BeanProcessorImpl"></bean>

Spring容器

BeanFactory

BeanFactory 只是对 IOC 容器最基本行为作了定义,而不关心 Bean 是怎样定义和加载的。 定义了一些 getBean、containsBean、isSingleton、isPrototype 等基本容器方法

ApplicationContext

和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能:

  • 当系统创建ApplicationContext容器时,默认会预初始化所有的singleton Bean(可以为<bean…/>元素指定lazy-init="true"阻止容器预初始化Bean)

  • 继承了 MessageSource 接口,提供国际化支持(当程序创建ApplicationContext容器时,Spring自动查找配置文件中名为messageSource的bean实例)

  • 资源访问

  • 事件机制(Spring的事件框架有两个重要成员:ApplicationEvent、ApplicationListener)

Spring IoC容器的初始化和依赖注入

Bean的定义分为三步

  • Resource定位:根据配置进行资源定位

  • BeanDefinition的载入:将定位到的信息保存到BeanDefinition中

  • BeanDefinition的注册:将BeanDefinition的信息发布到容器中

Spring AOP

AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,提高程序的可重用性,降低模块间的耦合度,并有利于未来的可扩展性和可维护性
Spring AOP就是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP会使用JDK Proxy去创建代理对象,而对于没有实现接口的对象,Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理

相关术语

通知

通知描述了切面要完成的工作以及何时执行

  • 前置通知(Before):在目标方法调用前调用通知功能;
  • 后置通知(After):在目标方法调用之后调用通知功能,不关心方法的返回结果;
  • 返回通知(AfterReturning):在目标方法成功执行之后调用通知功能;
  • 异常通知(AfterThrowing):在目标方法抛出异常后调用通知功能;
  • 环绕通知(Around):通知包裹了目标方法,在目标方法调用之前和之后执行自定义的行为。

连接点

通知功能被应用的时机(如调用接口的时候)

切点

切点定义了通知功能被应用的范围

切面

切面是通知和切点的结合,定义了何时、何地应用通知功能。

引入

在无需修改现有类的情况下,向现有的类添加新方法或属性。

织入

把切面应用到目标对象并创建新的代理对象的过程。

应用场景

权限校验、日志记录、性能监控、事务控制

基于注解的方式

创建切面:@Aspect

定义切入点:@Pointcut

execution(方法修饰符 返回类型 方法所属的包.类名.方法名称(方法参数))

//com.macro.mall.tiny.controller包中所有类的public方法都应用切面里的通知
execution(public * com.macro.mall.tiny.controller.*.*(..))
//com.macro.mall.tiny.service包及其子包下所有类中的所有方法都应用切面里的通知
execution(* com.macro.mall.tiny.service..*.*(..))
//com.macro.mall.tiny.service.PmsBrandService类中的所有方法都应用切面里的通知
execution(* com.macro.mall.tiny.service.PmsBrandService.*(..))

通知类型

  • @Before
  • @After
  • @AfterReturning
    • returning
  • @AfterThrowing
    • throwing
  • @Around:增强处理方法的第一个参数必须是ProceedingJoinPoint类型,在增强方法内调用该参数的proceed()方法才会执行目标方法

启用AspectJ框架的自动代理,自动生成动态代理对象

  • 注解类上添加@EnableAspectJAutoProxy或者在 spring 配置文件中添加<aop:aspectj-autoproxy />
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class) //引入AspectJAutoProxyRegister.class对象
public @interface EnableAspectJAutoProxy {
	//true——使用CGLIB基于类创建代理;false——使用java接口创建代理
	boolean proxyTargetClass() default false;
 
	//是否通过aop框架暴露该代理对象,aopContext能够访问.
	boolean exposeProxy() default false;
}

在切面中引入其他类的方法得到更好的实现:@DeclareParents

  • value:连接点所处的类
  • defaultImpl:引入接口的实现类

配置多个切面,可以使用@Order注解指定执行顺序

示例代码

//选择连接点
public interface RoleService {
    void printRole(int i);
}
@Service
public class RoleServiceImpl implements RoleService {
    public void printRole(int i) {
        if(i == 0) throw new RuntimeException();
        System.out.println("role service....");
    }
}
//创建切面
@Aspect
public class RoleAspect {
    @Pointcut("execution(* com.hzb.service.impl.*.*(..))")
    public void print(){

    }
    @Before("print()")
    public void before(){
        System.out.println("before.........");
    }

    @After("print()")
    public void after(){
        System.out.println("after...........");
    }

    @AfterReturning("print()")
    public void afterReturning(){
        System.out.println("afterReturning.........");
    }

    @AfterThrowing("print()")
    public void afterThrowing(){
        System.out.println("afterThrowing............");
    }

    @Around("print()")
    public void around(ProceedingJoinPoint joinPoint){
        System.out.println("around before.......");
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("around after..........");
    }
}

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.hzb.service")
public class AopConfig {
    @Bean
    public RoleAspect roleAspect(){
        return new RoleAspect();
    }

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
        RoleService service = context.getBean(RoleService.class);
        service.printRole(1);
        System.out.println("###########");
        service.printRole(0);
    }
}

运行结果:
around before.......
before.........
role service....
afterReturning.........
after...........
around after..........
###########
around before.......
before.........
afterThrowing............
after...........
around after..........


//引入其他类的方法得到更好的实现
public interface RoleVerifier {
    boolean verify(boolean boo);
}
public class RoleVerifierImpl implements RoleVerifier {
    public boolean verify(boolean boo) {
        return boo;
    }
}
@Aspect
public class RoleAspect {
    @DeclareParents(value = "com.hzb.service.impl.RoleServiceImpl+",defaultImpl = RoleVerifierImpl.class)
    public RoleVerifier roleVerifier;
    ...
}
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("com.hzb.service")
public class AopConfig {
    @Bean
    public RoleAspect roleAspect(){
        return new RoleAspect();
    }
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
        RoleService service = context.getBean(RoleService.class);
        RoleVerifier roleVerifier = (RoleVerifier)service;
        if(roleVerifier.verify(false)){
            service.printRole(1);
        }else{
            System.out.println("验证失败");
        }
    }
}

搭建一个AOP切面步骤

  • 将切面类和业务逻辑组件(目标方法所在类)都加入到容器中,并且要告诉Spring哪个类是切面类(标注了@Aspect注解的那个类)
  • 在切面类上的每个通知方法上标注通知注解,告诉Spring何时何地运行,当然最主要的是要写好切入点表达式,这个切入点表达式可以参照官方文档来写
  • 开启基于注解的AOP模式,即加上@EnableAspectJAutoProxy注解,这是最关键的一点

基于XML的方式

  • 通知类用 bean 标签配置起来
  • 使用 aop:config 声明 aop 配置
  • 使用 aop:pointcut 配置切入点表达式
  • 使用 aop:aspect 配置切面
  • 使用 aop:xxx 配置对应的通知类型

配置实例

<bean class="com.hzb.aop.RoleAspect" id="roleAspect"/>
<aop:config>
    <aop:aspect ref="roleAspect">
        <!-- 引入 -->
        <aop:declare-parents types-matching="com.hzb.service.impl.RoleServiceImpl" 
                                 implement-interface="com.hzb.service.RoleVerifier"
                                 default-impl="com.hzb.service.impl.RoleVerifierImpl"/>
        <aop:pointcut id="print" expression="execution(* com.hzb.service.impl.*.*(..))"/>
        <aop:before method="before" pointcut-ref="print"/>
        <aop:after method="after" pointcut-ref="print"/>
        <aop:after-returning method="afterReturning" pointcut-ref="print"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="print"/>
        <aop:around method="around" pointcut-ref="print"/>
    </aop:aspect>
</aop:config>

访问目标方法的参数

增强方法的第一个参数定义为JoinPoint类型,JoinPoint里包含了如下几个常用的方法

  • getArgs():返回执行目标方法的参数

  • getSignature():返回被增强的方法的相关信息

  • getTarget():返回目标对象

  • getThis():返回生成的代理对象

事务管理

XML配置

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

<!-- 配置事务的通知引用事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager"> 
	<tx:attributes> 
<!-- 
	指定方法名称:是业务核心方法   
    read-only:是否是只读事务。默认 false,不只读。  
    isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。   
    propagation:指定事务的传播行为。  
    timeout:指定超时时间。默认值为:-1。永不超时。  
    rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。没有默认值,任何异常都回滚。  
    no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚。没有默认值,任何异常都回滚。  
-->  	 
    	<tx:method name="*" read-only="false" propagation="REQUIRED"/>  
    	<tx:method name="find*" read-only="true" propagation="SUPPORTS"/> 
	</tx:attributes> 
</tx:advice> 

<!-- 配置切入点表达式和事务通知的对应关系 -->
<aop:config> 
    <aop:pointcut expression="execution(* com.itheima.service.impl..*.*(..))" id="pt1"/> 
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/> 
</aop:config> 

注解式事务

事务是逻辑上的一组操作,要么都执行,要么都不执行。

1、配置事务管理器并注入数据源

2、开启 spring 对注解事务的支持

方式一:配置文件

<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

方式二:java配置方式

@Configuration
@EnableTransactionManagement
@ImportResource(locations = {"classpath:applicationContext.xml"})
public class TransactionConfig implements TransactionManagementConfigurer {
    @Autowired
    private DataSource dataSource;

    @Bean(name = "transactionManager")
    public TransactionManager annotationDrivenTransactionManager() {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

3、在业务层使用@Transactional 注解

属性名说明
value当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器
propagation事务的传播行为,默认为REQUIRED
isolation事务的隔离级别,默认为DEFAULT
timeout事务的超时时间,默认值为-1
read-only指定事务是否为只读事务,默认值为false;为了省略那些不需要事务的方法,比如读取数据,可以设置为true
rollback-for用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔
no-rollback-for抛出no-rollback-for指定的异常类型,不回滚事务

隔离级别

脏读:允许一个事务去读取另一个事务中未提交的数据

读/写提交:一个事务只能读取另一个事务已经提交的数据,不可重复读

可重复读:解决读/写提交存在的不可重读问题,可重复读是针对数据库同一条记录而言的

序列化: 最高的隔离级别,完全服从 ACID 的隔离级别

脏读不可重读幻读
脏读
读/写提交×
可重复读××
序列化×××

TransactionDefinition 接口中定义了五个表示隔离级别的常量

  • TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
  • TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
  • TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

传播行为

一个方法调用另一个方法时,可以对事务的特性进行传播配置,称为传播行为

支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

不支持当前事务的情况:

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

  • TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

注意的问题

  • Spring AOP的@Transactional只能应用到public方法才有效

  • Spring的AOP的@Transactional的自调用失效问题:一个类的方法去调用自身的另一个方法,事务注解没有起作用
    原因是@Transactional的实现原理是AOP,而AOP的实现原理是动态代理,自己调用自己的过程过程不存在代理对象的调用

    • 解决方法:对于自调用失效问题,使用两个类或者从容器中获取当前类的代理对象再去调用方法
  • 错误使用Service:分开两次调用方法,每次调用方法时都会启用一个事务,方法完成后会释放该事务,这样前后两次调用是在两个不同的事务中完成的,这就可能产生数据不一致的问题

  • 过长时间占用事务:在方法中进行一些与数据库事务无关的操作,而只有等到方法完成后,才会释放数据库事务资源,所以对于这种时间长并且不需要使用事务资源的操作,要将它放在一个没有事务的环境中运行,防止占用事务资源

  • 错误捕捉异常:比如我们在一个事务中有两个操作,我们在代码中加入了try…catch…语句,当其中一个操作成功,另一个操作抛出异常时,由于catch语句对异常进行了处理,Spring在数据库事务所约定的流程中得不到任何异常信息,此时Spring就会提交事务,这样就导致了数据出现错误。因此在需要大量异常处理的代码中,我们要时刻记住Spring和我们的约定流程,在catch中把异常抛出,这样Spring才会捕捉到这个异常,并进行事务回滚

参考

  • https://snailclimb.gitee.io/javaguide
  • https://liayun.blog.csdn.net/article/details/115053350
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值