spring学习笔记

2021-01-23

spring

image-20200805104954081

Spring官网 https://spring.io/

image-20200805105315305

1.入门案例

1.1、导包

image-20200805103506043

一个普通的入门案例不需要太多的包,这几个基础包就够了。

1.2、编写配置文件

xml的头文件约束(网址)要与导的spring jar包版本一致

http://www.springframework.org/schema/beans/spring-beans.xsd

1
2
3
4
5
6
7
8
<?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="user" class="com.gec.domain.User">
</bean>
</beans>

1.3、测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.gec.test;

import com.gec.domain.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class test {

@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.application.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user);
}
}

1.2、通过class在IOC中找对象

image-20200805105544229

image-20200805105602223

2、控制反转

举个栗子:

image-20200805105630909

请注意你并没有“主动”去创造橙汁,橙汁是由饮品店创造的,而不是你,然而也完全达到了你的要求,甚至比你创造的要好上那么一些。

​ 将我们创建对象的方式反转了,以前创建对象方式是由开发人员自己完成的,包括后面学习的依赖关系也是由自己注入。

使用Spring之后,对象的创建以及依赖关系可以由spring完成创建以及注入

2.1、依赖注入

指 Spring 创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象

注入方式:

​ Set方法注入

​ 构造方法注入

​ 字段注入

注入类型:

​ 值类型注入 (8大基本数据类型)

​ 引用类型注入 (将依赖对象注入)

3、spring容器介绍

3.1beanFactory

image-20200805105905472

spring的最原始接口,原始接口的实现类功能最为单一和薄弱

beanFactory接口实现类的特点:每次在获得对象的时候,才会去创建对象

1
2
3
4
5
6
7
8
@Test
public void fun3()
{
BeanFactory ApplicationContext = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
UserService service = (UserService) ApplicationContext.getBean("userService");
service.addUser();
}

3.2.ApplicationContext

每次容器启动时,就会创建容器中配置的所有对象,并且该接口功能更加丰富和强大。
ApplicationContext接口有两个实现类:

image-20200805110100738

加载配置文件:

  • 从类路径下加载配置文件:ClassPathXmlApplicationContext
  • 从硬盘绝对路径下加载配置文件:FileSystemXmlApplicationContext(“d:/xxx/yyy/xxx”)

image-20200805110212272

4、Spring配置详解

4.1、bean元素基础

  • Bean元素:使用该元素描述需要Spring容器管理的对象
  • Class属性:被管理对象的完整类名
  • name属性:给被管理的对象起个名字。获得对象时根据该名称获得对象 可以重复,可以使用特殊字符

【经验小结】:开发中,尽量使用name属性

4.2、Spring容器中创建对象的方式

4.2.1.无参构造方式 (*****)

image-20200805111016736

4.2.2.静态工厂(了解)

image-20200805111036736

注意:createUser是在User类中自定义的方法

4.2.3、实例工厂(了解)

image-20200805111304451

4.3bean元素进阶

1.scope属性
  • Sigleton 默认值(单例):也就是拿到的对象都是同一个。单例对象.被标识为单例的对象在spring容器中只会存在一个实例。image-20200805111717630image-20200805111729437

  • Prototype 多例:被标识为多例的对象,每次获得才会创建,每次创建都是新的对象

  • request: web环境下.对象与request生命周期一致.

  • Session : web环境下,对象与session生命周期一致.

【经验小结】: 开发中scope属性什么时候用singleton,什么时候用prototype呢?

实际中,绝大多数都用默认的singleton,在整合Struts2框架的时候,scope必须配置为prototype多例的

2、生命周期

Spring生命周期方法

配置一个方法作为生命周期初始化方法,spring会在对象创建之后立即调用

Init-method

配置一个方法作为生命周期的销毁方法,spring容器在关闭并销毁所有容器中的对象之前调用

destroy-method

思考:**为什么销毁方法看不到?**

3.spring中分模块配置

image-20200805112744800

也就是将多个spring配置文件整合在一起

5.Spring中的属性注入

1.set方法注入

属性必须要有set的方法

  • 基本数据类型

image-20200805113158709

  • 引用类型-对象:需要用ref属性

image-20200805113627564

image-20200805113635550

结果显示:

image-20200805113702985

  • l复杂类型

    数组

    List

    Map

    Properties

2.构造方法注入

默认情况下,Spring 容器在创建对象的时候,使用无参构造创建,我们可以通过设置构造方法的方式告诉Spring容器使用构造方法去创建对象!

constructor-arg标签用来定义spring框架注入属性值的时候使用构造函数

name属性:填写你要注入的属性字段名

value属性:填写你要注入的属性字段对应的值

index属性:用来控制构造函数中参数的顺序 (默认从0开始)

type 属性:用来控制构造函数中参数的类型

  • 基本数据类型

  • 引用类型-对象

  • 复杂类型

    ​ 数组

    ​ List

    ​ Map

    ​ Properties

    ​ 构造方法注入分类:

1.默认情况

1
2
3
4
5
6
7
8
9
10
11
public class User {
private Integer id;
private String username;

//有参构造
public User(String username, Car car) {
System.out.println("User(String username, Car car)....");
this.username = username;
this.car = car;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<bean name="myCar" class="com.shop.domain.Car">
<property name="carname" value="保时捷"></property>
</bean>

<bean name="user" class="com.shop.domain.User">

<!-----通过有参构造注入---->
<!-----传入参数1---->
<constructor-arg name="username" value="tom"></constructor-arg>
<!-----传入参数2---->
<constructor-arg name="car" ref="myCar"></constructor-arg>
</bean>

观察打印结果:

image-20200805114815605

3构造方法重载

image-20200805135128545

通过index 属性设置(避免“值”给了错“属性”,区分第一个构造方法)

image-20200805135140647

观察打印结果:

image-20200805135206356

4、重构方法2

image-20200805135238867

也还是通过下标设置属性(还要再加上type=”integer”,区分第一个构造函数)

image-20200805135247102

观察打印结果:

image-20200805135352931

5、p名称空间注入

1.引入xml约束头文件

image-20200805135451736

2.在备案元素中配置

image-20200805135518380

  • 基本数据类型

  • 引用类型-对象

  • 复杂类型

    ​ 数组

    ​ List

    ​ Map

    ​ Properties

6.spel注入

image-20200805135625693

注意:

  • 第一对象类型不能使用#{}方式注入,

  • 第二${‘’},里面的单引号不能省略,外双里单:外面的双引号,里面用单引号。

=================================================

  • 基本数据类型

  • 引用类型-对象

  • 复杂类型

    ​ 数组

    ​ List

    ​ Map

    ​ Properties

【注意】:spel注入,对象类型不能使用#{}方式注入

7、复杂类型注入

Spring中使用DI注入复杂类型

准备工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CollectionBean {
private Object[] arr;
private List list; // list
private Map map;
private Properties pro;

public Object[] getArr() {
return arr;
}

public void setArr(Object[] arr) {
this.arr = arr;
}

1、Array数组类型

往数组注入单个值

1
2
3
4
<bean name="cb" class="com.shop.test.CollectionBean">
<property name="arr" value="杰克"></property>
</bean>

1
2
3
4
5
6
7
8
@Test
public void fun1()
{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean) ac.getBean("cb");
System.out.println(bean);
}

往数组注入多个值

1
2
3
4
5
6
7
8
9
10
11
12
13
<property name="arr">
<array>
<value>杰克</value>
<value>旺财</value>
<ref bean="user3"/>
</array>
</property>

<!--或者-->
<property name="arr" value="杰克 旺财">
</property>


1
2
3
4
5
6
7
8
@Test
public void fun1()
{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean) ac.getBean("cb");
System.out.println(bean);
}

2.List 集合

往list注入单个值

1
2
3
<bean name="cb2" class="com.shop.test.CollectionBean">
<property name="list" value="小黄"></property>
</bean>
1
2
3
4
5
6
7
8
@Test
public void fun2()
{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean) ac.getBean("cb2");
System.out.println(bean);
}

传入多个值

1
2
3
4
5
6
7
8
9
<property name="list">
<list>
<value>旺财</value>
<value>阿黄</value>
<value>潘金莲</value>
<ref bean="user3"/>
</list>
</property>

1
2
3
4
5
6
7
8
@Test
public void fun2()
{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean) ac.getBean("cb2");
System.out.println(bean);
}

image-20200805144412450

3、Map

1
2
3
4
5
6
7
8
9
10
<bean name="cb3" class="com.shop.test.CollectionBean">
<property name="map">
<map>
<entry key="url" value="http://localhost:8080"></entry>
<entry key="car" value-ref="car"></entry>
<entry key-ref="user" value-ref="user3"></entry>
</map>
</property>
</bean>

1
2
3
4
5
6
7
8
@Test
public void fun3()
{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean) ac.getBean("cb3");
System.out.println(bean);
}

4.Properties

1
2
3
4
5
6
7
8
9
<bean name="cb4" class="com.shop.test.CollectionBean">
<property name="pro">
<props>
<prop key="url">http://localhost:3306</prop>
<prop key="name">旺财</prop>
</props>
</property>
</bean>

1
2
3
4
5
6
7
8
@Test
public void fun4()
{
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
CollectionBean bean = (CollectionBean) ac.getBean("cb4");
System.out.println(bean);
}

5、String数据

1
2
3
4
5
<bean name="collection" class="com.gec.domain.Collection">
<!--多个数据用空格隔开-->
<property name="str" value="cc bb"/>
</bean>

8、注解开发IOC

注解是JDK1.5之后才有的新特性

JDK1.5之后内部提供的三个注解

​ @Deprecated 意思是“废弃的,过时的

​ @Override 意思是“重写、覆盖

​ @SuppressWarnings 意思是“压缩警告

在Spring框架注解操作一共针对以下几方面:

IOC 控制反转

DI 依赖注入

AOP 面向切面

注解存在意义:

使用注解取代xml配置!!!!!!!!!!

1、搭建使用注解环境

1.导包

img

不然会报一下异常

image-20200805195350654

2.引入xm头文件约束

image-20200805195654532

3.扫描包

1
2
3
4
5
<!-- 开启注解扫描扫描该包下的子孙包-->        
<context:component-scan base-package="com.shop.domain"></context:component-scan>
<!-- 等效-->
<context:component-scan base-package="com.shop"></context:component-scan>

不然会报出一下异常

image-20200805195542900

【开发小结】:

1. 使用注解方式开发,必须要先开启注解扫描,否则直接报错

2. Spring4以上版本,玩儿注解,需要添加aop包,目前课上使用的最新Spring5.1.2

2.IOC其他注解介绍

1.@Componment(作用在类上)

2.Spring中提供@Componment)的三个衍生注解(目前功能是一致的)

* @Controller –作用在WEB层

* @Service - 作用在业务层

* @Repository – 作用于持久层(dao层)

说明:使用这三个注解是为了让开发人员更明白注解类本身的用途,Spring后续版本可能会对其增强.

1
2
3
//配合上注解使用,规定创建的对象是单例还是多例
@Scope(scopeName="prototype")//多例
@Scope(scopeName="singleton")//单例
3、注解实现DI

说明:使用注解方式,可以不用提供set方法

  1. 如果注入的是普通类型,可以使用Value注解
1
2
3
4
public class User{
@Value(value="jack")
private Stirng name;
}

image-20200805200445308

image-20200805200500639

注意int 类型注入也要加入双引号

2.如果注入的是对象类型,可以使用如下注解

@AutoWired – 默认按类型自动装配

1
2
3
4
5
6
7
8
9
@Component(value = "car")
public class Car {
private String name;
private String color;

public String getName() {
return name;
}

1
2
3
4
5
6
7
8
9
10
@Repository(value="userservice")
public class User {
@Value(value="jack")
private String name;
private int age;
@Autowired
private Car car;
public String getName() {
return name;
}

@Qualifier - 强制使用名称注入

image-20200805200710014

说明:AutoWired和Qualifier都是spring框架提供的

@Resource是Java程序提供的,只不过被sprin框架所支持 - 相当于@AutoWired和@Qualifier一起使用

1
2
3
4
5
6
7
public class User {
@Value(value="jack")
private String name;
private int age;
@Resource(name="car2")
private Car car;

@Resource和@service

当你需要定义某个类为一个bean,则在这个类的类名前一行使用@Service(“XXX”),就相当于讲这个类定义为一个bean,bean名称为XXX;

当需要在某个类中定义一个属性,并且该属性是一个已存在的bean,要为该属性赋值或注入时在该属性上一行使用@Resource(name=”xxx”),相当于为该属性注入一个名称为xxx的bean。

===================================================================================================

Spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource、@PostConstruct以及@PreDestroy。
  @Resource的作用相当于@Autowired,只不过**@Autowired按byType自动注入,而@Resource默认按 byName自动注入**罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
 @Resource装配顺序

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

Aop

2.AOP

1.AOP概述(掌握)

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码

经典应用:事务管理、性能监视、安全检查、缓存 、日志等

Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码

AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,

AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入

2、AOP思想简单分析

假如在Service层增加事务:开启事务,提交事务。但是不修改原有的Service层代码。

第一种方案是继承这个了类,但是这种方法的耦合性高,因为用到了继承

1
2
3
4
5
6
7
8
public class UserService{

//开启事务
public void addUser(){
System.out.println("添加用户");
}
//关闭事务
}

利用继承实现切面加入事务,但是耦合度极高,因为用了继承

1
2
3
4
5
6
public class Demo extends UserService{

//开启事务
void addUser()
//关闭事务
}

第二种:AOP

image-20200806095055546

3.AOP中专业术语(重点)

**1.Target(目标类):**需要被代理的类。例如:ProductService

**2.joinPoint (连接点):**所谓连接点,就是那些可能被spring拦截的点(方法) ef:(上面的void addUser()方法)

**3.pointCut(切入点):**已经被增强的连接点(方法)

**4.advice(通知/增强):**那些增强的代码

**5.weaving(织入):**是指把增强advice应用到目标对象target来创建新的代理(proxy)的过程

6.poxy:代理类

**7.Aspectj(切面):**切入点pointcut和通知advice的结合

image-20200806100920051

4、AOP栗子

1、导入架包

spring核心包(4个+一个commons-logging-1.2.jar(spring4.1以后才需要))

image-20200805145959245

spring中AOP核心jar包(两个aop+aspects)

image-20200805150032566

第三方依赖包AOP jar包(两个:aopalliance+weaver)

img

spring-framework-3.0.2.RELEASE-dependencies\org.aopalliance\com.springsource.org.aopalliance\1.0.0\com.springsource.org.aopalliance-1.0.0.jar

img

img

img

img

img

img

img

img

spring-framework-3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE\com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar 这个包 是注解开发AOP的时候用的,测试的时候不导这个包也能跑

img

img

img

img

img

img

img

最终jar图:

img

2、导入约束

image-20200805151230340

image-20200805151202847

完整约束

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
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-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">

3、准备目标类(target)

1
2
3
4
5
6
7
8
public class UserService{
public void addUser(){
System.out.println("添加用户");
}
public void updateUser(){
Syste.out.pritln("更新用户");
}
}

4、准备通知(增强)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MyAdvie{
public void before(){
System.out.println("开启事务");
}
public void afterReturning(){
System.out.println("提交事务。。。")
}

public Object around (ProceeedingJoinPoint pjp) throw Throwable{
System.out.println("这是环绕通知之前的部分....");
Object proceed = pjp.proceed();
System.out.println("这是环绕通知之后的部分...");
return proceed;
}

public void afterException(){
System.out.println("不好了,出事情了");
}
public void after(){
System.out.println("出现异常,也会继续执行");
}

}


5、织入

将通知(增强)织入到目标类中

1.配置目标类
1
<bean name="productservice"  class=”com.shop,aa,ProductService"></bean>
2.配置通知
1
<bean name="mydvice" class="com.shop.aa.Myadvice"></bean>
3.织入配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!-- 配置目标类 -->        
<bean name="productservice" class="com.shop.weaving.ProductService"></bean>
<!-- 配置增强类 -->
<bean name="myadvice" class="com.shop.weaving.MyAdvice"></bean>

<!--进行织入操作 -->
<aop:config >
<!-- 已经被增强的连接点(方法)--->
<aop:pointcut expression="execution(public void com.shop.weaving.ProductService.addProduct())" id="pc"/>



<aop:aspect ref="myadvice">

<aop:before method="before" pointcut-ref="pc"/>

<aop:after method="afterReturning" pointcut-ref="pc"/>

<aop:around method="around" pointcut-ref="pc"/>

<aop:after-throwing method="afterException" pointcut-ref="pc"/>

<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>



</aop:config>


</beans>

4.测试
1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestDemo1 {
@Test
public void test1()
{

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

UserService userService = (UserService) applicationContext.getBean("userService");

userService.addUser();
}
}

5.执行结果:

image-20200805162457834

6、 aop:pointcut多种写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 <aop:pointcut expression="execution(public void com.shop.weaving.ProductService.addProduct())" id="pc"/>

<!---完整写法---->
1.public void com.shop.weaving.ProductService.addProduct()

<!----无访问修饰符--->
2.void com.shop.weaving.ProductService.addProduct()

<!----任意返回类型--->
3.* com.shop.weaving.ProductService.addProduct()

<!----方法名前缀任意--->
4.* com.shop.weaving.ProductService.*Product()


<!----方法的参数任意(0~n个)注意是两个点,不是三个--->
5.* com.shop.weaving.ProductService.addProduct(..)


<!----类名前缀任意--->
6.* com.shop.weaving.*Service.addProduct(..)

<!----匹配父包和子类 路径--->
7.* com.shop.weaving.*Service.*Product(..)

注意:

以下四个切入点标签的书写顺序,会影响到切入的顺序

1
2
3
4
5
6
<aop:before method="before" pointcut-ref="us"/>
<aop:after-returning method="after" pointcut-ref="us"/>
<aop:after-throwing method="thowe" pointcut-ref="us"/>
<aop:after method="after" pointcut-ref="us"/>

<aop:around method="arround" pointcut-ref="us"/>

5、切入点表达式解析

1
2
3
<!--配置切入点-->
<aop:pointcut id="pc" expression="execution(public void com.shop.target.UserService.addUser())"></aop:pointcut>

1
2
3
4
5
6
7
8
 public void com.shop.target.UserService.addUser()  
* void com.shop.target.UserService.addUser()
* com.shop.target.UserService.addUser()
* com.shop.target.UserService.*()
* com.shop.target.UserService.*(..)
* com.shop.target.*Service.*(..)
* com.shop.target..*Service.*(..)

6、通知/增强详解

1.通知介绍

1.前置通知(before)

  • 目标方法运行之前调用

2.后置通知(after-returning)

  • 目标方法运行之后调用
  • 如果出现异常则不会调用

3.环绕通知(around)

  • 目标方法之前和之后都会调用

4.异常拦截通知(afterException)

  • 如果出现异常,就会调用

5.后置通知(最终通知)(after)

  • 目标方法运行之后调用
  • 无论是否出现异常,都会被调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MyAdvice {

//前置通知:目标方法运行之前调用
public void before()
{
System.out.println("开启事务");
}

//后置通知:目标方法运行之后调用
public void afterReturning()
{
System.out.println("提交事务。。。");
}
//环绕通知:在目标方法之前和之后都会调用
public Object around(ProceedingJoinPoint pjp) thr ows Throwable
{
System.out.println("这是环绕通知之前的部分....");
Object proceed = pjp.proceed();
System.out.println("这是环绕通知之后的部分...");
return proceed;
}

//异常拦截通知:如果出现异常就会调用
public void afterException()
{
System.out.println("不好了,出事情了");
}

//后置通知:目标方法运行之后调用(无论是否异常都会调用)
public void after()
{
System.out.println("出现异常,也会继续执行");
}
}

通知(增强)的类型:

1.前置通知

before

  • 在目标类的目标方法执行之前执行

  • 实用场景:可以对方法的参数来做校验

  • 配置文件信息:<aop:before method=”before” pointcut-ref=”myPointcut3”/>

2.后置通知

after-returning

  • 在目标类的目标方法执行之后执行

  • 如果有异常则不会执行

  • 实用场景:可以修改方法的返回值

  • 在配置文件中编写具体的配置:<aop:after-returning method=”afterReturning” pointcut-ref=”myPointcut2”/>

3.最终通知

after

  • 在目标类的目标方法执行之后执行

  • 如果有异常仍然会执行

  • 实用场景:释放资源

  • 在配置文件中编写具体的配置:<aop:after method=”after” pointcut-ref=”myPointcut3”/>

**4.异常抛出通知 **

after-throwing

  • 在抛出异常时,执行该通知

  • 实用场景:包装异常的通知

  • 在配置文件中编写具体的配置:<aop:after-throwing method=”afterThorwing” pointcut-ref=”myPointcut3”/>

**5.环绕通知 **

around

  • 在目标类的目标方法执行前后都会去执行

  • 注意:实用环绕通知时,默认的目标方法不会去执行,需要ProceedingJoinPoin对来让目标对象的方法执行。

  • 在配置文件中编写具体的配置:<aop:around method=”around” pointcut-ref=”myPointcut2”/>

2.通知/增强测试

1
2
3
4
5
6
7
8
9
10
11
public class UserService{
public void addUser(){
int i=1/0;
System.out.println("添加用户");
}

public void updatUser(){
System,out.println("删除用户。。。");
}

}

afterException() 如果在代码运行过程中发生了异常,将会被此通知拦截

1
2
3
4
5
6
7
public class MyAdvice {
public void afterException()
{
System.out.println("不好了,出事情了");
}
}

1
2
3
4
5
6
7
8
9
10
11
<!--配置织入-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pc" expression="execution(public void com.shop.target.UserService.addUser())"></aop:pointcut>
<!--配置通知-->
<aop:aspect ref="myAdvice">

<aop:after-throwing method="afterException" pointcut-ref="pc"></aop:after-throwing>
</aop:aspect>
</aop:config>

打印观察测试:

image-20200805194308029

后置通知 (after-returning)

  • (目标方法运行之后调用)

  • (如果出现异常则不会调用)

image-20200805194408629

image-20200805194438483

后置通知(最终通知)(after)

  • 目标方法运行之后调用

  • (无论是否出现异常,都会被调用)

image-20200805194531264

观察打印结果:

img

3.通知/增强(advice)参数

Joinpoint参数(重点)

  • 每种通知类型,在方法名()内部还可以接收一个参数,该参数的类型是Joinpoint

  • 使用Joinpoint可以看到一些东西

**getTarget() **获得被代理的目标对象

getSignature().getName() 获取被代理目标类 中的目标方法

7、注解开发AOP

导包就不多说了跟原来xml开发一样

1、applicationContext.xml中添加扫描包的xml头文件约束(content)

  • 类扫描
  • AOP扫描
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

">


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

<!-- 配置AOP的注解扫描
此处开关必须要打开
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>


2.通知类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.gec.Service;


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

@Component
@Aspect
public class Demo {
@Before(value = "execution(public void com.gec.Service.UserService.addUser())")
public void before(){
System.out.println("开启事务");
}

@After(value="execution(* com.gec.Service.UserService.*User())")
public void after(){
System.out.println("提交事务");
}

}

3.连接点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.gec.Service;

import org.springframework.stereotype.Component;

@Component("userService")
public class UserService {

public void addUser(){
System.out.println("添加用户");
}

public void updateUser(){
System.out.println("gengxing用户");
}
}

4、测试

1
2
3
4
5
6
7

@Test
public void test2(){
ApplicationContext applicationContent = new ClassPathXmlApplicationContext("applicationContent2.xml");
UserService userService = (UserService) applicationContent.getBean("userService");
userService.addUser();
}

5、结果

image-20200806145657210

idea 小插曲 ctrl+F+F12:打开一个新的窗口显示类结构

image-20200806151548137

JDBCTemplete

spring中提供了一个可以操作数据库的对象.对象封装了jdbc技术.

JDBCTemplate => JDBC模板对象

与DBUtils中的QueryRunner非常相似.

1、构建环境

1.导包

2、编写测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.gec.test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;

public class test {


@Test
public void test() throws PropertyVetoException, SQLException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//设置连接上数据库的信息
comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mybatis");
comboPooledDataSource.setUser("root");
comboPooledDataSource.setPassword("root");
//获取连接对象
Connection connection = comboPooledDataSource.getConnection();
//查看连接信息
System.out.println(connection);
//new jdbcTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate();


//jdbcTemplate设置数据源
jdbcTemplate.setDataSource(comboPooledDataSource);
String sql= "update user set username =100 where id= 1";

/*//使用update重载方法
String sql= "update user set username =? where id= 1";
int update = jdbcTemplate.update(sql, "ceshi");
System.out.println(update);
*/


/*//查询
String sql= "select * from user ";
List<User> list = jdbcTemplate.query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
});
for (User user : list) {
System.out.println(user);
}

*/
/* //根据id查询
String sql= "select * from user where id=? ";
List<User> list = jdbcTemplate.query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
},1);
for (User user : list) {
System.out.println(user);
}
*/

/**
//查询记录总数,返回时整数
String sql= "select count(*) from user ";
Integer count = jdbcTemplate.queryForObject(sql,Integer.class);
System.out.println(count);

*/

/**删除
String sql =" delete from user where id =? ";
jdbcTemplate.update(sql,id);

*/




int update = jdbcTemplate.update(sql);
System.out.println(update);

}
}

3、执行结果:

image-20200806155401956

2、JDBCTemplate整合spring

1、导包(就不多bb了,跟上面一样)

2、创建用到的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.gec.domain;

public class User {
int id;
String username;
String password;


public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}

UserDao就h只有一个方法findAll(),,连JdbcTemplate jdbcTemplate;都没有,所以即不再这里写了,能猜得出来长什么样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.gec.dao;

import com.gec.domain.User;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

public class UserDaoImpl implements UserDao {

JdbcTemplate jdbcTemplate;


@Override
public List<User> findAll() {

String sql= "select * from user";
List<User> list = jdbcTemplate.query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
});
return list;
}

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}

2、编写applicationContext.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd

">
<bean name="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>

<bean name="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="c3p0" />
</bean>

<bean name="UserDaoImpl" class="com.gec.dao.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbctemplate"/>
</bean>

</beans>

3、测试

1
2
3
4
5
6
7
8
9
10
@Test
public void test6() throws PropertyVetoException, SQLException {
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDaoImpl userDaoImpl = (UserDaoImpl) classPathXmlApplicationContext.getBean("UserDaoImpl");
List<User> list = userDaoImpl.findAll();
for (User user : list) {
System.out.println(user);
}
}

结果:

image-20200806171816219

一套下来没什么bug,成就感很高

3、事务

1、介绍

事务特性:acid:原子性**(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)**

事务并发问题:

  • 脏读
  • 不可重复读
  • 幻读

2、Spring中事务介绍

spring封装了事务管理代码

事务操作:

  • 打开事务
  • 提交事务
  • 回滚事务

事务的隔离级别

1 读未提交

2 读已提交

4 可重复读

8 串行化

事务操作对象:

因为在不同平台,操作事务的代码各不相同.spring提供了一个接口

PlatformTransactionManager 接口

image-20200807093904048

image-20200807093915797

事务传播行为:

image-20200807093941646

image-20200807093948142

3、spring整合事务

spring + jdbctemplate+transaction

方式一:

(模板方式),不推荐使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--将连接池交给Spring-->
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/gz1960"></property>
<property name="user" value="root"></property>
<property name="password" value="12345"></property>
</bean>

<!-----1.将核心事务管理器配置到spring容器---->
<!--配置事务核心管理者-->
<bean name="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

<!---2.配置TransactionTemplate模板--->
<!--配置事务的模板-->
<bean name="template" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="dataSourceTransactionManager"></property>
</bean>

<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>

<bean name="accountDao" class="com.shop.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>

<bean name="accountService" class="com.shop.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="tt" ref="template"></property>
</bean>
</beans>


4.在Service中调用模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class AccountServiceImpl implements AccountService{
private AccountDao accountDao;
private TransactionTemplate tt;
@Override
public void transfer(Integer from, Integer to, double money) {

tt.execute(new TransactionCallbackWithoutResult() {

@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
//先减掉张三的钱
accountDao.lostMoney(from, money);


int i = 1/0;

//再把李四的钱加上
accountDao.addMoney(to, money);

}
});

}
public AccountDao getAccountDao() {
return accountDao;
}
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public TransactionTemplate getTt() {
return tt;
}
public void setTt(TransactionTemplate tt) {
this.tt = tt;
}

}

方式二:

利用AOP切入

1.第一步导包

image-20200807111205840

6个IOC(核心)包,4个AOP包、1个spring JdbcTemplate包、2个事务包、1个mysql驱动包、1个c3p0连接池包

2、创建需要的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.gec.domain;

public class User {
int id;
String username;
int money;


public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public int getMoney() {
return money;
}

public void setMoney(int money) {
this.money = money;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", money=" + money +
'}';
}

}

dao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.gec.dao;

import com.gec.domain.User;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

public class UserDaoImpl implements UserDao{


JdbcTemplate jdbcTemplate;

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

@Override
public int increase(int money, int from_userid) {
String sql= "update account set money=? where id=? ";
int update = jdbcTemplate.update(sql, money, from_userid);
System.out.println(sql);
return update;
}

@Override
public int decrease(int money, int from_userid) {
String sql= "update account set money=? where id=? ";
int update = jdbcTemplate.update(sql, money, from_userid);
System.out.println(sql);
return update;
}

@Override
public User findByid(int id) {

String sql= " select * from account where id= ? ";
List<User> list = jdbcTemplate.query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User user = new User();
user.setId(resultSet.getInt(1));
user.setUsername(resultSet.getString(2));
user.setMoney(resultSet.getInt(3));
return user;
}
}, id);

if(list.size()>0){
return list.get(0);
}
return null;
}
}

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.gec.Service;

import com.gec.dao.UserDao;
import com.gec.domain.User;

public class UserServiceImpl implements UserService{

UserDao userDao;

public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

@Override
public void zhuanqian(int id1, int id2, int money) {

User dUser = userDao.findByid(id1);
User iUser = userDao.findByid(id2);
//1 减去100
userDao.decrease(dUser.getMoney()-100,1);

// int i=1/0;
//2 加上100
userDao.increase(dUser.getMoney()+100,2);

}
}

3、配置xml文件
  • 头文件约束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd

">
  • bean创建以及AOP,事务管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<!--事务管理者对象
需要依赖 数据源 dataSource
-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="c3p0"/>
</bean>
<!--配置事务通知
事务管理属性介绍;
isolation 配置事务隔离级别:
1.读未提交
2.读已提交
4.可重复读
8.可串行化

read-only 配置是否只读:
1.否 如果 添加 删除 修改 false ->表示 可读 可写
2.是 如果是 查询操作 true -》 表示 只读

propagation 配置事务的 传播行为:
required:支持当前事务,如果没有事务,就新建一个事务。

transaction-manager 配置当前 事务通知 属于 transactionManager 管理
-->
<tx:advice id="mydavice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="zhuanqian" isolation="READ_UNCOMMITTED" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>

<!--配置 织入 操作-->
<aop:config>
<aop:pointcut id="pc" expression="execution(* com.gec.Service.UserServiceImpl.zhuanqian(..))"/>
<aop:advisor advice-ref="mydavice" pointcut-ref="pc"/>
</aop:config>


<bean name="user" class="com.gec.domain.User"/>


<bean name="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>


<bean name="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="c3p0" />
</bean>


<bean name="UserDaoImpl" class="com.gec.dao.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbctemplate"/>
</bean>


<bean name="UserServiceImpl" class="com.gec.Service.UserServiceImpl">
<property name="userDao" ref="UserDaoImpl"/>
</bean>

配置事务通知

image-20200807144606778

4、测试
1
2
3
4
5
6
7
8
9

@Test
public void test2() {
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext2.xml");
UserService userService = (UserService) classPathXmlApplicationContext.getBean("UserServiceImpl");
userService.zhuanqian(1,2,100);

}

方式三:

注解方式实现

1、导包

使用的包,与xml开发一致

2、xml中配置

  • 头文件约束:基本上一致,看看有没有context约束,有就不理,没有就加上,用于扫描事务。
  • 开启事务扫描
1
<tx:annotation-driven transaction-manager="transactionManager"/>

完整的xml中的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--事务管理者对象
需要依赖 数据源 dataSource
-->
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="c3p0"/>
</bean>

<!---开启事务扫描---->
<tx:annotation-driven transaction-manager="transactionManager"/>

<bean name="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
</bean>

<bean name="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="c3p0" />
</bean>

<bean name="UserDaoImpl" class="com.gec.dao.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbctemplate"/>
</bean>

<bean name="UserServiceImpl" class="com.gec.Service.UserServiceImpl">
<property name="userDao" ref="UserDaoImpl"/>
</bean>
</beans>

3、在类中添加注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24


public class UserServiceImpl implements UserService{
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}

@Override
@Transactional(isolation = Isolation.READ_UNCOMMITTED,propagation = Propagation.REQUIRED,readOnly = true)
public void zhuanqian(int id1, int id2, int money) {

User dUser = userDao.findByid(id1);
User iUser = userDao.findByid(id2);
//1 减去100
userDao.decrease(dUser.getMoney()-100,1);

// int i=1/0;
//2 加上100
userDao.increase(dUser.getMoney()+100,2);

}
}

注意:这个注解可以放在类,可以放在方法上

  • 放在类上,对类中的所有方法有效

  • 放在方法上,仅对当前方法有效

  • 类和方法上都有,方法上的优先级高。

    @Transactional(isolation = Isolation.READ_UNCOMMITTED,propagation = Propagation.REQUIRED,readOnly = true)

Mybatis整合

  • mybatis是使用核心对象SqlSession操作数据库

  • 要获得SqlSession实例,就需要SqlSessionFactory

  • 而SqlSessionFactory是SqlSessionFactoryBulider依据Mybatis核心配置文件中数据源、sql映射文件等信息构建的。

以前我们使用mybatis,就需要我们自己手动编写代码去创建这些对象,现在:让Spring容器帮我们去管理(创建对象)

初级整合:

1、导包

image-20200807161711108

2、创建需要用到的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.gec.domain;

public class User {
int id;
String username;
String money;


public User() {
}

public User(int id, String username, String money) {
this.id = id;
this.username = username;
this.money = money;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getMoney() {
return money;
}

public void setMoney(String money) {
this.money = money;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", money='" + money + '\'' +
'}';
}

}

mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.gec.mapper;

import com.gec.domain.User;
import org.apache.ibatis.annotations.Param;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public interface UserMapper {
List<User> findAll();
User findOne(@Param(value = "id") int id);
}

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gec.mapper.UserMapper">
<select id="findAll" resultType="com.gec.domain.User">
SELECT * from user;
</select>
<select id="findOne" resultType="com.gec.domain.User">
SELECT * from user where id= #{id} ;
</select>
</mapper>

Dao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.gec.dao;

import com.gec.domain.User;
import com.gec.mapper.UserMapper;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

public class UserDao {
SqlSessionTemplate sqlSessionTemplate;

public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}

public void addUser(User user){
String sql= "insert into ";
}

public List<User> selectlist(){
List<User> list = sqlSessionTemplate.selectList("com.gec.mapper.UserMapper.findAll");

return list;
}
public User selectone(int i){
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
User user = mapper.findOne(1);
return user;
}


}

3、创建spring配置文件

​ 1、导入头文件约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>

mybatis.config.xml

1
2
3
4
5
6
7
<?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>

</configuration>
      2、将需要的创建对象交给IOC

配置数据源

  • spring和myabtis整合,不再使用jdbc默认连接池技术,使用 一些第三方的数据连接池技术

  • 常见的有dbcp、C3p0 、Proxool等连接池技术

  • 导入dbcp连接池的jar包

  • 让spring管理dbcp数据连接池


配置SqlSessionFactoryBean

mybaits中SqlSessionFactory使用SqlsessionFactoryBulider构建

整合后,使用SqlSessionFactoryBean


配置SqlSessionTemplete

传统mybatis开发,使用SqlSession,有了SqlSession可以帮助我们操作数据库

而在Spring整合mybatis后,Spring框架封装了SqlSession类,提供了一个SqlSessionTemplete

SqlSessionTemplete特点:

  • 实现了SqlSession

  • 融合spring并简化部分流程化工作

  • 保证和当前spring中事务自动相关联

  • 自动管理会话生命周期(关闭、提交、回滚)

【开发小结】:

  • 传统的mybatis开发,sqlSession也是需要SqlSessionFactory对象才能得到session

  • 在SqlSessionTemplete的源码[e1] 中,明确提供了一个构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!---dbcp数据源---->
<bean name="basicDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?Unicode=true&amp;characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>

<!------创建sqlSessionFactory,
1.导入数据源
2.指定mybatis.config.xml配置文件的位置
3.mappers映射
1.可以在application中配置
2.也可以在myabtis.config.xml中配置
------>
<bean name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="basicDataSource"/>
<property name="configLocation" value="classpath:mybatis.config.xml" ></property>
<property name="mapperLocations">
<list>
<value>classpath:com/gec/mapper/UserMapper.xml</value>
</list>
</property>
</bean>


<!--
spring管理sqlSession
使用SqlSessionTemplate 的构造函数将sqlFactory注入进去
-->
<bean id="sqlsession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

<!----
配置spring管理dao层
将sqlSessionTemplete注入到dao实现类
--->
<bean name="userDao" class="com.gec.dao.UserDao">
<property name="sqlSessionTemplate" ref="sqlsession"/>
</bean>

【开发总结】:

  • spring的bean标签中的class对应的类路径不要打错,更不能乱打。

  • spring的bean标签中property元素中name属性对应的值,也不能乱打。

4、测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test
public void test (){

ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ApplicationContext.getBean("userDao");
List<User> list = userDao.selectlist();
for (User user : list) {
System.out.println(user);
}
}


@Test
public void test2 (){

ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ApplicationContext.getBean("userDao");
User selectone = userDao.selectone(1);
System.out.println(selectone);
}





**【开发小结】:**使用spring整合之后,可以将数据源、SqlSessionFactoryBean、SqlSessionTemplete都交给spring管理,简化了开发

中级整合:

根据上面初级整合修改。

由于每一个对dao层类都需要添加一个sqlSessionTemplate成员变量,并且IOC中注入,相对较麻烦,所以对sqlSessionTemplate进行改进

,通过引入Spring-mybatis包中的SqlSessionDaoSupport类

image-20200807200544010

1
2
3
4
5
//直接获取SqlSession  
public SqlSession getSqlSession() {
return this.sqlSession;
}

我们只要将自己的类继承这个类,那么就可以使用使用它的getSession()获取session

1
2
3
public class UserDao2 extends SqlSessionDaoSupport{
}

有get方法就有set方法,需要进行set才能get.

通过上面的源码可以看出,通过传入一个sqlSessionFactory,它会new sqlSessionTemplate给sqlSessionTemplate,所以

将SqlSessionFactoryBean注入给它

1
2
3
<bean  name="userDao" class="com.gec.dao.UserDao">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

Dao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public class UserDao extends SqlSessionDaoSupport {

public List<User> selectOne(){
/*
继承了SqlSessionDaoSupport,
直接通过this.getSqlSession(),得到sqlSession
*/
List<User> list = this.getSqlSession().selectList("com.gec.mapper.UserMapper.findAll");
return list;
}


}

【开发小结】:之前我们需要在dao的实现类中,提供sqlSessionTemplete的set方法,并将sqlTemp注入到dao实现类中。

​ 现在只需要让自己的类继承SqlSessionSupport,然后将SqlFac注入进来,就可以直接使用get()方法获取SqlSessionTemp (SqlSession),不需要创建sqlsession,也不需要提供setter方法

高级整合;

  1. 使用sqlsession内部的方法,都是写死的字符串来获取,很容易产生错误且错误还不易被发觉

  2. 每次都要在Dao中getMapper一下,繁琐,能不能让spring帮我们去getMapper操作,自己只提供一个接口呢?

根据中级的基础上再次改进。去掉Dao层,因为Dao是多余的,就只是单纯为了拿到UserMapp。

如果将UserMap交给IOC那么就可以删掉UserDao.

所以Spring提供了MapperFactoryBean直接注入映射器

使用MapperFactoryBean注入映射器

mapperFactoryBean特点:

  1. 继承了SqlSessionDaoSupport

    所以有传给它一个sqlSession工厂,来获取sqlSession

image-20200807210109022

1
2
3
4
5
6
7
<!-----MapperFactoryBean对象交给IOC,同时注入Mapper接口------>
<bean name="userDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.gec.mapper.UserMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>


直接将UserMapper交给了MapperFactoryBean类。

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13

@Test
public void test (){

ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//通过配置文件定义的UserDao来拿到mapper
//做到了去掉dao
UserMapper userDao = (UserMapper) ApplicationContext.getBean("userDao");
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
}

再次改进

因为以上方法配置多个mapper的时候,比较麻烦,每一个都有写。所以,这时候,spring提供可以直接通过package名,指定扫描该package下的所有,mapper接口,直接注册为MapperFactoryBean

1
2
3
4
5

<!--让spring管理dao -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.gec.mapper"></property>
</bean>

这个时候就不知道,springIOC会给每个mapper接口取什么名字,这个时候,可以通过一下方法看到取得名字

我们可以通过getBeanDefinitionNames()获取所有的bean名称

1
2
3
4
5
6
7
8
9
@Test
public void test2(){

ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
String[] names = ApplicationContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}

image-20200807211419795

给的名字是类的名字,但是是首字母是小写

根据spring的名字进行测试

1
2
3
4
5
6
7
8
9
10
11

@Test
public void test3(){

ApplicationContext ApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userDao = (UserMapper) ApplicationContext.getBean("userMapper");
List<User> list = userDao.findAll();
for (User user : list) {
System.out.println(user);
}
}

测试结果:image-20200808093214951