码迷,mamicode.com
首页 > 编程语言 > 详细

spring学习

时间:2021-07-02 15:49:28      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:pointer   字符   out   持久层   int   起点   容器   循环   struct   

//把创建的对象都放到applicationContext容器中,要用时,在通过id一个一个取出来

 

壹、第一个spring项目

  1. 主配置文件

<beans>
   <bean id="a" class="com.dh.service.impl.SomeServiceImpl"/>
</beans>

//在spring中,每一个java对象对应一个<bean>标签,即一个<bean>标签代表一个java对象

//id:对象的自定义名称,唯一值,spring通过这个名称找到该对象

//class:类的全限定名称(不能是接口,spring通过反射机制创建对象)

//spring把创建好的对象放到map集合中,集合的key就是上面的id值,value是创建的对象

//spring创建对象默认调用无参构造方法

 

  1. 测试程序

String config="beans.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(config);       (2)
?
Object object=ac.getBean("a");     //此时对象已经创建好了,通过id,从容器中获取相对应的对象
SomeService service=(SomeService)object;
service.doSome();

//只要是配置文件,都是从target/classes的根目录为起点开始找的

//ApplicationContext就是spring的容器,在创建spring容器时,会创建配置文件中的所有java对象,也就是在(2)步时,先创建容器,再创建java对象

//ClassPathXmlApplicationContext(config):表示从类路径(即target/classes目录)中加载spring的配置文件; FileSystemXmlApplicationContext():表示从本地磁盘中加载spring的配置文件,不常用

 

贰、常用的方法

  1. 获取容器中定义的对象数量

int nums=ac.getBeanDefinitionCount();
System.out.println(nums);

 

  1. 获取容器中定义的对象名称

String[] names=ac.getBeanDefinitionNames();
for (String n:names){
  System.out.println(n);         //a
}

 

 

叁、基于XML的DI

//在spring的配置文件中,使用标签和属性完成赋值,叫做基于XML的DI实现

 

一、set注入

//set方法的执行是在构造方法之后执行的

 

  1. 简单注入

<bean id="mystudent" class="com.dh.bao.Student">
   <property name="name" value="zs"/>
   <property name="age"  value="25"/>
</bean>

//name="属性名字",value="赋给属性的值"

//com.dh.bao.Student类中所赋值的属性必须有set()方法,没有就报错;有set(),但里面没有this.age = age;赋值语句,不报错,只是该属性不能被赋值,为null

//spring只关心有没有对应的set()方法,即使只有set()方法,没有属性,系统也会正常运行

//若在set()方法中有System.out.println(123);语句,该语句也会正常运行,输出123

 

  1. 引用类型注入

<bean id="mystudent" class="com.dh.bao.Student">
   <property name="name" value="zs"/>
   <property name="age"  value="25"/>
</bean>
?
<bean id="myschool" class="com.dh.bao.School">
   <property name="name" value="东华大学"/>
   <property name="address" value="上海"/>
   <property name="student" ref="mystudent"/>
</bean>

//com.dh.bao.School类中有Student属性,使用ref属性完成赋值

//两个<bean>标签的位置没有规定,谁在上方都可以。因为在第一次因位置没加载到时,会进行二次扫描

 

 

二、构造注入

//通过有参构造方法赋值

<bean id="gouzao" class="com.dh.bao.School">
  <constructor-arg name="name" value="gzdx"/>
  <constructor-arg name="address" value="gz"/>
  <constructor-arg name="student" ref="my2"/>
</bean>
<bean id="my2" class="com.dh.bao.Student">
  <constructor-arg name="name" value="ww"/>
  <constructor-arg name="age" value="66"/>
</bean>

 

 

三、引用类型的自动注入

  1. byName

<bean id="student" class="com.dh.bao.Student">
   <property name="name" value="ls"/>
   <property name="age"  value="77"/>
</bean>
?
<bean id="byName" class="com.dh.bao.School" autowire="byName">
   <property name="name" value="东大"/>
   <property name="address" value="上海"/>
</bean>

//student的<bean>中的id值必须和com.dh.bao.School类中的Student类型的引用名称相同

//在School的<bean>中添加autowire="byName"属性

 

  1. byType

//同源关系

  • java类中引用属性的数据类型和该引用属性相关的<bean>中的class是一样的

  • java类中引用属性的数据类型和该引用属性相关的<bean>中的class是父子关系

  • java类中引用属性的数据类型和该引用属性相关的<bean>中的class是接口与实现类关系

<bean id="byType" class="com.dh.bao.School" autowire="byType">
   <property name="name" value="东大"/>
   <property name="address" value="上海"/>
</bean>
<bean id="student" class="com.dh.bao.Student">
   <property name="name" value="ls"/>
   <property name="age"  value="77"/>
</bean>

//在spring的配置文件中也是由上而下逐行执行

//当执行到School的<bean>标签时,先创建school对象,然后再给其简单属性赋值。最后执行autowire="byType"语句;扫描整个spring配置文件,找与School对象的属性student同源的,即student的子类,student类本身,若student是接口,其实现类也行。找到之后赋值即可

//注意:在使用byType时,同时只能有一个符合条件,当有多个<bean>标签符合条件时,系统报错

 

 

 

肆、基于注解的DI

 

一、创建对象

  1. Component注解

@Component(value="myStudent")
public class Student {
   private String name;
   private int age;
  ...
}        

//当只有value一个属性时,value可以省略不写;也可以不指定对象的名称,Spring默认提供类名首字母小写为对象名称

 

  1. 配置文件中设置组件扫描器

<beans>
   <context:component-scan base-package="com.dh.bao"/>
</beans>

//spring会扫描base-package属性指定的包和其子包中所有的类,找到类中的注解,按照注解的功能创建对象或者赋值

 

 

二、多注解的分层

  1. @Repository:用在持久层上,放在dao的实现类上,创建dao对象

  2. @Service:用在业务层上,创建service对象,可以有事务等功能

  3. @Controller:用在界面层上,创建控制器对象

//以上3个注解的使用语法与@Component一样,都能创建对象,但这3个注解还有其他的额外功能

//当某个类不属于以上3种情况时,才用@Component创建对象

 

 

三、简单类型赋值

@Component(value="myStudent")
public class Student {
   @Value(value = "zs")
   private String name;
   @Value(value="29")
   private int age;
  ...
}    

//属性value是String类型

//@Value注解可以出现在属性的定义上(推荐),也可以出现在set()方法上

//@Value注解是通过反射机制赋值的,所以该类可以没有set()方法

 

 

四、引用类型赋值

 

@Autowired注解

@Component(value="mySchool")
public class School {
  @Value("donghua")
  private String name;
  @Value("shanghai")
  private String address;
  @Autowired
  private Student student;
  ...
}
?
@Component(value="myStudent")
public class Student {
  @Value(value = "zs")
  private String name;
  @Value(value="29")
  private int age;
  ...
}  

//@Autowired注解默认采用的是byType的方式自动注入

//只要通过组件扫描器扫描的包中有student属性对应的同源的关系的类就能完成该引用属性的赋值;或者spring配置文件有通过标签完成对该引用同源关系的类赋值的,也是可以的。即两种方式可以混用,即xml与注解

//@Autowired注解可以出现在属性的定义上(推荐),也可以出现在set()方法上。其是通过反射机制赋值,所以该类可以没有set()方法

 

1. byName(@Qualifier)
@Component(value="mySchool")
public class School {
   @Value("donghua")
   private String name;
   @Value("shanghai")
   private String address;
   
   @Autowired
   @Qualifier(value="aa")         //@Qualifier注解
   private Student student;
  ...
}
?
@Component(value="aa")
public class Student {
   @Value(value = "zs")
   private String name;
   @Value(value="29")
   private int age;
  ...
}  

//使用@Qualifier注解指定赋值的类的id值

 

2. required属性
@Autowired(required = true)

//required = true:表示引用类型赋值失败时,程序报错,并终止接下来的程序 (默认

//required = false:表示引用类型赋值失败时,程序正常执行,引用属性的值为null

 

 

@Resource注解

@Component(value="mySchool")
public class School {
   @Value("donghua")
   private String name;
   @Value("shanghai")
   private String address;
   
   @Resource
   private Student student;
  ...
}        

//@Resource注解来自jdk中,但spring框架支持该注解的功能,用法与@Autowired注解相似

//@Resource注解默认采用byName的方式,其先使用byName的方式进行赋值,若赋值失败,则采用byType的方式再次尝试

//若只想使用byName的方式,则需添加属性name:@Resource(name="aa")

//@Resource注解可以出现在属性的定义上(推荐),也可以出现在set()方法上。其是通过反射机制赋值,所以该类可以没有set()方法

 

 

 

伍、多个配置文件

//在resources/bao目录下有spring-1,spring-2,spring-3,3个配置文件

//在spring-1中添加

<beans>
   <import resource="classpath:bao/spring-2.xml"/>
   <import resource="target/classes:bao/spring-3.xml"/>     //classpath与target/classes是等价的
</beans>

//添加以上内容之后,spring-1就是主配置文件了,在测试程序中,只用加载spring-1,其他两个配置文件就会自动加载

//可以使用通配符“ * ”

 <import resource="classpath:bao/spring-*.xml"/>

//这样就可以一次将spring-2,spring-3都导入。但要注意,在通配符表示的范围中不能包含主配置文件spring-1,不然会陷入死循环,一直加载。可以通过给主配置文件改名或者修改目录,不在同一目录下

 

 

 

陆、AOP

//aop (Aspect Orient Programming) :面向切面编程。切面:给目标类增加的功能,就是切面

//aop的底层是基于动态代理的,它的的本质就是动态代理的规范化,是一个标准

//spring内部实现了aop规范,但其主要用于事务的处理。我们主要用一个专门做aop的框架:aspectJ

//所以aop支持jdk代理和cglib代理

 

一、切面表达式

execution (访问权限 方法的返回类型 方法的声明(参数) 异常)

//方法的返回类型,方法的声明(参数)这两项是必须要写的,其他两项可以省略不写

//每一部分之间用空格隔开

//切入表达式中可以用以下符号

  1. *:0至多个字符

  2. ..:用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包的路径

  3. +:用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类

//例子:excution(* set*(..)) :表示以set三个字母开头的任意方法

 

 

二、第一个例子

  1. 创建接口及目标类

package com.dh.bao;
?
public interface SomeService {
   void doSome(String name, Integer age);
}
?
public class SomeServiceImpl implements SomeService{           //目标类
   public void doSome(String name, Integer age) {          
       System.out.println("abc");
  }
}

 

  1. 创建切面类,切面表达式指向目标类

@Aspect
public class MyAspect {
  @Before(value = "execution(public void com.dh.bao.SomeServiceImpl.doSome(String,Integer))")
  public void MyBefore(){
      System.out.println("切面:" + new Date());
  }
}

 

  1. 声明目标及切面对象,还有声明自动代理生成器

<beans>
   <bean id="someservice" class="com.dh.bao.SomeServiceImpl"/>
   <bean id="myaspect" class="com.dh.bao.MyAspect"/>
?
   <aop:aspectj-autoproxy/>     //自动代理生成器
</beans>

//程序执行到<aop:aspectj-autoproxy/> 时,会扫描整个spring的配置文件中所有的类及对象,找到@Aspect及相关注解,将切面类指向的目标类转化为代理类

 

  1. 测试程序

String confg="applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(confg);
SomeService ss=(SomeService)ac.getBean("someservice");   //这里强转的是接口,而不是接口的实现类SomeServiceImpl
s.doSome("zs",20);    
?
//切面:Thu Jun 24 10:59:54 CST 2021
 abc                                          

//注意:此时ss对象的类型不是SomeService类,而是com.sun.proxy.$Proxy6类型

//若切面表达式写错了,程序依然会正常运行,不会报错;只是ss对象就不是代理类型,而是SomeService类型。原因是切面表达式不对,导致指定目标类错误,找不到目标类,也就无法完成转换

 

三、注解

@Aspect注解

//作用是:表明当前类为切面类

//位置:定义在类上

 

@Pointcut注解

//定义和管理切入点,若项目中有多个切入点表达式是重复的,则可以使用该注解,达到复用的目的

  1. 属性:value值(切入点表达式)

  2. 位置:在方法上

  3. 特点:当使用@Pointcut注解定义在一个方法上时,这个方法的名称就是切入表达式的别名。在其他的通知注解中,value属性的值就可以使用这个别名,代替切入点表达式

@Pointcut(value = "execution(public Integer com.dh.arround.MyArroundImpl.doSome())")
public void www(){
  //无需写代码
}

//注意:在其他通知注解中,value="www()",而不是value="www"。是要包含括号的

 

 

 

四、通知

//切面类相对于目标类执行的时间,在aop规范中称为Advice,也就是通知。

 

JoinPoint参数

//指定通知方法中的参数,即切面方法中的参数;所有通知方法都可以有这个参数

//通知方法:被通知注解(@Before,@After,@AfterReturning等)修饰的方法

public class MyAspect {
   @Before(value = "execution(public void com.dh.bao.SomeServiceImpl.doSome(String,Integer))")
   public void MyBefore(JoinPoint jp){
       System.out.println("目标方法的签名:" + jp.getSignature());
       System.out.println("目标方法的名称" + jp.getSignature().getName());
       
       Object args[]=jp.getArgs();       //获取目标方法的实参
      ...
}        

//目标方法的签名:void com.dh.bao.SomeServiceImpl.doSome(String,Integer)

 

@Before (前置通知注解)

  1. 属性:value值(切入点表达式)

  2. 位置:在方法上

  3. 特点:在目标类的方法之前执行;不改变目标方法的执行结果;不影响目标方法的执行

  4. 对切面方法的要求:要是public;要是void;方法名称自定义;方法若有参数,不能自定义,从几个参数类型中选择

 

@AfterReturning (后置通知)

  1. 属性:value值(切入点表达式);

    returning:自定义变量,表示目标方法的返回值(自定义的变量名必须和通知(切面)方法的形参名一样

  2. 位置:在方法上

  3. 特点:在目标类的方法之后执行;

    能够获取到目标方法的返回值,可以在切面方法中处理这个返回值

    不能改变目标方法的返回值在最后的输出

  4. 对切面方法的要求:要是public;要是void;方法名称自定义;方法有参数,推荐Object类型

public class AfterImpl implements After{
   public Integer doSome() {
       return 100;
  }
}
?
public class AfterAspect {
   @AfterReturning(value = "execution(public Integer com.dh.After.AfterImpl.doSome())",returning = "bbb")
   public void myAfter(Object bbb){            
       System.out.println("切面:" + bbbb);    //由获取的返回值可以输出一些额外的功能,但不能改变目标返回值的最后输出
       bbb=200;
  }
}
?
After after=(After)ac.getBean("afterimpl");
Integer in=after.doSome();
System.out.println(in);            //100,并没有变成200

 

 

@Around (环绕通知注解)

  1. 属性:value值(切入点表达式)

  2. 位置:在方法上

  3. 特点:

    在目标类的方法前后都能执行

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

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

  4. 对切面方法的要求:

public;

必须有一个返回值,推荐使用Object

方法名称自定义;

方法有参数,固定参数:ProceedingJoinPoint;其是JoinPoint接口的子接口,所以它能用JoinPoint中的所有方法

public class MyArroundImpl implements MyArround{
   public Integer doSome() {
       System.out.println("目标");
       return 1;
  }
}
?
public class MyArroundAspect {
   @Around(value = "execution(public Integer com.dh.arround.MyArroundImpl.doSome())")
   public Object myArround(ProceedingJoinPoint pjp) throws Throwable {
       Object result=null;
       System.out.println("目标前");           (1)
       result=pjp.proceed();                   (2)
       System.out.println("目标后");           (3)
       return result;
  }
}

//可在(1)处添加pjp.getArgs()方法,获取目标方法的实参,可修改实参,也可通过实参判断是否继续执行目标方法,即(2)

//(2)为执行目标方法,获取目标方法的返回值

//在(3)处可对目标方法的返回值进行修改,这是会影响最后在测试程序中的输出结果的。与@AfterReturning中的不同

 

 

@AfterThrowing (异常通知)

  1. 属性:value值(切入点表达式);

    throwing:自定义变量,表示目标方法抛出的异常(自定义的变量名必须和通知(切面)方法的形参名一样

  2. 位置:在方法上

  3. 特点:在目标类方法抛出异常时执行;

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

  4. 对切面方法的要求:要是public;要是void;方法名称自定义;参数只有一个:Exception,若还有就是JoinPoint

//常用的方法:

System.out.println(ex.getMessage());  //打印异常信息

 

 

@After (最终通知注解)

  1. 属性:value值(切入点表达式)

  2. 位置:在方法上

  3. 特点:在目标方法之后执行

    总是会执行,即使目标方法出现异常,也会执行(相当于try...catch中的finally)

所以一般用来做资源的关闭,清理工作

  1. 对切面方法的要求:要是public;要是void;方法名称自定义;方法没有参数,若有就是JoinPoint

 

 

五、cglib动态代理

//若目标类有接口时,使用的jdk代理。被转换的目标类的类型是:com.sun.proxy.$Proxy6

//若目标程序没有接口,spring框架就自动使用cglib代理,不需要程序员操作;被转换的目标类的类型是: com.dh.arround.MyArroundImpl$$EnhancerBySpringCGLIB$$8fc79603

 

//若目标类有接口,但你希望用cglib代理,则将<aop:aspectj-autoproxy/>自动代理生成器改为:

<aop:aspectj-autoproxy proxy-target-class="true"/>

 

//使用cglib代理的前提是:目标类能被继承

 

 

 

柒、spring与mybatis整合

 

spring配置文件分析

<!--Druid会自动跟url识别驱动类名,如果连接的数据库非常见数据库,配置属性driverClassName-->
<bean id="aaa" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
   <property name="url" value="jdbc:mysql://localhost:3306/test" />
   <property name="username" value="root" />
   <property name="password" value="123456" />
   <property name="maxActive" value="20" />
</bean>
?
<bean id="bbb" class="org.mybatis.spring.SqlSessionFactoryBean">
   <property name="dataSource" ref="aaa"/>
   <property name="configLocation" value="classpath:mybatis.xml"/>
</bean>

//通过连接数据源aaa和连接mybatis配置文件,在内部先创建SqlSessionFactory对象,然后再由该对象创建SqlSession对象

//因为是<property>标签,则com.alibaba.druid.pool.DruidDataSource类中一定有url属性对应的set()方法,因为该标签是通过set注入完成赋值的

 

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
   <property name="sqlSessionFactoryBeanName" value="bbb"/>
   <property name="basePackage" value="com.dh.dao"/>
</bean>

//MapperScannerConfigurer类会在内部调用getMapper()方法,生成每个dao接口的代理对象

//MapperScannerConfigurer会扫描basePackage指定的包中所有的接口,把每个接口都执行一次getMapper()方法,得到每个接口的dao对象,再把创建好的dao对象放进spring容器中

//dao对象的默认名称:接口名首字母小写

 

<bean id="ccc" class="com.dh.service.impl.StudentServiceImpl">
   <property name="studentDao" ref="studentDao"/>
</bean>

//此时给StudentServiceImpl类中的studentDao属性赋值的就是上面那步生成的dao对象

 

 

测试程序分析

String conf="applicationContext.xml";
ApplicationContext ac=new ClassPathXmlApplicationContext(conf);
StudentService ss=(StudentService)ac.getBean("ccc");
?
Student student=new Student();
student.setId(7);
student.setName("gg");
student.setAge(44);
student.setSex(0);
int nums=ss.addStudent(student);
List<Student> list=ss.queryStudent();
for(Student stu:list){
  System.out.println(stu);
}

//注意:此时的ss对象是com.dh.service.impl.StudentServiceImpl,而不是com.sun.proxy.$Proxy类型,在aop中才是

//spring与mybatis整合中常通过在service中声明dao对象,并给其赋值,再用其在service类中调用dao接口中的方法;而不是直接使用dao对象调用dao接口中的方法,中间要转一下

//spring与mybatis整合在一起使用时,事务是自动提交的,无需手动写SqlSession.commit()

 

 

 

捌、spring中的事务

//不同的数据库访问技术处理事务的方式不同,比如:jdbc(conn.commit())与mybatis(SqlSession.commit())处理事务的方式就不同,所以spring就提供了一种事务处理的统一模型,能用统一的步骤,方式完成多种不同数据库访问技术的事务处理

 

一、事务管理器

//即PlatformTransactionManager接口,封装了commit,rollback等方法

//该接口为每一种数据访问技术都设置好了专门的实现类,比如mybatis对应的实现类是DataSourceTransactionManager

 

二、事务定义接口

//即TransactionDefinition接口

//该接口中定义了事务描述相关的三类常量:事务的隔离级别,事务的传播行为,事务默认的超时时限

 

事务的隔离级别

隔离级别含义
ISOLATION_DEFAULT 默认, 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 读未提交, 允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。
ISOLATION_READ_COMMITTED 读已提交,允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。(Oracle 默认级别)
ISOLATION_REPEATABLE_READ 可重复读, 对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。(MYSQL默认级别)
ISOLATION_SERIALIZABLE 串行化, 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。

 

 

事务的传播行为

//只用掌握前面3种传播行为即可

传播行为含义
PROPAGATION_REQUIRED 指定的方法必须在事务内运行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新的事务。这是spring默认的传播行为
PROPAGATION_SUPPORTS 表示指定的当前方法可以在没有事务上下文中执行;但是如果存在当前事务的话,那么该方法会在这个事务中运行。查询操作就属于这种行为
PROPAGATION_REQUIRES_NEW 指定的方法总是会新建一个事务,并在新建的事务中执行,若当前存在事务,则将当前事务挂起(即暂停,包括事务中所有的方法也暂停),直到新事务执行完毕

//PROPAGATION_REQUIRED实例解释:PROPAGATION_REQUIRED传播行为指定方法a,若B事务中的b方法调用方法a,则a方法加入到B事务中执行;若没有事务的b方法调用a方法。则a方法自己新建一个事务c,在事务c中执行

 

传播行为含义
PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常
PROPAGATION_NESTED 表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务

 

 

 

事务默认的超时时限

//为了避免一个事务长时间占有资源,所以设置了超时时间

//表示一个方法或事务的最长执行时间,如果方法执行时间超过了这个超时时限,事务就会回滚。单位是:秒,整数值,默认值为:-1

 

 

三、提交事务与回滚事务时机

//当你的业务方法,执行成功,没有异常抛出,spring会在方法执行完毕,自动调用事务管理器中的commit方法,提交事务

//在默认设置下,事务只在出现运行时异常(Runtimeexception及其子类)或者错误(Error)时回滚事务,而在出现非运行异常时,主要是受检查异常(checked exception)时不回滚,自动调用事务管理器中的commit方法提交事务。

//不过可以人为设置,主动声明在出现特定受检查异常时像运行时异常一样回滚。同样,也可以声明一个事务在出现特定的异常时不回滚,即使特定的异常是运行时异常。

 

 

 

四、事务处理方案

 

@Transactional注解

//适用于中小型项目

//该注解使用的是spring框架中内部的aop来处理事务

//@Transactional注解只能用于public方法,用在非public方法时,Spring虽然不会报错,但不会将该方法纳入该事物中,因为spring会忽略所有非public方法上的@Transactional注解

 

属性
  1. propagation:事务传播行为设置,该属性类型为Propagation枚举(7个),默认值为:Propagation_REQUIRED

  2. isolation:事务隔离级别设置,该属性类型为Isolation枚举(5个),默认值为Isolation_DEFAULT

  3. readOnly:设置目标方法对数据库的操作是否只能读事务,而不能读写事务。该属性为boolean,默认读写,即为false

  4. timeout:事务超时时间设置。单位:秒,类型为:int,默认值为:-1,即没有时限

  5. rollbackFor:指定需要回滚的异常类(即出现该指定异常类后,该事物回滚,即使该异常属于非运行时异常)。类型为Class[],默认值为空数组,当只有一个异常类时,可以不使用数组。

  6. rollbackForClassName:指定需要回滚的异常类的类名,类型为String[],默认值为空数组,当只有一个异常类时,可以不使用数组。

  7. noRollbackFor:指定不需要回滚的异常类(即出现该指定异常类后,该事物不回滚,即使该异常属于运行时异常)。类型为Class[],默认值为空数组,当只有一个异常类时,可以不使用数组。

  8. noRollbackForClassName:指定不需要回滚的异常类的类名,类型为String[],默认值为空数组,当只有一个异常类时,可以不使用数组。

  9. value :可选的限定描述符,指定使用的事务管理器

 

底层原理

//spring使用aop机制,创建该注解所在类的代理对象,给方法加入事务功能。使用了环绕通知的方式,在目标业务方法执行之前,在切面方法中开启事务,在业务方法结束之后,提交事务,或者出现异常时,回滚事务,这一切都有spring框架自动控制。

 

使用步骤
@Transactional(propagation = Propagation.REQUIRED,    
              isolation = Isolation.DEFAULT,      //这些配置都是默认的,可以不写这些属性,直接@Transactional在方法上
              readOnly = false
              ......)
public void buy(Integer goodsId, Integer nums) {
  ...
}    
<bean id="ccc" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="aaa"/>
</bean>
<tx:annotation-driven transaction-manager="ccc"/>

//声明事务管理器并注册,是对数据源声明事物管理器

//ref属性:其值为数据源的id值,在这里是阿里的那个数据源

//<tx:annotation-driven transaction-manager="ccc"/>:是对事务管理器进行注册,创建代理对象

 

 

aspectj

//适合大型项目

//使用aspectj框架功能在spring的配置文件中声明类,方法的事务。这种方式业务方法与事务配置是完全分离

//使用该方式需要在提前加入aspectj的依赖

 

  1. 声明事务管理器

<bean id="ccc" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
   <property name="dataSource" ref="aaa"/>
</bean>

 

  1. 声明业务方法的业务属性

<tx:advice id="ddd" transaction-manager="ccc">
   <tx:attributes>
       <tx:method name="buy" propagation="REQUIRED"
                             isolation="DEFAULT" read-only="true"
                             rollback-for="java.lang.NullPointerException"
                             ......
       />
   </tx:attributes>
</tx:advice>

//name:方法名称(不包含包名和类名),可以使用完整方法名,也可以用通配符 “*” 。当你需要给成千上万的方法配置事务时,你可以给同一类的方法命名时有共同点,比如:以字母abc开头,则在name属性处填:abc*,就能给这一类的方法添加事务;当name=“*”,表示给目标类中所有方法都添加该事物

//当有一方法同时满足完整方法名,带有*的方法名,以及只有*即这三种情况同时存在时,spring会先匹配完成方法名,当匹配成功后,就不再匹配其他的;只有匹配失败,再去带有*的方法名中找,前两者都没匹配成功,才执行只有*的事务配置

 

  1. 配置aop

<aop:config>
   <aop:pointcut id="eee" expression="execution(void buy(..))"/>
   <aop:advisor advice-ref="ddd" pointcut-ref="eee"/>
</aop:config>

//<aop:pointcut>:配置切入点表达式,指定哪些包中的类需要使用业务(即目标业务方法所在的包名,类名)

//<aop:advisor>:配置增强器,关联advice和pointcut(连接目标类和业务方法)

spring学习

标签:pointer   字符   out   持久层   int   起点   容器   循环   struct   

原文地址:https://www.cnblogs.com/zhestudy-2021/p/14961117.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!