Spring框架
一. IoC
1. Spring的目标
集成 整合。

优势:

解耦合

侵入小

轻量级

2. IoC控制反转
将控制权(创建对象)从调用方转义到Spring容器。

以前对象的创建是由我们开发人员自己维护,包括依赖关系也是自己注入。使用了spring之后,对象的创建以及依赖关系可以由spring完成创建以及注入,反转控制就是反转了对象的创建方式,从我们自己创建反转给了程序创建(spring)

3. DI: Dependency Injection 依赖注入(属性注入)
spring这个容器中,替你管理着一系列的类,前提是你需要将这些类交给spring容器进行管理,然后在你需要的时候,不是自己去定义,而是直接向spring容器索取,当spring容器知道你的需求之后,就会去它所管理的组件中进行查找,然后直接给你所需要的组件.

// 1. 创建 一个 Spring 容器(Map), 装SpringBean  
// 2. 根据配置文件application.xml 创建了两个spring bean ,一个叫helloService  一个叫helloController
// 3. 根据配置文件application.xml 将helloService注入到helloController
// 4. 存入spring容器中
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");

// 从容器中获取对应的bean并且进行类型转换后返回
HelloController helloController = applicationContext.getBean("helloController", HelloController.class);

<!--创建对象: 对象取个ID;对象的类路径是什么-->
<bean name="helloService" class="com.woniuxy.cq.service.impl.HelloServiceImpl"/>

<!--创建对象: 对象取个ID;对象的类路径是什么-->
<bean id="helloController" class="com.woniuxy.cq.controller.HelloController">
    <!--对属性进入注入-->
    <property name="helloService" ref="helloService"/>
</bean>
 
4. 核心API
(1) BeanFactory bean工厂 用于创建bean
containsBean(String) : 是否包含这个bean
getAliases
getBean 获取bean
getBean(String name, Object… args) 根据名字及构造器参数来定位一个bean
getType 得到bean的类型
isPrototype 作用域,是否是原型
isSingleton 是否是单例
isTypeMatch
isTypeMatch
FACTORY_BEAN_PREFIX

(2) ApplicationContext 应用上下文
继承了以下接口,比Beanfactory提供更多的能力,包括对国际化的支持,对事件发布的能力,对资源加载的能力,获取环境的能力
MessageSource 对国际化支持
EnvironmentCapable 获取到环境
ApplicationEventPublisher 应用程序事件发布器
ResourcePatternResolver 资源加载器

API:
getId
getApplicationName 应用名字
getDisplayName
getStartupDate 启动时间
getParent 得到父容器
getAutowireCapableBeanFactory

实现类

ClassPathXmlApplicationContext xml配置的基于类路径
AnnotationConfigApplicationContext 注解配置
FileSystemXmlApplication xml配置基于文件系统
5.依赖配置
(1) Property

ref 引用类型: 引用已有的bean

<bean id="HelloService" class="com.woniu.cq.service.impl.HelloServiceImpl"></bean>

<property name="HelloService" ref="HelloService"/>
 
bean 引用类型: 临时新建一个bean

value 基本类型 string

list / set 集合

ref

value

bean

<property name="names" >
            <list>
                <value> mcf </value>
                <value> xixi </value>
                <value> anqila </value>
            </list>
</property>
 
map

 <property name="student" >
            <map>
                <entry key="学生一号" value="吃饱了"></entry>
                <entry key="学生二号" value="没吃饱"></entry>
                <entry key="学生三号" value="没有吃"></entry>
            </map>
</property>
 
6.依赖注入(两种方式)
(参考链接:https://blog.csdn.net/qq_35981996/article/details/89314216)

(1)Setter 注入:需生产set方法
例:比如主板类中(Mainboard)含有Cpu对象(Cpu)和Ram(Ram)对象:先生成此两个对象的set方法,分别注入:

public interface Mainboard extends PCComponent{
   private Cpu cpu;
   private Ram ram;
    public void setCpu(Cpu cpu);
    public void setRam(Ram ram);
 
​ 随后编写Spring的配置文件applicationContext.xml。中的name为类的一个别名,而class为类的完整名称。(ps:name的值需要与在set方法中中名字一直,例如setAaa,那么name就要是aaa)


<bean id="mainboard" class="com.spring.service.pc.Ipml.InterBoard">
    <!-- 值类型用value注入    引用类型用ref注入 -->
    <property name="grade" value="60"></property>
    <property name="cpu">
        <bean class="com.spring.service.pc.Ipml.InterCpu"></bean>
    </property>
    <property name="ram">
        <bean class="com.spring.service.pc.Ipml.KingstoneRam"></bean>
    </property>
</bean>
 
(2)构造器注入:(需生产带参数的构造方法)

​ 使用方法:

​ 第一,在类中,不用为属性设置setter方法,但是需要生成该类带参的构造方法。

​ 第二,在配置文件中配置该类的bean,并配置构造器,在配置构造器中用到了节点,该节点有四个属性:

​ · index是索引,指定注入的属性,从0开始;

​ · type是指该属性所对应的类型;

​ · ref 是指引用的依赖对象;

​ · value 当注入的不是依赖对象,而是基本数据类型时,就用value;

​ 例如:

​ ① 索引注入:


public class car implements Serializable {
    private String name;
    private String  color;
    private double price;
    public car(String name, String color, double price) {
        this.name = name;
        this.color = color;
        this.price = price;
    }

 
​ 在配置文件中,不再用标签,而是用标签代替,其中索引序号不能乱:

<constructor-arg index="0" value="B驰"></constructor-arg>
        <constructor-arg index="1" value="红色"></constructor-arg>
<constructor-arg index="2" value="66"></constructor-arg>
 
​ ② 类型注入,其中类型顺序不能乱

<constructor-arg type="java.lang.String" value="奥迪" ></constructor-arg>
        <constructor-arg type="java.lang.Double" value="500000"></constructor-arg>
<constructor-arg type="java.lang.String" value="黑色"></constructor-arg>
 
注意:属性中含有List,Array,Map类型时:了解如下写法:

<constructor-arg index="3" >
            <list>
                <value>张三</value>
                <value>李四</value>
                <value>王五</value>
            </list>
            <map>
                <entry key="id" value="1"></entry>
            </map>
 
7. 创建对象(三种方式)
(1) 构造器创建

(2) 静态工厂方法

​ ① 提供一个类,此类中包含一个静态方法,用于创建对象

public class teacherFactory {

    public static teacher getInstance(){
        return new teacher();
    }
}
 
​ ② 配置的是工厂方法所在的类(teacherFactory)以及静态工厂方法(getInstance),但是返回的是目标类型(teacher)

<!--  使用静态工厂的方式创建对象  -->
    <bean name="tf" class="com.woniu.cq.Factory.teacherFactory" factory-method="getInstance"></bean>
 
(3) 实例工厂

​ 单独声明一个工厂bean;在目标bean上指定工厂bean以及工厂方法及参数;创建出对象类型是目标类型

<!--  使用实例工厂的方式创建对象 -->
    <bean name="computer" class="com.woniu.cq.Factory.computerFactory"></bean>
    <bean name="com" factory-bean="computer" class="com.woniu.cq.Factory.computerFactory" factory-method="getcomputer">
        <constructor-arg index="0" value="100"/>
        <constructor-arg index="1" value="华为"/>
    </bean>
 
(4) 使用FactoryBean创建对象 (实现FactoryBean接口)

public class pigFactory implements FactoryBean {

    @Override
    public pig getObject() throws Exception {
        pig p = new pig();
        p.setColor("花猪");
        p.setType("中华猪");
        return p;
    }
 
    <!--  使用FactoryBean方法创建对象 -->
    <bean name="pig" class="com.woniu.cq.Factory.pigFactory"></bean>
 
8. 作用域:创建对象的行为
singleton 单例 默认 一个springBean只有一个实例
prototype 原型 每次创建一个
request 请求 每次请求创建一个
session 会话 每次会话创建一个
websocket websocket
9. Bean生命周期
​ (1) 初始化 :有一些 bean需要在初始化做一些操作

​ (2) 销毁:在销毁之前做一些操作

​ (3) 配置

​ ① 实现接口 InitializingBean, DisposableBean

​ ②Xml

    <bean name="connectionpoll" class="com.woniu.cq.entity.connection" init-method="init" destroy-method="disposable"></bean>

//实现接口
public class connection implements InitializingBean, DisposableBean {
    //自己定义的方法
    public void init(){
        System.out.println("初始化10个连接池");
    }
    //自己定义的方法
    public void disposable(){
        System.out.println("关闭10个连接池");
    }
    
    //实现的初始化方法
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("++++++++++++++++++++初始化10个连接池");
    }
    //实现的关闭方法
    @Override
    public void destroy() throws Exception {
        System.out.println("___________________关闭10个连接池");
    }
}
 
10. 配置
​ (1)XML配置

​ 优势:

​ ① 依赖关系清楚

​ ② 无须改动代码

​ 劣势:

​ ①配置繁琐

​ (2)XML + 注解混合方式

​ 优势:

​ ① 减少大量配置

​ 劣势:

​ ①对代码又一定的侵入

如何使用XML+注解配置

① :在XML的Beans中配置:

<context:component-scan base-package="com.woniu.cq"></context:component-scan>
 
②:在类、或者变量上添加注解

//在类上添加注解
@Component
public class HelloController {
    //在变量上添加注解
    @Autowired
    private HelloService helloService ;
 
XML 配置的相关注解

@Component 组件
@Controller 控制器
@Service 服务
@Repository 数据库访问服务DAO
@Autowired 自动装配,请求spring容器注入该属性
PS:前四种注解功能一致,只是Spring单独把他列出来了,作为一个分类。

11. 注解补充
@Value(“10”) => SpEL

详情参看:https://www.jianshu.com/p/e0b50053b5d3

@Resource ≈ @Autowired (Resource 是java官方注解)

默认按类型组装
NoUniqueBeanDefinitionException()

bean的名字规则:首字母小写

默认不是按照名字装配(byName),按照类型装配(byType)

指定一个 @Qualifier 加在属性,使用的地方
按照名字 beans 标签 default-autowire=“byName” 修改变量名
默认一个 @Primary 加在类声明上
@Resource

12. Java配置方式
@Configuration 把一个类标记为配置类,相当于配置文件

@ComponentScan 组件扫描

@Bean

依赖注入的两种方式:

① 直接调用:并不是调用方法,而是向spring容器请求一个bean,根据作用域决定是否是同一个

@Bean
public HelloDAO helloDAO(){
    return new HelloDAOImpl();
}

@Bean
public HelloService helloService(){
    //        System.out.println(helloDAO);
    HelloServiceImpl helloService = new HelloServiceImpl();
    HelloDAO helloDAO1 = helloDAO();//向spring容器请求一个helloDAO的bean
    HelloDAO helloDAO2 = helloDAO();
    System.out.println(helloDAO1);
    System.out.println(helloDAO2);
    helloService.setHelloDAO(helloDAO1);
    return helloService;
}
 
②方法参数

@Bean
public HelloService helloService(HelloDAO helloDAO){
    HelloServiceImpl helloService = new HelloServiceImpl();
    helloService.setHelloDAO(helloDAO);
    return helloService;
}
 
14. 注解整理
@Component 组件

@Controller 控制器

@Service 服务

@Repository 数据库访问服务DAO —> 加在类上,声明它是一个spring管理的bean,为创建一个对象

@Autowired spring提供, 默认byType

@Resource java标准注解,name type —> 注入依赖

@Value 注入值(spel表达式)

@Qualifier

@Primary —> 存在多个同类型的bean,区分

@Configuration : 声明这个类是一个配置类

@Bean : 加在方法上,配置一个spring的bean,bean名字是方法名,返回值是bean的内容,一般添加到外部的代码上

@Scope : 设置spring的bean的作用域(initMethod destoyMethod)

@ComponentScan : 组件扫描 (可以与@Bean混合使用)

@Imort : 导入另一个配置类

@ImportResource —> 基于java配置

@EnableAspectJAutoProxy 启动Aop(在配置类上加入注解)

@PointCut

@After

@Before

@Around —> AOP

15. Spring框架概念图


核心容器:core container
AOP支持
数据访问支持:
对JDBC封装
对ORM封装
事务抽象
Web支持
二. AOP
1. Aop的基本概念
优势
提高了代码的复用率
提高了代码可维护性
对业务代码无侵入
典型应用于

oop :面向对象编程 object oriented programming

纵向调用链路

controller

-> service

-> dao

aop :面向切面编程 aspect oriented programming

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。

核心概念
连接点: JoinPoint 可以进行切入的点 类的方法
方法名
参数
返回值
异常

切入点: PointCut 进行切面处理的连接点 被选出的连接点,定义如何选择连接点
切面: Aspect 切面相关的所有内容。包含了 在何处执行何种动作
通知: Advice 引入的新功能(性能统计、日志、事务管理…)
前置通知 方法执行前
后置通知 方法执行后
环绕通知 方法执行前后
异常通知 异常时执行
最终通知 finally
织入: Weave 将新功能加入到切入点的过程
目标对象: Target 切入点所在的对象,切面处理的对象(被代理对象,被通知的对象,被增强的类对象。)
引入: Introduction 为类增加新方法
2. AOP 示例步骤:(配置一个简单的AOP)
① 添加依赖

<properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   <maven.compiler.source>1.8</maven.compiler.source>
   <maven.compiler.target>1.8</maven.compiler.target>
   <aspectj.version>1.8.9</aspectj.version>
   
 </properties>
   
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>4.11</version>
     <scope>test</scope>
   </dependency>
   <!--会将其他包传递依赖进来-->
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>4.1.6.RELEASE</version>
   </dependency>
   
   <!--aspectj-->
   <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjrt</artifactId>
     <version>${aspectj.version}</version>
   </dependency>
   <dependency>
     <groupId>org.aspectj</groupId>
     <artifactId>aspectjweaver</artifactId>
     <version>${aspectj.version}</version>
   </dependency>
   
   
 </dependencies>
 
② 启动aop 在配置类上加入注解 @EnableAspectJAutoProxy

@Configuration//把一个类标记为配置类,相当于配置文件
@ComponentScan("com.woniu.cq")//扫描组件
@EnableAspectJAutoProxy//开启切面的自动代理功能
public class AopConf {

}
 
③ 编写切面(切入点,通知)

类加@Component @Aspect
新建方法 加注解 @PointCut
@Component
@Aspect //标识这是一个切面
public class foodAspect {

    //切面点
    @Pointcut("execution(* com.woniu.cq.service.*.*(..))")
    //第一个* 返回值
    //第二个* 所有类
    //第三个* 所有方法
    // (..)所有参数
    public void allService(){

    }

    //通知
    @Before("allService()") //前置通知
    public void doBefore(JoinPoint joinPoint){
        //方法签名
        Signature methodName = joinPoint.getSignature();
        //参数
        Object[] args = joinPoint.getArgs();
        //目标对象
        String name = joinPoint.getTarget().getClass().getName();

        System.out.println("访问"+name+"----"+methodName);
    }

}

    
    @Around("allService()")//环绕通知
     public void doAround(ProceedingJoinPoint proceedingJoinPoint){
         //前面
         long before = System.currentTimeMillis();
         //执行
         try {
             //调用连接点所在的方法
             Object[] args = proceedingJoinPoint.getArgs();
             proceedingJoinPoint.proceed(args);
     
         } catch (Throwable throwable) {
             throwable.printStackTrace();
         }
         long after = System.currentTimeMillis();
     
         //后面
         Signature signature = proceedingJoinPoint.getSignature();
         System.out.println(signature.getName()+"执行耗时"+(after-before)+"毫秒");
     }

 
3. Aop注解解析
@Before 前置通知(Before advice)
在某连接点(JoinPoint)——核心代码(类或者方法)之前执行的通知,但这个通知不能阻止连接点前的执行。

为什么不能阻止线程进入核心代码?

因为@Before注解的方法入参不能传ProceedingJoinPoint,而只能传入JoinPoint。要知道从aop走到核心代码就是通过调用ProceedingJionPoint的proceed()方法。而JoinPoint没有这个方法。 

这里牵扯区别这两个类:Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。暴露出这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到JoinPoint,这跟切面类型有关), 能决定是否走代理链还是走自己拦截的其他逻辑。建议看一下 JdkDynamicAopProxy的invoke方法,了解一下代理链的执行原理。这样你就能明白 proceed方法的重要性。
 
@After 后通知(After advice)
当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。

**@AfterReturning 返回后通知(After return advice) **
在某连接点正常完成后执行的通知,不包括抛出异常的情况。

@Around 环绕通知(Around advice)
包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。这时aop的最重要的,最常用的注解。用这个注解的方法入参传的是ProceedingJionPoint pjp,可以决定当前线程能否进入核心方法中——通过调用pjp.proceed();

@AfterThrowing
抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。

4. AOP的动态代理
(1)代理模式

目标类和代理类 实现了同一个接口

表示有共性,可以互换

共同的方法

proxy持有target的引用

调用target的方法来实现一些内容

(2)动态代理

任意给定一个类(实现某个接口),aop框架会自动生成一个代理类
实现该类的接口;
持有该类的实例的引用
将动态代理放入spring容器,取出的是动态代理类
(3)动态代理的两种实现方式

​ ① jdk动态代理 通过接口实现动态代理

//JDK 使用接口的方式实现动态代理
public class DynamicProxy {

    //目标对象
    private Object target;

    public Object getProxy(Object target){
        this.target=target;
        //target.getClass().getClassLoader() , target.getClass().getInterfaces() 这两个是在动态创建代理
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),//获取目标对象的类加载器
                target.getClass().getInterfaces(),//获取目标对象的接口
                //这个是在执行方法
                //方法拦截器invocationHandler  提供需要加入的功能
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开始执行方法---"+method.getName());
                        Object result = method.invoke(target, args);//执行方法
                        System.out.println("执行完方法了");
                        return result;
                    }
                }
        );
    }
}
 
在重写 InvocationHandler()中的invoke方法时 ,各个参数的含义

public Object invoke(Object proxy, Method method, Object[] args)

proxy :代理的实例

method :需要被执行的方法

args :参数

//JDK动态代理测试
public class ProxyTest {
    public static void main(String[] args) {
        //Buy为一个接口  Tuhao是实现Buy这个接口的一个类  重写了Buy这个接口的buy方法
        Buy tt = (Buy) new DynamicProxy().getProxy(new Tuhao());
        //这个时候输出的Class 类型已经不是Buy了,而是$Proxy0,是动态代理返回的代理类型
        System.out.println(tt.getClass().getSimpleName());
        //使用代理类型去执行buy方法,会自动在方法前后加上你在动态代理invoke 中加入的内容
        tt.buy();
    }
}

//JDK动态代理测试最后输出的结果为:
$Proxy0        //输出tt的classname
开始执行方法---buy //执行 invoke()方法中,目标方法前的内容
为所欲为的买买买!!!!!!!! //执行目标方法 buy()
执行完方法了 //执行 invoke()方法中,目标方法后的内容
 
​ ②cglib动态代理 支持通过继承实现动态代理

//cglib 继承实现动态代理
public class cglib {

    public Object getProxy(Object target){
        //增强器
        Enhancer enhancer = new Enhancer();
        //生成父类
        enhancer.setSuperclass(target.getClass());
        //添加方法拦截机
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("执行目标方法前做的事情");
                Object result = method.invoke(target, args);
                System.out.println("执行目标方法后做的事情");
                return result;
            }
        });
        //返回创建好的proxy
        return enhancer.create();
    }

}
 
​ 注意点

Enhancer 增强器
setSuperClass 生成父类
setCallback 添加方法拦截器
create 返回创建好的proxy(代理对象)
//Cglib动态代理测试
public class ProxyTest {
    public static void main(String[] args) {
        tuhao cg =(tuhao) new cglib().getProxy(new tuhao());
        cg.buy();
    }
}

//Cglib动态代理测试最后输出的结果为:
执行目标方法前做的事情
为所欲为的买买买!!!!!!!!
执行目标方法后做的事情
    
PS:输出这个结果的原因可以参考JDK动态代理
 
5. Spring整合的Jdbc
(1) 配置依赖(在pro.xml文件中进行配置)

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>4.3.4.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.48</version>
    </dependency>
        
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.22</version>
</dependency>
 
(2)属性文件配置(一般在resources目录下进行配置,配置文件类型为 properties)

jdbc.url=jdbc:mysql://localhost/user?useUnicode=true&characterEncoding=utf-8
jdbc.driver=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.passworld=123456
 
(3)配置类

@Configuration//把一个类标记为配置类,相当于xml配置文件
@ComponentScan("com.woniu.cq")//组件扫描
@PropertySource("jdbc.properties")//将属性文件配置引入
public class JdbcConf {
    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.user}")
    private String user;

    @Value("${jdbc.passworld}")
    private String passworld;

    //配置数据源
    @Bean
    public DataSource druidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUsername(user);
        druidDataSource.setPassword(passworld);
        druidDataSource.setUrl(url);
        druidDataSource.setDriverClassName(driver);
        return druidDataSource;
    }
    
    //获取Jdbc模板对象
    @Bean
    public JdbcTemplate jdbcTemplate(){
        JdbcTemplate jdbcTemplate = new JdbcTemplate(druidDataSource());
        return jdbcTemplate;
    }

}
 
(4)Jdbc的使用

  @Repository
  public class UserDAOImpl implements UserDAO {
      
      @Autowired
      private JdbcTemplate jdbcTemplate;
      
      //向user表中插入一条数据
      @Override
      public void insert(int userId, String username) {
      
          jdbcTemplate.update("insert into user(username) values(?)",username);
      }
  }

    //另一种查询方法 查询所有商品
    @Override
    public List<Product> serch() {
        String sql = "select * from product";
        //只需要传入sql语句,泛型Product,以及Product.class即可进行数据库查询,返回为集合
        List<Product> products = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Product>(Product.class));
        return products;
    }
 
更多 Aop使用JdbcTemplate的方式:


测试

也可以参考链接:https://blog.csdn.net/qq_38977097/article/details/81564680

6. Spring 事务管理( Spring声明式事务管理,核心实现就是基于Aop)
(1)事务的四大基本特性

事物的概述
⑴ 原子性(Atomicity)
  原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

⑵ 一致性(Consistency)
  一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

  拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

⑶ 隔离性(Isolation)
  隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

  即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

  关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。

⑷ 持久性(Durability)
  持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

  例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。
 
事务的隔离级别
读未提交
读提交
重复读(默认)
序列化(串行化)
关于理解事务的隔离级别,具体详情可参考:

https://blog.csdn.net/itcats_cn/article/details/81487466

事务的传播行为
事务传播行为类型    说明
*** PROPAGATION_REQUIRED**    如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS    支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY    必须存在一个事务,使用当前的事务,如果当前没有事务,就抛出异常。
*PROPAGATION_REQUIRES_NEW    新建事务,如果当前存在事务,把当前事务挂起。 两个物理事务实现
PROPAGATION_NOT_SUPPORTED    以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER    以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED    如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。 保存点实现,部分回滚
(2)事务的开启和结束

dml 数据操纵语言 insert delete update select => 一批同时成功同时失败的DML

ddl 数据定义语言 create drop alter truncate

事务开启

set autocommit=0;
begin / start transaction;
事务结束

commit / rollback;

链接断开

遇到ddl或者dcl ,事务会自动提交(比如说你去执行insert,delect,update,中间突然来个create table,会把前面的事务进行提交)

(3)Spring事务抽象:

PlatformTransactionManager 平台事务管理器 (这是一个标准)

PlatformTransactionManager 的API

getTransaction 得到事务
commit 提交事务
rollback 回滚事务
平台事务管理器的实现:

DataSourceTransactionManager JDBC事务管理器
HibernateTransactionManger Hibernate事务管理器
JTATransactionManager JTA 事务管理器
spring封装了事务管理的代码(打开,提交,回滚事务)
事务操作对象,因为在不同平台,操作事务的代码各不相同.spring提供了一个接口
————— PlatformTransactionManager 接口
————— 在不同平台,实现不同的接口即可
————— 注意:在spring中玩事务管理.最为核心的对象就是TransactionManager对象
 
TransactionDefinition 事务定义(事务的属性)

传播行为
隔离级别
超时
是否只读
名字
TransactionStatus(事务状态)

sNewTransaction 是否是新事务
hasSavepoint 是否有保存点
setRollbackOnly 标记为回滚
isRollbackOnly 是否标记为回滚
flush 建议将session中的数据写进数据库
isCompleted 是否已完成
7. 简单使用Spring 的事务
(1)基本配置:开启事务支持 @EnableTransactionManagement

@Configuration//把一个类标记为配置类,相当于xml配置文件
@ComponentScan("com.woniu.cq")//组件扫描
@PropertySource("jdbc.properties")//将属性文件配置引入
@EnableTransactionManagement开始事务支持
public class JdbcConf {
    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.user}")
    private String user;

    @Value("${jdbc.passworld}")
    private String passworld;

    //配置数据源
    @Bean
    public DataSource druidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUsername(user);
        druidDataSource.setPassword(passworld);
        druidDataSource.setUrl(url);
        druidDataSource.setDriverClassName(driver);
        return druidDataSource;
    }

    //事务管理
    @Bean
    public PlatformTransactionManager transactionManager(){
        return new DataSourceTransactionManager(druidDataSource());
    }

    //获取Jdbc模板对象
    @Bean
    public JdbcTemplate jdbcTemplate(){
        JdbcTemplate jdbcTemplate = new JdbcTemplate(druidDataSource());
        return jdbcTemplate;
    }

    //jdbc.properties 配置解析  【jdbc.properties内容可参看 二.5.(2)】
    @Bean
    public static PropertySourcesPlaceholderConfigurer placeholderConfigurer(){
        return new PropertySourcesPlaceholderConfigurer();
    }

    /**
     * 事务模板,执行事务内的操作(在使用编程时事务管理的时候会用到)
     * @return
     */
    @Bean
    public TransactionTemplate txTenplate(){
        return new TransactionTemplate(transactionManager());
    }

}
 
(2)使用事务管理

声明式事务管理 @Transactional => AOP
需要事务的地方加入@Transactional(可以加在类上 也可以加在方法上)
优点: 使用简单;
缺点:无法精细控制;
//模拟转账    
    @Transactional
    public void transfer(){
        //事务要做的事情:
        //执行转入钱的方法
        xxxxx
        //执行转出钱的方法
        xxxxx
    }
 
实现的方式为Aop

transactionManager.getTransaction();
try{
        
    proceedingJoinPoint().proceed();
    
    transactionManager.commit()
    
}catch(Exception e){
    transactionManager.rollback();
}
 
编程式事务管理 transactionTemplate
优点:可以进行精细事务控制;标记回滚
缺点:代码较繁琐
在配置中配置@Bean TransactionTemplate(可在2.7.(1)的简单配置中看到)
使用代码如下:

//以下单为例
@Service
public class OrderServiceImpl implements OderService {
    @Autowired
    private TransactionTemplate txTemplate;

    @Override
    public void order(){
        //开启事务管理
        txTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                //事务要做的事情:
                   //执行查询库存以及查询余额的方法
                xxxxx
                //执行增加订单的方法
                xxxxx
                //标记事务为回滚
                transactionStatus.setRollbackOnly();
            }
        });
    }
}
 
缺点:无法精细控制;
//模拟转账    
    @Transactional
    public void transfer(){
        //事务要做的事情:
        //执行转入钱的方法
        xxxxx
        //执行转出钱的方法
        xxxxx
    }
 
实现的方式为Aop

transactionManager.getTransaction();
try{
        
    proceedingJoinPoint().proceed();
    
    transactionManager.commit()
    
}catch(Exception e){
    transactionManager.rollback();
}
 
编程式事务管理 transactionTemplate
优点:可以进行精细事务控制;标记回滚
缺点:代码较繁琐
在配置中配置@Bean TransactionTemplate(可在2.7.(1)的简单配置中看到)
使用代码如下:

//以下单为例
@Service
public class OrderServiceImpl implements OderService {
    @Autowired
    private TransactionTemplate txTemplate;

    @Override
    public void order(){
        //开启事务管理
        txTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                //事务要做的事情:
                   //执行查询库存以及查询余额的方法
                xxxxx
                //执行增加订单的方法
                xxxxx
                //标记事务为回滚
                transactionStatus.setRollbackOnly();
            }
        });
    }
}
 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐