Spring

一、Spring 概述

1.1、什么是Spring框架:

  • Spring框架就是整合其它框架的框架,核心是IOC和AOP,由20多个模块组成,Spring、SpringMVC、SpringBoot、SpringCloud都属于它的模块

1.2、Spring的特点:

  • 轻量级:
    • 由20多个模块组成,每个jar包小于1M,核心jar包也就3M左右,对代码无污染。
  • 面向接口编程:
    • 使用接口,项目的可拓展性,可维护行极高,使用时接口指向实现类,切换功能只需要切换指向的实现类即可
  • AOP面向切面编程:
    • 将公共的,通用的,重复的代码、业务逻辑单独开发,在使用的时候再反织回去。底层是动态代理。
  • 整合其它框架:
    • 整合其它框架后使其它框架更好用。

1.3、什么是控制反转IOC:

  • 控制权交给Spring,Spring容器来控制对象的创建以及对象和对象之间的依赖关系(A对象含有B对象的实例)
  • 正转:程序员控制对象创建 Student stu=new Student()
  • 反转:Spring容器控制对象的创建。例如:<bean id="stu" class="com.lhl.pojo.Student"><bean>

1.4、什么是依赖注入:

  • 程序运行期间,依赖于外部容器给对象的属性赋值

1.5、什么是AOP

  • AOP就是面向切面编程,切面就是重复的,

二、IOC 控制反转

2.1、Spring入门程序

2.1.1、创建maven项目添加依赖

<dependencies>

    <!--junit依赖-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    
    <!--spring依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>

</dependencies>

2.1.2、定义实体类

package com.lhl.pojo;

public class Student {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }
}

2.1.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!--创建对象 调用构造方法-->
    <bean id="student" class="com.lhl.pojo.Student"></bean>
    
</beans>

2.1.4、测试程序

@Test
public void test(){
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    Student student = (Student) ac.getBean("student");
    System.out.println(student);
}

2.2、基于XML的DI

2.2.1、set注入

  • 使用set注入必须提供set方法和无参构造器
  • 定义实体类Student和School
public class Student {
    private String name;
    private int age;
    private School school;
    //setter
    //toString
}    
public class School {
    private String name;
    private String address;
    //setter
    //toString
}    
2.2.1.1、简单类型注入
<!--
    id="student" 对象的引用 ==> Student student = new Student()
    class="com.lhl.pojo.Student" ==>  创建对象的类型
    name="name" value="张三" ==>  给对象的name属性赋值张三
-->
<bean id="student" class="com.lhl.pojo.Student">
    <property name="name" value="张三"/>
    <property name="age" value="23"/>
</bean>
2.2.1.2、引用类型注入
<!--
    name="school" ref="school" ==> 引用类型赋值
-->
<bean id="student" class="com.lhl.pojo.Student">
    <property name="school" ref="school"/>
</bean>

2.2.2、构造方法注入

  • 必须提供相应的有参构造方法
public class Student {
    private String name;
    private int age;
    private School school;

    public Student(String name, int age, School school) {
        this.name = name;
        this.age = age;
        this.school = school;
    }
}    
public class School {
    private String name;
    private String address;

    public School(String name, String address) {
        this.name = name;
        this.address = address;
    }
}    
2.2.2.1、使用构造方法的参数名
<bean id="school" class="com.lhl.pojo.School">
        <property name="name" value="清华大学"/>
        <property name="address" value="北京海淀"/>
    </bean>

<bean id="student" class="com.lhl.pojo.Student">
    <constructor-arg name="name" value="李四"/>
    <constructor-arg name="age" value="10"/>
    <constructor-arg name="school" ref="school"/>
</bean>
2.2.2.2、使用构造方法的下标
<bean id="student" class="com.lhl.pojo.Student">
    <constructor-arg index="0" value="王五"/>
    <constructor-arg index="1" value="20"/>
    <constructor-arg index="2" ref="school"/>
</bean>
2.2.2.3、使用默认顺序
<bean id="student" class="com.lhl.pojo.Student">
    <constructor-arg  value="赵六"/>
    <constructor-arg  value="18"/>
    <constructor-arg  ref="school"/>
</bean>

2.2.3、引用类型自动注入

  • 通过bean标签的autowrie属性为引用类型隐式的自动注入
2.2.3.1、byName 方式自动注入
  • 底层通过调用set()方法进行注入,没有set()方法就无法注入
<!--
    autowire="byName" ==> 为引用类型属性自动注入与bean的id值一致的对象
-->
<bean id="student" class="com.lhl.pojo.Student" autowire="byName"/>
	<property name="name" value="张三"/>
    <property name="age" value="23"/>
	<!--<property name="school" ref="school"/>-->
</bean>
2.2.3.2、byType 方式自动注入
  • 底层也是调用set()方法进行注入,没有set()方法就无法注入

  • 通过byType方式,配置文件中的School类型的Bean必须是唯一的,出现多个无法注入。

  • 注入类型也可以是School类型的子类或实现类

<!--
   autowire="byType" ==> 注入类型也可以是School类型的子类或实现类
-->
<bean id="student" class="com.lhl.pojo.Student" autowire="byType">
    <property name="name" value="小明"/>
    <property name="age" value="10"/>
    <!--<property name="school" ref="school"/>-->
</bean>

2.3、基于注解的DI

2.3.1、定义Bean的注解

  • 以下四个注解都是创建对象,@Controller、@Service、@Repository这三个注解都是@Component注解的别名。为了增强可读性,建议分层使用。它们都有一个value属性用来指定bean的id,不指定value属性对象名默认是类型首字母小写。
  1. @Component:创建所有对象都可以使用
  2. @Controller:建议使用在控制层
  3. @Service:建议使用在业务层
  4. Repository:建议使用在数据访问层
//@Component("student")
//@Component(value = "student")
//@Controller
//@Service
//@Repository
@Component  //  ==> 默认对象名是类名首字母小写
public class Student {}

2.3.2、使用注解创建对象步骤

  • 在配置文件中添加包扫描
<!--spring容器会扫描该包及其子包下的注解-->
<context:component-scan base-package="com.lhl.pojo"/>
  • 在类上使用注解
@Component
public class Student {
    
}

2.3.3、简单类型注入@Value

  • @Value注解可以出现在属性上、setter方法上,spring6可以出现在构造器的形参上。出现在属性上可以没有setter方法仍然可以注入,@Value给八种基本类型和String注入值。
public class Student {

    @Value("张三")
    private String name;
    @Value("20")
    private int age;
    
 }

2.3.4、byType 自动注入@AutoWried

  • @AutoWried默认按照类型注入,如果同类型存在多个,则按照属性名进行二次匹配,匹配到则注入,没有匹配到则报错。
  • @Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。
//@Autowired(required = false)
@Autowired  // 自动注入与Schoo类型相同的对象
private School school;

2.3.5、byName 自动注入@Qualifier

  • @Autowired 与@Qualifier联合使用。@Qualifier 的 value 属性用于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。
@Autowired
@Qualifier("school")  // 自动注入id为school的School类对象
private School school;

2.4、Spring 配置文件的整合

1、按层拆分

  • applicationContext_controller.xml
<!--创建界面层对象-->
    <bean id="userController" class="com.lhl.controller.UserController">
        <property name="userService" ref="userService"/>
    </bean>
  • applicationContext_dao.xml
<!--创建数据访问层对象-->
<bean id="userMapper" class="com.lhl.dao.impl.UserMapperImpl">
</bean>
  • applicationContext_service.xml
<!--创建业务层对象-->
<bean id="userService" class="com.lhl.service.impl.UserServiceImpl">
    <property name="userMapper" ref="userMapper"/>
</bean>
  • 整合``total.xml`
<!--批量导入-->
<import resource="applicationContext_*.xml"/>

2、按功能拆分

  • applicationContext_users.xml
<bean id="uController" class="com.bjpowernode.controller.UsersController">
<bean id="uService" class="com.bjpowernode.controller.UsersService">
<bean id="uMapper" class="com.bjpowernode.controller.UsersMapper">
  • applicationContext_book.xml
<bean id="bController" class="com.bjpowernode.controller.BookController">
<bean id="bService" class="com.bjpowernode.controller.BookService">
<bean id="bMapper" class="com.bjpowernode.controller.BookMapper">
  • 整合total.xml
<import resource="applicatoinContext_*.xml"></import>

三、Spring AOP

3.1、AOP概述

  • 动态代理其实就是AOP思想的实现。
  • AOP(Aspect Oriented Programming):面向切面编程,是一种思想,底层是通过动态代理实现的
  • 面向切面编程:将与业务逻辑无关的通用的、重复的代码单独提取出来形成独立的组件,比如:事务、日志、安全验证等交叉业务。在执行业务逻辑时,需要它就以横向交叉的方式应用到业务流程当中。
  • Spring AOP使用的动态代理:JDK动态代理+CGLIB动态代理,如果代理的是接口,使用的是JDK动态代理。如果代理的是类,使用的是CGLIB动态代理。可以通过配置强制全部使用CGLIB动态代理。

3.2、Spring 的通知类型

  • Spring支持AOP的编程,常用的有以下几种:
    1. Before通知:在目标方法被调用前调用,涉及接口org.springframework.aop.MethodBeforeAdvice;
    2. After通知:在目标方法被调用后调用,涉及接口为org.springframework.aop.AfterReturningAdvice;
    3. Throws通知:目标方法抛出异常时调用,涉及接口org.springframework.aop.ThrowsAdvice;
    4. Around通知:拦截对目标对象方法调用,涉及接口为org.aopalliance.intercept.MethodInterceptor。

3.3、AOP 编程术语

  • **1.切面(Aspect):**就是交叉的业务。重复的、公共的、通用的功能。例如:日志、事务、权限。
  • **2.连接点(Joinpoint):**就是目标方法。
  • 3.切入点(Pointcut):指定切入的位置,即指定切入到哪个方法或哪些方法。多个连接点构成切入点。切入点可以是目标方法,可以是一个类中的所有方法,某个包下的所有类的方法等等。
  • 4.通知(Advice):就是切面方法或者说切面功能。是在目标方法执行前还是执行后还是出错时,还是环绕目标方法切入切面功能。
  • 5.目标对象(Target):操作谁谁就是目标对象
  • 6.代理对象(Proxy):目标对象织入通知后产生的新对象
  • 7.织入(Weaving):把通知应用到目标对象上的过程

3.4、AspectJ 对AOP的实现

3.4.1、AspectJ 的通知类型

  1. 前置通知@Before
  2. 后置通知@AfterReturning
  3. 环绕通知@Around
  4. 最终通知@After

3.4.2、AspectJ的切入点表达式

  • 切入点表达式就是切入目标方法的方法名
execution([访问控制权限修饰符] 返回值类型 [全限定类名]方法名(形式参数列表) [异常])
  • []中的内容表示可有可无,所以可以简化为:
execution(返回值类型 方法名(形式参数列表))
  • 各部分可以使用一下符号
  1. * 代码任意个任意的字符(通配符)
  2. 如果出现在方法的参数中,则代表任意参数。如果出现在路径中,则代表本路径及其所有的子路径
  • 实例:
execution(public * *(..)) 								//任意的公共方法
execution(* set*(..))									//任何一个以“set”开始的方法
execution(* com.xyz.service.impl.*.*(..))				//在com.xyz.service.impl包下的所有类的所有方法
execution(* com.xyz.service..*.*(..)):					//在com.xyz.service及其子包下的所有类的所有方法
execution(* *..service.*.*(..))							//serrvice之前可以有任意的子包
execution(* *.service.*.*(..))							//service只能是二级包下的所有类的所有方法

3.4.3、AspectJ基于注解式AOP

3.4.3.1、实现步骤
  1. pom.xml文件引入依赖
<dependencies>
    <!--junit依赖-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--spring依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--spring aspectj依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
</dependencies>
  1. 定义目标类和切面类,并使用注解创建对象和在目标方法加入通知
//目标类
@Service
public class TargetServiceImpl implements TargetService {
    //目标方法
    @Override
    public void targetMain() {
        System.out.println("目标类中的目标方法");
    }
}
//切面类
@Aspect//声明该类为切面类,交给AspectJ框架去识别切面类,来进行切面方法的调用
@Component
public class MyAspect {
    //切面方法
    //切点表达式
    @Before(value = "execution(public void targetMain())")
    public void aspectMain(){
        System.out.println("切面类中的切面方法");
    }
}
  1. 编写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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--添加包扫描-->
    <context:component-scan base-package="com.lhl"/>
    <!--开启自动代理-->
    <!--
        <aop:aspectj-autoproxy/> 
        默认proxy-target-class="false"表示使用jdk动态代理
        proxy-target-class="true"  表示使用CGLIB动态代理
    -->
    <aop:aspectj-autoproxy/>
</beans>
  • 在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。
  1. 测试程序
//测试类
public class MyTest {
    @Test
    public void test(){
        //创建容器并生成对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取切入后的目标类的代理类
        TargetService proxy = (TargetService) ac.getBean("targetServiceImpl");
        //执行增强功能的目标方法
        proxy.targetMain();
    }
}
3.4.3.2、@Before前置通知
/**
 * 在目标方法执行之前执行
 * 前置通知方法规范:
 * 1.访问权限是public
 * 2.方法没有返回值void
 * 3.方法的名字随意
 * 4.方法可以没有参数,如果有只能是JoinPoint类型
 *     该JoinPoin类型参数就是切入点表达式==>execution(public void targetMain())
 * 5.@Before注解表示是加入目标方法的前面:
 *      value:指定切入点表达式,即目标方法
 */
@Before(value = "execution(public void targetMain())")
public void myBefore(JoinPoint joinPoint){
    System.out.println("我是前置通知");
    System.out.println("目标方法名:" + joinPoint.getSignature());
    System.out.println("目标方法参数:" + joinPoint.getArgs());
    System.out.println("目标方法对象" + joinPoint.getTarget());
}
3.4.3.3、@AfterReturning 后置通知
/**
     * 在目标方法后执行
     * 后置通知方法规范:
     * 1.方法访问权限public
     * 2.方法没有返回值void
     * 3.方法名自定义
     * 4.方法参数可以目标方法的返回值,可以没有,也可以包含JoinPoint参数(它必须是第一个参数)
     * 5.使用@AfterReturning注解:
     *      参数value:指定切入点表达式
     *      returning:指定目标方法返回值的形参名称,此名称必须与切面方法参数名称一致
     *      returning = "obj"   ==>   obj = 目标方法返回值
     */
    @AfterReturning(value = "execution(public void targetMain())",returning = "obj")
    public void myAfterReturning(Object obj){
        System.out.println("我是后置通知");
        /*
            在切面方法中可以对目标方法的返回值进行操作
            目标方法返回值是八大基本类型或String,则无法改变目标方法的返回值
            目标方法的返回值是引用类型,则可以改变目标方法的返回值
         */
        System.out.println("目标方法返回值:"+ obj);
    }
	//也可以之加入后切方法,不对目标方法返回值操作
	@AfterReturning(value = "execution(public void targetMain())")
    public void myAfterReturning(){
        System.out.println("我是后置通知");
    }
}
3.4.3.4、@Around 环绕通知
/**
 * 环绕通知方法的规范
 * 1.访问权限是public
 * 2.方法返回值可有可无,和目标方法一致,需要就返回
 * 3.方法名自定义
 * 4.方法有参数,参数就是目标方法,它是ProceedingJoinPoint接口类型
 *      该接口的proceed()方法用于执行目标方法,返回值是目标方法返回值
 * 5.必须抛出异常
 * 6.环绕通知其实是拦截了目标方法
 */
@Around(value = "execution(public void targetMain())")
public void myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    //在目标方法前加入功能
    System.out.println("前环绕");
    //执行目标方法并获取返回值
    Object returnNum = proceedingJoinPoint.proceed();
    //在目标方法后加入功能
    System.out.println("后环绕");

    /*
        return returnNum; 该切面方法可以返回值,此时方法返回值类型最好为Object
        public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
    */
}
3.4.3.5、@After 最终通知
/**
 * 无论怎样都会执行该方法
 * 最终方法的规范
 * 1.访问权限是public
 * 2.方法没有返回值
 * 3.方法名自定义
 * 4.方法参数可有可无,有参数就是JoinPoint类型,就是切入点表达式
 * 5.使用 @After
 *      value:指定切入点表达式
 */
@After(value = "execution(public void targetMain())")
public void myAfter(){
    System.out.println("最终通知");
}
3.4.3.6、@Pointcut 定义切入点别名
  • 当很多切面方法使用相同的切入点表达式时,可以使用@Pointcut注解给表达式起别名
//切面类
@Aspect
@Component
public class MyAspect {

    //使用注解的方法的方法名就是切入表达式的别名,一般使用private,因为这个方法没用
    @Pointcut(value = "execution(public void targetMain())")
    public void pointCut(){}


    @Before(value = "pointCut()")
    public void myBefore(JoinPoint joinPoint){
        System.out.println("我是前置通知");
    }

    @AfterReturning(value = "pointCut()")
    public void myAfterReturning(){
        System.out.println("我是后置通知");
    }

    @Around(value = "pointCut()")
    public void myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("前环绕");
        Object returnNum = proceedingJoinPoint.proceed();
        System.out.println("后环绕");
    }

    @After(value = "pointCut()")
    public void myAfter(){
        System.out.println("最终通知");
    }
    
}
3.4.3.6、@Order定义切面的优先级
  • 可以使用@Order注解来标识切面类,为@Order注解的value指定一个整数型的数字,数字越小,优先级越高。
@Aspect
@Component
@Order(1) //设置优先级
public class YourAspect {
}

3.4.4、基于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: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">

    <!--纳入spring bean管理-->
    <bean id="vipService" class="com.powernode.spring6.service.VipService"/>
    <bean id="timerAspect" class="com.powernode.spring6.service.TimerAspect"/>

    <!--aop配置-->
    <aop:config>
        <!--切点表达式-->
        <aop:pointcut id="p" expression="execution(* com.powernode.spring6.service.VipService.*(..))"/>
        <!--切面-->
        <aop:aspect ref="timerAspect">
            <!--切面=通知 + 切点-->
            <aop:around method="time" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>
</beans>

四、Spring整合MyBatis

  • 整合步骤:
  1. IDEA新建maven项目,pom.xml文件中添加各种依赖
<dependencies>
    <!--单元测试-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <!--aspectj依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--spring核心ioc-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--做spring事务用到的-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--事务底层使用jdbc依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--mybatis依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.10</version>
    </dependency>
    <!--mybatis和spring集成的依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.1</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.9</version>
    </dependency>
    <!--阿里公司的数据库连接池-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.12</version>
    </dependency>
</dependencies>
  1. 基于三层架构,创建所有的包,并创建pojo类、mapper接口、service接口及实现类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-haOtlmOR-1684911731066)(C:\Users\lhl\AppData\Roaming\Typora\typora-user-images\image-20230522103359427.png)]

  1. 编写sqlMapper.xml文件,Mybatis配置文件、jdbc.properties文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FrHwrmf8-1684911731068)(C:\Users\lhl\AppData\Roaming\Typora\typora-user-images\image-20230522103505583.png)]

  1. 编写spring核心配置文件
  • 核心配置文件可以写在一个xml文件中,但最好分开写,每一层对应一个spring配置文件。

  • applicationContext_mapper.xml配置文件

<!--读取外部数据库配置文件-->
<context:property-placeholder location="jdbc.properties"/>

<!--创建数据源-->
<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>

<!--注册SqlSessionFactoryBean对象,给pojo类起别名,注册MyBatis核心配置文件-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--注册MyBatis核心配置文件-->
    <property name="configLocation" value="mybatis-config.xml"/>
    <!--配置数据源-->
    <property name="dataSource" ref="dataSource"/>
    <!--注册实体类,给pojo包下的类起别名-->
    <property name="typeAliasesPackage" value="com.lhl.pojo"/>
</bean>

<!--注册Sqlmapper文件,MapperScannerConfigurer这个类生成Mapper动态代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.lhl.mapper"/>
</bean>
  • applicationContext_service.xml
<!--引入applicationContext_mapper.xml  spring配置文件-->
<import resource="applicationContext_mapper.xml"/>

<!--使用注解创建对象,添加包扫描,扫描该包下的注解-->
<context:component-scan base-package="com.lhl"/>

五、Spring 事务

5.1、Spring 事务管理API

  • Spring对事务的管理底层实现方式是基于AOP实现的。采用AOP的方式进行了封装。
  • PlatformTransactionManager接口:spring事务管理器的核心接口。在Spring6中它有两个实现:
    • DataSourceTransactionManager:支持JdbcTemplate、MyBatis、Hibernate等事务管理。
    • JtaTransactionManager:支持分布式事务管理。

5.2、Spring 事务的五大隔离级别

  • 这些常量都是以Isolation_开头。形如: Isolation_XXX
  1. 默认(DEFAULT) :采用默认隔离级别。MySQL默认为REPEATABLE_READ(可重复读),Oracle默认为READ_COMMITTED(读已提交)。

  2. 读未提交(READ_UNCOMMITTED):存在脏读、不可重复读和幻读。

  3. 读已提交(READ_COMMITTED):解决脏读,存在不可重复读和幻读。

  4. 可重复读(REPEATABLE_READ):解决脏读、不可重复读。存在幻读。

  5. 串行化(SERIALIZABLE):不存在并发问题

  • 读取数据库的三大问题:
  1. 脏读:一个事务读取到另一个事务没有提交(增删改)的数据
  2. 不可重复读:在一一个事务中,因为其他事务所提交的修改和删除,第一次查询和第二次查询数据不一致。
  3. 幻读:在一个事务中,因为其他事务提交的插入操作,每次读取的数据不一致

5.3、Spring 事务的七大传播特性

  • 所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如:A 事务中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的维护情况,就称为事务传播行为。事务传播行为是加在方法上的。
  • 事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。
  1. REQUIRED:必须开启事务,加入的方法有事务就加入,没有就新建。
  2. REQUIRES_NEW:开启新事务,无论有没有事务都开启新事务,将之前的事务挂起,不存在事务嵌套。
  3. SUPPORTS:支持当前事务,有就支持,没有也不新建。
  4. NEVER:以非事务运行,如果有事务存在,抛出异常。
  5. NOT_SUPPORTED:不支持事务,以非事务方式运行,之前有事务就将其挂起。
  6. MANDATORY :必须运行在事务当中,如果有就使用当前事务。
  7. NESTED:有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。

5.4、声明式事务之注解方式

  • 通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。

  • @Transactional有几点需要注意

    1. 只能声明在public的method。原因是spring是通过JDK代理或者CGLIB代理的,生成的代理类,只能处理public方法,注解放在类名称上面,这样你配置的这个@Transactional 对这个类中的所有public方法都起作用,@Transactional 在方法名上,只对这个方法有作用,同样必须是public的方法。
    2. 不能被类内部方法调用。还是因为代理的原因,类内部自调用,不会经过代理类,所以@Transactional不会生效
  • 配置事务注解驱动

<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--ref="dataSource" ==> 引入的applicationContext_mapper.xml文件中的数据源对象-->
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--添加事务驱动,即开启事务注解,使用事务注解放假添加事务-->
<!--<tx:annotation-driven> 也可以这样写==> <tx:annotation-driven transaction-manager="事务管理器对象名字">-->
<tx:annotation-driven></tx:annotation-driven>
  • 在类或方法上添加@Transactional设置属性
@Transactional //使用默认方式
    /*@Transactional(propagation = Propagation.REQUIRED,//设置事务的传播特性,默认为Propagation.REQUIRED
                    isolation = Isolation.DEFAULT,//设置事务的隔离级别,默认为Isolation.DEFAULT
                    readOnly = true,//设置事务为只读事务,只允许select语句执行默认值是false
                    timeout = 10,//设置事务的超时时间,只计算DML语句执行时间,超过10秒DML语句没有执行完毕,就会回滚。默认是-1永不超时
                    rollbackFor = RuntimeException.class,//方法抛出运行异常就回滚。
                    rollbackForClassName = "RuntimeException",//方法抛出运行异常就回滚。
                    noRollbackFor = ArithmeticException.class,//方法遇到算术异常不回滚。
                    noRollbackForClassName = "ArithmeticException"//方法遇到算数异常不回滚
    )*/
    public int insert(Accounts accounts) {
        int count = accountsMapper.insertAccount(accounts);
        return count;
    }

5.5、声明式事务之XML方式

<!--声明式事务的配置 为事务管理添加数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!--使用xml方式声明事务-->
<!--配置切面的属性,哪些方法需要添加什么事务传播特性-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true"/>  <!--所有以get开头命名的方法设为只读-->
        <tx:method name="select*" read-only="true"/>
        <tx:method name="find*" read-only="true"/>
        <tx:method name="search*" read-only="true"/>
        <tx:method name="add*" propagation="REQUIRED" />
        <tx:method name="save*" propagation="REQUIRED" no-rollback-for="ArithmeticException"/>
        <tx:method name="insert*" propagation="REQUIRED"  no-rollback-for="ArithmeticException"/>
        <tx:method name="delete*" propagation="REQUIRED"/>
        <tx:method name="remove*" propagation="REQUIRED"/>
        <tx:method name="clean*" propagation="REQUIRED"/>
        <tx:method name="update*" propagation="REQUIRED"/>
        <tx:method name="modify*" propagation="REQUIRED"/>
        <tx:method name="set*" propagation="REQUIRED"/>
        <tx:method name="change*" propagation="REQUIRED"/>
        <tx:method name="*" propagation="SUPPORTS"/>
    </tx:attributes>
</tx:advice>
<!--使用AOP的技术进行切入点织入-->
<aop:config >
    <!--切入点表达式:指定在哪个包下的哪些类中的哪些方法添加事务处理-->
    <aop:pointcut id="pointcat" expression="execution(* com.bjpowernode.service.*.*(..))"></aop:pointcut>
    <!--完成切面与切入点绑定-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcat"></aop:advisor>
</aop:config>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值