0%

Spring5

基本概念

一些框架整合:

  • SSH:Struct2+Spring+Hibernate
  • SSM:SpringMvc+Sping+Mybatis

Spring特点

  • 轻量级、非入侵式、开源免费框架
  • 控制反转(IOC)、面向切面编程(AOP)
  • 支持事务处理,对框架整合支持

组成

image-20210509190514905

IOC理论

此时有一个接口UserDao和一些实现类,用户可能会调用不同的实现类

image-20210504141738119

如果按照一般的设计理念来说,会在UserServiceImpl中指定实现类

1
private UserDao userDao = new UserDapMysqlImpl;

如果这样做,每次用户变更需求需要在业务层中更改代码,十分麻烦,此时可以按照IOC思想,使用DI(依赖注入)的实现方式,将控制权转移到用户手中

1
2
3
4
5
private UserDao userDao;

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

此时用户在指定具体的实现方法时只需要传入特定的实现类,即可调用不同实现类中的方法,而程序员不需要再更改业务层中的代码。

MyTest.java部分代码

1
2
3
UserSercive userService = new UserServiceImpl;
userService.setUserDao(new UserDaoSqlserverImpl);
userService.getUser();

本质区别

之前程序控制权在业务层手中,IOC中程序控制权在用户手中

image-20210504142807255

解耦的过程

image-20210504142935159

IOC本质

控制反转IoC是一种设计思想,DI(依赖注入)是实现IoC的一种方法,没有IoC的程序中,我们使用面向对象编程,对象的创建与对象之间的依赖完全硬编码在程序中,对象的创建程序由程序自己控制,控制反转后将对象的创建转移给第三方。

控制反转是一种通过描述(XML或注解)并通过第三方生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,实现方式为依赖注入。

HelloSpring

  • 导入maven配置(webmvc整合了多个依赖,避免了多次导入的重复)

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.12.RELEASE</version>
    </dependency>
  • 实体类Hello.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Hello {
    private String str;

    public String getStr() {
    return str;
    }

    public void setStr(String str) {
    this.str = str;
    }

    @Override
    public String toString() {
    return "hello{" +
    "str='" + str + '\'' +
    '}';
    }
    }
  • 配置文件beans.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="myHello" class="com.lan5th.pojo.Hello">
    <property name="str" value="Hello world!"/>
    </bean>
    </beans>
  • 测试类HelloTest.java

    1
    2
    3
    4
    5
    6
    7
    public class HelloTest {
    public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    Hello myHello = (Hello) context.getBean("myHello");
    System.out.println(myHello);
    }
    }

IOC创建对象的方式

  1. 默认无参构造创建对象

    同上<property name="str" value="Hello world!"/>用法

  2. 如果使用有参构造创建对象

    • 下标赋值

      1
      2
      3
      <bean id="myHello" class="com.lan5th.pojo.Hello">
      <constructor-arg index="0" value="Hello world!"/>
      </bean>
    • 类型赋值,不推荐使用(多个同类型值难以处理)

      1
      2
      3
      <bean id="myHello" class="com.lan5th.pojo.Hello">
      <constructor-arg type="java.lang.String" value="Hello world!"/>
      </bean>
    • 参数名赋值

      1
      2
      3
      <bean id="myHello" class="com.lan5th.pojo.Hello">
      <constructor-arg name="str" value="Hello world!"/>
      </bean>

在配置文件加载时,容器中所有注册的对象都已经被实例化了一次

Spring配置

Alias

配置别名之后新旧两个名字都可以正常获取到对象

1
<alias name="user" alias="userNew"/>

但一般取别名时不适用alias,而是直接在bean里面配置name属性

Bean

1
2
3
4
5
6
7
8
<!--
id:bean的唯一标识符,相当于对象名
class:bean对象所对应的全限定名,包名+类型
name:别名,且可以同时配置多个别名,' ,;'字符都可以分割
-->
<bean id="myHello" class="com.lan5th.pojo.Hello" name="hello hello1,hello2;hello3">
<property name="str" value="Hello world!"/>
</bean>

Import

impot一般用于团队开发使用,可以将多个配置文件合并导入一个总的配置文件

假设项目中有三人进行开发,这三个人负责不同类的开发,不同的类注册在不同的beans中,我们可以利用import将所有的beans.xml合并为一

application.xml

1
2
3
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>

DI依赖注入

  • 构造器注入

    1
    2
    3
    <bean id="myHello" class="com.lan5th.pojo.Hello">
    <constructor-arg index="0" value="Hello world!"/>
    </bean>

    与之前相同,不再复述

  • Set方式注入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //数据结构
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
    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
    <bean id="myHello" class="com.lan5th.pojo.Hello">
    <property name="name" value="张三"/>
    <property name="address" ref="address"/>
    <property name="books">
    <array>
    <value>红楼梦</value>
    <value>西游记</value>
    <value>水浒传</value>
    <value>三国演义</value>
    </array>
    </property>
    <property name="hobbys">
    <list>
    <value>听歌</value>
    <value>吃饭</value>
    <value>看电影</value>
    </list>
    </property>
    <property name="card">
    <map>
    <entry key="身份证" value="123"/>
    <entry key="学号" value="1234"/>
    </map>
    </property>
    <property name="games">
    <set>
    <value>LOL</value>
    <value>COC</value>
    <value>BOB</value>
    <value>MO</value>
    </set>
    </property>
    <property name="wife">
    <null/>
    </property>
    <property name="info">
    <props>
    <prop key="driver">xxx</prop>
    <prop key="url">xxx</prop>
    <prop key="username">lan5th</prop>
    <prop key="password">123456</prop>
    </props>
    </property>
    </bean>
  • 拓展方式注入

    p命名空间对应set方法、c命名空间对应construct方法(注意使用前需要提前添加依赖)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="helloBean" class="com.lan5th.pojo.Hello" p:str="hello"/>

    </beans>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?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:c="http://www.springframework.org/schema/c"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="helloBean" class="com.lan5th.pojo.Hello" c:_0="hello1"/>

    </beans>

Bean作用域

image-20210509194146854

1
<bean id="helloBean" class="com.lan5th.pojo.Hello" p:str="hello" scope="singleton"/>
  1. 单例模式(Spring默认)
  2. 原型模式:每次从容器中get时都会产生一个新对象
  3. 其余的request、session、application一般在web开发中用到

Bean自动装配

  • 自动装配是Spring满足bean依赖的一种方式
  • Spring会在上下文中自动寻找,并自动给bean装配属性

Spring三种装配方法

  1. xml显式配置
  2. java中显式配置
  3. 隐式自动装配bean

自动转配类型

分别根据set方法中提供的变量名和变量类型自动在上下文中寻找对应实体进行装配

1
2
3
4
5
6
7
8
9
public class Hello {
private String str;
private Dog dog;
//...

public void setDog(Dog dog) {
this.dog = dog;
}
}
  • ByName

    1
    2
    3
    4
    <bean id="myHello" class="com.lan5th.pojo.Hello" autowire="byName">
    <property name="str" value="Hello world!"/>
    </bean>
    <bean id="dog" class="com.lan5th.pojo.Dog"/>
  • ByType

    1
    2
    3
    4
    <bean id="myHello" class="com.lan5th.pojo.Hello" autowire="byType">
    <property name="str" value="Hello world!"/>
    </bean>
    <bean id="dog" class="com.lan5th.pojo.Dog"/>

byName时需要保证所有beanid唯一,而byType需要保证所有bean的class唯一

使用注解自动装配

jdk1.5,Spring2.5之后就开始支持注解开发了

使用注解前:

  • 导入约束xmlns:context约束
  • 配置注解支持<context:annotation-config/>
1
2
3
4
5
6
7
8
9
10
11
12
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解支持-->
<context:annotation-config/>

</beans>

@Autowired

  • 可以在属性或set方法上使用
  • 使用Autowired后我们可以不用编写set方法了,前提是这个自动装配的属性在IOC(Spring)容器中存在,且符合byName规则

拓展:

  • @Nullable注解表示该字段可以为null

  • @Autowired注解中有一个required参数,表示该对象是否为必需,与@Nullable相似

    1
    2
    3
    public @interface Autowired {
    boolean required() default true;
    }
    1
    2
    3
    4
    5
    6
    public class Hello {
    private String str;
    @Autowired(required = false)
    private Dog dog;
    //...
    }

如果Autowired自动装配的环境比较复杂,自动装配无法通过一个注解完成时,可以增加@Qualifier(value = "")去配合@Autowired的使用,指定一个唯一的bean对象注入

1
2
3
4
5
<bean id="myHello" class="com.lan5th.pojo.Hello">
<property name="str" value="Hello world!"/>
</bean>
<bean id="dog" class="com.lan5th.pojo.Dog"/>
<bean id="dog1" class="com.lan5th.pojo.Dog"/>
1
2
3
4
5
6
7
public class Hello {
private String str;
@Autowired
@Qualifier(value = "dog1")
private Dog dog;
//...
}

@Resource@autoWired类似,但是是java原生提供的注解

  • 都是用来自动装配的,都可以放在属性字段或set方法上
  • @Autowired默认通过byType方式,有多个重复Type再通过byName方式查找
  • @Resource默认通过byName方式,有多个重复id再通过byType方式查找

使用注解开发

xml功能更齐全,适用于任何场合,维护简单方便,而注解适用于简单的类,且只能作用于本类上

一般采用:

  • xml管理bean
  • 注解进行属性注入

Spring4后要使用注解开发必须导入aop包,并导入<context:annotation-config/>约束

1
2
3
<!--扫描特定包,使其中的注解生效-->
<context:component-scan base-package="com.lan5th.pojo"/>
<context:annotation-config/>

属性注入

可以在属性上加,也可以在set方法上加

1
2
3
4
5
6
7
8
9
10
@Component
public class Hello {
private String str;

//相当于<property name="name" value="lan5th"/>
@Value("lan5th")
public void setStr(String str) {
this.str = str;
}
}

注解衍生

基于MVC三层架构

  • dao层:@Repository
  • service层:@Service
  • controller层:@Controller

这三个注解跟@Component功能类似,将某个类注册到Spring中,装配Bean,但区分于不同的开发模块

自动装配配置

1
2
3
@Autowired:自动通过类型、名字装配
@Nullable:说明字段可以为null
@Resource:自动通过名字,类型装配

作用域

1
2
3
4
5
6
7
//prototype:原型模式
//singleton:单例模式,还可以通过@Lazy注解指定懒汉式加载
@Component
@Scope("prototype")
public class User {
...
}

使用Java的方式配置Spring

现在完全不使用Spring的xml配置

JavaConfig是Spring的一个字项目,在Spring4之后成为了核心功能

配置类Lan5thConfig.java

1
2
3
4
5
6
7
@Configuration//配置类注解
@ComponentScan("com.lan5th.pojo")//扫描指定包
public class Lan5thConfig {
public Hello hello(){
return new Hello();
}
}

Hello.java

1
2
3
4
5
@Component
public class Hello {
@Value("lan5th")
private String str;
}

测试类ConfigTest.java

1
2
3
4
5
6
7
8
public class ConfigTest {
public static void main(String[] args) {
//注意:之前使用的是ClassPathXmlApplicationContext通过配置文件获得上下文,现在使用的是注解获取上下文
ApplicationContext context = new AnnotationConfigApplicationContext(Lan5thConfig.class);
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello);
}
}

这种纯java的配置方式在SpringBoot中随处可见

代理模式

作为SpringAOP的底层

image-20210523144021826

静态代理

角色分析:

  • 抽象角色:一般使用接口或抽象类实现

    1
    2
    3
    public interface Rent{
    public void rent();
    }
  • 真实角色:被代理的角色

    1
    2
    3
    4
    5
    public class Host implements Rent{
    public void rent(){
    System.out.println("房东出租房子");
    };
    }
  • 代理角色:代理真实角色,一般代理后会做一些附属操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Proxy implements Rent{
    private Host host;
    public Proxy(){}
    public Proxy(Host host){
    this.host = host;
    }
    public void rent(){
    seeHouse();
    host.Rent();
    hetong();
    fare();
    }
    public void seeHouse(){
    System.out.println("中介带领看房");
    }
    public void hetong(){
    System.out.println("签租借合同");
    }
    public void fare(){
    System.out.println("收中介费");
    }
    }
  • 客户:访问代理对象的角色

    1
    2
    3
    4
    5
    6
    7
    public class Client{
    public void Test(){
    Host host = new Host();
    Proxy proxy = new Proxy(host);
    proxy.rent();
    }
    }

代理模式优点:

  • 使真实角色的操作更加纯粹,不再关心一些公共业务
  • 公共业务交给代理角色,实现业务分工
  • 公共业务发生扩展的时候方便集中管理

缺点:

  • 一个真实角色对应一个代理角色,代码量翻倍,开发效率降低

动态代理

角色和静态代理一样,区别是动态代理类使动态生成的,不是直接写好的

  • 基于接口–jdk动态代理【此处使用】
  • 基于类–cglib
  • java字节码实现–JAVAssist

使用到两个类:

  • Proxy:动态生成代理对象

    Proxy.newProxyInstance()传入三个参数:

    • ClassLoader:InvocationHandler实现类位置
    • Interface:需要实现的接口
    • InvocationHandler:InvocationHandler类对象,这里直接指定this
  • InvocationHandler:调用处理程序并返回结果

    只有一个函数invoke(),得到代理对象后执行具体方法时自动运行,用过method.invoke()指定代理的类和参数

ProxyInvocationHandler.java

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
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口,这里适用于所有类,具体实现时可以指定一个具体类
private Object target;

public void setTarget(Object target) {
this.target = target;
}

//动态生成代理类
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}

//处理代理示例并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里可以在代理的具体函数前后添加其他操作,如下面的log()函数
log(method.getName());
Object result = method.invoke(target, args);
return result;
}

public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}

ProxyTest.java

1
2
3
4
5
6
7
8
9
10
11
12
public class ProxyTest {
public static void main(String[] args) {
//真实类
Dog dog = new Dog();
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//设置代理对象
pih.setTarget(dog);
//这里强转为真实类的接口,而不是真实类
Animal dog01 = (Animal) pih.getProxy();
dog01.shout();
}
}

执行结果

image-20210524145043627

优点:

  • 包括静态代理的所有优点
  • 一个动态代理代理的是一个接口,一般对应一类业务
  • 一个动态代理可以代理多个类,只需要这些类实现同一个接口,减少了静态代理的缺点

AOP

只有IOC容器中的组件才能获得AOP功能,在获取实例时必须从容器中获取

面向切面编程,是OOP(面向对象)的延续,思想和动态代理模式类似,降低耦合性,提高可重用性和开发效率

image-20210524140941713

image-20210524141123942

预先导包

1
2
3
4
5
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>

方式一:使用Spring自带接口

接口Animal.java

1
2
3
public interface Animal {
public void shout();
}

实现类Dog.java

1
2
3
4
5
6
public class Dog implements Animal {
//...
public void shout(){
System.out.println("wo wo!");
}
}

执行前输出LogBefore.java

1
2
3
4
5
public class LogBefore implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"类正在执行"+method.getName()+"方法");
}
}

执行后输出LogAfter.java

1
2
3
4
5
public class LogAfter implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(method.getName()+"方法执行完毕,返回结果为"+returnValue);
}
}

配置beansaop.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
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--注册bean-->
<bean id="dog" class="com.lan5th.pojo.Dog"/>
<bean id="logBefore" class="com.lan5th.service.LogBefore"/>
<bean id="logAfter" class="com.lan5th.service.LogAfter"/>

<!--配置aop,需要先导入aop约束-->
<aop:config>
<!--切入点,expression表达式:execution(返回值类型 包名.类名.方法.参数)-->
<aop:pointcut id="pointcut" expression="execution(* com.lan5th.pojo.Dog.*(..))"/>
<!--配置环绕增加-->
<aop:advisor advice-ref="logBefore" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="logAfter" pointcut-ref="pointcut"/>
</aop:config>

</beans>

测试类AopTest.java

1
2
3
4
5
6
7
8
public class AopTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("aop.xml");
//注意此处强转为接口
Animal dog = (Animal) context.getBean("dog");
dog.shout();
}
}

方式二:自定义类

自定义类DIYLog.java

1
2
3
4
5
6
7
8
public class DIYLog {
public void before(){
System.out.println("方法将要执行");
}
public void after(){
System.out.println("方法执行完毕");
}
}

重新配置aop.xml

1
2
3
4
5
6
7
8
9
10
<bean id="diy" class="com.lan5th.service.DIYLog"/>
<aop:config>
<!--自定义切面,ref标识要引用的类-->
<aop:aspect ref="diy">
<aop:pointcut id="pointcut" expression="execution(* com.lan5th.pojo.Dog.*(..))"/>
<!--直接标识出执行的时机:before,after等-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>

方式三:注解实现

aop注解参数格式:访问修饰符 返回值类型 方法全类名(参数),如:

  • 指定某个方法execution(public int com.lan5th.service.calculate(int, int))
  • 指定所有方法execution(* com.lan5th.service.calculate.*(..))

==切面类和被切入的类都需要被注入到容器中==

带注解的自定义类AnnotationLog.java

1
2
3
4
5
6
7
8
9
//告诉spring这是一个切面类
@Aspect
public class AnnotationLog {
//注意不要导成junit的注解
@Before("execution(* com.lan5th.pojo.Dog.*(..))")
public void log(){
System.out.println("方法执行前");
}
}

重新配置aop.xml,或给配置类上添加@EnableAspectJAutoProxy

1
2
3
<bean id="AnnAop" class="com.lan5th.service.AnnotationLog"/>
<!--开启注解 可添加proxy-target-class项,默认为false使用jdk,若设置为true则使用cglib-->
<aop:aspectj-autoproxy/>

常用方法

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
@Aspect
@Component
public class MyAspect {
//配置整体切入点
@Pointcut("execution(public int com.lan5th.beans.Calculator.calculate(int, int ))")
void pointCut(){}
//获取参数
@Before("pointCut()")
public void logBefore(JoinPoint joinPoint){
System.out.println(Arrays.asList(joinPoint.getArgs()));
}
//获取方法名
@After("pointCut()")
public void logAfter(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName());
}
//获取结果
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(Object result){
System.out.println(result);
}
//捕捉异常
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logError(Exception exception){
exception.printStackTrace();
}
}

需要注意一下环绕方法,需要手动放行

通过JoinPoint参数可以获取到被切入方法的许多信息

1
2
3
4
5
6
7
8
9
10
11
@Around("execution(* com.lan5th.pojo.Dog.*(..))")
//需要引入参数,代表我们获取处理切入的点
public void Around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around方法执行前");
//还可以获取签名,一般用于日志
Signature signature = joinPoint.getSignature();
System.out.println(signature);
//执行方法
joinPoint.proceed();
System.out.println("Around方法执行前");
}

整合Mybatis

搭建Mybatis环境

步骤:

  1. 导入jar包

    • junit

    • mtbatis

    • mysql

    • spring

    • spring-jdbc【新增】

    • mybatis-spring【新增】

      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
      <dependencies>
      <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
      </dependency>
      <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.2</version>
      </dependency>
      <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.31</version>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.12.RELEASE</version>
      </dependency>
      <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
      </dependency>
      <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.1.9.RELEASE</version>
      </dependency>
      <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.2</version>
      </dependency>
      <!--lombok自选-->
      <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.12</version>
      </dependency>
      </dependencies>
  2. 编写配置文件

  3. 测试

Mybatis-Spring

步骤:

  1. 编写数据源

  2. 编写SqlSessionFactory

  3. 编写SqlSessionTemplate

    SqlSessionTemplate是SqlSession的一个实现,在代码中可以无缝替代之前的SqlSession

  4. 编写实现类

  5. 将实现类注入到Spring中

image-20210524200916839

User.javaUserMapper.javaUserMapper.xml与之前相同

方式一:直接实例化SqlSessionTemplate对象

新增实现类UserMapperImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;

public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}

public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}

精简化mybatis-config.xml

1
2
3
4
5
6
7
8
9
10
<?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>
<!--之后只在这里写一些设置-->
<typeAliases>
<package name="com.lan5th.pojo"/>
</typeAliases>
</configuration>

Spring核心文件spring-dao.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
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--配置数据源-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<!--绑定mybatis-config配置文件,一般只在这个文件中写一些设置,不包含mapper-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/lan5th/dao/*.xml"/>
</bean>

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<!--只能使用构造器注入,因为没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

<!--添加实现类-->
<bean id="userMapper" class="com.lan5th.dao.UserMapperImpl">
<property name="sqlSession" ref="sqlSessionTemplate"/>
</bean>
</beans>

测试类MSTest.java

1
2
3
4
5
6
7
8
9
10
11
public class MSTest {
@Test
public void msTest(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper mapper = context.getBean("userMapper",UserMapper.class);
List<User> userList = mapper.selectUser();
for (User user : userList) {
System.out.println(user);
}
}
}

方法二:继承SqlSessionDaoSupport类

新增实现类UserMapperImpl2.java

1
2
3
4
5
6
7
8
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
public List<User> selectUser() {
//继承后可以直接获取sqlSession
SqlSession sqlSession = getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
return mapper.selectUser();
}
}

简化spring-dao.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
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--配置数据源-->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="datasource"/>
<!--绑定mybatis-config配置文件,一般只在这个文件中写一些设置,不包含mapper-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/lan5th/dao/*.xml"/>
</bean>

<!--继承SqlSessionDaoSupport后不需要再实例化SqlSessionTemplate了,因为父类已经实现-->

<!--添加实现类2-->
<bean id="userMapper2" class="com.lan5th.dao.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>

声明式事务

配置文件方式

让mybatis通过spring来管理事务,借由DataSourceTransactionManager类实现

配置spring-dao.xml

注意需要导入aop和tx的依赖

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
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">

<!--其他bean...-->

<!--配置声明式事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"/>
</bean>

<!--结合AOP实现事务置入-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--propagation事务传播特性-->
<tx:attributes>
<tx:method name="selectUser" read-only="true" propagation="REQUIRED"/>
<!--其他一般方法采用默认配置-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

<!--配置Aop切入-->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.lan5th.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>

七种事务传播方式

image-20210524210943304

如果不在Spring中实现事务,就需要在代码中手动配置事务

注解方式

  • 配置数据源DataSource

  • 主类上添加@EnableTransactionManagement

  • 需要转为事务操作的方法上添加@Transactional

  • 配置事务管理器来管理事务

    1
    2
    3
    4
    5
    @Bean
    public PlatformTransactionManager transactionManager(){
    //这里传入配置好的数据源
    return new DataSourceTransactionManager(datasource());
    }