Spring

介绍

spring全家桶:spring , springmvc ,spring boot , spring cloud

spring: 出现是在2002左右,解决企业开发的难度。减轻对项目模块之间的管理,
类和类之间的管理, 帮助开发人员创建对象,管理对象之间的关系。
spring核心技术 ioc , aop 。能实现模块之间,类之间的解耦合。

耦合:是指两个或两个以上的体系或两种运动形式间通过相互作用而彼此影响以至联合起来的现象。

解耦合:指将程序的结构变得松散不是固定的而是可变化的 例如接口的实现类是可以更换的 而直接写的类是顶死的 解耦就是用数学方法将两种运动分离开来处理问题。程序的解耦合大概就是让代码和程序不绑定不互相依赖

依赖:classa中使用classb的属性或者方法, 叫做classa依赖classb


框架怎么学: 框架是一个软件,其它人写好的软件。
1)知道框架能做什么, mybatis–访问数据库, 对表中的数据执行增删改查。
2)框架的语法, 框架要完成一个功能,需要一定的步骤支持的,
3)框架的内部实现, 框架内部怎么做。 原理是什么。
4)通过学习,可以实现一个框架。

IOC(反转控制)

spring的第一个核心功能 ioc

IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。
描述的:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。

控制: 创建对象,对象的属性赋值,对象之间的关系管理。
反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,
给属性赋值。

正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。

public static void main(String args[]){
Student student = new Student(); // 在代码中, 创建对象。--正转。
}

容器:是一个服务器软件, 一个框架(spring)

为什么要使用 ioc : 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。

java中创建对象有哪些方式:

  1. 构造方法 , new Student()
  2. 反射
  3. 序列化
  4. 克隆
  5. ioc :容器创建对象
  6. 动态代理

ioc的体现:
servlet 1: 创建类继承HttpServelt
2: 在web.xml 注册servlet , 使用 myservlet
com.bjpwernode.controller.MyServlet1

3. 没有创建 Servlet对象, 没有 MyServlet myservlet = new MyServlet()

4. Servlet 是Tomcat服务器它能你创建的。 Tomcat也称为容器 Tomcat作为容器:里面存放的有Servlet对象, Listener , Filter对象

IoC的技术实现 ,
DI 是ioc的技术实现,
DI(Dependency Injection) :依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建,
赋值,查找都由容器内部实现。

spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制

spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。

几种一般注入

<!--
声明Student对象
注入:就是赋值的意思
简单类型
di:给属性赋值
1.set注入(设置注入):spring调用类的set方法;你可以再set方法中完成属性赋值<property>(普通赋值 调用类中写好的set方法)
-->
<bean id="myStudent" class="com.dyw.Ba01.Student">
<property name="age" value="20"/><!--调用类中的对应的set方法 setName()-->
<property name="name" value="丁杨维"/>
</bean>


<!--
对引用类型进行set设置注入
引用类型的set注入:spring调用类的set方法
-->
<bean id="myStudent" class="com.dyw.Ba01.Student">
<property name="age" value="20"/>
<property name="name" value="丁杨维"/>
<property name="school" ref="mySchool"/>
</bean>

<bean id="mySchool" class="com.dyw.Ba02.School">
<property name="name" value="成信大"/>
<property name="address" value="四川成都双流区"/>
</bean>


<!--
2.构造注入:spring调用类有参构造方法,再创建对象的同时,在构造方法中给属性赋值.
构造注入使用<constructor-arg>标签
<constructor-arg>标签:一个<constructor-arg>标签表示构造方法的一个参数
<constructor-arg>标签属性:
name:表示构造方法的形参名
index:表示构造方法的参数的位置.参数从左往右位置是0,1,2
value:构造方法的形参类型是简单类型,使用value
ref:构造方法的形参是引用类性,使用ref;
-->

<bean id="myStudent" class="com.dyw.Ba01.Student">
<constructor-arg name="age" value="20"/>
<constructor-arg name="name" value="丁杨维"/>
<constructor-arg name="school" ref="mySchool"/>
</bean>
<!--使用index-->
<bean id="myStudent01" class="com.dyw.Ba01.Student">
<constructor-arg index="0" value="丁杨维"/>
<constructor-arg index="1" value="20"/>
<constructor-arg index="2" ref="mySchool"/>
</bean>
<bean id="mySchool" class="com.dyw.Ba02.School">
<constructor-arg name="name" value="cuit"/>
<constructor-arg name="address" value="sccd"/>
</bean>

<!--省略index 但要按照0,1,2的顺序-->
<bean id="myStudent02" class="com.dyw.Ba01.Student">
<constructor-arg value="阿丁"/>
<constructor-arg value="20"/>
<constructor-arg ref="mySchool"/>
</bean>

引用类型的自动注入

<!--
引用类型的自动注入:spring框架根据某些规则可以给引用类型赋值.不用我们自己给引用类型赋值了
使用的规则常用的是byName,byType
1.byName(按名称注入):java类中的引用类型的属性名和spring容器中(配置文件)<bean>的id一样
且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型.
-->
<!--1.ByName-->
<bean id="myStudent" class="com.dyw.Ba03.Student" autowire="byName">
<property name="age" value="20"/>
<property name="name" value="丁杨维"/>
</bean>

<bean id="school" class="com.dyw.Ba03.School" scope="prototype">
<property name="name" value="cuit"/>
<property name="address" value="sccd"/>
</bean>

<!--
ByType:按类型注入: java中引用的数据类型和spring容器中(配置文件)<bean>的class=""中的
类是同源关系,这样的bean能够赋值给引用类型
同源就是一类的意思:
1.java类中引用类型的数据类型和bean的class的值是一样的
2.java类中引用类型的数据类型和bean的class的值是父子关系.
3.java类中引用类型的数据类型和bean的class的值是接口和实现类的关系.

注意bean下是常规的set注入除了引用类型(它是由ByType注入)
-->

<!--2.ByType-->
<bean id="Student01" class="com.dyw.Ba04.Student" autowire="byType">
<property name="name" value="丁杨维"/>
<property name="age" value="20"/>
</bean>

<bean id="mSchool" class="com.dyw.Ba04.School">
<constructor-arg name="name" value="cuit"/>
<constructor-arg name="address" value="sccd"/>
</bean>

spring-conetxt 和 spring-webmvc是spring中的两个模块

spring-context:是ioc功能的,创建对象的。
spring-webmvc做web开发使用的, 是servlet的升级。
spring-webmvc中也会用到spring-context中创建对象的功能的。

junit : 单元测试, 一个工具类库,做测试方法使用的。
单元:指定的是方法, 一个类中有很多方法,一个方法称为单元。

使用单元测试
1.需要加入junit依赖。

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>

2.创建测试作用的类:叫做测试类
src/test/java目录中创建类

3.创建测试方法

1)public 方法
2)没有返回值 void
3)方法名称自定义,建议名称是test + 你要测试方法名称
4)方法没有参数
5)方法的上面加入 @Test ,这样的方法是可以单独执行的。 不用使用main方法。

配置文件

  1. 多个配置优势
    1.每个文件的大小比一个文件要小很多。效率高
    2.避免多人竞争带来的冲突。

如果你的项目有多个模块(相关的功能在一起) ,一个模块一个配置文件。
学生考勤模块一个配置文件, 张三
学生成绩一个配置文件, 李四

多文件的分配方式:

  1. 按功能模块,一个模块一个配置文件
  2. 按类的功能,数据库相关的配置一个文件配置文件, 做事务的功能一个配置文件, 做service功能的一个配置文件等

基于注解的DI

2.基于注解的di: 通过注解完成java对象创建,属性赋值。
使用注解的步骤:
1.加入maven的依赖 spring-context ,在你加入spring-context的同时, 间接加入spring-aop的依赖。
使用注解必须使用spring-aop依赖

2.在类中加入spring的注解(多个不同功能的注解)

3.在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置

学习的注解:
1.@Component

@Component 用于创建对象 等同于bean的功能
value里的值就等同于<bean>中的id
value的值应当是唯一的 创建的对象在整个spring容器中就一个

位置:定义类的上面
@Component(value = "myStudent")
如果就只有value一个参数也可省略value
@Component("myStudent")
也可不写(中的内容)spring提供默认名称(类的名称但首字母小写)
@Component
class Student{

}
一般用第二种
<!--
声明组件扫描器(component-scan),组件指的就是java对象
base-package:指定注解在你项目中的包名
component-scan工作方式:spring会扫描遍历vase-package指定的包
把包中和子包中的所有类遍历,找到类中的注解,按照注解的功能,按照注解的功能创建对象,或给属性赋值
-->

<!--在配置文件中添加一个组件扫描器-->
<context:component-scan base-package="com.dyw.Ba01"/>

<!--组件扫描器指定多个包的三种方式-->
<!--第一种:使用多次组件扫描器,指定不同的包-->
<context:component-scan base-package="com.dyw.Ba01"/>
<context:component-scan base-package="com.dyw.Ba02"/>

<!--第二种:使用分隔符(;或,)分隔多个包名-->
<context:component-scan base-package="com.dyw.Ba01;com.dyw.Ba02"/>

<!--第三种方式:指定父包-->
<context:component-scan base-package="com.dyw"/>

​ 2.@Respotory

(用在持久层上的):放在dao实现类的上面
表示创建dao对象,dao对象是可以访问数据库的.
用法于@Component一致,但还有额外功能

​ 3.@Service

(用在业务层上的):放在service的实现类上面,创建service对象,service对象是做业务处理的,可以有事务功能
用法于@Component一致,但还有额外功能

​ 4.@Controller

(用在控制器上的):放在控制器(处理器)类的上卖弄,创建控制器对象的,
控制器对象,能够接收用户提交的参数,显示请求的处理结果
用法于@Component一致,但还有额外功能

@Respotory(持久层) @Service(业务层) @Controller(控制层)用于给项目分层

5.@Value


@value简单类型的属性赋值
属性: value 是String类型的,表示简单类型的属性值
位置:1.在属性定义的上面,无需set方法,推荐使用.
2.在set方法上面

@Value(value = "20")
private int age;
@Value(value = "丁杨维")
private String name;

@Value("20")
public int getAge() {
return age;
}


​ 6.@Autowired

@Autowired应用类型赋值
@Autowired:spring框架提供的注解,实现引用类型的赋值.
spring中通过注解给引用类型赋值,使用的是自动注入原理,支持byName,byType
@Atuowired:默认使用的是byType自动注入

@Autowired默认属性为 require = true 表示引用类型赋值失败,程序报错,并终止执行
require=false:引用类型如果赋值失败,程序正常执行,引用类型是null

位置:1) 在属性定义的上面,无需set方法,推荐使用
2) 在set方法的上面

如果使用byName方式 需要做的是:
1.在属性上面加入@Autowired
2.在属性的上面加入@Quallifier(value="bean的id"):表示使用指定名称的bean完成赋值
//byType
@Autowired
@Qualifier(value = "school01")
private School school;


//byName
@Autowired
@Qualifier(value = "school01")
private School school;

​ 7.@Resource

是用之前需要再pom.xml中添加j2ee依赖
@Resource赋值引用类型: 来自jdk中的注解,spring框架提供了对这个注解的功能的支持,可以使用它给引用类型赋值
* 使用的也是自动注入原理,支持byName,byType,默认是byName
* 位置: 1)在属性定义的上面,无需set方法,推荐使用
* 2)在set方法的上面
*
* 默认是byName: 先使用byName自动注入,如果byName注入失败,再使用byType
* 不写(name = "")是默认属性名 写上就可以指定名称


@Resource(name = "school03")
private School school;

其他注解

注解可以通过与配置文件结合的方式解耦合

name=丁杨维
age=20
<!--anno.properites-->

<context:component-scan base-package="com.dyw.Ba07"/>

<!--将配置文件路劲导入-->
<context:property-placeholder location="classpath:anno.properties"/>

<!--然后便可再java文件中使用${}加注解的方式赋值-->
public class Student {
@Value("${age}")
private int age;
@Value("${name}")
private String name;

但是@Component好像不能使用这个方式赋值 会报错

用户处理请求:
用户form ,参数name ,age—–Servlet(接收请求name,age)—Service类(处理name,age操作)—dao类(访问数据库的)—mysql


aop

1.动态代理
实现方式:jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。
jdk动态代理要求目标类必须实现接口

cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。
子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的

2.动态代理的作用:
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。

3.Aop:面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。
Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了,
让开发人员用一种统一的方式,使用动态代理。

  1. AOP(Aspect Orient Programming)面向切面编程
    Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。
    切面的特点: 一般都是非业务方法,独立使用的。
    Orient:面向, 对着。
    Programming:编程

oop: 面向对象编程

怎么理解面向切面编程 ?
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能

术语:
1)Aspect:切面,表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,
常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。

2)JoinPoint:连接点 ,连接业务方法和切面的位置。 就某类中的业务方法,一个类中的每一个方法都是连接点
3)Pointcut : 切入点 ,对连接点的筛选,筛选出需要增强功能的方法
4)目标对象: 给哪个类的方法增加功能, 这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间。

说一个切面有三个关键的要素:
1)切面的功能代码,切面干什么
2)切面的执行位置,使用Pointcut表示切面执行的位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

5.aop的实现
aop是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:
1.spring:spring在内部实现了aop规范,能做aop的工作。
spring主要在事务处理时使用aop。
我们项目开发中很少使用spring的aop实现。 因为spring的aop比较笨重。

2.aspectJ: 一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
aspectJ框架实现aop有两种方式:
1.使用xml的配置文件 : 配置全局事务
2.使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。

6.学习aspectj框架的使用。
1)切面的执行时间, 这个执行时间在规范中叫做Advice(通知,增强)
在aspectj框架中使用注解表示的。也可以使用xml配置文件中的标签

@Aspect:是aspectj框架中的注解
作用:表示当前类是切面类
切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
位置:在类定义的上面
切面类中的方法:
定义方法:实现切面功能的
方法的定义要求:
1.公共方法 public
2.方法没有返回值
3.方法的名称自定义
4.方法可以有参数,也可以没有参数
如果有参数,参数不是自定义的,有几个参数类型可以使用


指定通知方法中的参数:JoinPoint
* JoinPoint:业务方法,要加入切面功能的业务方法
* 作用是:可以在通知方法中获取方法执行的信息,例如方法的名称,方法的实参.
* 如果你的切面功能中需要用到方法的信息,就加入JoinPoint.
* 这个JoinPoint参数的值是由框架赋予的,必须是第一个位置的参数,否装上述功能都没有 程序还会报错

​ 1)@Before

@Before:表示前置通知
* 属性:value,是切入点表达式,表示切面的功能执行的位置.
* 位置:在方法的上面
*特点: 1.在目标方法之前先执行
* 2.不会改变目标方法的执行结果
* 3.不会影响目标方法的执行.
* execution(public void com.dyw.ba01.SomeServiceImpl.doSome(String,Integer)表示切入的位置

​ 2)@AfterReturning

* 后置通知定义方法,方法是实现切面功能的.
* 方法定义要求
* 1.公共方法public
* 2.方法没有返回值
* 3.方法名称自定义
* 4.方法有参数的,推荐Object,参数名自定义


@AfterReturning:后置通知
* 属性:1.value 切入点表达式
* 2.returning 自定义变量,表示方法的返回值的.
* 自定义变量名必须和通知方法的形参一样
* . 位置:在方法定义的上面
* 特点:
* 1在目标方法之后生效
* 2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
* 3.可以修改返回值
*
*
* 后置通知的执行
* Object res = doOther();
* myAfterReturning(res);
* System.out.println(res);

​ 3)@Around


* 环绕通知方法的定义格式
* 1.public
* 2.必须要有一个返回值,推荐使用Object
* 3.方法名称自定义
* 4.方法有参数,固定的参数ProceedingJoinPoint

@Around: 环绕通知
属性:value 切入点表达式
位置:在方法的定义上面
特点:
1.他是功能最强的通知
2.在目标方法的前和后都能被调用执行
3.控制目标方法是否被调用执行
4.修改原来的目标方法的执行结果.影响最后调用的结果


环绕通知.等同于jdk动态代理的InvocationHandler接口

参数: ProceedingJoinPoint就等同于jdk动态代理的Method
作用:执行目标方法的
返回值:就是目标方法的执行结果,可以被修改


ProceedingJoinPoint是继承了JoinPoint 所以它具有JoinPoint的特性

环绕通知:经常做事务,在目标方法之前开启事务,执行目标方法,在目标方法之后提交事务
@param proceedingJoinPoint
@return

​ 4)@AfterThrowing 异常通知
​ 5)@After 后置通知

@Pointcut用于指定切入点的别名

@Before(value = "myPointcut()")
public void myBefore01(){
System.out.println("前置通知");
}

/**
* @Pointcut:定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用.
* 可以使用@Pointcut
* 属性:value 切入点表达式
* 位置:自定义方法的上面
* 特点:
* 当使用@Pointcut定义在一个方法的上面,此时这方法的名称就是切入点表达式的别名.
* 当其他的通知中,value属性就可以使用这个方法的名称,代替切入点表达式
*/

@Pointcut(value = "execution(* *..SomeService.doSome(..))")
public void myPointcut(){
//无需代码 只是用该方法名代替切入点表达式
}

2)表示切面执行的位置,使用的是切入点表达式。

    com.service.impl
    com.bjpowrnode.service.impl
    cn.crm.bjpowernode.service


  execution(* *..service.*.*(..))
											readme
用aspectj框架实现aop


使用aop的目的是给已经存在的一些了业务方法,增加额外的功能,前提是不会更改原来类中的方法

### 使用aspectj实现aop的基本步骤
#### 1.新建maven项目
####2.加入依赖
1. spring依赖
2. aspectj依赖
3. junit单元测试
#### 3.创建目标类:接口和他的实现类

#### 4.创建切面类:普通类
1. 在类上面加入注解@Aspect
2. 在类中定义方法,方法就是切面要执行的功能代码
在方法上面加入aspectj中的通知注解,例如@Before
有需要指定切入点表达式execution()

#### 5.创建spring配置文件:声明对象,把对象交给容器去统一管理声明对象你可以使用注解或xml配置文件<bean>
1. 声明目标对象
2. 声明切面类对象
3. 声明aspectj框架中的自动代理生成器标签.
自动代理生成器:用来完成代理对象的自动创建功能的.

#### 6.创建测试类,从spring容器中获取目标对象(实际就是代理对象).通过代理执行方法,实现aop的功能

mybatis框架和spring集成

第四章: 把mybatis框架和spring集成在一起,像一个框架一样使用。

用的技术是:ioc 。
为什么ioc:能把mybatis和spring集成在一起,像一个框架, 是因为ioc能创建对象。
可以把mybatis框架中的对象交给spring统一创建, 开发人员从spring中获取对象。
开发人员就不用同时面对两个或多个框架了, 就面对一个spring

mybatis使用步骤,对象
1.定义dao接口 ,StudentDao
2.定义mapper文件 StudentDao.xml
3.定义mybatis的主配置文件 mybatis.xml
4.创建dao的代理对象, StudentDao dao = SqlSession.getMapper(StudentDao.class);

List students = dao.selectStudents();

要使用dao对象,需要使用getMapper()方法,
怎么能使用getMapper()方法,需要哪些条件
1.获取SqlSession对象, 需要使用SqlSessionFactory的openSession()方法。
2.创建SqlSessionFactory对象。 通过读取mybatis的主配置文件,能创建SqlSessionFactory对象

需要SqlSessionFactory对象, 使用Factory能获取SqlSession ,有了SqlSession就能有dao , 目的就是获取dao对象
Factory创建需要读取主配置文件

我们会使用独立的连接池类替换mybatis默认自己带的, 把连接池类也交给spring创建。

主配置文件:
1.数据库信息

<environment id="mydev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/springdb"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="123456"/>
</dataSource>
  1. mapper文件的位置

    <mappers>
    <mapper resource="com/bjpowernode/dao/StudentDao.xml"/>
    <!--<mapper resource="com/bjpowernode/dao/SchoolDao.xml" />-->
    </mappers>

通过以上的说明,我们需要让spring创建以下对象
1.独立的连接池类的对象, 使用阿里的druid连接池 一般我么只需要配置url,user,password,maxActive(最大连接数);

2.SqlSessionFactory对象

3.创建出dao对象

<!--DruidDataSource大部分属性都是参考DBCP的,如果你原来就是使用DBCP,迁移是十分方便的。-->
init-method 这里xml配置文件指定init是为了druid连接池启动时自动执行 destroy-method也是指销毁数据库连接池会制动执行close
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}" /><!--可以使用properties文件指定jdbc的url等内容 然后加载 最后使用注解来解耦合或是隐藏敏感信息-->
<property name="username" value="${jdbc_user}" />
<property name="password" value="${jdbc_password}" />

<property name="filters" value="stat" />

<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="6000" />
<property name="minIdle" value="1" />

<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />

<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />

<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />

<property name="asyncInit" value="true" />
</bean>
<!--在上面的配置中,通常你需要配置url、username、password,maxActive这三项。
Druid会自动跟url识别驱动类名,如果连接的数据库非常见数据库,配置属性driverClassName
asyncInit是1.1.4中新增加的配置,如果有initialSize数量较多时,打开会加快应用启动时间-->
                                            mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--配置日志-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

<!--设置别名-->
<typeAliases>
<!--实体类所在的包名-->
<package name="com.dyw.domain"/>
</typeAliases>

<mappers>
<!--name:是包名,是这个包中所有的mapper.xml一次都能加载-->
<package name="com.dyw.dao"/>
</mappers>
</configuration>
								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">
0.使用了properties文件 加载
<context:property-placeholder location="classpath:jdbc.properties"/>

1.声明数据源 这里用到了上面的druid数据库的spring<bean>配置
<!--声明数据源DataSource ,作用是使用数据库的 这里声明的是DruidDataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.root}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="20"/>
</bean>

2.声明SqlSessionFactoryBean(spring-mybatis集成中的类 通过这个工厂类可以创建SqlSessionFactory对象)
主要是指定数据源 因为mybatis.xml中我们是没有指定的 并且需要将mybatis.xml加载 将配置信息导入里面指定的mapper文件和domian类
<!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库的值付给了dataSource属性-->
<property name="dataSource" ref="dataSource"/>
<!--mybatis主配置文件的位置
configLocation属性是Resource类型,读取配置文件
他的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>

3.获取了SqlSessionFactory对象后 需要通过他生成的sqlSession对象 再调用getMapper(Dao.class)方法获取到dao接口的代理对象 让其放入到spring容器中管理
<!--创建dao对象,使用sqlSession的getMapper(StudentDao.class)
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象.
不用指定id
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--
指定包名,包名是dao接口所在的包名.
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行一次getMapper方法,得到每个接口的dao对象.
创建好了的dao对象放入到spring的容器中的. 创建好的dao对象的名称时接口的首字母小写
-->
<!--简单复习 如果赋值时对象内的属性为简单类型 是使用value赋值 如果时引用对象则是ref-->
<!--之所以扫描dao接口的包 是为了获得dao.class调用getMapper(Dao.class)方法创建dao接口的代理对象-->
<property name="basePackage" value="com.dyw.dao"/>
</bean>

4.创建业务类(有业务方法)的对象
<bean id="studentService" class="com.dyw.service.studentServiceImpl">
<property name="studentDao" ref="studentDao"/>
</bean>
</beans>

需要学习就是上面三个对象的创建语法,使用xml的bean标签。

连接池:多个连接Connection对象的集合, List connlist : connList就是连接池

通常使用Connection访问数据库
Connection conn =DriverManger.getConnection(url,username,password);
Statemenet stmt = conn.createStatement(sql);
stmt.executeQuery();
conn.close();

使用连接池
在程序启动的时候,先创建一些Connection
Connection c1 = …
Connection c2 = …
Connection c3 = …
List connlist = new ArrayLits();
connList.add(c1);
connList.add(c2);
connList.add(c3);

Connection conn = connList.get(0);
Statemenet stmt = conn.createStatement(sql);
stmt.executeQuery();
把使用过的connection放回到连接池
connList.add(conn);

Connection conn1 = connList.get(1);
Statemenet stmt = conn1.createStatement(sql);
stmt.executeQuery();
把使用过的connection放回到连接池
connList.add(conn1);


spring的事务处理

事务的基本要素 4大性质

  1. 原子性:事务开始后的所有操作,要么全部成功,要么全部失败回滚,不可能停滞在中间环节.事务执行过程中出错,会回滚到事务开始之前的状态,所有操作就像没有发生一样.也就是说事务是一个不可分割的整体,就像化学中学过的原子,是事务构成的基本单位.
  2. 一致性:事务开始前和结束后,数据库的完整性约束没有被破坏,如A向B转张,不可能出现A扣了钱而B却没有加的情况
  3. 隔离性:同一时间,只允许一个事务请求同一数据,不同事务之间彼此没有任何干扰,如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账.
  4. 持久性:事务完成之后,事务对数据库更新将会被保存写在数据库上,不能回滚.

回答问题
1.什么是事务
讲mysql的时候,提出了事务。 事务是指一组sql语句的集合, 集合中有多条sql语句
可能是insert , update ,select ,delete, 我们希望这些多个sql语句都能成功,
或者都失败, 这些sql语句的执行是一致的,作为一个整体执行。

2.在什么时候想到使用事务
当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证
这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。

在java代码中写程序,控制事务,此时事务应该放在那里呢?
service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句

3.通常使用JDBC访问数据库, 还是mybatis访问数据库怎么处理事务
jdbc访问数据库,处理事务 Connection conn ; conn.commit(); conn.rollback();
mybatis访问数据库,处理事务, SqlSession.commit(); SqlSession.rollback();
hibernate访问数据库,处理事务, Session.commit(); Session.rollback();

4.3问题中事务的处理方式,有什么不足
1)不同的数据库访问技术,处理事务的对象,方法不同,
需要了解不同数据库访问技术使用事务的原理
2)掌握多种数据库中事务的处理逻辑。什么时候提交事务,什么时候回顾事务
3)处理事务的多种方法。

总结: 就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法。

5.怎么解决不足
spring提供一种处理事务的统一模型, 能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。

使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理
使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理。

spring事务的属性

Spring事务属性对应TransactionDefinition类里面的各个方法。TransactionDefinition类方法如下所示:

public interface TransactionDefinition {

/**
* 返回事务传播行为
*/
int getPropagationBehavior();

/**
* 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
*/
int getIsolationLevel();

/**
* 事务超时时间,事务必须在多少秒之内完成
*/
int getTimeout();

/**
* 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的
*/
boolean isReadOnly();

/**
* 事务名字
*/
@Nullable
String getName();
}

事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面:传播行为、隔离规则、回滚规则、事务超时、是否只读。

6.处理事务,需要怎么做,做什么
spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring就可以了

1)事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback
事务管理器是一个接口和他的众多实现类。
接口:PlatformTransactionManager ,定义了事务重要方法 commit ,rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库—spring创建好的是DataSourceTransactionManager
hibernate访问数据库—-spring创建的是HibernateTransactionManager

怎么使用:你需要告诉spring 你用是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于的事务管理器实现类, 在spring的配置文件中使用<bean>声明就可以了
例如,你要使用mybatis访问数据库,你应该在xml配置文件中
<bean id=“xxx" class="...DataSourceTransactionManager">

Spring事务的隔离级别

你的业务方法需要什么样的事务,说明需要事务的类型。
说明方法需要的事务:
1)事务的隔离级别:有4个值。
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
READ_UNCOMMITTED读未提交(A事务在执行过程中,读到了B事务修改但未提交的数据,可能会发生B事务未修改回滚但被A事务读取到的情况,出现了脏读)。未解决任何并发问题。
READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
SERIALIZABLE:串行化(事务A执行完毕后才执行B事务)。不存在并发问题。

ISOLATION_SERIALIZABLE(串行化) 隔离规则类型在开发中很少用到。举个很简单的例子。咱们使用了ISOLATION_SERIALIZABLE规则。A,B两个事务操作同一个数据表并发过来了。A先执行。A事务这个时候会把表给锁住,B事务执行的时候直接报错。

  1. 事务隔离级别为**ISOLATION_READ_UNCOMMITTED(读未提交)**时,写数据只会锁住相应的行。

  2. 事务隔离级别为可**ISOLATION_REPEATABLE_READ(可重复读)**时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。

  3. 事务隔离级别为**ISOLATION_SERIALIZABLE(串行化)**时,读写数据都会锁住整张表。

  4. 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能(隔离级别越高 执行的时间越长)的影响也就越大。

  2) 事务的超时时间: 表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。
单位是秒, 整数值, 默认是 -1.
/**
为了使应用程序很好地运行,事务不能运行太长的时间。因为事务可能涉及对后端数据库的锁定,也会占用数据库资源。事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。
*/

3) 事务是否只读:isReadOnly() : boolean isReadOnlu() true表示是只读,false表示非只读
/**
如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据, 这个时候我们应该给该事务设置只读属性,这样可以帮助数据库引擎优化事务。提升效率。
*/
  1. 事务的传播行为: 控制业务方法是不是有事务的, 是什么样的事务的。
    7个传播行为,表示你的业务方法调用时,事务在方法之间是如果使用的.
	/**当前事务传播行为是REQUIRED时,如果存在一个事务那么直接使用当前事务,如果没有事务,那么就创建一个事务,如:单独MethodB时上下文没有事务,执行时会创建一个新的事务;如果MethodA方法中上下文存在事务,在A中调用B时B会发现上下文中存在事务,MethodB就不会创建一个新的事务,而是直接加入到A的事务中*/
PROPAGATION_REQUIRED

/** 需要使用 JtaTransactionManager作为事务管理器。
上下文没有事务,它会开启一个新的事务。如果一个事务已经存在,则先将这个存在的事务挂起。*/
PROPAGATION_REQUIRES_NEW

/** 如果存在事务,支持当前事务,如果没有事务,则非事务的执行.但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。 */
PROPAGATION_SUPPORTS
以上三个需要掌握的
/** 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。即必须有事务,没有一个事务就抛出异常*/
PROPAGATION_MANDATORY

/** 如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行。 */
PROPAGATION_NESTED

/** 总是非事务地执行,如果存在一个活动事务,则抛出异常。*/
PROPAGATION_NEVER

/** 总是非事务地执行,并挂起任何存在的事务。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作为事务管理器。 */
PROPAGATION_NOT_SUPPORTED

PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:

  1. 事务提交事务,回滚事务的时机
    1)当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器commit
  2)(默认情况下)当你的业务方法抛出运行时异常或ERROR(即未受查异常), spring执行回滚,调用事务管理器的rollback
运行时异常的定义: RuntimeException 和他的子类都是运行时异常, 例如NullPointException , NumberFormatException

3) 当你的业务方法抛出非运行时异常, 主要是受查异常时,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException, SQLException

/**
注意:你可以声明事务遇到特定的受查异常时像遇到运行时异常那样回滚,同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行时异常.
*/

总结spring的事务
1.管理事务的是 事务管理和他的实现类
2.spring的事务是一个统一模型
1)指定要使用的事务管理器实现类,使用spring容器创建管理对象
2)指定哪些类,哪些方法需要加入事务的功能
3)指定方法需要的隔离级别,传播行为,超时

  你需要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。

spring框架中提供的事务处理方案
1.适合中小项目使用的, 注解方案。
spring框架自己用aop(aspectj框架的 @Around 环绕依赖)实现给业务方法增加事务的功能, 使用**@Transactional注解**增加事务。
@Transactional注解是spring框架自己注解,放在public方法的上面,表示当前方法具有事务。
可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等等

使用@Transactional的步骤:
1.需要声明事务管理器对象


@Transactional注解

1. @Transactional介绍

  1. @Transactional注解 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
  2. 虽然@Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional注解应该只被应用到 public 方法上,这是由Spring AOP的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
  3. 默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

2. @Transactional注解属性

  1. @Transactional注解里面的各个属性和咱们在上面讲的事务属性里面是一一对应的。用来设置事务的传播行为、隔离规则、回滚规则、事务超时、是否只读
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

/**
* 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。myBatis是DataSourceTransactionManager 事务管理器
*/
@AliasFor("transactionManager")
String value() default "";

/**
* 同上。
*/
@AliasFor("value")
String transactionManager() default "";

/**
* 事务的传播行为,默认值为 REQUIRED。
*/
Propagation propagation() default Propagation.REQUIRED;

/**
* 事务的隔离规则,默认值采用 DEFAULT mysql中是可重复读。
*/
Isolation isolation() default Isolation.DEFAULT;

/**
* 事务超时时间。
*/
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

/**
* 是否只读事务
*/
boolean readOnly() default false;

/**
* 用于指定能够触发事务回滚的异常类型 Class对象的数组。
*/
Class<? extends Throwable>[] rollbackFor() default {};

/**
* 同上,指定类名 这个String类型的数组。
*/
String[] rollbackForClassName() default {};

/**
* 用于指定不会触发事务回滚的异常类型
*/
Class<? extends Throwable>[] noRollbackFor() default {};

/**
* 同上,指定类名
*/
String[] noRollbackForClassName() default {};

}
  1. value、transactionManager属性

​ 它们两个是一样的意思。当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。大多数项目只需要一个事务管理器。然而,有些项目为了提高效率、或者有多个完全不同又不相干的数据源,从而使用了多个事务管理器。机智的Spring的Transactional管理已经考虑到了这一点,首先定义多个transactional manager,并为qualifier属性指定不同的值;然后在需要使用@Transactional注解的时候指定TransactionManager的qualifier属性值或者直接使用bean名称。配置和代码使用的例子:

<tx:annotation-driven/>

<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource1"></property>
<qualifier value="datasource1Tx"/>
</bean>

<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource2"></property>
<qualifier value="datasource2Tx"/>
</bean>
public class TransactionalService {

@Transactional("datasource1Tx")
public void setSomethingInDatasource1() { ... }

@Transactional("datasource2Tx")
public void doSomethingInDatasource2() { ... }

}
  1. propagation属性

    propagation用于指定事务的传播行为,默认值为 REQUIRED。propagation有七种类型,就是我们在上文中讲到的事务属性传播行为的七种方式,如下所示:

propagation属性 事务属性-传播行为 含义
REQUIRED TransactionDefinition.PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务,则加入到这个事务中。这是最常见的选择。
SUPPORTS TransactionDefinition.PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY TransactionDefinition.PROPAGATION_MANDATORY 表示该方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常。
REQUIRES_NEW TransactionDefinition.PROPAGATION_REQUIRES_NEW 表示当前方法必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。
NOT_SUPPORTED TransactionDefinition.PROPAGATION_NOT_SUPPORTED 表示该方法不应该运行在事务中。如果当前存在事务,就把当前事务挂起。
NEVER TransactionDefinition.PROPAGATION_NEVER 表示当前方法不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。
NESTED TransactionDefinition.PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
  1. isolation属性

    ​ isolation用于指定事务的隔离规则,默认值为DEFAULT。@Transactional的隔离规则和上文事务属性里面的隔离规则也是一一对应的。总共五种隔离规则,如下所示:

    @isolation属性 事务属性-隔离规则 含义 脏读 不可重复读 幻读
    DEFAULT TransactionDefinition.ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
    READ_UNCOMMITTED TransactionDefinition.ISOLATION_READ_UNCOMMITTED 允许读取尚未提交的数据变更(最低的隔离级别)
    READ_COMMITTED TransactionDefinition.ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据
    REPEATABLE_READ TransactionDefinition.ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改
    SERIALIZABLE TransactionDefinition.ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的

  1. timeout

    ​ timeout用于设置事务的超时属性。

  2. readOnly

    ​ readOnly用于设置事务是否只读属性。

  3. rollbackFor、rollbackForClassName、noRollbackFor、noRollbackForClassName

    ​ rollbackFor、rollbackForClassName用于设置那些异常需要回滚;noRollbackFor、noRollbackForClassName用于设置那些异常不需要回滚。他们就是在设置事务的回滚规则。

@Transactional注解的使用

  1. @Transactional注解内部实现依赖于Spring AOP编程。而AOP在默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为。
  2. @Transactional 注解尽量直接加在方法上:因为每个方法的执行情况不一样,加载类上的话不能更好地区分每个方法的操作 比如查询只需要readOnly
  3. @Transactional 注解必须添加在public方法上,private、protected方法上是无效的 spring的@Transactional是基于springAOP特性实现
  4. @Transactional注解在外部调用的函数上才有效果,内部调用的函数添加无效,要切记。这是由AOP的特性决定的。(即在同一个类中只有只有由@Transactional的方法调用另一个方法有效,两个方法都有@Transactional注解只有外部的生效,内部的不生效 @Transactional注解只有外部调用才有效。)
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {
NullPointerException.class, NotEnoughException.class
}

2.开启事务注解驱动, 告诉spring框架,我要使用注解的方式管理事务。
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
spring给业务方法加入事务:
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知

     @Around("你要增加的事务功能的业务方法名称")
     Object myAround(){
       开启事务,spring给你开启
          try{
             buy(1001,10);
              spring的事务管理器.commit();
          }catch(Exception e){
         spring的事务管理器.rollback();
          }
         
     }

3.在你的方法的上面加入@Trancational 在注解中配置参数

2.适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中
声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。

实现步骤: 都是在xml配置文件中实现。 即不是用@Apest 和 @Trancational 注解 完全使用xml配置文件
1)要使用的是aspectj框架,需要加入依赖

org.springframework
spring-aspects
5.2.5.RELEASE

2)声明事务管理器对象

<bean id="xx" class="DataSourceTransactionManager">
 3) 声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)
 4)  配置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:tx="http://www.springframework.org/schema/tx" 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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--
把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
spring知道jdbc.properties文件的位置
-->
<context:property-placeholder location="classpath:jdbc.properties" />

<!--声明数据源DataSource, 作用是连接数据库的-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--set注入给DruidDataSource提供连接数据库信息 -->
<!-- 使用属性配置文件中的数据,语法 ${key} -->
<property name="url" value="${jdbc.url}" /><!--setUrl()-->
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.passwd}" />
<property name="maxActive" value="${jdbc.max}" />
</bean>

<!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的
SqlSessionFactory sqlSessionFactory = new ..
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库连接池付给了dataSource属性-->
<property name="dataSource" ref="myDataSource" />
<!--mybatis主配置文件的位置
configLocation属性是Resource类型,读取配置文件
它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
-->
<property name="configLocation" value="classpath:mybatis.xml" />
</bean>

<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。

-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<!--指定包名, 包名是dao接口所在的包名。
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象。
创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
-->
<property name="basePackage" value="com.bjpowernode.dao"/>
</bean>

<!--声明service-->
<bean id="buyService" class="com.bjpowernode.service.impl.BuyGoodsServiceImpl">
<property name="goodsDao" ref="goodsDao" />
<property name="saleDao" ref="saleDao" />
</bean>

<!--声明式事务处理:和源代码完全分离的-->
<!--1.声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource" />
</bean>

<!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
id:自定义名称,表示 <tx:advice> 和 </tx:advice>之间的配置内容的
transaction-manager:事务管理器对象的id 不设置就是默认配置
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--tx:attributes:配置事务属性-->
<tx:attributes>
<!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
name:方法名称,1)完整的方法名称,不带有包和类。
2)方法可以使用通配符,* 表示任意字符
propagation:传播行为,枚举值
isolation:隔离级别
rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,com.bjpowernode.excep.NotEnoughException"/>

<!--使用通配符,指定很多的方法-->
<tx:method name="add*" propagation="REQUIRES_NEW" />
<!--指定修改方法-->
<tx:method name="modify*" />
<!--删除方法-->
<tx:method name="remove*" />
<!--查询方法,query,search,find-->
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>

<!--配置aop-->
<aop:config>
<!--配置切入点表达式:指定哪些包中类,要使用事务
id:切入点表达式的名称,唯一值
expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象

com.bjpowernode.service
com.crm.service
com.service
-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>

<!--配置增强器:关联adivce和pointcut
advice-ref:通知,上面tx:advice哪里的配置
pointcut-ref:切入点表达式的id
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
</aop:config>


</beans>

web项目中怎么使用容器对象。

  1. 做的是javase项目有main方法的,执行代码是执行main方法的,
    在main里面创建的容器对象
    ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);

  2. web项目是在tomcat服务器上运行的。 tomcat一起动,项目一直运行的。

需求:
web项目中容器对象只需要创建一次, 把容器对象放入到全局作用域ServletContext中。

怎么实现:
使用监听器 当全局作用域对象被创建时 创建容器 存入ServletContext

监听器作用:
1)创建容器对象,执行 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
2)把容器对象放入到ServletContext, ServletContext.setAttribute(key,ctx)

监听器可以自己创建,也可以使用框架中提供好的ContextLoaderListener


 private WebApplicationContext context;
 public interface WebApplicationContext extends ApplicationContext
 ApplicationContext:javase项目中使用的容器对象
WebApplicationContext:web项目中的使用的容器对象

把创建的容器对象,放入到全局作用域
key: WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
value:this.context

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);