spring总结

引言


原生web开发中存在哪些问题

  • 传统Web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)

  • 部分Java EE API较为复杂,使用效率低(例如:JDBC开发步骤)。

  • 侵入性强,移植性差(例如:DAO实现的更换,从Connection到SqlSession)。

Spring


Spring的概念

  • Spring框架是一个轻量级开放源代码JavaEE应用程序一站式框架,其主要核心为IOC和AOP
    • 轻量级开放源代码
      • 开源免费,轻量级框架是不依赖其他的框架能独立使用 (EJB)
    • 一站式
      • 表现层 Servlet SpringMVC
      • 业务层 Service Spring在整个web项目中起到承上启下的作用
      • 数据层 JDBC SpringDataJpa
    • IOC和AOP
      • IOC(控制反转) 以前创建对象 UserDao user = new UserDao();
        • 将对象的创建交给Spring框架(容器)
        • DI(依赖注入)
      • AOP(面向切面)
        • 在不修改原代码的情况,对象功能进行增强
        • 底层原理(动态代理)

Spring 作用

  • Spring是一个项目管理框架,同时也是一套Java EE解决方案。

  • Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。

  • Spring并未替代现有框架产品,而是将众多框架进行有机整合,简化企业级开发,俗称"胶水框架"。

Spring的组成

Spring架构由诸多模块组成,可分类为

  • 核心技术:依赖注入,事件,资源,i18n,验证,数据绑定,类型转换,SpEL,AOP
  • 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。
  • 数据访问:事务,DAO支持,JDBC,ORM,封送XML。
  • Spring MVC和 Spring WebFlux Web框架。
  • 集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
  • 语言:Kotlin,Groovy,动态语言。

在这里插入图片描述

spring的IOC底层实现原理

在这里插入图片描述

Spring快速入门


引入spring依赖

<dependencies>
        <!--
            如果是只是IOC的开发,那么只需要导入Spring的四个核心就可以了
                beans、core、context、expression
            Context中已经包含了其他依赖
        -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.6</version>
    </dependency>

</dependencies>

spring的配置文件

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        配置UserDao的信息
                id:对象的唯一标识
                class:对象的全限定名
     -->
    <bean id="userDao" class="com.qf.dao.UserDao"></bean>

</beans>

测试

创建Spring容器,获取对象实例

public class SpringTest {
    /**
     * Spring的开发步骤
     *   1、引入Spring依赖
     *   2、编写spring的配置文件(配置bean的信息)
     *   3、创建spring容器获取对象
     */
    @Test
    public void test01(){
        //获取Spring容器
        ApplicationContext ac
                = new ClassPathXmlApplicationContext("applicationContext.xml");
        //以前创建UserDao对象  UserDao userDao = new UserDao();
        //使用Spring容器获取对象
        UserDao userDao = (UserDao) ac.getBean("userDao");
        userDao.addUser();
    }
}

Spring的开发细节


BeanFactory的类间关系

  • SpringIOC容器类的结构
  • 最顶层的接口 BeanFactory
  • 子接口: ApplicationContext
    • ClassPathXmlApplicationContext 针对类路径下的xml文件配置的方式
    • AnnotationConfigApplicationContext 针对纯注解式开发配置的方式
    • FileSystemXmlApplicationContext 针对系统路径下的xml文件配置的方式
  • BeanFactory 和ApplicationContext 区别
    • BeanFactory使用的懒加载的形式,对象在使用的时候才会创建
    • ApplicationContext,当程序启动的时候会直接加载配置文件创建对象 (web推荐使用)
@Test
public void test01(){
    //创建Spring容器对象 ApplicationContext
    ApplicationContext ac =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    Object userDao = ac.getBean("userDao1");
    System.out.println(userDao);
}
@Test
public void test02(){
    //创建Spring容器对象 BeanFactory
    Resource resource = new ClassPathResource("applicationContext.xml");
    BeanFactory beanFactory = new XmlBeanFactory(resource);
    Object userDao1 = beanFactory.getBean("userDao1");
    System.out.println(userDao1);
}

getBean方法

  • getBean(“对象id标识”);
  • getBean(类对象);
  • getBean(“对象id标识”,类对象);
@Test
public void  test03(){
    //创建Spring容器对象 ApplicationContext
    ApplicationContext ac =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    //缺点:获取对象的时候需要强制转换
    //UserDao userDao = (UserDao) ac.getBean("userDao1");
    //userDao.addUser();
    //缺点:如果一个类有多个对象,则报错
    //UserDao userDao = ac.getBean(UserDaoImpl.class);
    //userDao.addUser();
    UserDaoImpl userDao1 = ac.getBean("userDao1", UserDaoImpl.class);
    UserDaoImpl userDao2 = ac.getBean("userDao2", UserDaoImpl.class);
    userDao1.addUser();
    userDao2.addUser();
}

bean标签中的scope属性

  • scope属性的取值
    • singleton :表示创建的对象是单例的(默认取值)
    • prototype :表示创建的对象是原型模型
    • 基于web
      • request :表示创建的对象再request范围内
      • session :表示创建的对象再session范围内
      • globalSession : Spring5.X弃用
<!-- 设置为单例模式(默认单例)   -->
<bean id="userDao2" class="com.qf.dao.impl.UserDaoImpl"></bean>
<!-- 设置为原型模式   -->
<bean id="userDao3" class="com.qf.dao.impl.UserDaoImpl" scope="prototype"></bean>
@Test
public void test04(){
    //创建Spring容器对象 ApplicationContext
    ApplicationContext ac =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDaoImpl userDao3 = ac.getBean("userDao3", UserDaoImpl.class);
    UserDaoImpl userDao4 = ac.getBean("userDao3", UserDaoImpl.class);
    System.out.println(userDao3);
    System.out.println(userDao4);
}

创建的对象三种方式

  • a、直接配置bean标签 id 对象的唯一标识 class 对象的全限定名
  • b、通过静态工厂来创建对象(了解)
  • c、通过非静态工厂来创建对象(了解)

静态工厂

<!--  方式二:静态工厂   class:工厂类     factory-method:静态工厂方法-->
<bean id="userDao5" class="com.qf.factory.UserDaoFactory" factory-method="getUserDao1"/>

非静态工厂

<!--  方式三:非静态工厂   factory-bean:工厂类的对象     factory-method:工厂非静态方法-->
<!-- 先创建工厂类的对象   -->
<bean id="factory" class="com.qf.factory.UserDaoFactory"></bean>
<bean id="userDao6" factory-bean="factory" factory-method="getUserDao2"></bean>
@Test
public void test05(){
    //创建Spring容器对象 ApplicationContext
    ApplicationContext ac =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDaoImpl userDao4 = ac.getBean("userDao4", UserDaoImpl.class);
    System.out.println(userDao4);
    UserDaoImpl userDao5 = ac.getBean("userDao5", UserDaoImpl.class);
    System.out.println(userDao5);
    UserDaoImpl userDao6 = ac.getBean("userDao6", UserDaoImpl.class);
    System.out.println(userDao6);
}

创建对象的生命周期

  • SpringIOC容器创建Bean的生命周期
  • 当创建的实例是单例模式:
    • 当Spring容器创建的时候,对象就会被创建
    • Spring容器关闭,就会销毁类的对象
  • 当创建的实例是原型模式:
    • Spring容器创建,并不会初始化,而是什么时候使用,什么时候初始化
    • Spring容器创建,不会销毁类的对象。而是交给Java内存回收机制

属性配置

<bean id="userDao7" class="com.qf.dao.impl.UserDaoImpl" 
      scope="prototype" init-method="init" destroy-method="destroy">
</bean>

UserDaoImpl

public class UserDaoImpl implements UserDao {
    @Override
    public int addUser() {
        System.out.println("UserDaoImpl添加。。");
        return 0;
    }
    public void init(){
        System.out.println("UserDaoImpl对象初始化");
    }
    public void destroy(){
        System.out.println("UserDaoImpl对象销毁");
    }
}

测试

/**
 * Spring容器创建对象的生命周期
 *     使用ApplicationContext作为Spring容器,
 *   当创建的实例是单例模式:
 *   	当Spring容器创建的时候,对象就会被创建
 *      Spring容器关闭,就会销毁类的对象
 *   当创建的实例是原型模式:
 *       Spring容器创建,并不会初始化,而是什么时候使用,什么时候初始化
 *       Spring容器创建,不会销毁类的对象。而是交给Java内存回收机制
 */
@Test
public void test06(){
    //创建Spring容器对象 ApplicationContext
    ClassPathXmlApplicationContext ac =
            new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDaoImpl userDao7 = ac.getBean("userDao7", UserDaoImpl.class);
    System.out.println(userDao7);

    //Spring容器关闭
    ac.close();
}

依赖注入(DI)


在Spring创建对象的同时,为其属性赋值,称之为依赖注入。

set方法注入

创建对象时,Spring工厂会通过Set方法为对象的属性赋值。

范例:定义一个Bean类型

public class User {
    private String name;
    private int age;
    private double money;
    private String hobby;
    private Date birthday;
    //省略set、get方法、构造方法
}

属性注入

<!-- 依赖注入:使用set方法进行注入 user.setName("") user.setBirthday(new Date())-->
<bean id="user" class="com.qf.pojo.User">
    <!--   简单类型注入(String+基本类型)     -->
    <property name="name" value="张三"/>
    <property name="age" value="10"/>
    <property name="hobby" value="打篮球"/>
    <property name="money" value="200.22"/>
    <!--   对象类型注入(String+基本类型)     -->
    <property name="birthday" ref="date"/>
</bean>
<bean id="date" class="java.util.Date">
    <property name="year" value="122"/>
</bean>

构造方法注入

范例:定义一个Bean类型 提供构造方法

public class User {
    private String name;
    private int age;
    private double money;
    private String hobby;
    private Date birthday;
    //省略set、get方法、构造方法
}

构造方法注入

<!-- 依赖注入:使用构造方法进行注入  User user = new User("cxk",30,3000,"打篮球",new Date())-->
<bean id="user1" class="com.qf.pojo.User">
    <!--  通过构造方法名注入       -->
    <!--        <constructor-arg name="name" value="李四"/>-->
    <!--        <constructor-arg name="age" value="20"/>-->
    <!--        <constructor-arg name="money" value="222.22"/>-->
    <!--        <constructor-arg name="hobby" value="跳"/>-->
    <!--        <constructor-arg name="birthday" ref="date"/>-->
    <!--  通过构造方法的参数类型注入      -->
    <!--        <constructor-arg type="java.lang.String" value="王五"/>-->
    <!--        <constructor-arg type="int" value="30"/>-->
    <!--        <constructor-arg type="double" value="333.33"/>-->
    <!--        <constructor-arg type="java.lang.String" value="唱"/>-->
    <!--        <constructor-arg type="java.util.Date" ref="date"/>-->
    <!--  通过构造方法的下标注入      -->
    <constructor-arg index="0" value="赵六"/>
    <constructor-arg index="1" value="60"/>
    <constructor-arg index="2" value="444.44"/>
    <constructor-arg index="3" value="rap"/>
    <constructor-arg index="4" ref="date"/>
</bean>

复杂类型注入

复杂类型指的是:list、set、map、array、properties等类型

定义复杂Bean类型

public class Person {
    private List<User> userList;
    private Map<String,String> map;
    private Set<String> set;
    private String[] arr;
    private Properties properties;
    //省略set、get方法、构造方法
}

给复杂类型注入属性值

<bean id="person" class="com.qf.pojo.Person">
    <property name="userList">
        <list>
            <bean class="com.qf.pojo.User"></bean>
            <bean class="com.qf.pojo.User"></bean>
            <bean class="com.qf.pojo.User"></bean>
        </list>
    </property>
    <property name="set">
        <set>
            <value>1</value>
            <value>2</value>
            <value>3</value>
        </set>
    </property>
    <property name="arr">
        <array>
            <value>a</value>
            <value>b</value>
            <value>c</value>
        </array>
    </property>
    <property name="map">
        <map>
            <entry key="k1" value="v1"></entry>
            <entry key="k2" value="v2"></entry>
            <entry key="k3" value="v3"></entry>
        </map>
    </property>
    <property name="properties">
        <props>
            <prop key="driver">com.mysql.jdbc.Dirver</prop>
            <prop key="url">jdbc:mysql:///java2101</prop>
        </props>
    </property>
</bean>

自定义类型的注入

范例:在UserServiceImpl中注入UserDaoImpl类型的属性

public class UserServiceImpl {
    private UserDaoImpl userDao;
    public void setUserDao(UserDaoImpl userDao) {
        this.userDao = userDao;
    }

    public void addUser(){
        userDao.addUser();
    }
}

<!--  属性注入演示  -->
<!-- 创建UserDaoImpl对象    -->
<bean id="userDao" class="com.qf.dao.UserDaoImpl"></bean>
<!-- 创建UserServiceImpl对象    -->
<bean id="userService" class="com.qf.service.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>

依赖注入案例


Spring整合DBUtils实现JDBC开发

EmpServiceImpl

public class EmpServiceImpl {
    private EmpDaoImpl empDao;

    public void setEmpDao(EmpDaoImpl empDao) {
        this.empDao = empDao;
    }

    public List<Emp> getAll(){
        try {
            return empDao.getAll();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
}

EmpDaoImpl

public class EmpDaoImpl {
    private QueryRunner queryRunner;

    public EmpDaoImpl(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    public List<Emp> getAll() throws SQLException {
        return queryRunner.query("select * from emp ",new BeanListHandler<Emp>(Emp.class));
    }
}

依赖注入配置

<!-- 使用Spring整合DBUtils工具类   -->
<!-- 创建EmpServiceImpl对象   -->
<bean id="empService" class="com.qf.service.EmpServiceImpl">
    <property name="empDao" ref="empDao"/>
</bean>
<!-- 创建EmpDaoImpl对象   -->
<bean id="empDao" class="com.qf.dao.EmpDaoImpl">
    <constructor-arg name="queryRunner" ref="queryRunner"></constructor-arg>
</bean>
<!-- 创建QueryRunner对象   -->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
    <constructor-arg name="ds" ref="dataSource"/>
</bean>
<!-- 创建DruidDataSource对象   -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <!--   注入DruidDataSource连接池的配置信息     -->
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql:///java2101"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
    <!--        <property name="initialSize" value="40"/>-->
    <!--        <property name="maxActive" value="100"/>-->
    <!--        <property name="minIdle" value="20"/>-->
    <!--        <property name="maxWait" value="600000"/>-->
</bean>

测试

@Test
public void test05(){
    //创建Spring容器对象
    ApplicationContext ac =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    EmpServiceImpl empService = ac.getBean("empService", EmpServiceImpl.class);
    List<Emp> empList = empService.getAll();
    empList.stream().forEach(System.out::println);
}

Spring注解开发


开启注解扫描

<!--  开启注解扫描  -->
<context:component-scan base-package="com.qf"/>

Component 注解

用于替换自建类型组件的 <bean…>标签;可以更快速的声明bean

@Component 注解的含义:创建类的对象,相当于xml中配置的bean标签

  • @Controller 针对于控制器层的类的注解
  • @Service 针对于业务层的类的注解
  • @Repository 针对持久层的类的注解
@Component
//@Service("empService")
//@Controller("empService")
//@Repository("empService")
public class EmpServiceImpl implements EmpService{ 
}

Autowired注解

主要用于属性注入

@Autowired 自动装配:将对象属性自动进行注入(类型注入)

@Qualifier(“empDaoImpl1”) 限定要自动注入的bean的id,一般和@Autowired联用

注意:@Autowired先会按照类型注入,如果这个类有多个实例,那么再按照名称注入,如果有多个实例那么需要使用Qualifier进行指定

@Component
public class EmpServiceImpl implements EmpService{
    @Autowired
    @Qualifier("empDaoImpl1")
    //@Resource(name="empDaoImpl2")
    private EmpDao empDao;
    public void addEmp(){
        empDao.addEmp();
    }
}

Resource注解

主要用于属性注入

@Resource 自动装配:将对象属性自动进行注入(名称注入)

注意:默认按照名称进行装配,如果找到指定的名称则直接注入。如果没有找到,那么再按类型称注入,如果有多个实例那么需要使用name属性进行指定

@Component//@Controller   @Service  @Repository 
public class EmpServiceImpl implements EmpService{
    @Resource(name="empDaoImpl2")
    private EmpDao empDao;

    public void addEmp(){
        empDao.addEmp();
    }
}

Spring与Mybaits整合


将 SqlSessionFactory、Mapper、Service 都交给SpringIOC容器管理

导入依赖

<dependencies>
    <!--   持久层依赖     -->
    <!--   数据库驱动依赖     -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <!--   Mybatis依赖     -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.4</version>
    </dependency>
    <!--   druid连接池依赖     -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>

    <!--  Spring依赖     -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.6</version>
    </dependency>
    <!--  Spring和Mybatis整合依赖     -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.2</version>
    </dependency>
    <!--  Spring整合持久层依赖     -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.6</version>
    </dependency>

    <!--  其他依赖     -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
</dependencies>

配置SqlSessionFactory

<!--  SqlSessionFactoryBean用来整合Mybatis的类  -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--    配置mybatis数据源连接池    -->
    <property name="dataSource" ref="dataSource"/>
    <!--    配置mybatis的配置信息    -->
    <!--    <property name="typeAliasesPackage" value="com.qf.pojo"/>  -->
    <!--    <property name="mapperLocations" value="classpath:com/qf/mapper/*.xml"/>  -->
    <!--    加载mybatis的配置文件    -->
    <property name="configLocation" value="classpath*:mybatis-config.xml"/>
</bean>

配置MapperScannerConfigurer

管理创建Mapper对象,存入工厂管理

  • 扫描所有Mapper接口,去构建Mapper实现

  • 将Mapper实现存入工厂管理

  • Mapper实现对象在工厂中的id是:“首字母小写的-接口的类名”,

    例如:UseMapper==>userMapper , OrderMapper==>orderMapper

<!--    
	MapperScannerConfigurer扫描所有的mapper映射文件,
	并且将Mapper接口创建并交给Spring容器  (要保证接口和映射的名称一致、路径一致)  
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.qf.mapper"/>
</bean>

配置Service

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper mapper;
    @Override
    public List<User> getAll() {
        return mapper.getAll();
    }
}

测试

public class SMTest {
    @Test
    public void test01(){
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        UserServiceImpl userServiceImpl = ac.getBean("userServiceImpl", UserServiceImpl.class);
        List<User> userList = userServiceImpl.getAll();
        userList.stream().forEach(System.out::println);
    }
}

Spring细节补充


导入外部配置文件

db.propertis

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///java2101
jdbc.username=root
jdbc.password=123456

applicationContext.xml

<!-- 引入外部的配置文件  -->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 获取外部的配置文件中的属性值  格式:${key}    注意:不能使用username -->

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

将配置文件分离

applicationContext.xml Spring主配置文件

applicationContext-dao.xml Spring整合Mybatis的配置文件

applicationContext-xxx.xml 未来Spring整合其他技术的配置文件

applicationContext.xml

<!-- 引入其他Spring的配置文件   -->
<!--整合Mybatis -->
<import resource="classpath:applicationContext-dao.xml"/>

applicationContext-dao.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 引入外部的配置文件  -->
    <context:property-placeholder location="classpath:db.properties"/>

    <!-- Spring整合Mybatis   -->
    <!-- 配置Druid数据源   -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 配置数据源   -->
        <property name="dataSource" ref="dataSource"/>
        <!-- mybatis的配置   -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.qf.mapper"/>
    </bean>
</beans>

Spring整合Mybatis后的日志

Mybatis核心配置文件

<configuration>
    <!--  设置mybatis日志    -->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--  设置类型别名    -->
    <typeAliases>
        <package name="com.qf.pojo"/>
    </typeAliases>
</configuration>

Spring配置文件中引入

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 配置数据源   -->
    <property name="dataSource" ref="dataSource"/>
    <!-- mybatis的配置 -->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>

Spring整合Junit


导入依赖

<!--  Spring单元测试依赖  -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.6</version>
</dependency>

测试类

@RunWith(SpringJUnit4ClassRunner.class)  //使用Spring来执行单元测试
@ContextConfiguration("classpath:applicationContext.xml") //加载spring的配置文件
public class SMTest {
    @Autowired
    private UserService userService;
    @Test
    public void test01(){
        List<User> userList = userService.getAll();
        userList.stream().forEach(System.out::println);
    }
}

纯注解开发


常见的注解(还有上面的几个注解):

  • @Configuration 表示当前这个类是Spring配置类
  • @ComponentScan 扫描指定包上的注解
  • @Import 引入其他的配置类
  • @Bean 将创建的对象添加到SpringIOC容器
  • @Value 将值注入到属性中
  • @PropertySource 导入外部的Properties配置文件键

主配置类

@Configuration   // 表示当前这个类是一个spring配置类(相当于applicationContext.xml)
@ComponentScan("com.qf")   //开启注解扫描
@Import(MybatisConfiguration.class)  //引入其他的spring的配置类
public class SpringConfiguration {
}

Spring整合Mybatis配置类

@Configuration
@PropertySource(value={"classpath:db.properties"})
@Import(MapperScannerConfigurerConfiguration.class)
public class MybatisConfiguration {
    @Value("${jdbc.driver}") //获取配置文件中值,并且注入变量
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Bean("dataSource")   //将当前方法返回的对象交给Spring容器
    public DataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //设置数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        //设置Mybatis的信息
        sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        return sqlSessionFactoryBean;
    }
}

因为配置文件加载顺序问题,所以需要将 MapperScannerConfigurer对象单独配置

@Configuration
public class MapperScannerConfigurerConfiguration {
    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.qf.mapper");
        return mapperScannerConfigurer;
    }
}

或者直接使用@MapperScan(“com.qf.mapper”)注解代替MapperScannerConfigurer类

@Configuration
@PropertySource(value={"classpath:db.properties"})
@MapperScan("com.qf.mapper")
public class MybatisConfiguration {
    @Value("${jdbc.driver}") //获取配置文件中值,并且注入变量
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Bean("dataSource")   //将当前方法返回的对象交给Spring容器
    public DataSource getDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(driver);
        druidDataSource.setUrl(url);
        druidDataSource.setUsername(username);
        druidDataSource.setPassword(password);
        return druidDataSource;
    }
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        //设置数据源
        sqlSessionFactoryBean.setDataSource(dataSource);
        //设置Mybatis的信息
        sqlSessionFactoryBean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
        return sqlSessionFactoryBean;
    }
}

SpringAOP开发


原理

在这里插入图片描述

动态代理

Spring的底层技术就是动态代理

  • 动态代理分类
    • JDK动态代理
    • Cglib动态代理

JDK动态代理

接口:定义代理对象和被代理对象需要做的事情

//定义代理类和被代理类共同做的事情
public interface Subject {
    void saleHouse();
}

实现类:目标对象(被增强对象)

public class RealSubject implements Subject{
    @Override
    public void saleHouse() {
        System.out.println("买房子....");
    }
}

代理工厂

public class JDKProxyFactory {

    /**
     * 使用jdk动态代理,获取代理对象
     * @param obj   被代理对象
     * @return      代理对象
     */
    public static Object getProxy(Object obj){
        //类加载器
        ClassLoader classLoader = JDKProxyFactory.class.getClassLoader();
        //被代理对象所实现的接口
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        //对对象的方法调用的拦截
        InvocationHandler invocationHandler = new InvocationHandler() {
            /**
             *
             * @param proxy             代理对象
             * @param method            拦截方法
             * @param args              拦截方法的参数
             * @return                  返回的是该方法的返回值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //做增强工作
                System.out.println("找客户。。。。");
                Object invoke = method.invoke(obj, args);
                System.out.println("办手续。。。。");
                return invoke;
            }
        };
        Object proxyInstance = 
            Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxyInstance;
    }
}

测试

@Test
public void test01(){
    //通过代理工厂获取代理对象,通过代理对象调用方法
    Subject proxy = (Subject) JDKProxyFactory.getProxy(new RealSubject());
    proxy.saleHouse();
    //说明代理对象不属于RealSubject类,而是Subject接口的实现类
    System.out.println(proxy instanceof RealSubject); //false
}

Cglib动态代理

类:目标对象(被增强对象) 不需要实现接口

public class RealSubject{
    public void saleHouse() {
        System.out.println("买房子....");
    }
}

动态代理工厂

public class CglibProxyFactory {
    public static Object getProxy(Object obj){
        //创建Cglib代理工具类对象
        Enhancer enhancer = new Enhancer();
        //设置代理对象的父类(代理对象和被代理对象是继承关系)
        enhancer.setSuperclass(obj.getClass());
        Callback callback = new MethodInterceptor() {
            /**
             *
             * @param o                代理对象
             * @param method           拦截的方法
             * @param objects          拦截的方法参数
             * @param methodProxy      方法的代理对象
             * @return                 方法的返回值
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, 
                                    MethodProxy methodProxy) throws Throwable {
                
                //做增强工作
                System.out.println("找客户cglib。。。。");
                Object invoke = method.invoke(obj, objects);
                System.out.println("办手续cglib。。。。");
                return invoke;
            }
        };
        //拦截被代理对象的方法
        enhancer.setCallback(callback);
        //创建代理对象
        Object o = enhancer.create();
        return o ;
    }
}

测试

 @Test
public void test02(){
    //被代理对象
    RealSubject realSubject = new RealSubject();
    //通过代理工厂获取代理对象,通过代理对象调用方法
    //代理对象
    RealSubject proxy = (RealSubject) CglibProxyFactory.getProxy(realSubject);
    proxy.saleHouse();
    //说明代理对象属于RealSubject类,而且就是RealSubject类的子类
    System.out.println(proxy instanceof RealSubject); //true
}

总结

JDK动态代理和Cglib动态代理

  • 两者都可以实现产生代理对象
  • JDK动态代理是java原生自带的,Cglib动态代理是第三方的
  • JDK动态代理实现代理:代理对象和目标对象之间实现同一个接口
  • Cglib动态代理实现代理:代理对象是目标对象的子类

SpringAOP

AOP专业术语

  • 连接点(Joinpoint): 表示一切能被增强方法
  • 切入点(Pointcut): 被增强的方法
  • 通知、增强(Advice): 增强的功能(通知的类型:前置通知、后置通知、异常通知、最终通知、环绕通知)
  • 目标对象(Target): 被代理对象
  • 织入(Weaving): 将通知加入连接点的过程
  • 代理(Proxy): 代理对象
  • 切面(Aspect): 连接点被增强的内容称之为切面(切入点+通知)

环境搭建

导入依赖

<!-- springAOP切面开发的依赖  -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.6</version>
</dependency>

基于XML配置

aop配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.qf.aop"/>
   <!-- SpringAOP的配置   -->
    <!-- 创建增强类   -->
    <bean id="advice" class="com.qf.advice.MyAdvice"></bean>
    <aop:config>
        <!-- SpringAOP的切面  -->
        <aop:aspect ref="advice">
            <!-- SpringAOP切入点 id:切入点的唯一标识   expression:切入点表达式 -->
            <!--
                  切入点表达式
                  方法的全称
                  public void com.qf.service.impl.UserServiceImpl.addUser();
                  * com.qf.service.impl.UserServiceImpl.addUser();
                  * com.qf.service..*.*(..);
            -->
            <aop:pointcut id="pc" expression="execution(* com.qf.service..*.*(..))"/>
            <!-- SpringAOP的通知配置  -->
			<!-- <aop:before method="before" pointcut-ref="pc"></aop:before>-->
			<!-- <aop:after method="after" pointcut-ref="pc"></aop:after>-->
   			<!-- <aop:after-throwing method="throwing" pointcut-ref="pc"/>-->
			<!-- <aop:after-returning method="returning" pointcut-ref="pc"/>-->
            <aop:around method="around" pointcut-ref="pc"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

增强(通知)类

//增强类
public class MyAdvice {
    
//    public void before(){
//        System.out.println("前置通知(在目标对象的方法之前执行)");
//    }
//    public void after(){
//        System.out.println("最终通知(在目标对象的方法之后执行(有没有异常都会执行))");
//    }
//    public void throwing(){
//        System.out.println("异常通知(在目标对象的方法之后执行(如果有异常则才会执行))");
//    }
//    public void returning(){
//        System.out.println("后置通知(在目标对象的方法之后执行(如果有异常则不会执行))");
//    }
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            System.out.println("前置通知(在目标对象的方法之前执行)");
            //System.out.println("方法本身");
            //执行目标对象的方法
            Object o = joinPoint.proceed();
            //获取到目标对象
            Object target = joinPoint.getTarget();
            System.out.println(target);
            //获取目标对象的方法
            Signature signature = joinPoint.getSignature();
            System.out.println(signature.getName());
            System.out.println("后置通知(在目标对象的方法之后执行(如果有异常则不会执行))");
            //通过调用目标对象的方法获取到返回值,并继续返回
            return o;
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("异常通知(在目标对象的方法之后执行(如果有异常则才会执行))");
        } finally {
            System.out.println("最终通知(在目标对象的方法之后执行(有没有异常都会执行))");
        }
        return null;
    }
}

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AOPTest {
    @Autowired
    private UserService userService;
    @Test
    public void test01(){
        int count = userService.addUser();
        System.out.println(count);
        //userService.deleteUser();
    }
}

结果

前置通知(在目标对象的方法之前执行)
addUser...
com.qf.service.impl.UserServiceImpl@543588e6
addUser
后置通知(在目标对象的方法之后执行(如果有异常则不会执行))
最终通知(在目标对象的方法之后执行(有没有异常都会执行))
100

基于注解配置

xml配置文件

<!-- 开启注解扫描 -->
<context:component-scan base-package="com.qf"/>
<!-- 开启AOP的注解扫描   -->
<aop:aspectj-autoproxy/>

增强类

@Component
@Aspect   //设置为切面
public class MyAdvice2 {
    @Pointcut("execution(* com.qf.service..*.*(..))")
    public void pc(){}

    @Before("pc()")
    public void before(){
        System.out.println("前置通知(在目标对象的方法之前执行)");
    }
    @After("execution(* com.qf.service..*.*(..))")
    public void after(){
        System.out.println("最终通知(在目标对象的方法之后执行(有没有异常都会执行))");
    }
    @AfterThrowing("execution(* com.qf.service..*.*(..))")
    public void throwing(){
        System.out.println("异常通知(在目标对象的方法之后执行(如果有异常则才会执行))");
    }
    @AfterReturning("execution(* com.qf.service..*.*(..))")
    public void returning(){
        System.out.println("后置通知(在目标对象的方法之后执行(如果有异常则不会执行))");
    }

//    @Around("execution(* com.qf.service..*.*(..))")
//    public Object around(ProceedingJoinPoint joinPoint){
//        try {
//            System.out.println("前置通知(在目标对象的方法之前执行)");
//            //System.out.println("方法本身");
//            //执行目标对象的方法
//            Object o = joinPoint.proceed();
//            //获取到目标对象
//            Object target = joinPoint.getTarget();
//            System.out.println(target);
//            //获取目标对象的方法
//            Signature signature = joinPoint.getSignature();
//            System.out.println(signature.getName());
//            System.out.println("后置通知(在目标对象的方法之后执行(如果有异常则不会执行))");
//            //通过调用目标对象的方法获取到返回值,并继续返回
//            return o;
//        } catch (Throwable e) {
//            e.printStackTrace();
//            System.out.println("异常通知(在目标对象的方法之后执行(如果有异常则才会执行))");
//        } finally {
//            System.out.println("最终通知(在目标对象的方法之后执行(有没有异常都会执行))");
//        }
//        return null;
//    }
}

测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class AOPTest {
    @Autowired
    private UserService userService;
    @Test
    public void test01(){
        userService.deleteUser();
    }
}

结果

前置通知(在目标对象的方法之前执行)
deleteUser...
后置通知(在目标对象的方法之后执行(如果有异常则不会执行))
最终通知(在目标对象的方法之后执行(有没有异常都会执行))

SpringAOP事务管理


事务

事务概念

事务是一个原子操作。是一个最小执行单元。可以由一个或多个SQL语句组成,在同一个事务当中,所有的SQL语句都成功执行时,整个事务成功,有一个SQL语句执行失败,整个事务都执行失败。

事务的特性

一个事务中的事务操作,不可再分,要一起成功要么一起失败

一个或多个事务的操作,在操作前后数据是一致的

一个事务的操作不能影响到另外一个事务

一旦事务条件,将会永久保存到数据库中

并发问题

事务并发时的安全问题

问题描述
脏读一个事务读取到另一个事务还未提交的数据。大于等于 read-commited 可防止
不可重复读一个事务内多次读取一行数据的相同内容,其结果不一致。大于等于 repeatable-read 可防止
幻读一个事务内多次读取一张表中的相同内容,其结果不一致。serialized-read 可防止

隔离级别

名称描述
default(默认值)(采用数据库的默认的设置) (建议)
read-uncommited读未提交
read-commited读提交 (Oracle数据库默认的隔离级别)
repeatable-read可重复读 (MySQL数据库默认的隔离级别)
serialized-read序列化读

隔离级别由低到高为:read-uncommited < read-commited < repeatable-read < serialized-read

隔离级别的特性

  • 安全性:级别越高,多事务并发时,越安全。因为共享的数据越来越少,事务间彼此干扰减少。

  • 并发性:级别越高,多事务并发时,并发越差。因为共享的数据越来越少,事务间阻塞情况增多。

自定义事务管理

技术:Spring+DBUtils、AOP

1)增强类 (负责对方法进行增强,开启事务、提交事务、回滚事务、关闭资源)

2)配置切面(增强+切入点)

注意:要保证这个操作中的Connetion是同一事务管理类

public class TXAdvice {
    @Autowired
    TXUtils txUtils;
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            //开启事务
            txUtils.start();
            //调用目标对象的方法
            Object obj = joinPoint.proceed();
            //提交事务
            txUtils.commit();
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            //回滚事务
            txUtils.rollback();
        }finally {
            //释放资源
            txUtils.close();
        }
        return null;
    }
}

事务管理工具类

//事务工具类
@Component
public class TXUtils {
    @Autowired
    DataSource dataSource;
    ThreadLocal<Connection> tl = new ThreadLocal<>();
    public Connection getConnection(){
        try {
            Connection conn = tl.get();
            if(conn == null){
                conn = dataSource.getConnection();
                //保存ThreadLocal中
                tl.set(conn);
            }
            return conn;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return null;
    }
    public void start(){
        Connection connection = getConnection();
        try {
            connection.setAutoCommit(false);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public void commit(){
        Connection connection = getConnection();
        try {
            connection.commit();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public void rollback(){
        Connection connection = getConnection();
        try {
            connection.rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
    public void close(){
        Connection connection = getConnection();
        try {
            connection.close();
            //在ThreadLocal移除Connection对象
            tl.remove();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

DBUtils实现的Dao操作

@Repository
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private QueryRunner qr;
    @Autowired
    TXUtils txUtils;

    @Override
    public Account selectAccount(String name) throws SQLException {
        return qr.query(txUtils.getConnection(),
                        "select * from tb_account where name = ?",
                        new BeanHandler<Account>(Account.class),
                        name);
    }

    @Override
    public int updateAccount(String name, double money) throws SQLException {
        String sql = "update tb_account set money = money + ? where name = ?";
        return qr.update(txUtils.getConnection(),
                         sql,
                         money,
                         name);
    }
}

业务层转账操作

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    @Override
    public String zhuanzhang(String fromName, String toName, double money) 
        throws SQLException {
        //1、判断用户是否存在
        Account account = accountDao.selectAccount(fromName);
        if(account == null){
            return "用户不存在";
        }
        //2、判断余额是否足够
        if(account.getMoney() < money){
            return "余额不足";
        }
        //3、判断对方账户是否存在
        Account toAccount = accountDao.selectAccount(toName);
        if(toAccount == null){
            return "对方用户不存在";
        }
        //4、我方扣钱
        accountDao.updateAccount(fromName,-money);
        System.out.println(10/0);
        //5、敌方加钱
        accountDao.updateAccount(toName,money);
        return "转账成功";
    }
}

SpringAOP事务管理配置

<bean id="advice" class="com.qf.advice.TXAdvice"></bean>

<!-- AOP配置-->
<aop:config>
    <!-- AOP切面配置-->
    <aop:aspect ref="advice">
        <!-- AOP切入点配置-->
        <aop:pointcut id="pc" expression="execution(* com.qf.service..*.*(..))"/>
        <!-- AOP通知配置-->
        <aop:around method="around" pointcut-ref="pc"/>
    </aop:aspect>
</aop:config>

注解式配置

<!-- 开启springAOP的注解扫描 -->
<aop:aspectj-autoproxy/>
@Component
@Aspect
public class TXAdvice { //通知增强
    @Autowired
    TXUtils txUtils;
    
    @Pointcut("execution(* com.qf.service..*.*(..))")
    public void pc(){}
    
    @Around("pc()")   
    public Object around(ProceedingJoinPoint joinPoint){
        try {
            //开启事务
            txUtils.start();
            //调用目标对象的方法
            Object obj = joinPoint.proceed();
            //提交事务
            txUtils.commit();
            return obj;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            //回滚事务
            txUtils.rollback();
        }finally {
            //释放资源
            txUtils.close();
        }
        return null;
    }    
}

声明式事务管理

导入依赖

<!-- Spring声明式事务管理依赖 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.3.6</version>
</dependency>
<!-- Spring声明式事务织入依赖 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

配置事务管理器

 <!-- 配置Spring事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源信息-->
    <property name="dataSource" ref="dataSource"/>
</bean>

注意:DataSourceTransactionManager 和 SqlSessionFactoryBean 要注入同一个DataSource,否则事务控制失败!!!

配置事务通知(增强)

<!-- 配置Spring事务的增强类-->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
    <!-- 配置Spring事务的增强类的信息-->
    <tx:attributes>
        <!--
           name:Spring:事务增强类增强的方法
           isolation:事务的隔离级别
           propagation:事务传播行为
                REQUIRED(以事务的方式运行,如有事务,加入事务,如无事务,则创建事务)   更新
                SUPPORTS(有事务则以事务方式运行,没有事务以非事务方式运行)            查询
           read-only:是否只读     查询true  更新false
           timeout:超时时间(默认为-1表示永久等待)   设置单位为‘秒’
           rollback-for:设置哪些异常回滚
           no-rollback-for:设置哪些异常不回滚

            增强方法的配置技巧
                通配符匹配
                    get*
                    select*
                    find*
                    query*
                    * 其他所有方法
           -->
        <tx:method name="get*" isolation="DEFAULT" 
                   propagation="SUPPORTS" read-only="true" timeout="-1"/>
        <tx:method name="select*" isolation="DEFAULT" 
                   propagation="SUPPORTS" read-only="true" timeout="-1"/>
        <tx:method name="find*" isolation="DEFAULT" 
                   propagation="SUPPORTS" read-only="true" timeout="-1"/>
        <tx:method name="query*" isolation="DEFAULT"
                   propagation="SUPPORTS" read-only="true" timeout="-1"/>

        <tx:method name="*" isolation="DEFAULT" 
                   propagation="REQUIRED" read-only="false" timeout="-1"/>
    </tx:attributes>
</tx:advice>
属性说明

isolation 隔离级别

名称描述
default(默认值)(采用数据库的默认的设置) (建议)
read-uncommited读未提交
read-commited读提交 (Oracle数据库默认的隔离级别)
repeatable-read可重复读 (MySQL数据库默认的隔离级别)
serialized-read序列化读

propagation 事务传播行为

名称作用
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

当涉及到事务嵌套(Service调用Service)时,可以设置:

  • SUPPORTS = 不存在外部事务,则不开启新事务;存在外部事务,则合并到外部事务中。(适合查询)

  • REQUIRED = 不存在外部事务,则开启新事务;存在外部事务,则合并到外部事务中。 (默认值)(适合增删改)

readonly 读写性

  • true:只读,可提高查询效率。(适合查询)
  • false:可读可写。 (默认值)(适合增删改)

timeout事务超时时间

当前事务所需操作的数据被其他事务占用,则等待。

  • 1000:自定义等待时间1000(秒)。
  • -1:由数据库指定等待时间,默认值。(建议)

rollbackFor 配置回滚的异常

noRollbackFor 配置不回滚的异常

配置事务切面

<!-- 配置Spring切面-->
<aop:config>
    <!-- 配置Spring切入点-->
    <aop:pointcut id="pc" expression="execution(* com.qf.service..*.*(..))"/>
    <!-- 配置Spring增强类-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
</aop:config>

基于注解配置

 <!-- Spring声明式事务基于注解的配置-->
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源信息-->
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--  开启Spring事务的包扫描  -->
<tx:annotation-driven transaction-manager="transactionManager"/>

在service层的方法或者类上添加注解

  • 注解加在类上表示所有的方法都使用事务管理
  • 注解加在方法上仅表示当前方法使用事务管理
@Transactional(isolation = Isolation.DEFAULT,
               propagation = Propagation.REQUIRED,readOnly = false,timeout = -1)
  • 57
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是二次元穿越来的

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值