IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring -> 正文阅读

[Java知识库]Spring

Spring容器的基本应用

管理bean组件

1、实例化容器

  • ClassPathXmlApplicationContext:通过类路径查询
  • FileSystemXmlApplicationContext:通过文件路径查询
        ApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
        ApplicationContext fxa = new FileSystemXmlApplicationContext("/src/main/resources/applicationContext.xml");
        System.out.println("ca:" + ca);
        System.out.println("fxa:" + fxa);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MO58wENY-1655689935123)(image-20211207202717582.png)]

2、配置bean

在applicationContext.xml文件中添加配置:<bean id="标识名" class="bean组件全路径"></bean>

<bean id="deptDAO" class="com.qf.dao.DeptDAOImpl"></bean>

3、getBean

使用ApplicationContext.getBean("标识名")获取JavaBean对象

        ApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean对象,返回对象是接口对象
        DeptDao deptDao = ca.getBean("deptDaoImpl", DeptDao.class);

对象的创建模式

  • singleton:单例 每一个spring容器中只定义一个对象实例
  • prototype:多例 每调用一次getBean返回一个新的对象
  • request:一次交互中,一个实例
  • session:一次会话中,一个实例

默认是singleton,可以通过<bean scope="prototype"/>修改创建模式

对象的创建时机

时机

  • singleton模式:容器创建时实例化JavaBean对象
  • prototype模式:调用getBean()方法时创建

延迟加载

singleton模式:可以使用<bean lazy-init="true" />延迟实例化。当调用getBean时才创建,或者在<bean default-lazy-init="true">推迟所以单例bean的创建时机

对象的初始化和销毁

  • <bean>元素中添加init-method=""指定初始化的方法名
  • destory-method=""指定销毁的方法名
  • 初始化:在构造方法之后自动执行
  • 销毁:在调用容器的close方法之后执行,并且只适用于单例模式的组件中

IOC特性

概念

Inverse of Controller:控制反转

  • 控制:对象的创建、初始化、销毁和对象之间关系的指定
  • 反转:将控制的逻辑交给第三方框架或者容器负责,当两个组件之间的关系发生改变时,只需要修改框架或者容器的配置

作用

处理组件之间的调用,以低耦合的方式建立组件之间的关系

DI(依赖注入)

Setter注入

  1. 添加成员变量,并提供set方法

    @Data //set
    public class SaveController {
        private DeptDao deptDao; //成员变量
    
        public String execute() {
            System.out.println("执行新增操作");
            deptDao.add();
            return "success";
        }
    }
    
  2. 在applicationContext.xml中配置bean

        <!--id:Impl的标识(唯一)  class:impl文件地址  scope:创建模式   init-method:指定初始化方法名-->
        <bean id="deptDaoImpl" class="dao.impl.DeptDaoImpl" scope="prototype"
              init-method="myinit"></bean>
    
        <!--id:controller的标识(唯一) class:controller文件地址-->
        <bean id="saveController" class="controller.SaveController">
            <!--name:controller中成员变量    ref:Impl的标识-->
            <property name="deptDao" ref="deptDaoImpl"></property>
        </bean>
    
  3. 实现

            //创建容器
            AbstractApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
            SaveController acBean = ac.getBean("saveController", SaveController.class);
            acBean.execute();
    

    思路:

    1. 根据applicationcontext.xml文件创建容器
    2. 在xml文件中查找 id=saveController的bean,通过class反射找到SaveController文件
    3. 查找属性为 deptDao 方法,并且查找id=deptDaoImpl的bean,给daoDao属性赋值
    4. 这时daoDao通过反射已经有值,并且通过第二个参数使用泛型得到对象acBean
    5. acBean调用Dao中的方法

构造方法注入

  1. 添加成员变量,并提供带参的构造方法(建议也添加无参构造方法)

    @NoArgsConstructor
    @AllArgsConstructor
    public class SaveController {
        private DeptDao deptDao; //成员变量
    
        public String execute() {
            System.out.println("执行新增操作");
            deptDao.add();
            return "success";
        }
    }
    
  2. 在applicationContext.xml中配置bean

        <!--id:Impl的标识(唯一)  class:impl文件地址  scope:创建模式   init-method:指定初始化方法名-->
        <bean id="deptDaoImpl" class="dao.impl.DeptDaoImpl" scope="prototype"
              init-method="myinit"></bean>
    
        <!--id:controller的标识(唯一) class:controller文件地址-->
        <bean id="saveController" class="controller.SaveController">
            <!--name:构造方法形成名称   ref:需要注入的bean对象的id值-Impl的标识-->
            <constructor-arg name="deptDao" ref="deptDaoImpl"></constructor-arg>
            <!--index:参数的顺序-->
            <!--<constructor-arg index="0" ref="deptDaoImpl"></constructor-arg>-->
        </bean>
    
  3. 实现

    注:与setter的实现一致

    思路:查找带参数的构造方法,并且查找id=deptDaoImpl的bean,给daoDao属性赋值

自动装配

  1. xml配置

    • 写法一:autowire=“byType”

      容器中存在一个与属性相同类型的bean(大于1个不行)

      <!--id:Impl的标识(唯一)  class:impl文件地址  scope:创建模式   init-method:指定初始化方法名-->
          <bean id="deptDaoImpl" class="dao.impl.DeptDaoImpl" scope="prototype"
                init-method="myinit"></bean>
      
          <!--id:controller的标识(唯一) class:controller文件地址 autowire:与属性类型相同的bean-->
          <bean id="saveController" class="controller.SaveController" autowire="byType"></bean>
      

      备注:set中deptDao属性与 id="deptDaoImpl"类型相同

    • 写法二:

          <!--id:Impl的标识(唯一)  class:impl文件地址  scope:创建模式   init-method:指定初始化方法名-->
          <bean id="deptDao" class="dao.impl.DeptDaoImpl" scope="prototype"
                init-method="myinit"></bean>
      
          <!--id:controller的标识(唯一) class:controller文件地址 autowire:与属性名称相同的bean-->
          <bean id="saveController" class="controller.SaveController" autowire="byName"></bean>
      

      备注:set中deptDao属性与 id="deptDaoImpl"名称相同

  2. Controller

    @Data
    public class SaveController {
        private DeptDao deptDao; //成员变量
    
        public String execute() {
            System.out.println("执行新增操作");
            deptDao.add();
            return "success";
        }
    }
    
  3. 实现

    与setter注入的实现相同

    思路:根据set的属性类型或者名称与xml配置匹配

数据注入

使用set注入

@Data
public class MessageBean {
    private String name;
    private int age;
    private List<String> colors;
    private Set<String> cities;
    private Map<String, String> books;
    private Properties properties;
    private List<String> typeList = new ArrayList<String>();

    public void setTypeList(String types) {
        String[] arr = types.split(",");
        Arrays.stream(arr).forEach(type -> {
            typeList.add(type);
        });
    }

    public void show() {
        System.out.println("基本数据类型:" + name + "\t" + age);

        System.out.print("list集合:");
        colors.forEach(System.out::print);

        System.out.print("set集合:");
        cities.forEach(System.out::print);

        System.out.print("map集合:");
        Set<String> strings = books.keySet();
        strings.forEach(Key -> {
            System.out.print(Key + ":" + books.get(Key));
        });
        
        System.out.print("properties集合:");
        Set<Object> objects = properties.keySet();
        objects.forEach(obj -> {
            System.out.print(obj + "" + properties.getProperty(obj.toString()));
        });

        System.out.print("入参类型String,成员变量是list:");
        typeList.forEach(System.out::print);
    }
}
@Data
public class DbBean {
    private String username;
    private String password;
    private String url;
    private String driver;
}

基本数据

        <property name="name" value="张三"></property>
        <property name="age" value="22"></property>

结果:基本数据类型:张三 22

list

        <property name="colors">
            <list>
                <value>black</value>
                <value>red</value>
            </list>
        </property>

结果:list集合:black red

set

        <property name="cities">
            <set>
                <value>南京</value>
                <value>北京</value>
            </set>
        </property>

结果:set集合:南京 北京

map

        <property name="books">
            <map>
                <entry key="1001" value="java基础"></entry>
                <entry key="1002" value="k8s"></entry>
            </map>
        </property>

结果:map集合:1001:java基础 1002:k8s

Properties集合

        <property name="properties">
            <props>
                <prop key="username">root</prop>
                <prop key="password">1234</prop>
            </props>
        </property>

结果:properties集合:password 1234 username root

入参类型String,成员变量是list

<property name="typeList" value="png,jpg"></property>

结果:入参类型String,成员变量是list:png jpg

properties文件信息注入

    <bean id="dbBean" class="pojo.DbBean">
        <property name="driver" value="#{db.driver}"></property>
        <property name="password" value="#{db.password}"></property>
        <property name="url" value="#{db.url}"></property>
        <property name="username" value="#{db.username}"></property>
    </bean>

结果:DbBean(username=root, password=1232456, url=localhost:3306, driver=com.jdbc.mysql.driver)

AOP特性

概念

主要解决的是一对多组件的调用问题,以低耦合的方式指定组件之间的调用关系

  • oop:object oriented programming(面向对象编程),关注的是对象,任何封装和抽象行为与特征
  • aspect oriented programming(面向方面[切面]编程),以oop为基础,更加关注的是方面,方面组件主要用来封装通用的逻辑,以低耦合的方式切入某一批目标对象中

参数概念

  • aspect:方面

    封装共通的功能组件,作用到某一批目标组件上

  • pointcut:切入点

    指定目标组件的表达式,方面组件和某一批目标组件的方法有关系

  • joinPoint:连接点

    切入点是连接点的集合,方面组件和某一个组件的方法关系

  • advice:通知

    指定方面组件和目标组件方法之间的作用时机,比如先执行方面组件在执行目标组件

  • target:目标

    利用切入点指定的组件和方法

  • autoProxy:动态代理

    Spring采用动态代理的技术实现AOP机制,当使用AOP之后,从容器中getBean()获取的目标组件,返回一个动态代理类,调用代理类的方法,代理类根据通知类型负责调用方面组件和目标组件的方法

    Spring动态代理技术:

    1. CGLIB技术:目标组件没有接口实现

      public class 代理类 extends 原目标类型{}

    2. Proxy技术:目标组件有接口实现

      public class 代理类 implement 原目标类型{}

通知类型

方面组件在目标组件什么位置执行?

  • brfore 前置通知:之前执行
  • after-returning 后置通知:之后执行,目标组件没有异常才会执行方面组件
  • after-throwing 异常通知:在目标组件抛异常之后执行
  • after 最终通知:之后执行,不管是否发生异常
  • around 环绕通知:之前和之后执行
        try {
            前置通知
            执行目标方法
            后置通知
        }catch (){
            异常通知
        }finally {
            最终通知
        }

切入点

切入点用于指定目标组件和方法

方法限定表达式

execution(修饰符? 返回类型 方法名(参数列表) throws 异常?)

  • 实例1:匹配容器中所有public修饰的方法
    execution(public * *(..))

  • 实例2:匹配容器中所有set开头的方法
    execution(* set*(..))

  • 实例3:匹配容器中AccounServlet类中的所有方法

    execution(* com.xyz.service.AccountService.*(..))

  • 实例4:匹配容器中service包下所有类的所有方法

    execution(* com.xyz.service.*.*(..))

  • 实例5:匹配容器中service包及其子包中所有类的所有方法
    execution(* com.xyz.service..*.*(..))

类型限定表达式

within(类型)

  • 实例1:匹配容器中service包下所有类的所有方法
    within(com.xyz.service.*)
  • 实例2:匹配容器中service包及其子包下所有类的所有方法
    within(com.xyz.service..*)
  • 实例3:匹配AccountService类中所有的方法
    within(com.xyz.service.AccountService)

Bean名称限定

按照bean元素的id值进行匹配:bean(id值)

  • 实例1:匹配容器中id=deptDao的对象
    bean(deptDao)
  • 实例2:匹配容器中id以Dao结尾的对象
    bean(*Dao)

args参数限定表达式

按照参数类型匹配

实例:匹配容器中只有一个参数并且参数类型是符合Serializable接口的方法

 args(java.io.Serializable)
 public void f1(String str){}
 bean(id)&&arg(..)

步骤

流程

  1. 导入jar

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.9</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>5.3.13</version>
            </dependency>
    
  2. 定义方面组件

    public class LoggerBean {
        //ProceedingJoinPoint:连接点对象,只有环绕通知才可以获取对象
        public void logger(ProceedingJoinPoint pjp) throws Throwable {
            String targetClz = pjp.getTarget().getClass().getName();//获取目标组件的类型
            //获取方法名
            String targetMethod = pjp.getSignature().getName();
            String info = targetClz + "." + targetMethod;
            System.out.println("用户在" + new Date().toLocaleString() + "完成了" + PropertiesUtil.getVal(info) + "事务");
            //执行目标组件方法
            pjp.proceed();
        }
    }
    
  3. 在applicationContent.xml配置文件中添加aop配置

        <bean id="LoggerBean" class="pojo.LoggerBean"></bean>
        <aop:config>
            <!--定义切入点:指定目标文件-->
            <aop:pointcut id="controllerPonitCut" expression="within(controller.*)"/>
            <!--定义方面-->
            <aop:aspect id="loggerAspect" ref="LoggerBean">
                <!--定义通知-->
                <aop:around method="logger" pointcut-ref="controllerPonitCut"></aop:around>
            </aop:aspect>
        </aop:config>
    
  4. 实现

        @Test
        public void LoggerBean() {
            ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
            SaveController acBean = ac.getBean("saveController", SaveController.class);
            acBean.execute();
        }
    

在这里插入图片描述

示例

练习:利用AOP实现异常处理,将异常信息写入文件
切入点:所有的controller
方面组件:异常信息写入文件-->模拟
通知类型:异常通知
  1. 导入jar

  2. 编写方面组件

    public class ExceptionBean {
        Logger logger = Logger.getLogger(ExceptionBean.class);
    
        public void exec(Exception e) {
            logger.error("异常信息");
            logger.error("异常类型" + e);
            StackTraceElement[] stackTrace = e.getStackTrace();
            Arrays.stream(stackTrace).forEach(es -> logger.error(es));
        }
    }
    
  3. 编写Controller

    @NoArgsConstructor
    @AllArgsConstructor
    public class SaveController {
        public String execute() {
            System.out.println(0 / 0);//创建错误,产生错误信息
            return "success";
        }
    }
    
  4. 编写aop配置

        <bean id="saveController" class="controller.SaveController"></bean>
    
        <bean id="ExceptionBean" class="pojo.ExceptionBean"></bean>
    
        <aop:config>
            <aop:pointcut id="controllerPonitCut" expression="within(controller.*)"/>
            <aop:aspect id="execAspect" ref="ExceptionBean">
                <aop:after-throwing method="exec" pointcut-ref="controllerPonitCut" throwing="e"/>
            </aop:aspect>
        </aop:config>
    
  5. 完成logger配置

            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
    
    #logger 日志器:控制消息的级别,并指定消息输出的目的地
    log4j.rootLogger=debug, myconsole,myfile
    #appender 输出器:消息输出的方式
    log4j.appender.myconsole=org.apache.log4j.ConsoleAppender
    log4j.appender.myfile=org.apache.log4j.FileAppender
    log4j.appender.myfile.File=F:\\idealogger\\mylog.txt
    #layout 布局器:消息以什么格式输出
    log4j.appender.myconsole.layout=org.apache.log4j.TTCCLayout
    log4j.appender.myfile.layout=org.apache.log4j.PatternLayout
    log4j.appender.myfile.layout.ConversionPattern=[%t] %5p -%m%n
    

注解配置

扫包

Bean

在Spring容器中开启组件扫描

  • 控制层:@Controller
  • 业务层:@Service
  • dao层:@Repository
  • 其他:@Component

添加注解后的作用:

  • id属性名默认是类型的首字母小写,如果需要自定义id的值,可以使用**@Repository("自定义id")**
  • 默认采用的单例模式创建bean对象,可以使用**@Scope("prototype")**改变创建模式
  • 如果是单例,默认在容器启动时创建bean对象,可以使用**@Lazy**注解推迟到第一次调用getBean方法实例化
  • 使用**@PostConstructor指定初始化方法,@PreDestory**指定销毁的方法

演示:

<context:component-scan base-package="dao"></context:component-scan>
<context:component-scan base-package="controller"></context:component-scan>
@Repository
@Scope("prototype")
@Lazy
public class DeptDAOImpl implements DeptDAO {
    @PostConstruct
    private void myinit() {
        System.out.println("myinit");
    }

    @PreDestroy
    private void mydestroy() {
        System.out.println("mydestroy");
    }
}

@Controller
public class SaveController 

ClassPathXmlApplicationContext csc = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(csc.getBean("saveController", SaveController.class));
System.out.println(csc.getBean("deptDAOImpl", DeptDAO.class));

IOC

如果容器中两个bean组件有调用关系,可以使用下面注解

  • @Autowired

    默认按照类型匹配注入,如果容器在有多个bean对象符合要求,可以使用**@Qualifier("需要注入的bean的id值")**

  • @Resource --jdk提供的

    默认按照名称匹配注入,如果匹配失败,则按照类型匹配。如果容器中有多个bean对象符合要求,可以使用**@Resource(name="需要注入的bean的id值")**

演示:

@Repository
public class DeptDAOImpl implements DeptDAO

@Controller
public class SaveController {
    @Autowired
    private DeptDAO deptDao; //成员变量
    
    public String execute() {
        //System.out.println(0 / 0);
        System.out.println("执行新增操作");
        deptDao.add();
        return "success";
    }
}


ClassPathXmlApplicationContext csc = new ClassPathXmlApplicationContext("applicationContext.xml");
SaveController saveController = csc.getBean("saveController", SaveController.class);
saveController.execute();
csc.close();

Properties

properties文件中常量数据注入给bean

  1. 将properties文件数据加载到spring容器中

    <util:properties id="db" location="classpath:db.properties"/>
    
  2. 在bean组件上使用**@Component(或其他)注解,并在成员变量的上方使用@value**注解注入properties文件中的数据

    @Data
    @Component
    public class DbBean {
        @Value("#{db.username}")
        private String username;
        @Value("#{db.password}")
        private String password;
        @Value("#{db.url}")
        private String url;
        @Value("#{db.driver}")
        private String driver;
    }
    
    ClassPathXmlApplicationContext csc = new ClassPathXmlApplicationContext("applicationContext.xml");
    System.out.println(csc.getBean("dbBean", DbBean.class));
    

Aop相关注解

  • 在Spring配置文件中启用aop注解

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  • 方面组件中,使用相应的注解标记

    在类上方使用@Component注解标记将该组件扫描到spring容器中
    在类上方使用@Aspect将该组件定义为方面组件
    定义一个空方法,在方法上方使用@Pointcut定义切入点表达式
    在方面组件的方法上定义通知,通知类型如下
    
    • @Before:前置通知
    • @AfterReturning:后置通知
    • @AfterThrowing:异常通知
    • @After:最终通知
    • @Around:环绕通知
    @Component
    @Aspect //定义方面组件
    public class ExceptionBean {
        Logger logger = Logger.getLogger(ExceptionBean.class);
    
        //定义切入点
        @Pointcut("within(controller..*)")
        public void mypoint() {
        }
    
        //定义通知类型
        @AfterThrowing(pointcut = "mypoint()", throwing = "e")
        public void exec(Exception e) {
            logger.error("异常信息");
            logger.error("异常类型" + e);
            StackTraceElement[] stackTrace = e.getStackTrace();
            Arrays.stream(stackTrace).forEach(es -> logger.error(es));
        }
    }
    

Spring与Mybatis整合

导入依赖

  • mybatis.jar 用于mybatis开发
  • mysql.jar 数据库连接
  • druid Druid连接池
  • spring-context.jar spring核心包
  • spring-aspects.jar 通过aspects支持:面向方面
  • mybatis-spring.jar mybatis和spring整合的包
  • spring-jdbc.jar Spring 对JDBC 数据访问
  • juinit.jar 单元测试
  • guava.jar 提供集合,缓存,支持原语句,并发性,常见注解,字符串处理,I/O和验证的实用方法
  • mybatis-generator-core.jar mybatis逆向工程
  • log4j.jar 日志

xml配置

定义SqlSessionFactoryBean

application.xml

    <!--引用db.properties文件数据-->
    <util:properties id="db" location="classpath:db.properties"/>

    <!--将SqlSessionFactoryBean交给spring容器管理,通过FactoryBean获取SqlSessionFactory,
    通过SqlSessionFactory获取SqlSession-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--配置数据量连接信息  ref:连接资源-->
        <property name="dataSource" ref="myDataSoruce"></property>
        <!--配置mapper文件	value:需要扫描的xml文件-->
        <property name="mapperLocations" value="mapper/*.xml"></property>
        <!--定义别名		value:提供扫包的方式定义别名-->
        <property name="typeAliasesPackage" value="entity"></property>
    </bean>

    <bean id="myDataSoruce" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="#{db.driver}"/>
        <property name="url" value="#{db.url}"/>
        <property name="username" value="#{db.username}"/>
        <property name="password" value="#{db.password}"/>
        <property name="initialSize" value="#{db.initialSize}"/>
        <property name="maxActive" value="#{db.maxActive}"/>
        <property name="minIdle" value="#{db.minIdle}"/>
        <property name="maxWait" value="#{db.maxWait}"/>
    </bean>

MapperScannerConfigurer

? 在spring配置文件中定义MapperScannerConfigurer组件,通过该组件会根据定义的包路径扫描各个mapper(Dao)接口,并注册对应的MapperFactoryBean对象,最终通过MapperFactoryBean.getObject()获取接口的代理对象

  <!--将MapperScannerConfigurer交给Spring容器管理,通过这个组件自动扫描各个mapper接口,并注册的MapperFactoryBean。
  SqlSessionFactory属性可以不指定,使用autowired方式自动注入-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="dao"></property>
    </bean>

如果指定的某个包下并不完全是我们定义的mapper(dao)接口,使用annotationClass缩小搜索和注册范围

@Target(TYPE)
@Retention(RUNTIME)
public @interface MyBatisAnnotation {
}


@Repository
@MyBatisAnnotation
public interface UserMapper {
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="dao"></property>
        <!--name:用于指定一个注解标记,当指定了annotationClass时,MapperScannerConfigurer只注册使用了annotationClass注解标记的接口-->
        <property name="annotationClass" value="annotation.MyBatisAnnotation"></property>
    </bean>

事务

什么是事务

? 事务是一系列的动作,它们综合在一起是一个完整的工作单元,这些动作要么全部完成,只要一个失败,就回到最初的状态,仿佛什么都没有发生

? ACID:原子性、一致性、隔离性、持久性

spring事务

? 在spring平台,提供了事务的支持,本质上是数据库支持对事务的处理,没有数据库的事务支持,spring是无法提供事务功能的。
使用spring事务管理组件之后,事务的控制不需要程序员编写,spring自动完成,spring并不是直接管理事务,而是提供了事务管理器接口PlatformTransactionManager接口,接口中定义了统一的编程模型,具体事务管理机制由各个平台实现。

Connection con = getCon();
con.setAutocommit(false);
...业务逻辑...
commit/rollback

事务定义:TransactionDefinition

传播行为

  • REQUIRED(常用):如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  • MANDATORY:强制的 使用当前的事务,如果当前没有事务,就抛出异常。
  • REQUIRES_NEW:需要JTA事务管理器的支持,新建事务,如果当前存在事务,把当前事务挂起。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • NESTED:嵌套 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作

隔离级别

  • int ISOLATION_DEFAULT = -1; 使用数据库默认的隔离级别
  • int ISOLATION_READ_UNCOMMITTED = 1;读未提交,A事务可以读到B事务未提交的数据
  • int ISOLATION_READ_COMMITTED = 2;读提交,A事务可以读到B事务已提交的数据
  • int ISOLATION_REPEATABLE_READ = 4;可重复读,A事务读不到B事务已提交的数据
  • int ISOLATION_SERIALIZABLE = 8;串行化

只读

? 如果事务只对数据进行查询的操作,数据库可以利用事务的只读特性来进行一些特定的优化

超时

? 为了使应用程序正常运行,事务不能运行太长时间,因为事务可以涉及对数据库的锁定,所以长时间的事务会占用数据库资源, 事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待事务结束。

回滚机制

? 回滚规则定义了哪些异常会导致事务回滚而哪些不会,默认情况下,事务只有遇到运行期间异常时才会回滚,而在遇到检查型异常时不会回滚。

事务管理

  • 编程式事务管理
  • 声明式事务管理
    1. 基于xml事务配置
    2. 基于注解事务配置

编程式事务与声明式事务的区别

  1. 编程式事务需要修改业务代码,可以更具体管理事务,但是代码的侵入性较强
  2. 声明式事务以AOP机制作用到各个组件,编写更方便,实现类事务和业务组件的解耦
//REQUIRED
A(){
B();
}
//NOT_SUPPORTED
B(){
setAutoCommit(false);
...
commit;

事务实现

xml

application.xml

    <!--配置事务管理器-->
    <!--id:数据源事务管理器标识     class:mybatis对应的数据源事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--name:数据源    ref:使用的数据源-->
        <property name="dataSource" ref="myDataSoruce"></property>
    </bean>

    <!--配置事务范围以及类型,指定事务组件-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!--指定给哪些方法添加事务的控制   no-rollback-for="java.lang.NumberFormatException"/>-->
            <tx:method name="execute" propagation="REQUIRED" isolation="DEFAULT" timeout="3000" read-only="false"
                       rollback-for="java.lang.ClassNotFoundException"></tx:method>
            <!-- <tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT" timeout="3000" read-only="false"/>-->
        </tx:attributes>
    </tx:advice>

    <!--事务aop配置-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="within(controller..*)"></aop:advisor>
    </aop:config>

注解

    <!--开启事务的注解 transaction-manager:指定事务管理器的标识-自定义,默认transactionManager-->
    <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>

    <!--配置事务管理器-->
    <!--id:数据源事务管理器标识     class:mybatis对应的数据源事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--name:数据源    ref:使用的数据源-->
        <property name="dataSource" ref="myDataSoruce"></property>
    </bean>
@Controller
public class DeptController {
    @Autowired
    private DeptMapper deptMapper;

    @Transactional
    public void execute() throws  ClassNotFoundException{

hod>

</tx:attributes>
</tx:advice>

<!--事务aop配置-->
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="within(controller..*)"></aop:advisor>
</aop:config>

### 注解

```xml
    <!--开启事务的注解 transaction-manager:指定事务管理器的标识-自定义,默认transactionManager-->
    <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>

    <!--配置事务管理器-->
    <!--id:数据源事务管理器标识     class:mybatis对应的数据源事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--name:数据源    ref:使用的数据源-->
        <property name="dataSource" ref="myDataSoruce"></property>
    </bean>
@Controller
public class DeptController {
    @Autowired
    private DeptMapper deptMapper;

    @Transactional
    public void execute() throws  ClassNotFoundException{
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 17:56:45  更:2022-06-25 18:00:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 10:44:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码