Spring

Spring框架由Rod Johnson开发,2004年发布了Spring框架的第一版。Spring是一个从实际开发中抽取出来的框架,因此它完成了大量开发中的通用步骤,留给开发者的仅仅是与特定应用相关的部分,从而大大提高了企业应用的开发效率。

SSM-MyBatis篇:SSM框架中项目持久层:MyBatis
SSM-SpringMVC篇:SSM框架中项目逻辑层:SpringMVC
MavenJava项目管理神器:Maven

Spring简介

作用:为代码“解耦合”,降低代码间的耦合度(符合软件工程中的高内聚、低耦合的特点),让对象和对象(模块和模块)之间关系不是通过代码关联,而是通过配置来说明。

Spring根据代码的功能特点,使用Ioc降低业务对象之间耦合度,Ioc使得主业务在相互调用的过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了,而是由Spring容器统一管理,自动注入,即赋值。而AOP使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由Spring容器统一完成织入

官网:

Spring安装包:

- spring-framework-5.2.5.RELEASE
	- docs # 相关文档
		- javadoc-api # 相关api
		- kdoc-api
		- spring-framework-reference # 开发手册
	- libs # 源码
		- spring-model-5.2.5.RELEASE.jar # 相关模块的字节码.class文件
		- spring-model-5.2.5.RELEASE-javadoc.jar # 相关模块的开发文档
		- spring-model-5.2.5.RELEASE-sources.jar # 相关模块的源文件.java
	- schema # 相关模块的约束文件

Spring优点

  • 轻量
  • 针对接口编程、解耦合
  • AOP编程的支持
  • 方便集成各种优秀框架

Ioc控制反转

Ioc的概念

概念:IocInversion of Control,控制反转,是一个理论,把对象的创建、属性赋值,对象的声明周期都交给代码以外的容器管理。

Ioc分为控制和反转

  • 控制:对象创建、属性赋值,对象的声明周期
  • 反转:把开发人员管理对象的权限转移给了代码之外的容器实现,由容器完成对象的管理。
  • 正转:开发人员在代码中,使用new构造方法创建对象。

Ioc的实现

  • 依赖注入:DIDependency Injection,程序只需要提供要使用的对象的名称即可,对象如何创建,如何从容器中查找,获取都由容器内部自己实现

Spring的配置文件

<!-- xxx.xml(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">
    
	<!--    声明对象
	id:自定义对象名称,唯一值(可没有。spring提供默认的名称)
	class:类的全限定名称,spring通过反射机制创建对象,不能是接口

	spring根据id和class创建对象,把对象放入到spring的一个map之中
	map.put(id, object)
	-->
    <bean id="someService" class="org.bjpowernode.service.impl.SomeServiceImpl"/>
</beans>  

Spring的标准配置文件

  • 根标签是beans
  • beans后面是约束文件说明,beans里面是bean声明
  • beanjava对象,Spring容器创建的java对象

创建对象并调用接口方法

package org.bjpowernode;

import org.bjpowernode.service.SomeService;
import org.bjpowernode.service.impl.SomeServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
    public static void main(String[] args) {
        /* 一般创建对象格式 */
        // SomeService service = new SomeServiceImpl(); Impl一般指接口实现类
        // service.doSome();
        
        /* 指定spring的配置文件
        * 从类路径(classpath)之下开始的路径,一般是resources
        * */
        String config = "beans.xml";
        // 创建容器对象, ApplicationContext表示spring容器对象,通过ctx获取某个Java对象
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        // 从容器中获取指定名称的对象,使用getBean("id")
        SomeService service = (SomeService)ctx.getBean("someService");
        // 调用对象的方法,接口中的方法
        service.doSome();
    }
}

Spring容器创建对象的特点

Spring创建对象特点:

  1. Spring创建对象调用的构造函数默认是无参构造,如果定义了有参构造,则再定义无参构造.

  2. 创建Spring容器对象的时候,会读取配置文件,创建文件中声明所有的的Java对象,即new ClassPathXmlApplicationContext(config);

    • 优点:获取对象的速度快,因为对象已经创建好了

    • 缺点:占用内存

  3. String对象存在Spring内部的map中,并从map进行获取对象(可以调用ApplicationContext接口里面的方法试一试)

创建非自定义类对象

<beans>
	<bean id="date" class="java.util.Date"/>
</beans>

DI注入

基于XML的DI

Spring调用类的五参数构造方法,创建对象,对象创建后给属性赋值

  • 使用xml配置文件中的标签和属性
  • 使用注解

DI分类:

  • set注入,设值注入
  • 构造注入

set注入,简单类型注入(简单的数据类型和String

<beans>
<!-- 
	set注入,Spring调用类的set方法(必须要有set方法),完成赋值
 	<property name="名称" value="值" />
-->
    <bean id="myStudent" class="com.bjpowernode.ba01.Student">
        <property name="name" value="lh"/>
        <property name="age" value="12"/>
    </bean>
</beans>

注入过程:按照前调用无参构造的原则,其后调用setxx方法进行赋值(和属性名无关,无关是不是类的属性),而非直接通过有参构造进行赋值

给非自定义类同理

set注入,引用类型注入

<beans>
<!-- 
	set注入
 	<property name="名称" ref="beanid" />
-->
    <bean id="myst" class="com.bjpowernode.ba01.Student">
        <property name="name" value="lh"/>
        <property name="age" value="12"/>
        <property name="sl" ref="mysl"/>
    </bean>
    <bean id="mysl" class="com.bjpowernode.ba01.School">
        <property name="name" value="yc"/>
        <property name="age" value="32"/>
    </bean>
</beans>

构造注入:Spring调用类中的有参构造方法,在创建对象的同时,给属性赋值

<beans>
    <!--
		<constructor-arg>:表示一个构造方法的形参
				标签属性:name:构造方法形参名
						index:构造方法的参数位置T
						value:简单类型的形参值
						ref:引用类型的形参值
	-->
    <!-- name -->
    <bean id="myst" class="com.bjpowernode.ba01.Student">
        <constructor-arg name="name" value="pl"/>
        <constructor-arg name="age" value="234"/>
        <constructor-arg name="sl" ref="mysl"/>
    </bean>
    <!-- index -->
    <bean id="myst" class="com.bjpowernode.ba01.Student">
        <constructor-arg index="0" value="pl"/>
        <constructor-arg index="1" value="234"/>
        <constructor-arg index="2" ref="mysl"/>
    </bean>
    <!-- 省略index -->
    <bean id="mysl" class="com.bjpowernode.ba01.School">
        <property name="name" value="yc"/>
        <property name="age" value="32"/>
    </bean>
</beans>

引用类型自动注入:Spring可以根据某些规则给引用类型完成赋值,只对引用类型有效,规则byNamebyType

  • byName:按名称注入,Java类中引用类型属性名称和Spring容器中beanid一样,且数据类型也一样,这样的bean能够赋值给引用类型。
  • byType:按类型注入,Java类中引用类型的数据类型和Spring容器中beanclass同源关系,则可以赋值给引用类型。

byName规则

<beans>
    <bean id="st" class="com.bjpowernode.ba01.Student" autowire="byName">
        <property name="name" value="lh"/>
        <property name="age" value="123"/>
    </bean>

    <!-- 此处的id要和引用该类型的类中的引用数据名称相同 -->
    <bean id="sl" class="com.bjpowernode.ba01.School">
        <property name="name" value="yc"/>
        <property name="age" value="231"/>
    </bean>
</beans>

byType规则(只能有一个,具有唯一性)

同源关系

  • java中引用类型的数据类型和beanclass值是一样的。
  • java中引用类型的数据类型(父)和beanclass(子)值是父子类关系的。
  • java中引用类型的数据类型和beanclass值是接口和实现类关系的。
<beans>
    <bean id="st" class="com.bjpowernode.ba01.Student" autowire="byType">
        <property name="name" value="lh"/>
        <property name="age" value="123"/>
    </bean>

    <bean id="mysl" class="com.bjpowernode.ba01.School">
        <property name="name" value="yc"/>
        <property name="age" value="231"/>
    </bean>
</beans>

多个配置文件:在实际应用里,随着应用规模的增加,系统中Bean数量也大量增加口,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将Spring配置文件分解成多个配置文件。

  • 按功能模块分,-个模块一个配置文件。
  • 按类的功能分,数据库操作相关的类在一个文件,配置redis, 事务等等的一个配置文件。

Spring管理多个配置文件:常用的是包含关系的配置文件,项目中有一个总的文件,里面是有import标签包含其他的配置文件

<!--
	classpath:表示类路径。类文件所在的目录,spring通过 类路径加载配置文件
-->
<import reource="classpath:/其他文件路径1"/>
<import reource="classpath:/其他文件路径2"/> <!--  或者不加/-->

使用通配符*

<!--
不同包含总配置文件本身
-->
<import reource="classpath:/其他文件路径_*"/>
基于注解的DI

概念:使用Spring提供的注解,完成Java对象创建,属性赋值

Component:需要在类上使用注解@Component,该注解的value属性用于指定该beanid

@Component(value="myTest") /* 如果不加默认是类首字母小写的类名 */
class Test {
	// do something    
}
<beans>
    <!--
		component-scan 组件扫描器,组件是Java对象
		base-package 注解在项目中的包名,框架扫描这个包和子包中的所有类,找类中的所有注解
					遇到注解后,按照注解表示的功能,去创建对象,给属性赋值
	-->
	<context:component-scan base-package="com.bjpowernode.ba01"/>
</beans>

@Component功能相同的创建对象的注解。

  • @Repository:放在dao接口的实现类上面,表示创建dao对象,持久层对象,能访问数据库
  • @Service:放在业务层接口的实现类上面,表示创建业务层对象,业务层对象有事务的功能
  • @Controller:放在控制器类的上面,表示创建控制器对象。属于表示层对象。控制器对象能接受请求,把请求的处理结果显示给用户。

以上四个注解都能创建对象,但是@Repository @Service @Controller有角色说明,表示对象是分层的。对象是属于不能层的,具有额外的功能。


扫描多个包的三种方式

<beans>
    <!-- 多次使用组件扫描器 -->
	<context:component-scan base-package="com.bjpowernode.ba01"/>
	<context:component-scan base-package="com.bjpowernode.ba02"/>
	<!-- 使用分隔符 -->
	<context:component-scan base-package="com.bjpowernode.ba01;com.bjpowernode.ba02"/>
    <!-- 指定父包 -->
	<context:component-scan base-package="com.bjpowernode"/>
</beans>

注解指定属性值的方式

  • 简单类型value
  • 引用类型

简单类型value

放置位置:

  • 在属性定义的上面,无需set方法,推荐使用
  • set方法的上面
@Component(value = "MyStudent")
public class Student {
    @Value(value = "lh")
    private String name;
    @Value(value = "12")
    private int age;
    // do something
}

value通过外部配置属性文件获得

// .xml
<!--    读取外部属性配置文件.properties -->
    <context:property-placeholder location="classpath:/myconf.properties"/>
// Student.java
@Component(value = "MyStudent")
public class Student {
    // @Value("${key}")
    @Value("${myname}")
    private String name;
    @Value("${myage}")
    private int age;
	// do something
}
# myconf.properties
myname=lh
myage=23
// app.java
package com.bjpowernode;

import com.bjpowernode.ba01.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void Test() {
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        Student st = (Student) ctx.getBean("MyStudent");
        System.out.println(st);
    }
}

引用类型

放置位置:

  • 在属性定义的上面,无需set方法,推荐使用
  • set方法的上面
@Component(value = "MyStudent")
public class Student {
    @Value("${myname}")
    private String name;
    @Value("${myage}")
    private int age;
    /**
     * 引用类型
     * @Autowired: spring框架提供的,给引用类型赋值的,使用自动注入的原理
     *          支持byType和byName,默认是byType
     */
    @Autowired()
    private School sl; // 提示:school必须在student赋值前先赋值
}

使用byName方法(唯一性)

// School.java
@Component("MySchool")
public class School {
    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Value("${lname}")
    private String name;
    @Value("${lage}")
    private int age;
}
// Student.java
@Component(value = "MyStudent")
public class Student {
	// 此处与Qualifier一起用
    /** @Autowired: 给引用类型赋值
    *   @Qualifier(value="bean id")
    */
    @Autowired
    @Qualifier(value = "MySchool")
    private School sl;
}

Autowired(required): required默认是true

true: spring在启动的时候,创建容器对象时候,会检查引用类型是否赋值成功,如果赋值失败,终止程序执行, 并报错。

false:引用类型赋值失败,程序正常执行,不报错。引用类型的值是null

另一种引用注解的方式:@Resource,为JDK提供,此处不作讲解。

AOP面向切面编程

AOP的概念

概念:AOPAspect Orient Programming,面向切面编程。

  • Aspect:表示切面,给业务方法增加的功能,叫做切面,一般是非业务功能,可复用,例如日志功能,事务功能,权限检查,参数检查,统计检查。
  • Orient:面向,对着
  • Programming:编程

AOP的作用

  • 让切面编程复用

  • 让开发人员专注业务逻辑,提高开发效率

  • 实现业务功能和其他业务功能解耦合

  • 给存在的业务方法,增加功能,不用修改原来的代码

AOP的术语

  • Aspect:表示切面,给业务方法增加的功能
  • JoinPoint连接点,连接切面的业务方法,在这个业务方法执行时,会同时执行切面的功能
  • PointCut:切入点,是一个或多个连接点集合,表示这些方法执行时,都能增加切面的功能
  • target:目标对象,给那个对象增加切面的功能,这个对象就是目标对象
  • Advice:通知(增强),表示切面执行时间,在目标方法之前执行切面,还是目标方法之后执行切面

AOP三个最主要的要素:AspectPointCutAdvice,这个概念是在Advice时间,在PointCut的位置,执行Aspect

AOP是一个动态的思想,在程序运行期间,创建代理(ServiceProxy),使用代理执行方法时,增加切面的功能,这个代理对象是存在内存中的。

AOP技术思想的实现

使用框架实现AOP,框架有很多,著名的有两个

  • SpringSpring框架实现AOP思想中的部分功能。Spring框架实现AOP的操作比较繁琐、比重。
  • Apsectj:独立的框架,专门是AOP,属于Eclipse

Apsectj官网:

Apsectj

Apsectj可以通过xml和注解两种方式实现AOP

通知Advice

  • @Before:前置通知
  • @AfterReturnning:后置通知,在目标方法结束后触发
  • @Around:环绕通知
  • @AfterThrowing:异常通知
  • @After:最终通知

位置PointCut

表达式:execution(方法的定义)

execution(访问权限类型(可省略) 返回值类型 包名类名.方法名(参数类型和参数个数) 抛出异常类型(可省略))

可使用通配符

符号 意义
* 0至多个任意字符
·· 用在方法参数中,表示任意多个参数;用在包名后,表示当前包及其子包路径
+ 用在类名后,表示当前类及其子类;用在接口后,表示当前接口及其实现类

举例

// 切面类
package com.bjpowernode.handle;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * 切面类的注解,表示当前类是一个切面类
 */
@Aspect
public class MyAspect {
    @Before(value = "execution(" +
            "public " +
            "void " +
            "com.bjpowernode.service.impl.SomeServiceImpl.doSome(String, Integer))")
    public void myBefore() {
        System.out.println("time" + new Date());
    }
}

// 接口
package com.bjpowernode.service;

public interface SomeService {
    void doSome(String name, Integer age);
}
// 实现类
package com.bjpowernode.service.impl;

import com.bjpowernode.service.SomeService;
import org.springframework.stereotype.Component;

public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name, Integer age) {
        System.out.println("do somehtin");
    }
}
<!-- 配置文件 -->
<?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">
    <bean id="myStudent" class="com.bjpowernode.service.impl.SomeServiceImpl"/>

    <bean id="MyAspect" class="com.bjpowernode.handle.MyAspect"/>
    <!-- 声明自动代理生成器:目的是创建目标对象的代理
     调用aspectj框架中的功能,寻找spring容器中的所有目标对象,
    把每个目标对象加入切面类中的功能,生成代理。
    这个代理对象是修改的内存中的目标对象,这 个目标对象就是代理对象(ServiceProxy)
    -->
    <aop:aspectj-autoproxy />
</beans>
// 主函数
package com.bjpowernode;

import com.bjpowernode.service.SomeService;
import com.bjpowernode.service.impl.SomeServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void Test() {
        String config = "applicationContext.xml";
        ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
        SomeService service = (SomeService) ctx.getBean("myStudent");
        service.doSome("lh", 20);
    }
}
# 结果输出
timeSun Feb 06 19:25:54 CST 2022	# Before前置切面,完成
do somehtin

此时xml会创建三个对象,一个是实现类对象,一个是切面类对象,一个是代理切面类对象

# 实现类对象
myStudent
# 切面类对象
MyAspect
# 代理切面类对象
org.springframework.aop.config.internalAutoProxyCreator

获取执行切面方法的方法的信息

@Aspect
public class MyAspect {
    @AfterReturning(value = "execution(" +
            "public " +
            "void " +
            "com.bjpowernode.service.impl.SomeServiceImpl*.doSome(String, Integer))")
    public void myBefore(JoinPoint js) {
        System.out.println(js.getSignature());
        System.out.println("time" + new Date());
    }
}

JoinPoint不管之后加什么参数,该参数都放在第一个位置

其他此类和接口方法可自行看源码


@AfterReturnning:此方法比较特殊,因为是在方法执行之后执行,所以其可以获取方法的返回结果retunning=自定义的变量,表示目标方法的返回值的,自定义变量名称必须和通知方法的形参名一-样

@AfterReturnning(value = "", returning="res")
public void mytest(Object res) {
    // 
}

环绕通知:@Around。其他通知是相当于一个配件,该通知相当于一个容器,内部包含方法执行

//环绕置通知方法的定义
//1)方法是public
//2)方法是必须有返回值,推荐使用object类型
//3)方法名称自定义
//4)方法必须有ProceedingJoinPoint参数,是用来执行方法的
@Aspect
public class MyAspect {
    @Around(value = "" +
            "execution(public void com.bjpowernode.service.impl.SomeServiceImpl2.doSome(..))")
    public Object fd(ProceedingJoinPoint pp) throws Throwable {
        // 执行目标方法
        Object rr = null;
        rr = pp.proceed();
        return rr;
    }
}

辅助注解:@Pointcut

/**
@Pointcut:定义和管理切入点,不是通知注解。
	属性: value 切入点表达式
	位置: 在一个自定义方法的上面, 这个方法看做是切入点表达式的别名。
		 其他的通知注解中,可以使用方法名称,就表示使用这个切入点表达式了
*/
@Pointcut("execution(public void com.bjpowernode.service.impl.SomeServiceImpl2.doSome(..))")
public void my() { // 此时代表execution中的内容
    // 无需代码
}
@After(value = "my()") // 即可
publid void my() {
    // do something
}

Spring集成MyBatis

MyBatis博客可参考:

集成思路

spring能集成很多的框架,是spring一个优势功能。通过集成功能,让开发人员使用其他框架更方便。

集成使用的是spring Ioc核心技术。

SpringMyBatis进行整合,主要解决的问题是将SqlSessionFactory对象交给Spring管理,所以,该整合只需要将SqlSessionFactory的对象生成器SqlSessionFactoryBean注册在Spring容器中,再将其注册给Dao的实现类即可完成整合。

实现SpringMyBatis的整合常用的方式:扫描的Mapper动态代理

<!-- 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"
       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="jdbc.properties"/>
    <bean id="mydataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    
    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 此处mybatis配和spring创建对象 -->
        <!-- 指定数据源 -->
        <property name="dataSource" ref="mydataSource"/>
        <!-- 指定mybatis配置文件 -->
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>
    
    <!-- MapperScannerConfigurer循环basePackage所表示的包,把包中的每个接口都找到
        调用sqlsession.getmapper,把每个dao都创建出代理对象然后放在容器中
     -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定SqlSessionFactory对象名称 -->
        <property name="sqlSessionFactoryBeanName" value="factory"/>
        <!-- 指定基本包名,生成studentDao -->
        <property name="basePackage" value="com.bjpowernode.dao"/>
    </bean>

    <bean id="studentService" class="com.bjpowernode.service.impl.ServiceStudentImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>
</beans>

实现流程

下面具体阐述该过程

  • 利用阿里云数据连接池模块DruidDataSource并引入外部配置文件jdbc.propertie来连接数据库
  • 利用mybatis-spring.jar包中的模块沟通连接MyBatisSpring模块,此处使用了两个模块
    • SqlSessionFactoryBean:引入mybatis.xml并通过SqlSessionFactoryBuilder来获取SqlSessionFactory接口,此时抛弃了传统的通过mybatis.xml配置数据库,而是通过DruidDataSource来配置数据连接池,该继承自DefaultSqlSessionFactory,与SqlSessionFactory殊途同归。
    • MapperScannerConfigurer:首先获取SqlSessionFactoryBean,然后使用basePackage属性来获取所有的dao接口并配合主配置文件mybatis.xml中的mapper来实现动态代理,同getMapper(xxx.class)方法,生成的dao接口的工具类(实现类)默认是接口名首字母的小写,然后服务即可利用该实现类实现业务
  • 定义服务的<bean/>实现类,参数使用dao生成的实现类(引用类型)即可。

Spring事务

Spring的事务概念

什么是事务?事务是一些sql序列的集合, 是多条sq|,作为一个整体执行。

什么情况下需要事务?

一个操作需要多条(2条或2条以上的sql),sql语句-起完成,操作才能成功。

事务:加在业务类的方法上面(public方法 上面),表示业务方法执行时,需要事务的支持。

不同的数据库访问技术,处理事务是不同的

  • JDBC访问数据库,事务处理

    public void updateAccount() {
        conn.setAutoCommit(false);
        /* 
        * do something
        */
        conn.setAutoCommit(true);
    }
    
  • MyBatis执行数据库,事务处理

    public void updateAccount() {
        SqlSession session = SqlSession.openSession(false);
        try {
        	/* 
        	* do something
        	*/
            session.commit();
        } catch {
            session.rollback();
        }
    }
    

    spring统一管理事务,把不同的数据库访问技术的事务处理统一起来。

Spring事务管理器

Spring框架使用事务管理器对象,管理所有的事务。

事务管理器接口:PlatformTransactionManager,作用:定义了事务的操作,主要是commit()rollback()

事务管理器实现类:一种数据库的访问技术有一个实现类,由具体的实现类具体完成事务的提交和回滚。

  • JDBCMyBatis访问数据库有自己的事务管理器实现类:DataSourceTransactionManager
  • hibernate框架,他的事务管理器实现类:HibernateTransactionManager

事务使用的是AOP的环绕通知@Around

Spring的事务定义

事务定义接口TransactionDefinition中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对他们的操作。

隔离级别:控制事务之间影响的程度

  • ISOLATION_DEFAULT:采用DB默认的事务隔离级别,MySQL默认是ISOLATION_REPEATABLE_READOracle默认是ISOLATION_READ_COMMITTED

  • ISOLATION_READ_COMMITTED:读已提交,解决脏读,存在不可重复读和幻读

  • ISOLATION_READ_UNCOMMITTED:读未提交,未解决任何并发问题

  • ISOLATION_REPEATABLE_READ:可重复读解决脏读和不可重复读,存在幻读

  • ISOLATION_SERIALIZABLE:串行化,不存在并发问题

事务超时:表示一个业务方法最长的执行时间,没有到达时间没有执行完毕,spring回滚事务 。

以秒为单位,整数值,默认是-1。

传播行为:业务方法在调用时事务在方法之间的传递和使用,使用传播行为可以标识是否有事务。

  • PROPAGATION_REQUIREDSpring默认传播行为,方法在调用时,如果存在事务,则使用当前事务;如果没有事务,则新建事务,方法在新事务中执行

  • PROPAGATION_REQUIRES_NEW:方法需要一个新事务,如果调用方法时,存在一个事务,则原来的事务暂停,直到新事务执行完毕;如果方法调用时没有事务,则新建一个事务,在新事务中执行代码

  • PROPAGATION_SUPPORTS:支持,有事务则可以正常执行,没有事务也可以正常执行,一般是查询操作

  • PROPAGATION_MANDATORY

  • PROPAGATION_NESTED

  • PROPAGATION_NEVER

  • PROPAGATION_NOT_SUPPORTED

Spring注解管理事务

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

属性:

  • propagation:事务的传播行为,他使用的是Propagation的枚举值,默认值为Propagation.REQUIRED
  • isolation:用于设置事务的隔离级别。该属性类型为Isolation枚举,默认值为Isolation.DEFAULT
  • readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为boolean,默认值为false
  • timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为int,默认值为-1,即没有时限。
  • rollbackFor:表示回滚的异常类列表,他的值是一个数组,每个值是异常类型的class
  • rollbackForClassName:表示回滚的异常类列表他的值是异常类名称,是String类型的值。
  • noRollbackFor:不需要回滚的异常类列表。是class类型的。
  • noRollbackForClassName:不需要回滚的异常类列表,是String类型的值。

注解使用步骤:

  • Spring的配置文件,声明事务的内容

    • 声明事务管理器,说明使用哪个事务管理器
    • 声明使用注解管理事务,开启是注解驱动
  • 在类的源代码中,加入@Transactional

    @Transactional(
            propagation = Propagation.REQUIRED,
            isolation = Isolation.DEFAULT,
            readOnly = false,
            timeout = 20,
            rollbackFor ={NullPointerException.class, NotEnoughException.class})
    

    applicationContext.xml文件

    <!-- 事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--指定数据源-->
        <property name="dataSource" value="mydataSource"/>
    </bean>
    <!-- 开启事务注解驱动
        transaction-manager:指定事务管理器id
     -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    

事务默认是运行时产生异常,此时不用谢rollback

使用特点

  • spring框架自己提供的事务控制。
  • 适合中小型项目。(直接在源代码中进行更改)
  • 使用方便,效率高

AspectJ配置事务

使用步骤

  • pom.xml中加入spring-aspectj的依赖
  • spring的配置文件声明事务的内容
    • 声明事务管理器
    • 声明业务方法需要的事务属性
    • 声明切入点表达式

applicationContext.xml

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--指定数据源-->
    <property name="dataSource" value="mydataSource"/>
</bean>

<!-- 声明业务方法的事务属性 -->
<tx:advice id="serviceAdvice" transaction-manager="transactionManager">
    <!-- 给具体的业务方法增加事务的说明 -->
    <tx:attributes>
        <!-- name: 业务方法的名称/带有部分通配符的名称/使用*表示以上方法以外的(不是所有) -->
        <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="10"/>
    </tx:attributes>
</tx:advice>

切入点表达式

<aop:config>
    <!--
    声明切入点表达式
    expression:切入点表达式,表 示那些类和类中的方法要参与事务
    id: 切入点表达式的名称,唯-值
    -->
    <aop:pointcut id="servicePointcut" expression="execution(* *..service..*.*(..))"/>
    <!--关联切入点表达式和事务通知-->
    <aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicePointcut"/>
</aop:config>

意义:如果aop:advisorpointcut-ref所表示的切入点表达式和advice-ref相同,则执行AOP

Spring和Web

maven选择webapp

<groupId>com.bjpowernode</groupId>
<artifactId>testmy</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>

此处内容是根据servlet来配置的,为了继续后续SSM框架的学习,后期使用SpringMVC进行整合。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐