Spring5

Spring5

Spring 框架概述

  1. Spring 是轻量级的开源的JavaEE框架

  2. Spring 可以解决企业应用开发的复杂性

  3. Spring两个核心IOC和Aop

    1)IOC:控制反转

    2)Aop:面向切面

  4. Spring特点

  5. Spring5开始学习!!!

入门前下载相关问题

1)Spring版本:5.2.25

2)下载地址:下不了一点 直接找百度网盘安装包

Spring 5.2.5压缩包

链接:https://pan.baidu.com/s/1sEKdJx7D-PDnyvNIZIS9sw 提取码:fc3n

3)jdk版本:1.8关于为什么用这个版本小白条哥解释了

4)导入相关五个基本jar包

image-20231117182337072.png

其中一个jar包下载地址common-loging-1.1.1.jar包的下载地址https://repo1.maven.org/maven2/commons-logging/commons-logging/1.1.1/

其他jar包spring5文件中都有

IOC容器

IOC概念和原理

什么是IOC

1)控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理

2)目的:为了耦合度降低

IOC底层原理

用到:xml解析 工厂模式 反射

1)IOC过程

a.xml配置文件

b.创建工厂类

image-20231114195251266.png

题外话:工厂模式是什么?

是一种简单的设计模式,是一种解耦的方式,但是工厂还是会有耦合度的,为了进一步降低耦合度,使用IOC容器

IOC接口(BeanFactory)

1)IOC思想基于IOC容器完成,IOC容器底层就是对象工厂

2)Spring提供IOC容器实现两种方式(两个接口)

a.BeanFactory:IOC容器基本实现,是Spring内部的接口,一般不提供开发人员使用

加载配置文件的时候不会*创建对象,在获取对象(使用)才去创建对象,说人话:什么时候用什么时候创建对象

b.ApplicationContext:BeanFactory接口的子接口,提供更强大的功能,一般由开发人员使用

加载配置文件时候就会*把在配置文件对象进行创建

ApplicationContext接口的实现类

1)FileSystemXmlApplicationContext

new FileSystemXmlApplicationContext(“磁盘下的路径如:D:\java\spring01\src\bean1.xml”)

2)ClassPathXmlApplicationContext

new ClassPathXmlApplicationContext(“类路径如:bean1.xml”)

IOC操作 Bean管理

什么是Bean管理

0)Bean管理指的是两个操作:

1)Spring创建对象

2)Spring注入属性

String name

setName()原先的这种方法现在交给Spring来做,这叫注入属性

Bean管理操作
xml配置文件方式

基于xml方式创建对象

1)在Spring配置文件中,使用bean标签,标签里添加对应属性,就可以实现对象创建

2)bean标签中的常用属性

*id属性:给你的对象取一个别名,唯一的标识

*class属性:创建对象所在类的全路径(包+类路径)

*name属性:和id一样,不过其中可以加特殊符号,了解即可

3)创建对象的时候,默认也是执行无参构造方法

基于xml方式注入属性

DI:依赖注入(注入属性)

第一种:使用set()方法注入

1)创建类,定义属性和对应的set方法

2)在Spring配置文件配置对象创建,配置属性引入

     <bean id="book" class="com.zhe.spring5.Book"> 创建对象
         <property name="bname" value="小黄书"></property> 属性注入
         <property name="bauto" value="段元哲"></property>
     </bean>

第二种:使用有参构造进行注入

1)创建类,定义属性和对应的有参构造方法

2)在Spring配置文件配置对象创建,配置属性引入

 <bean id="zhe" class="com.zhe.spring5.Orders">  创建对象
        <constructor-arg name="oname" value="蛋糕"></constructor-arg>属性注入
        <constructor-arg name="oadress" value="china"></constructor-arg>
    </bean>

第三种(了解):p名称空间注入

就是set方法的简化版本

使用这种方式,简化基于xml配置方式

1)添加p名称空间在配置文件中

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"//新加个这个即可
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

2)进行属性注入,在bean标签里面进行操作

     <bean id="book" class="com.zhe.spring5.Book" p:bname="小黄书" p:bauto="段元哲">//现在p名称空间方式实现
<!--         <property name="bname" value="小黄书"></property>-->
<!--         <property name="bauto" value="段元哲"></property>-->//原先set方法实现
     </bean>

xml注入其他属性类型

字面量

1)null值

         <property name="adress">
             <null/>//就是把value这个属性换成了<null/>标签
         </property>

2)属性值包含特殊符号

       <property name="adress"> 要设置的属性值为value="<< 南 京 >>"
            <value><![CDATA[<< 南 京 >>]]></value>
         </property>
也可以用转义 &lt; &gt;

注入属性-外部Bean

对对象的属性进行注入

需求:

1)创建两个类

2)在一个类中调用另一个类的方法

配置方法:

3)在Spring文件中进行配置

      <bean id="userService" class="com.zhe.spring5.service.UserService">
            <property name="UserDao" ref="UserDapImpl"></property>name中放的是需要调用方法所在类的名,这个类需要在UserService中定义并且有set方法 name:类里面对象类型的属性名称 ref中放的是创建UserDaoImpl对象bean标签id值
      </bean>
      <bean id="UserDapImpl" class="com.zhe.spring5.dao.UserDaoImpl"></bean>

注入属性-内部Bean

      <bean id="emp" class="com.zhe.spring5.bean.Emp">
          <property name="name" value="段元哲"></property>
          <property name="gender" value="男"></property>
          <property name="dept">
               <bean id="dept" class="com.zhe.spring5.bean.Dept">
                   <property name="dname" value="安保"></property>
               </bean>
          </property>
      </bean>

注入属性-级联赋值

配置外部Bean赋值后再引入到另一个Bean

       <bean id="emp" class="com.zhe.spring5.bean.Emp">
                     <property name="name" value="段元哲"></property>
                     <property name="gender" value="男"></property>
                      <property name="dept" ref="dept"></property>
                     <property name="dept.dname" value="安保部门"></property>//写法二 需要在Emp中生成个dept的get方法
       </bean>
      <bean id="dept" class="com.zhe.spring5.bean.Dept">
           <property name="dname" value="安保部门"></property>//写法一
      </bean>

注入集合类型属性

(以set方法方式为例)

1)注入数组类型属性

 <property name="courses">
                 <array>
                     <value>mysql</value>
                     <value>java</value>
                 </array>
            </property>

2)注入List集合类型属性

  <property name="list">
                  <list>
                       <value>mysql</value>
                      <value>java</value>
                  </list>
            </property>
​

3)注入Map集合类型属性

    <property name="maps">
                   <map>
                       <entry key="JAVA" value="java"></entry>
                       <entry key="PHP" value="php"></entry>
                   </map>
            </property>

细节

1)在集合里面设置对象类型值

 <bean id="stu" class="com.zhe.spring5.bean.Spring.Stu">   
<property name="courseList">
                 <list>
                     <ref bean="course1"></ref> 引入对象
                     <ref bean="course2"></ref>
                 </list>
            </property>
        </bean>
两个对象 为他两个设置值 在外面设置
    <bean id="course1" class="com.zhe.spring5.bean.Spring.Course">
        <property name="courseName" value="mhyhsql"></property>
​
    </bean>
​
  <bean id="course2" class="com.zhe.spring5.bean.Spring.Course">
      <property name="courseName" value="java"></property>
  </bean>

上面这种方法只能用在bean中 只有一个类可以使用,我想让多个类使用该怎么办呢?

2)把集合注入部分提取出来

提取出来作为公共部分

1)在Spring配置文件中引入名称空间util

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

2)使用util标签实现注入集合标签(以注入list为例)

   1)设置属性
<util:list id="bookList">
         <value>水浒传</value>
        <value>西游记</value>
        <value>红楼梦</value>
    </util:list>
   2)注入
    <bean id="book" class="com.zhe.spring5.bean.Spring.Book">
        <property name="list" ref="bookList"></property> 将ref里面的bookList注入到name当中的list里面
    </bean>
FactoryBean

String有两种类型的Bean,一种普通Bean,一种工厂Bean(FactoryBean)

普通Bean

在配置文件中定义Bean类型就是返回类型

工厂Bean

在配置文件中定义Bean类型可以和返回类型不一样

1)创建类,让这个类作为工厂Bean,实现接口FactoryBean

2)实现接口里面的方法,在实现的方法中定义返回的Bean类型

这样就可以实现即使在配置文件中配置的是一个类(工厂类),返回值却是另一个类(工厂类中方法返回的类型)

@Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
//定义返回的Bean类型
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCourseName("Java");
        return course;
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
Bean作用域

单实例和多实例

1)在Spring里面,设置创建Bean实例是单实例还是多实例

2)默认是单实例对象

3)如何设置单实例还是多实例

scope属性

1)Spring配置文件bean标签里面属性scope用于设置单实例还是多实例

2)默认值singleton,表示是单实例对象

3)属性值prototype,表示是多实例对象

二者区别

singleton加载Spring配置文件时候就会创建单实例对象

prototype在调用getBean方法时候创建多实例对象

Bean生命周期

生命周期

从对象创建到对象销毁的过程

bean生命周期

1)通过构造器创建bean实例(无参构造

2)为bean的属性设置值和对其他bean引用(调用set方法)

3)调用bean的初始化的方法(需要进行配置 init-method=“”)

4)bean 对象可以使用了(获取到了

5)当容器关闭时,调用bean销毁方法(需要配置 destroy-method=“” )

bean后置处理器

1)通过构造器创建bean实例(无参构造

2)为bean的属性设置值和对其他bean引用(调用set方法)

** 把bean实例传递bean后置处理器的方法postProcessBeforeInitialization

3)调用bean的初始化的方法(需要进行配置 init-method=“”)

** 把bean实例传递bean后置处理器的方法postProcessAfterInitialization

4)bean 对象可以使用了(获取到了

5)当容器关闭时,调用bean销毁方法(需要配置 destroy-method=“” )

自动装配(xml方式)

什么是自动装配

根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入

autowire标签

bean标签autowire,配置自动装配

属性值

byName 根据属性名称注入,注入值bean的id值要和类属性名称一样

byType 根据属性类型注入

         <bean id="emp" class="com.zhe.spring5.bean.bean.Emp" autowire="byType">
<!--             <property name="dept" ref="dept"></property>   就是代替了这部分-->  
         </bean>
    <bean id="dept2" class="com.zhe.spring5.bean.bean.Dept">
          <property name="name" value="财务部"></property>
    </bean>
引入外部属性文件(xml方式)jdbc没学过暂时跳过

直接配置数据库信息

1)配置德鲁伊连接池

2)引入德鲁伊连接池依赖jar包

德鲁伊jar包下载地址

https://repo1.maven.org/maven2/com/alibaba/druid/1.1.9/

要下载名称为druid-1.1.9.jar的不要有后缀,有后缀的不全

好像还有弄jdbc的包啊

这一集(p19)应该有jdbc没学过的知识先暂时跳过

引入外部属性文件配置数据库

注解配置文件方式

什么是注解

1)注解是代码特殊标记,格式:@注解(属性名称=属性值,属性名称=属性值…)

2)注解作用在类上面,方法上面,属性上面

3)使用目的:简化xml配置

基于注解方式实现对象创建

创建对象注解种类

1)@Component

2)@Service

3)@Controller

4)@Repository

功能相同,都可以用来创建bean实例

基于注解方式创建对象实现流程

1)引入依赖

image-20231117192642915.png

2)开启组件扫描

在spring配置文件中

引入contxt,然后用标签开启组件扫描

<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 http://www.springframework.org/schema/context/spring-context.xsd">
           <context:component-scan base-package="com.zhe.spring5"></context:component-scan>进行组件扫描,如果扫描多个包就用逗号隔开,扫描包上层目录
</beans>

注意:

<!-- 不扫描包中全部类-->
       <context:component-scan base-package="com.zhe.spring5" use-default-filters="false">
<!-- 扫描带该注解的类-->
              <context:include-filter type="annotation"
                                      expression="org.springframework.stereotype.Component"/>
       </context:component-scan>
​
<!--扫描包中全部类-->
       <context:component-scan base-package="com.zhe.spring5">
<!-- 不扫描带该注解的类-->
                 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
       </context:component-scan>

3)创建类,在类上面添加创建对象注解

注意:在注解里面value的属性值即为bean标签的id值

可省略不写,默认值是类名称首字母小写

基于注解方式实现属性注入

注入属性注解种类

1)@AutoWired:根据属性类型进行注入

第一步:在两个类中添加创建对象注解

第二步:在其中一个类中添加另一个类的属性,在属性上面使用注解(不需要添加set方法)

2)@Qualifier:根据属性名称注入

和上面@AutoWired一起使用

3)@Resource:可以根据类型,也可以根据名称进行注入

@Resource 根据类型注入

@Resource(name = “ddd”) 根据名称注入,就是创建对象注解里面的名称@Component(value = “ddd”)

4)@Value:注入普通类型

@Value(value = "abc")
private String name;    将abc注入到name

完全注解开发

1)创建配置文件,替代xml配置文件

@Configuration   代表这是配置文件
@ComponentScan(basePackages = {"com.zhe.spring5"}) 配置文件的扫描范围
public class springConfig {
}

2)测试类写法

    @Test
    public void test3() {
        ApplicationContext context = new AnnotationConfigApplicationContext(springConfig.class);  注意这里没有双引号!!!!
        service2 service2 = context.getBean("service2", service2.class);
        System.out.println(service2);
        service2.add();
    }

AOP

AOP概念和原理

什么是AOP

1)面向切面编程,隔离业务逻辑各个部分,使耦合度降低,提高程序可重用性

2)人话:不通过修改源代码的方式,在主干功能里面添加新功能

3)通过登录例子来理解

1700294899665-97846f0f-0ab8-4ca3-b0fd-c6d65b0c2c5a.jpeg

AOP底层原理

AOP底层使用动态代理

1)有接口情况,使用JDK动态代理

  • 创建接口实现类代理对象,增强类的方法

1700296191323-00adb847-f05b-460a-afb1-3144a22a4517.jpeg

2)没有接口情况,使用CGLIB动态代理

  • 创建子类的代理对象,增强类的方法

image-20231118163237138.png

JDK动态代理实现

1.使用JDK动态代理,使用Proxy类里面的方法创建代理对象

1)调用newProxyInstance方法

方法的三个参数

第一个参数:类加载器

第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口

第三个参数:实现这个接口InvocationHandler,创建代理对象,写增强的方法

编写代码实现JDK动态代理

1)创建接口,定义方法 (UserDao)定义的方法是add()

2)创建接口实现类,实现方法(User)

3)使用Proxy类创建接口代理对象

public class JDKProxy {
    public static void main(String[] args) {
        //创建接口实现类代理对象
        Class[] interfaces = {UserDao.class};
        UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(new User()));
        int result = dao.add(1, 2);
        System.out.println(result);
​
    }
​
​
    //创建代理对象
    static  class UserDaoProxy implements InvocationHandler{
​
        //把创建的是谁的代理对象,把谁传递过来
        //有参数构造传递
        private Object obj;
        public UserDaoProxy(Object obj){
            this.obj = obj;
        }
​
        //增强的逻辑
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
​
            //方法之前
            System.out.println("方法之前执行。。。。" + method.getName() + "传递的参数。。。" + Arrays.toString(args));
​
​
            //增强的方法
            Object res = method.invoke(obj, args);
​
​
​
            //方法之后
            System.out.println("方法之后执行。。。。" + obj);
            return res;
        }
    }
}
​

AOP操作术语

连接点

类里面哪些方法可以被增强,这些方法称为连接点

切入点

实际被增强的方法,称为切入点

通知(增强)

1)实际增强的逻辑部分称为通知(增强)

2)通知有多种类型

  • 前置通知
  • 后置通知
  • 环绕通知
  • 异常通知
  • 最终通知

切面

把通知应用到切入点的过程

AOP操作(准备工作)

Spring框架一般都是基于AspectJ实现AOP操作

什么是AspectJ

AspectJ不是Spring组成部分,是独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作

基于AspectJ实现AOP操作

1)基于xml配置文件实现

2)基于注解方式实现

在项目工程中引入AOP相关依赖

jar包下载连接在这里Spring/AOP at main · Aha-lxj/Spring · GitHub

赶紧用maven吧hh…

切入点表达式

1)作用:知道对哪个类里面的哪个方法进行增强

2)语法结构

execution(【权限修饰符】【返回类型】 【类全路径】 【方法名称】 (【参数列表】))

注意:权限修饰符可以省略,返回类型不能省略,可以用通配符*来表示,参数列表可以用…来表示

AOP操作(AspectJ注解)

1)创建类,在类里面定义方法

2)创建增强类(编写增强逻辑)

在增强类里面创建方法,让不同方法代表不同通知类型

3)进行通知的配置

  • 在Spring配置文件中,进行注解扫描

    <?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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           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
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    ​
    ​
    <!--    注解扫描-->
        <context:component-scan base-package="com.zhe.spring5.aop"></context:component-scan>
    ​
    ​
    
  • 使用注解创建类和增强类的对象

  • 在增强类上面添加注解@Aspect

  • 在Spring配置文件中开启生成代理对象

        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

    4)配置不同类型的通知

    在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置

    package com.zhe.spring5.aop;
    ​
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    ​
    @Component
    @Aspect//生成代理对象
    public class UserProxy {
    ​
    ​
        //前置通知
        @Before(value = "execution(* com.zhe.spring5.aop.User.add(..))")
        public void before(){
            System.out.println("before...");
        }
        //最终通知
       //在方法执行之后执行,不管有没有异常都会执行
        @After("execution(* com.zhe.spring5.aop.User.add(..))")
        public void after(){
            System.out.println("after......");
        }
       //后置通知(返回通知)
       //在方法返回结果之后执行,有异常出现就不执行
        @AfterReturning("execution(* com.zhe.spring5.aop.User.add(..))")
        public void afterReturning(){
            System.out.println("afterReturning.......");
        }
       //异常通知,出现异常之后执行
        @AfterThrowing("execution(* com.zhe.spring5.aop.User.add(..))")
        public void afterThrowing(){
            System.out.println("afterThrowing.......");
        }
        //环绕通知
        @Around("execution(* com.zhe.spring5.aop.User.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
    ​
            System.out.println("环绕之前.......");
    ​
            //被增强的方法执行
    ​
            proceedingJoinPoint.proceed();
            System.out.println("环绕之后.......");
        }
    }
    ​
    

5)相同的切入点抽取

    //相同切入点抽取
    @Pointcut(value = "execution(* com.zhe.spring5.aop.User.add(..))")
    public void pointDemo(){
        
    }
    //前置通知
    @Before(value = "pointDemo()")
    public void before(){
        System.out.println("before...");
    }

6)多个增强类对同一个方法进行增强,设置增强类优先级

在增强类上面添加注解@Order(数字类型值),值越小,优先级越高

7)完全注解开发

创建配置类,不需要创建xml配置文件

@Configuration
@ComponentScan(basePackages = {"com.zhe.spring5.aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class configAop {
}
​

AOP操作(AspectJ配置文件)了解即可

1)创建两个类,增强类和被增强类,在类里面定义方法

2)在Spring配置文件中创建两个类对象

         <bean id="book" class="com.zhe.spring5.aop_xml.Book"></bean>
         <bean id="bookProxy" class="com.zhe.spring5.aop_xml.BookProxy"></bean>

3)在Spring配置文件中配置切入点

<!--    配置aop增强-->
    <aop:config>
<!--            切入点-->
        <aop:pointcut id="p" expression="execution(* com.zhe.spring5.aop_xml.Book.add(..))"/>
<!--        配置切面-->
        <aop:aspect ref="bookProxy">
                  <aop:before method="before" pointcut-ref="p"></aop:before>
        </aop:aspect>
    </aop:config>

事务

事务概念

什么是事务

事务是数据库操作的基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有的操作都失败

事务四个特性(ACID)

1)原子性

2)一致性

3)隔离性

4)持久性

搭建事务操作环境

(需要jdbc技术啊。。。ok学成归来,继续事务)

1)使用JdbcTemplate需要先引入jar包

image-20231125141914815.png

2)场景:经典转账,1号向2号转100块钱

1700827580140-ba094746-5bf6-4135-8494-94e041e8a8f9.jpeg
实现步骤

1)创建数据库表,添加记录

2)创建service 搭建dao,完成对象创建和注入关系

3)创建转账方法,实现转账

xml配置

<!-- 组件扫描-->
    <context:component-scan base-package="com.zhe"></context:component-scan>
​
<!--创建数据库连接池  德鲁伊连接池-->
    <bean id="dataSouce" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="url" value="jdbc:mysql:///atguigu"/>
        <property name="username" value="root"/>
        <property name="password" value="dyz200472"/>
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> 注意mysql8+要有cj
    </bean>
<!--创建jdc模板-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
         <property name="dataSource" ref="dataSouce"></property>
    </bean>

dao中代码

@Repository
public class UserDaoImpl implements UserDao {//UserDao是自己实现的接口,里面定义了两个转账方法
    @Autowired//注入jdbc模板对象
    private JdbcTemplate jdbcTemplate;
​
    @Override
    public void addMoney() {
     String sql = "update t_bank set money=money+? where id = ?";
        jdbcTemplate.update(sql,100,2);
    }
​
    @Override
    public void subMoney() {
      String sql = "update t_bank set money=money-? where id = ?";
      jdbcTemplate.update(sql,100,1);
    }
}
​

service层代码

@Service(value = "userService")
public class UserService {
//注入UserDao
    @Autowired
    private UserDao userDao;
​
    public void accountMoney(){
        userDao.subMoney();
        userDao.addMoney();
    }
}

大致逻辑:在service层注入dao,在dao层注入JdbcTemplate,在JdbcTemplate中又注入数据库连接池

事务场景引入

发现问题

如果转账时,subMoney方法后面出现了异常,就会出现1号钱少了,但是因为异常导致程序中断导致2号钱没有增加,于是1号:我钱呢?哎不对我钱呢?

解决问题

*使用事务进行解决,用事务就可以拯救1号的钱钱~

*事务操作过程

1)开启事务

2)进行业务操作

3)没有异常:提交事务 出现异常:事务回滚

Spring事务管理介绍

事务添加地点

事务添加到javaEE三层结构里面的Service层(业务逻辑层)

在Spring进行事务管理操作

1)有两种方式:编程式事务管理和声明式事务管理

2)编程式就是在代码中写,spring中一般使用声明式事务管理

声明式事务管理

1)基于注解方式

2)基于xml方式

3)底层使用AOP原理

Spring事务管理API

提供一个接口,代表事务管理器,这个接口针对不同的框架实现不同的实现类

image-20231124205825380.png

PS:idea快捷键ctrl+h 查看向下或者向上的继承操作

Spring声明式事务管理-注解方式实现

1.在Spring配置文件中配置事务管理器

<!--    创建事务管理器-->
    <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--   注入数据源-->
        <property name="dataSource" ref="dataSouce"></property>
    </bean>

2.在Spring配置文件中,开启事务注解

1)引入名称空间tx

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
">
​

2)开启事务注解

<!--开启事务注解-->
<tx:annotation-driven transaction-manager="TransactionManager"></tx:annotation-driven>

3.在Service类上面(或者Service类里面的方法上面)添加事务注解

1)@Transactional,可以添加到类上面,也可以添加到方法上面

2)如果添加到类上面,为这个类里面所有的方法都添加事务

3)如果添加到方法上面,为这个方法添加事务

Sping声明式事务管理-事务参数

传播行为

propagation:事务传播行为

1)当一个事务方法被另一个事务方法调用时,这个事务方法如何进行

2)Spring定义了7种类传播行为,重点记住以下三种

required:如果有事务在运行,当前方法就在这个事务内运行,否则,就启动一个新的事务,并在新的事务内运行

1700887937433-ee4ef187-a47e-46cc-a46e-1f15f9a5cc2a-17008879693923.jpeg

required_new:当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起

image-20231125125900873.png

supports:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中

1700888581624-5eae087f-2f30-491e-ae38-3b60267ff6cf.jpeg

隔离级别

ioslation:事务隔离级别

1)事务的特性之一隔离性,使得多事务操作之间不会产生影响,不考虑隔离性会产生很多问题

2)产生的读问题:脏读,不可重复读,虚(幻)读

脏读

一个未提交事务读取到另一个未提交事务的数据

1700889366871-982f7ef3-9460-4fc6-bc62-cbe3c0796bcb.jpeg

岳不群改变金额后,东方不败读取到数据,但是岳不群进行事务回滚,没有提交,对数据库进行修改,发生了脏读

不可重复读

一个未提交事务读取到另一个提交事务修改数据

导致一个事务先后读取同一条记录,但两次读取的数据不同,即不可重复读

1700889900106-1c9981e5-0d07-438d-a365-465131e25d9a - 副本.jpeg

幻读

一个未提交事务读取到另一个提交事务添加数据

3)通过设置事务隔离级别,解决读问题

级别分类

image-20231125133153107.png

设置隔离级别,MySQL默认REPEATABLE_READ这个级别

@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
其他参数

timeout

1)事务需要在一定时间内进行提交,如果不提交进行回滚

2)默认值是-1,设置时间以秒单位进行计算

readOnly:是否只读

1)读:查询操作,写:添加修改删除操作

2)默认值false,表示可以查询,添加修改删除操作

3)设置为true之后,只能查询

rollbackFor:回滚

设置出现哪些异常进行事务回滚

noRollbackFor:不回滚

设置出现哪些异常不进行事务回滚

Spring声明式事务管理-完全注解开发

配置类

Configuration//配置类
@ComponentScan(basePackages = "com.zhe")//组件扫描
@EnableTransactionManagement//开启事务
public class TxConfig {
    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");//驱动
        druidDataSource.setUrl("jdbc:mysql:///atguigu");//数据库地址
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("dyz200472");
        return druidDataSource;
    }
​
    //创建jdbcTemplate对象
    @Bean
    //到ioc容器中找到DruidDataSource
    public JdbcTemplate getjdbcTemplate(DruidDataSource druidDataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入DruidDataSource
        jdbcTemplate.setDataSource(druidDataSource);
        return jdbcTemplate;
    }
    //创建事务管理器对象
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DruidDataSource druidDataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(druidDataSource);
        return dataSourceTransactionManager;
    }
}

测试类

    @Test
    public void test2(){
        ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值