Spring总结

目录

Spring简介

Spring结构

IOC

        Spring容器类型

        bean对象创建方式

        对象的创建策略

        对象的销毁方式

        生命周期方法

        获取bean对象的方式

        入门案例 

        依赖注入DI

                三种依赖注入的方式:

                        Setter注入

                        Constructor注入

                        自动注入

                注入bean类型

                注入基本数据类型、字符串类型

                注入list、set

                         注入简单数据类型list集合

                        注入bean类型list集合

                注入map

                注入Properties对象

        注解实现IOC

                准备工作

                @Component

                @Repository、@Service、@Controller

                @Scope

                @Autowired

                @Qualifier

                @Value

                @Configuration

                @ComponentScan

                @PropertySource

                @Bean

                @Import

        Spring整合MyBatis

                搭建环境

                编写配置文件

                编写POJO类、持久层、service层

                Spring整合Junit进行单元测试

AOP

相关术语

配置AOP流程

通知类型: 

通知的各种类型的标签

切点表达式

注解配置AOP

事务

        案例:

                准备工作:

                创建配置文件:

                编写代码步骤:

                编写代码:

        事务管理方案:

        事务管理器:

        测试事务管理器:

        事务控制API:

        事务的相关配置:

        事务的传播行为:

        事务的隔离级别:

        注解配置声明式事务:

        配置类代替xml中的注解事务支持:


部分图片来自百战程序员

Spring简介

Spring是一个开源框架,为简化企业级开发而生。

它以IOC(控制反转)AOP(面向切面)为思想内核,提供了控制层SpringMVC、数据层SpringData、服务层事务管理等众多技术,并可以整合众多第三方框架。

Spring将很多复杂的代码变得优雅简洁,有效的降低代码的耦合度,极大的方便项目的后期维护、升级和扩展。

Spring官网地址: Spring | Home

Spring结构

Spring框架根据不同的功能被划分成了多个模块,这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块

  • Core Container:Spring核心模块任何功能的使用都离不开该模块,是其他模块建立的基础。
  • Data Access/Integration:该模块提供了数据持久化的相应功能。
  • Web:该模块提供了web开发的相应功能。
  • AOP:提供了面向切面编程实现
  • Aspects:提供与AspectJ框架的集成,该框架是一个面向切面编程框架。
  • Instrumentation:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
  • Messaging:为Spring框架集成一些基础的报文传送应用
  • Test:提供与测试框架的集成

IOC

        Spring容器类型

容器接口:

  • BeanFactory:BeanFactory是Spring容器中的顶层接口,它可以对Bean对象进行管理。
  • ApplicationContext:ApplicationContext是BeanFactory的子接口。它除了继承 BeanFactory的所有功能外,还添加了对国际化、资源访问、事件传播等方面的良好支持。

ApplicationContext有以下三个常用实现类:

容器实现类

  • ClassPathXmlApplicationContext:该类可以从项目中读取配置文件
  • FileSystemXmlApplicationContext:该类从磁盘中读取配置文件
  • AnnotationConfigApplicationContext:使用该类不读取配置文件,而是会读取注解

        bean对象创建方式

1、默认根据POJO类的无参构造方法创建
<!--使用构造方法创建bean-->
<bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl"></bean>

2、如果POJO类没有无参构造,那么就需要使用工厂类创建(普通方法)
<!--使用工厂类的方法创建bean-->
<bean id="studentDaoFactory" class="com.itbaizhan.dao.StudentDaoFactory"></bean>
<bean id="studentDao" factory-bean="studentDaoFactory" factory-method="getStudentDao"></bean>

3、使用工厂类的静态方法创建
<!--使用工厂的的静态方法创建bean-->
<bean id="studentDao" class="com.itbaizhan.dao.StudentDaoFactory2" factory-method="getStudentDao"></bean>

        对象的创建策略

Spring通过配置<bean>中的scope属性设置对象的创建策略,共有五种创建策略:

  • singleton:单例默认策略。

整个项目只会创建一个对象,通过<bean>中的lazy-init属性可以设置单例对象的创建时机:
lazy-init="false"(默认):立即创建,在容器启动时会创建配置文件中的所有Bean对象。
lazy-init="true":延迟创建,第一次使用Bean对象时才会创建。
配置单例策略:

<bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2" scope="singleton"

lazy-init="false"></bean>

  • prototype:多例,每次从容器中获取时都会创建对象。
<!-- 配置多例策略 -->
<bean id="studentDao"  class="com.itbaizhan.dao.StudentDaoImpl2" scope="prototype"></bean>

  • request:每次请求创建一个对象,只在web环境有效。
  • session:每次会话创建一个对象,只在web环境有效。
  • gloabal-session:一次集群环境的会话创建一个对象,只在web环境有效。

        对象的销毁方式

对象的创建策略不同,销毁时机也不同:

  • singleton:对象随着容器的销毁而销毁。
  • prototype:使用JAVA垃圾回收机制销毁对象。
  • request:当处理请求结束,bean实例将被销毁。
  • session:当HTTP Session最终被废弃的时候,bean也会被销毁掉。
  • gloabal-session:集群环境下的session销毁,bean实例也将被销毁。

        生命周期方法

bean对象的生命周期包含

创建——使用——销毁

,Spring可以配置Bean对象在创建和销毁时自动执行的方法:

 

配置生命周期方法

<!-- init-method:创建对象时执行的方法  destroy-method:销毁对象时执行的方法  -->


<bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2" scope="singleton"

   init-method="init" destroy-method="destory"></bean>

        获取bean对象的方式

 1、通过id/name获取

配置文件
<bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2"></bean>
<bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2"></bean>
获取对象
StudentDao studentDao=(StudentDao) ac.getBean("studentDao");

2、通过类型获取 

配置文件
<bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2"></bean>
获取对象
StudentDao studentDao2=ac.getBean(StudentDao.class);

可以看到使用类型获取不需要强转。 

3、通过id/name+类型获取 

虽然使用类型获取不需要强转,但如果在容器中有一个接口的多个实现类对象,则获取时会报错,此时需要使用类型+id/name获取 

配置文件
<bean name="studentDao" class="com.itbaizhan.dao.StudentDaoImpl2"></bean>
<beann ame="studentDao1" class="com.itbaizhan.dao.StudentDaoImpl"></bean>
获取对象
StudentDao studentDao2 = ac.getBean("studentDao",StudentDao.class);

        入门案例 

代码编写步骤

  1. 创建POJO类、持久层接口和实现类
  2. 编写配置文件,将实现类注入到Spring容器中
  3. 测试

1、创建Maven工程,引入依赖

<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.13</version>
  </dependency>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>
</dependencies>

2、创建POJO类、持久层接口和实现类

package com.itbaizhan.pojo;

public class Student {
    private int sid;
    private String sname;
    private int age;
    private String sex;

    public Student(int sid, String sname, int age, String sex) {
        this.sid = sid;
        this.sname = sname;
        this.age = age;
        this.sex = sex;
    }

    public Student() {
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}
package com.itbaizhan.dao;


import com.itbaizhan.pojo.Student;
import org.springframework.stereotype.Repository;


public interface StudentDao {


    // 根据id查询学生
    Student findById(int id);
}
package com.itbaizhan.dao;


import com.itbaizhan.pojo.Student;
import org.springframework.stereotype.Repository;

@Repository
public class StudentDaoImpl implements StudentDao {

    @Override
    public Student findById(int id) {
        return new Student(1,"路飞",16,"男");
    }
}

 3、编写bean.xml配置文件,配置文件中配置需要Spring帮我们创建的对象。

 (id属性就是获取对象的key,class代表获取哪个对象)

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


  <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl"></bean>
</beans>

 测试从Spring容器中获取对象。 

public class TestContainer{
  @Test
  public void t1(){
    // 创建Spring容器
    Application Contextac = new ClassPathXmlApplicationContext("bean.xml");
    // 从容器获取对象
    StudentDaostudent Dao1=(StudentDao) ac.getBean("studentDao");
    StudentDaostudent Dao2=(StudentDao) ac.getBean("studentDao");
    System.out.println(studentDao1.hashCode());
    System.out.println(studentDao2.hashCode());
    System.out.println(studentDao1.findById(1));
   }
}

        依赖注入DI

依赖注入(Dependency Injection,简称DI),它是Spring控制反转思想的具体实现。

控制反转将对象的创建交给了Spring,但是对象中可能会依赖其他对象。比如service类中要有dao类的属性,我们称service依赖于dao。之前需要手动注入属性值,

代码如下:

                三种依赖注入的方式:

                        Setter注入

配置文件中,给需要注入属性值的<bean>中设置<property>

1、创建持久层接口和持久层接口实现类和POJO类

public interface StudentDao {


    // 根据id查询学生
    Student findById(int id);
}
public class StudentDaoImpl implements StudentDao {

    @Override
    public Student findById(int id) {
        return new Student(1,"路飞",16,"男");
    }
}
public class Student {
    private int sid;
    private String sname;
    private int age;
    private String sex;

    public Student(int sid, String sname, int age, String sex) {
        this.sid = sid;
        this.sname = sname;
        this.age = age;
        this.sex = sex;
    }

    public Student() {
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}

2、创建逻辑层

这个逻辑层中有一个StudentDao类型的属性,一定要为这个属性创建getter、setter方法

package com.itbaizhan.service;

import com.itbaizhan.dao.StudentDao;
import com.itbaizhan.pojo.Student;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class StudentService {
    private StudentDao studentDao; // bean属性
    private String name; //字符串类型
    private int count; //基本数据类型
    private List<String> names; // 字符串类型List集合
    private List<Student> students1; // 对象类型List集合
    private Set<Student> students2; // 对象类型Set集合
    private Map<String,String> names2; // 字符串类型Map集合
    private Map<String,Student> students3; // 对象类型Map集合
    private Properties properties; //Properties类型

    public StudentDao getStudentDao() {
        return studentDao;
    }

    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public List<String> getNames() {
        return names;
    }

    public void setNames(List<String> names) {
        this.names = names;
    }

    public List<Student> getStudents1() {
        return students1;
    }

    public void setStudents1(List<Student> students1) {
        this.students1 = students1;
    }

    public Set<Student> getStudents2() {
        return students2;
    }

    public void setStudents2(Set<Student> students2) {
        this.students2 = students2;
    }

    public Map<String, String> getNames2() {
        return names2;
    }

    public void setNames2(Map<String, String> names2) {
        this.names2 = names2;
    }

    public Map<String, Student> getStudents3() {
        return students3;
    }

    public void setStudents3(Map<String, Student> students3) {
        this.students3 = students3;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public Student findById(int id){
        return studentDao.findById(id);
    }

    @Override
    public String toString() {
        return "StudentService{" +
                "studentDao=" + studentDao +
                ", name='" + name + '\'' +
                ", count=" + count +
                ", names=" + names +
                ", students1=" + students1 +
                ", students2=" + students2 +
                ", names2=" + names2 +
                ", students3=" + students3 +
                ", properties=" + properties +
                '}';
    }
}

3、编写bean.xml文件(将StudentService类添加到Spring容器中)

 <!--name:对象的属性名 ref:容器中对象的id值-->

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
              http://www.springframework.org/schema/context
              http://www.springframework.org/schema/context/spring-context.xsd">
        
        <bean id="studentDaoImpl" class="com.itbaizhan.dao.StudentDaoImpl"></bean>
        <bean id="studentService" class="com.itbaizhan.service.StudentService">
                <!-- 使用setter方法实现依赖注入 -->
                <property name="studentDao" ref="studentDaoImpl"></property>
        </bean>
</beans>

  4、测试

public class TestContainer {
    @Test
    public void t1(){
        //创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //获取对象
        StudentService studentService = (StudentService) ac.getBean("studentService");
        System.out.println(studentService.findById(1));
    }
}

                        Constructor注入

给需要注入属性值的<bean>中设置<constructor-arg>

持久层接口和实现类和POJO类和Service类和测试类都延续上面的

bean.xml文件需要重新编写,将property标签改为constructor-arg标签,对应的被注入类需要添加构造方法

    <bean id="studentDaoImpl" class="com.itbaizhan.dao.StudentDaoImpl"></bean>

        <bean id="studentService" class="com.itbaizhan.service.StudentService">
                <!-- 使用constructor方法实现依赖注入 -->
                <constructor-arg name="studentDao" ref="studentDaoImpl"></constructor-arg>
        </bean>
public class TestContainer {
    @Test
    public void t1(){
        //创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //获取对象
        StudentService studentService = (StudentService) ac.getBean("studentService");
        System.out.println(studentService.findById(1));
    }
}

                        自动注入

自动注入不需要在<bean>标签中添加其他标签注入属性值,而是自动从容器中找到相应的bean对象设置为属性值。

自动注入有两种配置方式:

  • 全局配置:在<beans>中设置default-autowire属性可以定义所有bean对象的自动注入策略。
  • 局部配置:在<bean>中设置autowire属性可以定义当前bean对象的自动注入策略。

autowire的取值如下:

  • no:不会进行自动注入。
  • default:全局配置default相当于no,局部配置default表示使用全局配置
  • byName:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供set方法。
  • byType:在Spring容器中查找类型与属性类型相同的bean,并进行注入。需要提供set方法。
  • constructor:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供构造方法。

只需要修改bean.xml配置文件即可

<bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl"></bean> 
<!-- 使用自动注入实现依赖注入 -->
        <bean id="studentService" class="com.itbaizhan.service.StudentService" autowire="byName"></bean>

测试 

public class TestContainer {
    @Test
    public void t1(){
        //创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //获取对象
        StudentService studentService = (StudentService) ac.getBean("studentService");
        System.out.println(studentService.findById(1));
    }
}

                注入bean类型

使用property标签

name:对象的属性名  ref:容器中对象的id值

        <bean id="studentDaoImpl" class="com.itbaizhan.dao.StudentDaoImpl"></bean>
        <bean id="studentService" class="com.itbaizhan.service.StudentService">
                <!-- 使用setter方法实现依赖注入 -->
                <property name="studentDao" ref="studentDaoImpl"></property>
        </bean>

                注入基本数据类型、字符串类型

使用value标签

name:属性名  value:属性值

        <bean id="studentDaoImpl" class="com.itbaizhan.dao.StudentDaoImpl"></bean>
        <bean id="studentService" class="com.itbaizhan.service.StudentService">
                <!-- 注入bean类型 -->
                <property name="studentDao" ref="studentDaoImpl"></property>
                <!-- 注入基本数据类型或字符串 -->
                <property name="name" value="路飞"></property>
        </bean>

                注入list、set

根据StudentService类我们可以看到有List、Set类型的属性

package com.itbaizhan.service;

import com.itbaizhan.dao.StudentDao;
import com.itbaizhan.pojo.Student;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class StudentService {
    private StudentDao studentDao; // bean属性
    private String name; //字符串类型
    private int count; //基本数据类型
    private List<String> names; // 字符串类型List集合
    private List<Student> students1; // 对象类型List集合
    private Set<Student> students2; // 对象类型Set集合
    private Map<String,String> names2; // 字符串类型Map集合
    private Map<String,Student> students3; // 对象类型Map集合
    private Properties properties; //Properties类型

    public StudentDao getStudentDao() {
        return studentDao;
    }

    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public List<String> getNames() {
        return names;
    }

    public void setNames(List<String> names) {
        this.names = names;
    }

    public List<Student> getStudents1() {
        return students1;
    }

    public void setStudents1(List<Student> students1) {
        this.students1 = students1;
    }

    public Set<Student> getStudents2() {
        return students2;
    }

    public void setStudents2(Set<Student> students2) {
        this.students2 = students2;
    }

    public Map<String, String> getNames2() {
        return names2;
    }

    public void setNames2(Map<String, String> names2) {
        this.names2 = names2;
    }

    public Map<String, Student> getStudents3() {
        return students3;
    }

    public void setStudents3(Map<String, Student> students3) {
        this.students3 = students3;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public Student findById(int id){
        return studentDao.findById(id);
    }

    @Override
    public String toString() {
        return "StudentService{" +
                "studentDao=" + studentDao +
                ", name='" + name + '\'' +
                ", count=" + count +
                ", names=" + names +
                ", students1=" + students1 +
                ", students2=" + students2 +
                ", names2=" + names2 +
                ", students3=" + students3 +
                ", properties=" + properties +
                '}';
    }
}

1、property标签使用list标签注入list集合 

                         注入简单数据类型list集合

<bean id="studentDaoImpl" class="com.itbaizhan.dao.StudentDaoImpl"></bean>
        <bean id="studentService" class="com.itbaizhan.service.StudentService">
                <!-- 注入bean类型 -->
                <property name="studentDao" ref="studentDaoImpl"></property>
                <!-- 注入基本数据类型或字符串 -->
                <property name="name" value="路飞"></property>
                <property name="count" value="1"></property>
                <!-- 注入简单数据类型list -->
                <property name="names">
                        <list>
                                <value>索隆</value>
                                <value>乔巴</value>
                        </list>
                </property>
        </bean>

测试:

public class TestContainer {
    @Test
    public void t1(){
        //创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //获取对象
        StudentService studentService = (StudentService) ac.getBean("studentService");
        System.out.println(studentService);
    }
}

                        注入bean类型list集合

<bean id="studentDaoImpl" class="com.itbaizhan.dao.StudentDaoImpl"></bean>
        <bean id="studentService" class="com.itbaizhan.service.StudentService">
                <!-- 注入bean类型 -->
                <property name="studentDao" ref="studentDaoImpl"></property>
                <!-- 注入基本数据类型或字符串 -->
                <property name="name" value="路飞"></property>
                <property name="count" value="1"></property>
                <!-- 注入简单数据类型list -->
                <property name="names">
                        <list>
                                <value>索隆</value>
                                <value>乔巴</value>
                        </list>
                </property>
                <!-- 注入bean类型list -->
                <property name="students1">
                        <list>
                                <bean class="com.itbaizhan.pojo.Student">
                                        <property name="sid" value="1"></property>
                                        <property name="sname" value="索隆"></property>
                                        <property name="sex" value="男"></property>
                                        <property name="age" value="16"></property>
                                </bean>
                                <bean class="com.itbaizhan.pojo.Student">
                                        <property name="sid" value="1"></property>
                                        <property name="sname" value="娜美"></property>
                                        <property name="sex" value="女"></property>
                                        <property name="age" value="20"></property>
                                </bean>
                        </list>
                </property>
        </bean>

测试

public class TestContainer {
    @Test
    public void t1(){
        //创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //获取对象
        StudentService studentService = (StudentService) ac.getBean("studentService");
        System.out.println(studentService);
    }
}

  

                注入map

简单数据类型map集合:

简单数据类型Map集合:
<bean id="studentService" class="com.itbaizhan.service.StudentService">
<!-- 简单数据类型map集合 name:属性名 -->
  <property name="names2">
    <map>
      <entry key="student1" value="bz"/>
      <entry key="student2" value="sxt"/>
    </map>
  </property>
</bean>

对象类型map集合

对象类型Map集合:
<bean id="studentService" class="com.itbaizhan.service.StudentService">
<!-- 对象类型map集合 name:属性名 -->
  <property name="students3">
    <map>
      <entry key="student1" value-ref="s1"/>
      <entry key="student2" value-ref="s2"/>
    </map>
  </property>
</bean>
<bean id="s1" class="com.itbaizhan.domain.Student">
  <property name="id" value="1"/>
  <property name="name" value="尚学堂"/>
  <property name="address" value="北京"/>
</bean>
<bean id="s2"class="com.itbaizhan.domain.Student">
  <property name="id" value="2"/>
  <property name="name" value="百战"/>
  <property name="address" value="北京"/>
</bean>

                注入Properties对象

注入Properties对象
<bean id="studentService" class="com.itbaizhan.service.StudentService">
  <property name="properties">
    <props>
      <prop key="配置1">值1</prop>
      <prop key="配置2">值2</prop>
    </props>
  </property>
</bean>

        注解实现IOC

                准备工作

注解配置和xml配置对于Spring的IOC要实现的功能都是一样的,只是配置的形式不一样。

  1. 创建一个新的Spring项目。(还是使用之前的)
  2. 编写pojo,dao,service类。(还是使用之前的)
  3. 编写空的配置文件,如果想让该文件支持注解,需要添加新的约束:
  4. 并且需要配置扫描包,只有被扫描的包的注解才能生效
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        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
                  Index of /schema/context
                  http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 要在配置文件中配置扫描的包,扫描到该注解才能生效。 -->
        <context:component-scan base-package="包名"></context:component-scan>
    
    </beans>

                @Component

作用:用于创建对象,放入Spring容器,相当于<bean id="" class="">

位置:类上方

1、类上添加注解

 2、测试

public class TestStudentService {
    @Test
    public void t1(){
        //创建容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        StudentService studentService = (StudentService) ac.getBean("studentService");
        System.out.println(studentService);
    }

}

         

注意:

  • 要在配置文件中配置扫描的包,扫描到该注解才能生效
    <context:component-scan base-package="com.itbaizhan"></context:component-scan>
  • @Component注解配置bean的默认id是首字母小写的类名也可以手动设置bean的id值。

 

                @Repository、@Service、@Controller

作用:这三个注解和@Component的用法和作用一样,

使用它们是为了区分该类属于什么层

位置:

  • @Repository用于Dao层
  • @Service用于Service层
  • @Controller用于Controller层

                @Scope

作用:指定bean的创建策略

位置:类上方

 

取值:

singleton

prototype

request

session

globalsession

 

                @Autowired

作用:从容器中查找符合属性类型的对象自动注入属性中。用于代替<bean>中的依赖注入配置。

位置:属性上方setter方法上方构造方法上方

注意: 

  1. @Autowired写在属性上方进行依赖注入时,可以省略setter方法。

容器中没有对应类型的对象会报错 

// 如果StudentDaoImpl没有放到容器中会报错
//@Component("studentDao")
public class StudentDaoImpl implements StudentDao{
  public StudentfindById(int id) {
    // 模拟根据id查询学生
    return new Student(1,"百战程序员","北京");
   }
}

容器中有多个对象匹配类型时会找beanId等于属性名的对象,找不到会报错 

// 如果容器中都多个同类型对象,会根据id值等于属性名找对象
@Component("studentDao")
public class StudentDaoImpl implements StudentDao{
  public Student findById(int id) {
    // 模拟根据id查询学生
    return new Student(1,"百战程序员","北京");
   }
}
@Component
public class StudentDaoImpl2 implements StudentDao{
  public Student findById(int id) {
    // 模拟根据id查询学生
    return new Student(1,"百战程序员","北京");
   }
}

                @Qualifier

作用:在按照类型注入对象的基础上再按照bean的id注入

位置:属性上方

注意:@Qualifier必须和@Autowired一起使用

                @Value

作用:注入String类型和基本数据类型的属性值。

位置:属性上方

 

                @Configuration

此时基于注解的IOC配置已经完成,但是我们依然离不开Spring的xml配置文件。接下来我们脱离bean.xml,使用纯注解实现IOC

 

在真实开发中,我们一般还是会保留xml配置文件,很多情况下使用配置文件更加方便。

纯注解实现IOC需要一个Java类代替xml文件。这个Java类上方需要添加@Configuration,表示该类是一个配置类,作用是代替配置文件。

package com.itbaizhan;

import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {

}

                @ComponentScan

作用:指定spring在初始化容器时扫描的包

位置:配置类上方

只有被扫描的包下的注解才会生效

package com.itbaizhan;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.itbaizhan")
public class SpringConfig {

}

而如果使用配置类代替配置文件,那么创建容器对象使用的ApplicationContext实现类就需要使用AnnotationConfigApplicationContext,并传递配置类的class对象 

                @PropertySource

作用:代替配置文件中的<context:property-placeholder>扫描配置文件

位置:配置类上方

注意:配置文件位置前要加关键字classpath

                @Bean

作用:将方法的返回值对象放入Spring容器中。如果想将第三方类的对象放入容器,可以使用@Bean

位置:配置类的方法上方

属性:name:给bean对象设置id

注意:@Bean修饰的方法如果有参数spring会根据参数类型从容器中查找可用对象

举例:如果想将jdbc连接对象放入Spring容器,我们无法修改Connection源码添加@Component,此时就需要使用将@Bean该对象放入Spring容器

package com.itbaizhan;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import java.sql.Connection;
import java.sql.DriverManager;

@Configuration
@ComponentScan("com.itbaizhan")
@PropertySource("classpath:db.properties")
public class SpringConfig {

    @Bean("connection")
    public Connection getConnection(){
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            return DriverManager.getConnection("jdbc:mysql:///mysql","root","root");
        }catch (Exception e){
            return null;
        }
    }
}

                @Import

作用:如果配置过多,会有多个配置类,该注解可以为主配置类导入其他配置类

位置:主配置类上方

        Spring整合MyBatis

                搭建环境

1、引入Junit和Spring整合Junit依赖

<!-- junit,如果Spring5整合junit,则junit版本至少在4.12以上 -->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>
<!-- spring整合测试模块 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>5.3.13</version>
</dependency>

                编写配置文件

1.在Spring整合MyBatis中,Spring提供的对SqlSessionFactory的封装类是SqlSessionFactoryBean

2.在Spring整合MyBatis中,Spring提供的对SqlSession的封装类是SqlSessionTemplate

1、 编写数据库配置文件db.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///student
jdbc.username=root
jdbc.password=root

2、创建MyBatis配置文件SqlMapConfig.xml,数据源、扫描接口都交由Spring管理,不需要在MyBatis配置文件中设置。

<!-- 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>
</configuration>
<!-- spring配置文件 -->

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    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
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd">
  <!-- 包扫描 -->
  <context:component-scan base-package="com.itbaizhan"></context:component-scan>


  <!-- 读取配置文件 -->
  <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
  <!-- 创建druid数据源对象 -->
  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"></property>
    <property name="url" value="${jdbc.url}"></property>
    <property name="username" value="${jdbc.username}"></property>
    <property name="password" value="${jdbc.password}"></property>
  </bean>


  <!-- Spring创建封装过的SqlSessionFactory -->
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
  </bean>


  <!-- Spring创建封装过的SqlSession -->
  <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
  </bean>


</beans>

                编写POJO类、持久层、service层

准备数据库
CREATE DATABASE `student`;
USE `student`;
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(255) DEFAULT NULL,
 `sex` varchar(10) DEFAULT NULL,
 `address` varchar(255) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

insert into `student`(`id`,`name`,`sex`,`address`) values (1,'百战程序员','男','北京'),(2,'北京尚学堂','女','北京');
准备实体类
public class Student {
  private int id;
  private String name;
  private String sex;
  private String address;
  
  // 省略构造方法/getter/setter/tostring
}
编写持久层接口

@Repository
public interface StudentDao {
  // 查询所有学生
  @Select("select * from student")
  List<Student> findAll();


  // 添加学生
  @Insert("insert into student values(null,#{name},#{sex},#{address})")
  void add(Student student);
}
编写service类
@Service
public class StudentService {
  // SqlSession对象
  @Autowired
  private SqlSessionTemplate sqlSession;


  // 使用SqlSession获取代理对象
  public List<Student> findAllStudent(){
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    return studentDao.findAll();
   }
}

 注:使用SqlSessionTemplate创建代理对象还是需要注册接口或者映射文件的。

在MyBatis配置文件注册接口
<configuration>
  <mappers>
    <mapper class="com.itbaizhan.dao.StudentDao"></mapper>
  </mappers>
</configuration>
创建sqlSessionFactory时指定MyBatis配置文件
<!-- 创建Spring封装过的SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource"></property>
  <property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
</bean>

                Spring整合Junit进行单元测试

之前进行单元测试时都需要手动创建Spring容器,能否在测试时让Spring自动创建容器呢?

引入Junit和Spring整合Junit依赖 

<!-- junit,如果Spring5整合junit,则junit版本至少在4.12以上 -->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>
<!-- spring整合测试模块 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>5.3.13</version>
</dependency>
编写测试类
// JUnit使用Spring方式运行代码,即自动创建spring容器。
@RunWith(SpringJUnit4ClassRunner.class)
// 告知创建spring容器时读取哪个配置类或配置文件
// 配置类写法:@ContextConfiguration(classes=配置类.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class StudentServiceTest {
  @Autowired
  private StudentService studentService;
  
  @Test
  public void testFindAll(){
    List<Student> allStudent = studentService.findAllStudent();
    allStudent.forEach(System.out::println);
   }
}

                自动创建代理对象   

Spring提供了MapperScannerConfigurer对象,该对象可以自动扫描包创建代理对象,并将代理对象放入容器中,此时不需要使用SqlSession手动创建代理对象

AOP

AOP的全称是Aspect Oriented Programming,即面向切面编程。是实现功能统一维护的一种技术,它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

  • 作用:在不修改源码的基础上,对已有方法进行增强。
  • 实现原理:动态代理技术
  • 优势:减少重复代码、提高开发效率、维护方便
  • 应用场景:事务处理、日志管理、权限控制、异常处理等方面。

我们使用的是AspectJ框架实现的AOP,所以需要引入依赖

     <!-- AspectJ -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>

相关术语

为了更好地理解AOP,就需要对AOP的相关术语有一些了解

名称

说明

Joinpoint(连接点)

指能被拦截到的点,在Spring中只有方法能被拦截。

Pointcut(切点)

指要对哪些连接点进行拦截,即被增强的方法。

Advice(通知)

指拦截后要做的事情,即切点被拦截后执行的方法。

Aspect(切面)

切点+通知称为切面

Target(目标)

被代理的对象

Proxy(代理)

代理对象

Weaving(织入)

生成代理对象的过程

配置AOP流程

1、编写通知类

2、在bean.xml配置文件中,将通知类写入

3、配置aop

        1、编写aop标签

        2、配置切面以及切点的通知类

                1、配置切点

                2、配置通知类调用的方法和通知的切点id

通知类型: 

AOP有以下几种常用的通知类型:

通知类型

描述

前置通知

在方法执行前添加功能

后置通知

在方法正常执行后添加功能

异常通知

在方法抛出异常后添加功能

最终通知

无论方法是否抛出异常,都会执行该通知

环绕通知

在方法执行前后添加功能

通知的各种类型的标签

<!-- 配置AOP -->
<aop:config>
  <!-- 配置切面 -->
  <aop:aspect ref="myAspectJAdvice">
    <!-- 配置切点 -->
    <aop:pointcut id="myPointcut" expression="execution(* com.itbaizhan.dao.UserDao.*(..))"/>

    <!-- 前置通知 -->
    <aop:before method="myBefore" pointcut-ref="myPointcut"></aop:before>
    <!-- 后置通知 -->
    <aop:after-returning method="myAfterReturning"pointcut-ref="myPointcut"/>
    <!-- 异常通知 -->
    <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="ex"/>
    <!-- 最终通知 -->
    <aop:after method="myAfter" pointcut-ref="myPointcut"></aop:after>
    <!-- 环绕通知 -->
    <aop:around method="myAround" pointcut-ref="myPointcut"></aop:around>
  </aop:aspect>
</aop:config>

切点表达式

使用AspectJ需要使用切点表达式配置切点位置,写法如下:

  • 标准写法:访问修饰符 返回值 包名.类名.方法名(参数列表)
  • 访问修饰符可以省略。
  • 返回值使用*代表任意类型。
  • 包名使用*表示任意包,多级包结构要写多个*,使用*..表示任意包结构
  • 类名和方法名都可以用*实现通配。

 

参数列表

  • 基本数据类型直接写类型
  • 引用类型写包名.类名
  • *表示匹配一个任意类型参数
  • ..表示匹配任意类型任意个数的参数
  • 全通配:* *..*.*(..)

注解配置AOP

Spring可以使用注解代替配置文件配置切面:

  1. 在xml中开启AOP注解支持
    <!-- 开启注解配置Aop -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  2. 在通知类上方加入注解@Aspect
  3. 在通知方法上方加入注解@Before/@AfterReturning/@AfterThrowing/@After/@Around

如果有多个el表达式是相同的那么可以编写一个方法,并在该方法头上添加@Pointcut注解

设置值为el表达式

然后就可以在通知方法上直接调用该方法就行了

事务

        案例:

                准备工作:

1、准备数据库

CREATE DATABASE `spring` ;
USE `spring`;
DROP TABLE IF EXISTS `account`;


CREATE TABLE `account` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(255) DEFAULT NULL,
 `balance` double DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


insert into `account`(`id`,`username`,`balance`) values (1,'张三',1000),(2,'李四',1000);

2、创建maven项目,引入依赖

<dependencies>
  <!--  mybatis  -->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
  </dependency>
  <!--  mysql驱动包  -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
  </dependency>
  <!--  druid连接池  -->
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
  </dependency>
  <!-- spring -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.13</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.3.13</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.13</version>
  </dependency>
  <!-- MyBatis与Spring的整合包,该包可以让Spring创建MyBatis的对象 -->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.6</version>
  </dependency>


  <!-- junit,如果Spring5整合junit,则junit版本至少在4.12以上 -->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>
  <!-- spring整合测试模块 -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.13</version>
    <scope>test</scope>
  </dependency>
</dependencies>

                创建配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    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
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd">
  <!-- 包扫描 -->
  <context:component-scan base-package="com.itbaizhan"></context:component-scan>
  <!-- 创建druid数据源对象 -->
  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql:///spring"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
  </bean>

<!-- Spring创建封装过的SqlSessionFactory -->
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
  </bean>

<!-- 该对象可以自动扫描持久层接口,并为接口创建代理对象 -->
	<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <!-- 配置扫描的接口包 -->
	  <property name="basePackage" value="com.itbaizhan.dao"></property>
	</bean>
</beans>

                编写代码步骤:

1、创建POJO类

2、创建持久层接口

3、创建逻辑层

4、编写配置文件

5、测试

                编写代码:

1、编写POJO类

public class Account{
private Integer id;
private String username;
private Double balance;
	省略构造方法、getter、setter
}

2、编写持久层接口 

 3、编写逻辑层

 测试 

此时测试代码如果出现异常那么就导致数据出错,张三钱没了,但是李四钱没有增加,此时就需要使用事务来使程序具有原子性 

模拟转账出错:

@Service
public class AccountService {

    @Autowired
    private AccountDao accountDao;

    public void transfer(int id1,int id2,double price){
        Account a1 = accountDao.findById(id1);
        a1.setBalance(a1.getBalance()-price);
        accountDao.update(a1);
        
        int i = 1/0;

        Account a2 = accountDao.findById(id2);
        a2.setBalance(a2.getBalance()+price);
        accountDao.update(a2);
    }
}

        事务管理方案:

在service层手动添加事务可以解决该问题:

@Autowired
private SqlSessionTemplate sqlSession;
public void transfer(int id1, int id2, double price) {
  try{
    // account1修改余额
    Account account1 = accountDao.findById(id1);
    account1.setBalance(account1.getBalance()-price);
    accountDao.update(account1);

    int i = 1/0; // 模拟转账出错

    // account2修改余额
    Account account2 = accountDao.findById(id2);
    account2.setBalance(account2.getBalance()+price);
    accountDao.update(account2); 
    sqlSession.commit();
   }catch(Exception ex){
    sqlSession.rollback();
   }
}

但在Spring管理下不允许手动提交和回滚事务。此时我们需要使用Spring的事务管理方案,在Spring框架中提供了两种事务管理方案:

  1. 编程式事务:通过编写代码实现事务管理。
  2. 声明式事务:基于AOP技术实现事务管理。

在Spring框架中,编程式事务管理很少使用,我们对声明式事务管理进行详细学习。

Spring的声明式事务管理在底层采用了AOP技术,其最大的优点在于无需通过编程的方式管理事务,只需要在配置文件中进行相关的规则声明,就可以将事务规则应用到业务逻辑中。

使用AOP技术为service方法添加如下通知:

        事务管理器:

Spring依赖事务管理器进行事务管理,事务管理器即一个通知类,我们为该通知类设置切点为service层方法即可完成事务自动管理。由于不同技术操作数据库,进行事务操作的方法不同。如:JDBC提交事务是connection.commit(),MyBatis提交事务是sqlSession.commit(),所以Spring提供了多个事务管理器。

事务管理器名称

作用

org.springframework.jdbc.datasource.DataSourceTransactionManager

针对JDBC技术提供的事务管理器。适用于JDBC和MyBatis。

org.springframework.orm.hibernate3.HibernateTransactionManager

针对于Hibernate框架提供的事务管理器。适用于Hibernate框架。

org.springframework.orm.jpa.JpaTransactionManager

针对于JPA技术提供的事务管理器。适用于JPA技术。

org.springframework.transaction.jta.JtaTransactionManager

跨越了多个事务管理源。适用在两个或者是多个不同的数据源中实现事务控制。

我们使用MyBatis操作数据库,接下来使用DataSourceTransactionManager进行事务管理。

        测试事务管理器:

引入依赖

<!-- 事务管理 -->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.3.13</version>
</dependency>
<!-- AspectJ -->
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.7</version>
</dependency>

 编写配置文件

<!-- 事务管理器  -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 进行事务相关配置 -->
<tx:advice id= "txAdvice">
  <tx:attributes>
    <tx:method name="*"/>
  </tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
  <!-- 配置切点 -->
  <aop:pointcut id="pointcut" expression="execution(* com.itbaizhan.service.*.*(..))"/>
  <!-- 配置通知 -->
  <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"></aop:advisor>
</aop:config>

 配置完事务过后,不管我们有没有出错他都会正常运行,出错自动回滚,没有出错提交事务

        事务控制API:

Spring进行事务控制的功能是由三个接口提供的,这三个接口是Spring实现的,在开发中我们很少使用到,只需要了解他们的作用即可:

PlatformTransactionManager接口

PlatformTransactionManager是Spring提供的事务管理器接口,所有事务管理器都实现了该接口。该接口中提供了三个事务操作方法:

  • TransactionStatus getTransaction(TransactionDefinition definition):获取事务状态信息。
  • void commit(TransactionStatus status):事务提交
  • void rollback(TransactionStatus status):事务回滚

 

TransactionDefinition接口

TransactionDefinition是事务的定义信息对象,它有如下方法:

  • String getName():获取事务对象名称。
  • int getIsolationLevel():获取事务的隔离级别。
  • int getPropagationBehavior():获取事务的传播行为。
  • int getTimeout():获取事务的超时时间。
  • boolean isReadOnly():获取事务是否只读。

 

TransactionStatus接口

TransactionStatus是事务的状态接口,它描述了某一时间点上事务的状态信息。它有如下方法:

  • void flush() 刷新事务
  • boolean hasSavepoint() 获取是否存在保存点
  • boolean isCompleted() 获取事务是否完成
  • boolean isNewTransaction() 获取是否是新事务
  • boolean isRollbackOnly() 获取是否回滚
  • void setRollbackOnly() 设置事务回滚

        事务的相关配置:

<tx:advice>中可以进行事务的相关配置:

<tx:advice id="txAdvice">

  <tx:attributes>

    <tx:method name="*"/>

    <tx:method name="find*" read-only="true"/>

  </tx:attributes>

</tx:advice>

<tx:method>中的属性: 

  • name:指定配置的方法。*表示所有方法,find*表示所有以find开头的方法。
  • read-only:是否是只读事务,只读事务不存在数据的修改,数据库将会为只读事务提供一些优化手段,会对性能有一定提升,建议在查询中开启只读事务。
  • timeout:指定超时时间,在限定的时间内不能完成所有操作就会抛异常。默认永不超时
  • rollback-for:指定某个异常事务回滚,其他异常不回滚。默认所有异常回滚。
  • no-rollback-for:指定某个异常不回滚,其他异常回滚。默认所有异常回滚。
  • propagation:事务的传播行为
  • isolation:事务的隔离级别

        事务的传播行为:

事务的传播行为:

事务传播行为是指多个含有事务的方法相互调用时,事务如何在这些方法间传播。

如果在service层的方法中调用了其他的service方法,假设每次执行service方法都要开启事务,此时就无法保证外层方法和内层方法处于同一个事务当中。

// method1的所有方法在同一个事务中
public void method1(){
  // 此时会开启一个新事务,这就无法保证method1()中所有的代码是在同一个事务中
  method2();
  System.out.println("method1");
}
public void method2(){
  System.out.println("method2");
}

事务的传播特性就是解决这个问题的,Spring帮助我们将外层方法和内层方法放入同一事务中。

传播行为

介绍

REQUIRED

默认。支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

SUPPORTS

支持当前事务,如果当前没有事务,就以非事务方式执行。

MANDATORY

支持当前事务,如果当前没有事务,就抛出异常。

REQUIRES_NEW

新建事务,如果当前存在事务,把当前事务挂起。

NOT_SUPPORTED

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

NEVER

以非事务方式执行,如果当前存在事务,则抛出异常。

NESTED

必须在事务状态下执行,如果没有事务则新建事务,如果当前有事务则创建一个嵌套事务

        事务的隔离级别:

事务隔离级别反映事务提交并发访问时的处理态度,隔离级别越高,数据出问题的可能性越低,但效率也会越低。

隔离级别

脏读

不可重复读

幻读

READ_UNCOMMITED(读取未提交内容)

Yes

Yes

Yes

READ_COMMITED(读取提交内容)

No

Yes

Yes

REPEATABLE_READ(重复读)

No

No

Yes

SERIALIZABLE(可串行化)

No

No

No

如果设置为DEFAULT会使用数据库的隔离级别。

  • SqlServer , Oracle默认的事务隔离级别是READ_COMMITED
  • Mysql的默认隔离级别是REPEATABLE_READ

        注解配置声明式事务:

Spring支持使用注解配置声明式事务。用法如下:

1、注册事务注解驱动

<!-- 注册事务注解驱动 -->
<tx:annotation-driven transaction-manager = "transactionManager"></tx:annotation-driven>

2、在需要事务支持的方法或类上加@Transactional

@Service
// 作用于类上时,该类的所有public方法将都具有该类型的事务属性
@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)
public class AccountService{
  @Autowired
  private AccountDao accountDao;
  /**
   * 转账
   * @param id1 转出人id
   * @param id2 转入人id
   * @param price 金额
   */
  // 作用于方法上时,该方法将都具有该类型的事务属性
  @Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT)
  public void transfer(int id1, int id2, double price) {
    // account1修改余额
    Account account1 = accountDao.findById(id1);
    account1.setBalance(account1.getBalance()-price);
    accountDao.update(account1);
    int i = 1/0; // 模拟转账出错
    // account2修改余额
    Accountaccount2=accountDao.findById(id2);
    account2.setBalance(account2.getBalance()+price);
    accountDao.update(account2);
   }
}

        配置类代替xml中的注解事务支持:

在配置类上方写@EnableTranscationManagement

并将要用的对象添加到Spring容器中

public class SpringConfig{
@Bean
public DataSource getDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
	druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
	druidDataSource.setUrl("jdbc:mysql:///spring");
	druidDataSource.setUsername("root");
	druidDataSource.setPassword("root");
	return druidDataSource;
}

@Bean
public SqlSessionFactoryBean getSqlSession(DataSourcedataSource){
	SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
	sqlSessionFactoryBean.setDataSource(dataSource);
	returns qlSessionFactoryBean;
}

@Bean
public MapperScannerConfigurer getMapperScanner(){
	MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
	mapperScannerConfigurer.setBasePackage("com.itbaizhan.dao");
	return mapperScannerConfigurer;
}

@Bean
public DataSourceTransactionManagerget TransactionManager(DataSourcedata Source){
	DataSourceTransaction Managermanager = new DataSourceTransactionManager();
	manager.setDataSource(dataSource);
	return manager;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值