SSM框架_Spring5

1、Spring

简介

  • Spring:春天------>给软件行业带来了春天!
  • 2002,首次推出了Spring框架的雏形:interface21框架!
  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版。
  • Rod JohnsonSpring Framework创始人,著名作者。很难想象Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
  • Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!
  • SSH:Struct2 + Spring + Hibernate!
  • SSM:SpringMVC + Spring + Mybatis!

官网:https://spring.io/projects/spring-framework#overview

官方下载地址:https://repo.spring.io/release/org/springframework/spring/

GitHub:https://github.com/spring-projects/spring-framework

依赖

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.15</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.15</version>
</dependency>

优点

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

总结一句话:Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Qpsp8Gr-1645945227366)(F:\桌面\img\20201109101410173.png)]

拓展

现代化的Java开发!说白就是基于Spring的开发!


Spring Boot

  • 一个快速开发的脚手架。
  • 基于SpringBoot可以快速的开发单个微服务。
  • 约定大于配置。

Spring Cloud

  • SpringCloud是基于SpringBoot实现的。

因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!承上启下的作用!


spring的弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!”。

2、IOC理论推导

IOC是一种设计思想

当Dao层接口有多个实现类的时候,Service层使用set方法去传递具体的对象,而不再是指定单一的实现类。

private UserDao userDao;

//利用set进行动态实现值的注入!
public void setUserDao(UserDao userDao) { //多态
    this.userDao = userDao;
}
  • 当Dao层接口有多个实现类的时候,Service层使用set去传递具体的对象,而不再是指定单一的实现类。
  • 之前,程序是主动创建对象!现在,控制权在程序猿手上!*====》控制反转*
  • 使用了set注入后,程序不再具有主动性,而是变成了被动的接收对象!

这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了。系统的耦合性大大降低~,可以更加专注的在业务的实现

上!这是IOC的原型!

IOC本质

**控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,**也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了


采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

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

3、HelloSpring

1、新建一个maven项目,编写实体类

public class Hello {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Hello{" +
                "name='" + name + '\'' +
                '}';
    }

}

2、编写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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--使用Spring来创建对象,在Spring这些都称为Bean
        之前:
         类型 变量名 = new 类型();
         Hello hello = new Hello();
        现在:
         id = 变量名
         class = new的对象
         property 相当于给对象中的属性设置一个值!
     -->
    
    <bean id="hello" class="com.dl.pojo.Hello">
        <property name="name" value="Spring"/>
    </bean>

</beans>

3、测试

import com.dl.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        //获取Spring的上下文对象!
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        //我们的对象现在都在Spring中的管理了,我们需要使用,直接去里面取出来就可以!
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}

思考问题?

对象是谁创建的?

对象是由Spring创建的。


Hello对象的属性是怎么设置的?

Hello对象的属性是由Spring容器设置的。


  • 这个过程就叫控制反转:
  • 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的。
  • 反转:程序本身不创建对象,而变成被动的接收对象。
  • 依赖注入:就是利用set方法来进行注入的。
  • IOC是一种编程思想,由主动的编程变成被动的接收。
  • 可以通过new ClassPathXmlApplicationContext去浏览一下底层源码。
  • OK,到了现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改。
  • 所谓的IOC,一句话搞定:对象由Spring来创建,管理,装配!

4、IOC创建对象的方式

4.1 使用无参构造创建对象,默认!

注意:

  • 使用无参构造创建对象,默认的方式,属性是set方法设置的
  • ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”); 执行完,beans里面的所有对象都创建好了

public class User {
    private String name;
    public User(){
        System.out.println("无参构造!");
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
<bean id="user" class="com.dl.pojo.User">
    <property name="name" value="小白"/>
</bean>
public static void main(String[] args) {
    //Spring容器,把所有的bean都创建了
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    User user = (User) context.getBean("user"); 
}

4.2 使用有参创建对象

方式一:下标赋值 index

package com.dl.pojo;

public class User {
    private String name;

    public User(String name){
        this.name = name;
        System.out.println("有参构造!name:"+this.name);
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
<bean id="user1" class="com.dl.pojo.User">
    <constructor-arg index="0" value="小白1"/>
</bean>

方式二:参数类型匹配 type

不建议使用,因为当参数的类型相同时。。。。

<bean id="user2" class="com.dl.pojo.User">
    <constructor-arg type="java.lang.String" value="小白2"/>
</bean>

方式三:参数名匹配 name

基本数据类型和String使用value ,引用数据类型使用ref

<bean id="user3" class="com.dl.pojo.User">
    <constructor-arg name="name" value="小白3"/>
</bean>

4.3 总结:

在配置文件加载的时候,容器中管理的所有对象就已经初始化了!不管你从中取多少次同一个对象,他们的地址都一样的!

ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”); 执行完,beans里面的所有对象都创建好了

5、Spring配置

给bean添加别名

如果添加了别名,那么也可以通过别名来获取这个bean

<bean id="user" class="com.dl.pojo.User">
    <property name="name" value="小白"/>
</bean>
<alias name="user" alias="newUser"/>

bean的配置

<!--
    id:bean的唯一标识符
    class:bean对象所对应的全限定名。包名+类名
    name:也是别名,而且name可以取多个别命,用逗号、空格或者分号分开
-->
    <bean id="user" class="com.dl.pojo.User" name="nameNewUserT1,nameNewUserT2">
        <property name="name" value="小白"/>
    </bean>
    <alias name="user" alias="aliasNewUser"/>

import

  • 这个import。一般用于团队开发使用,它可以将多个配置文件,导入合并为一个。
  • 假设,现在项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的!

applicationContext.xml

<import resource="bean.xml"/>
<import resource="bean2.xml"/>
<import resource="bean3.xml"/>

使用的时候,直接使用总的配置就可以了。

6、DI依赖注入

6.1 通过构造器注入

constructor-arg

<bean id="user1" class="com.dl.pojo.User">
    <constructor-arg index="0" value="小白1"/>
</bean>

6.2 通过set注入【重点】

  • 依赖注入:set注入!
  • 依赖:bean对象的创建依赖于Spring容器
  • 注入:bean对象中的所有属性,由spring容器注入

1、环境搭建

复杂类型

package com.dl.pojo;

public class Address {
    private String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }
}

真实测试对象

package com.dl.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String [] books;
    private List<String> hobbys;
    private Map<String,String> card;
    private Set<String> games;
    private Properties info;
    private String wife;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address +
                ", books=" + Arrays.toString(books) +
                ", hobbys=" + hobbys +
                ", card=" + card +
                ", games=" + games +
                ", info=" + info +
                ", wife='" + wife + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobbys() {
        return hobbys;
    }

    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    public String getWife() {
        return wife;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }
}

2、注入

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

    <bean id="address" class="com.dl.pojo.Address">
        <property name="address" value="湖南"/>
    </bean>

    <bean id="student" class="com.dl.pojo.Student">
        <!--普通值注入,value-->
        <property name="name" value="小白"/>
        <!-- 对象注入,ref  -->
        <property name="address" ref="address"/>
        <!-- 数组注入,array -->
        <property name="books">
            <array>
                <value>Java基础</value>
                <value>JavaOOP编程</value>
                <value>JavaWeb</value>
                <value>SSM框架</value>
            </array>
        </property>
        <!-- list注入 -->
        <property name="hobbys">
            <list>
                <value>听音乐</value>
                <value>看视频</value>
            </list>
        </property>
        <!-- set注入 -->
        <property name="games">
            <set>
                <value>古墓丽影:崛起</value>
                <value>古墓丽影:暗影</value>
            </set>
        </property>
        <!-- map注入 -->
        <property name="card">
            <map>
                <entry key="身份证" value="111111111111111111" />
                <entry key="银行卡" value="1111111111111111111"/>
            </map>
        </property>
        <!-- null值注入 -->
        <property name="wife">
            <null/>
        </property>
        <!-- Properties配置文件注入 -->
        <property name="info">
            <props>
                <prop key="username">admin</prop>
                <prop key="password">111111</prop>
            </props>
        </property>
    </bean>
</beans>

测试

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
        /*	输出结果:
            Student{
                name='小白',
                address=Address{address='湖南'},
                books=[Java基础, JavaOOP编程, JavaWeb, SSM框架],
                hobbys=[听音乐, 看视频],
                card={身份证=111111111111111111,
                银行卡=1111111111111111111},
                games=[古墓丽影:崛起, 古墓丽影:暗影],
                info={password=111111, username=admin},
                wife='null'
            }
         */

    }
}

6.3 通过namespace注入

我们可以使用p命名空间和c命名空间进行注入:

实体类:

package com.dl.pojo;

public class User {
    private String name;
    private String age;

    public User() {
    }

    public User(String name, String age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

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

bean.xml

p命名和c命名不能直接使用,需要导入约束

<?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"
       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">

<!--p命名空间注入:可以直接注入属性的值,property-->
    <bean id="user1" class="com.dl.pojo.User" p:name="小白1" p:age="3" />
<!--c命名空间注入:通过构造器注入:constructor-args ,必须要有有参构造器 -->
    <bean id="user2" class="com.dl.pojo.User" c:name="小白2" c:age="4" />
        
</beans>

测试:

ApplicationContext context1 = new ClassPathXmlApplicationContext("userBean.xml");
User user1 = context1.getBean("user1", User.class);
User user2 = context1.getBean("user2", User.class);
System.out.println(user1);  // User{name='小白1', age='3'}
System.out.println(user2);  // User{name='小白2', age='4'}

注意点:

  • p命名和c命名不能直接使用,需要导入约束:
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

6.4 Bean的作用域

Scope

描述

singleton

(默认值)将单个 Bean 定义限定为每个 Spring IoC 容器的单个对象实例。

prototype

将单个 Bean 定义的作用域限定为任意数量的对象实例。

request

将单个 Bean 定义的作用域限定为单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 Bean 实例,该实例是在单个 Bean 定义后面创建的。仅在网络感知Spring的上下文中有效。ApplicationContext

session

将单个 Bean 定义限定为 HTTP 的生命周期。仅在网络感知Spring的上下文中有效。Session``ApplicationContext

application

将单个 Bean 定义的作用域限定为 的生命周期。仅在网络感知Spring的上下文中有效。ServletContext``ApplicationContext

websocket

将单个 Bean 定义的作用域限定为 的生命周期。仅在网络感知Spring的上下文中有效。WebSocket``ApplicationContext

singleton 单例模式,默认就是单例

<bean id="user1" class="com.dl.pojo.User" p:name="小白1" p:age="3" scope="singleton"/>

prototype 原型模式,每次从容器中get的时候都会产生一个新的对象

<bean id="user2" class="com.dl.pojo.User" c:name="小白2" c:age="4" scope="prototype"/>

其余的request session application 这些只能在web开发中使用!

7、Bean属性的自动装配

自动装配:给bean封装引用类型的属性

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

在Spring中有三种装配的方式:

1.在xml中显示的配置

2.在Java中显示的配置

3.隐式的自动装配bean【重要】


测试环境:

一个人有一个名字,一只猫和一条狗。

方式一:byName自动装配

<!--  byName自动装配:会在容器上下文中查找,与自己对象set方法后面的值对应的beanid ,必须保证id唯一  -->
    <bean id="people" class="com.dl.pojo.People" autowire="byName">
        <property name="name" value="小白"/>
    </bean>

方式二:byType自动装配

<!--  byType自动装配:会在容器的上下文中查找,与对象属性类型一致的bean,必须保证class唯一  -->
    <bean id="people" class="com.dl.pojo.People" autowire="byType">
        <property name="name" value="小白"/>
    </bean>

方式三:使用注解实现自动装配

使用注解须知:

  • 1.导入约束:context约束
  • 2.配置注解的支持:context:annotation-config/
<?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和@Qualifier("")

当使用@Autowired尝试自动装配时,Spring框架会先根据byType模式找出所有匹配类型的对象,如果匹配类型的对象的数量为0,也就是没有匹配类型的对象,默认情况下会直接报错。如果匹配类型的对象的数量为1,则直接装配;如果匹配类型的对象的数量超过1个(有2个甚至更多个),会尝试byName来装配,如果存在名称匹配的对象,则成功装配,如果名称均不匹配,则装配失败,会报错。

  • 直接在属性上使用!
  • 这个注解不依赖于set方法注入,没有set方法也可以使用
  • 也可以在set方法上使用!
  • 前提是你的自动装配在IOC(Spring)容器中存在,且符合byType
  • 如果 @Autowired 显示的定义了required属性为false,那么这个对象可以为null,那么就可以不再beans.xml里面装配了
  • required属性的默认值为 true
<?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/>

    <bean id="cat" class="com.dl.pojo.Cat"/>
    <bean id="dog" class="com.dl.pojo.Dog"/>
    <bean id="people" class="com.dl.pojo.People">
        <property name="name" value="小白"/>
    </bean>
    
 </beans>
@Autowired
private Cat cat;
// 如果显示的定义了required属性为false,那么这个对象可以为null,否则不允许为空;required属性的默认值为 true
@Autowired(required = false)
private Dog dog;

当beans.xml有多个同类型的bean时,可以再加一个注解:@Qualifier(“cat1”)来指定一个唯一的bean对象注入

public class People {
    private String name;
    
    @Autowired
    @Qualifier("cat1")
    private Cat cat;
    
    @Autowired
    @Qualifier("dog1")
    private Dog dog;
}

@Nullable

字段加了这个注解,说明这个字段可以为null

public void setName(@Nullable String name) {
    this.name = name;
}

@Resource

@Resource是java的注解,不是Spring包下的

当使用@Resource注解尝试自动装配时,其工作原理是先尝试byName装配,如果存在名称匹配的对象,则直接装配,如果没有名称匹配的对象,则尝试byType装配。

可以加个name属性来指定bean:@Resource(name=“xxx”)

8、使用注解开发

【注意事项】

在Spring4之后,要使用注解开发,必须要保证aop的包(依赖)导入:

org.springframework:spring-aop:5.3.15(数字表示版本号)


使用注解需要导入context约束,添加注解的支持:

<?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>

context:component-scan 和 context:annotation-config :

<?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">

    <!--  开启注解支持 ,注册了几个注解的bean(@Autowired。。。。) -->
    <context:annotation-config/>
    <!--  指定要扫描的包,这个包下的所有注解就会生效;相当于注册扫描到的所有注解的bean,使用了这个,<context:annotation-config/> 就可以不写了  -->
    <context:component-scan base-package="com"/>

</beans>

注册bean的注解:@Component

@Component

  • 加在类上,在Spring中注册bean
  • @Component 相当于: 。默认spring中的Bean id 为类名的首字母小写
  • @Component(“user1”):相当于指定bean的id为user1。
// @Component 相当于:<bean id="user" class="com.dl.pojo.User" />
@Component 
public class User {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

属性注入的注解:@Value

@Value

  • 加在属性上,或者set方法上面
  • @Value(“小白”) 相当于
  • 这个注解不依赖于set方法,没有set方法也可以使用
// @Value("小白") 相当于 <property name="name" value="小白"/>
@Value("小白")
private String name;

@Component的衍生注解

@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!

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

这四个注解功能都是一样的,都是将某个类注册到Spring中,装配Bean。可以理解为在不同的层,有不同的写法,其作用都是注册bean

自动装配的注解

加载属性上面

  • @Autowried
  • @Qualifier
  • @Resource
  • @Nullable 字段标记了了这个注解,说明这个字段可以为null;

作用域的注解@Scope

加在类上面

  • @Scope
  • @Scope(“singleton”)
  • @Scope(“prototype”)

小结

xml与注解:

  • xml更加万能,适用于任何场合!维护简单方便
  • 注解不是自己的类使用不了,维护相队复杂!

xml与注解最佳实践:

  • xml用来管理bean;
  • 注解只负责完成属性的注入;
  • 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持

9、使用纯Java的方式配置Spring

  • 我们现在要完全不使用Spring的xml配置了,全权交给Java来做!
  • JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能!

@Configuration代表这是一个配置类,就是我们之前看的beans.xml

@Configuration代表这是一个配置类,就是我们之前看的beans.xml

@Configuration代表这是一个配置类,就是我们之前看的beans.xml

实体类

public class User {
    @Value("小白")
    private String name;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

SpringConfig类

// 这个类也会Spring容器托管,注册到容器中,因为它本来就是一个@Component
// @Configuration代表这是一个配置类,就是我们之前看的beans.xml
@Configuration
@ComponentScan("com.dl.pojo") //使用了注解,开启包扫描
@Import(SpringConfig2.class)  //导入另一个配置类
public class SpringConfig {

    // 注册一个bean,就相当于我们之前写的一个bean标签
    // 这个方法的名字,就相当于bean标签中id属性
    // 这个方法的返回值,就相当于bean标签中的class属性
    @Bean("user")  // 可以不指定,就是方法名getUser
    public User getUser(){
        return new User(); // 就是返回要注入到bean的对象!
    }
}

测试类

public class MyTest {
    public static void main(String[] args) {
        //如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfigApplicationContext 上下文来获取容器,通过配置类的class对象加载!
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        User user = context.getBean("user", User.class);
        System.out.println(user.getName());
    }
}

总结

方式一:在配置类中使用@Bean

方式二:不在配置类中使用@Bean,在实体类中使用@Component,在配置类中包扫描

如果同时使用了这两种方式,那么Spring会通过这两种方式分别创建一个不同的对象

10、代理模式

为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】

代理模式的分类:

  • 静态代理
  • 动态代理

10.1 静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象的人!

代码步骤:

1.接口

//抽象角色
public interface Rent {
    public void rent();
}

2.真实角色

//真实角色 --- 房东 , 实现接口
public class Host implements Rent{
    public void rent() {
        System.out.println("房东出租房子!");
    }
}

3.代理角色

//代理角色 ---- 房东 , 实现接口
public class Proxy implements Rent{
    private Host host;

    public Proxy() {
    }

    public Proxy(Host host) {
        this.host = host;
    }

    public void rent() {
        seeHost();
        host.rent();
        hetong();
        fee();
    }

    public void seeHost(){
        System.out.println("中介带你去看房!");
    }

    public void hetong(){
        System.out.println("签租赁合同!");
    }

    public void fee(){
        System.out.println("收中介费!");
    }

}

4.客户访问代理角色

// 客户
public class Client {
    public static void main(String[] args) {
        //房东要出租房子
        Host host = new Host();
        //中介帮房东出租房子,中介一般都有一些附属操作
        Proxy proxy = new Proxy(host);
        //客户不要直接面对房东,直接找中介
        proxy.rent();
    }
}

好处:

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

缺点:

  • 一个真实角色就会产生一个代理角色;代码量会翻倍,开发效率会降低!
  • 代理对象和真实对象实现了接口的所有方法,一旦接口增加方法,代理对象和真实对象都要进行相应的修改,增加维护成本

10.2 动态代理

  • 动态代理的底层是反射
  • 动态代理和静态代理的角色一样
  • 动态代理的代理角色是动态生成的,不是我们直接写的
  • 动态代理有两类:基于接口的动态代理(JDK动态代理)和 基于类的动态代理(cglib)
  • 也可以通过Java字节码实现:Javassist

在动态代理中,代理类及其实例是程序自动生成的,因此我们不需要手动去创建代理类。在Java的动态代理机制中,

InvocationHandler(Interface)接口和Proxy(Class)类是实现我们动态代理所必须用到的。事实上,Proxy通过使用InvocationHandler对

象生成具体的代理代理对象


InvocationHandler接口

每个代理的实例(代理对象)都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

只有一个方法:

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

InvocationHandler中的invoke() 方法决定了怎么样处理代理传递过来的方法调用。


Proxy类,java.lang.reflect.Proxy

JDK通过 Proxy 的静态方法 newProxyInstance() 动态地创建代理

/**     
     * @description   
     * @param loader:类加载器,只有三类
     * @param interfaces:目标类(被代理的类)所实现的接口
     * @param h:InvocationHandler的实例
     * @return     
     */
    public static Object newProxyInstance(ClassLoader loader,
            Class<?>[] interfaces,
            InvocationHandler h)

代码实现:

//抽象角色
public interface Rent {
    public void rent();
}
//真实角色 --- 房东 , 实现接口
public class Host implements Rent {
    public void rent() {
        System.out.println("房东出租房子!");
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//代理角色的处理角色
public class ProxyInvocationHandler implements InvocationHandler {
    private Object obj;  //被代理的对象
    public void setHost(Object obj) {
        this.obj = obj;
    }
    /*
     *每个代理的实例(代理对象)都有一个与之关联的 InvocationHandler 实现类,
     *如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在执行方法之前可加入一些操作
        seeHouse();
        //通过反射执行方法
        Object o= method.invoke(obj,args);//这里的第一个参数是被代理的对象,不是代理对象
        //在执行方法之后可加入一些操作
        hetong();
        return o;
    }
    public void seeHouse(){
        System.out.println("中介带你看房子!");
    }
    public void hetong(){
        System.out.println("签合同!");
    }
}
import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        //真实对象
        Host host = new Host();
        //代理角色的处理程序
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        //设置要代理的类
        handler.setHost(host);
        //动态创建代理对象
        Rent proxy = (Rent) Proxy.newProxyInstance(Rent.class.getClassLoader(), new Class[]{Rent.class}, handler);
        proxy.rent();
    }
}

好处:

  • 可以使真实角色的操作更加纯粹!不要去关注一些公共的业务!
  • 公共业务就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是一类业务!
  • 一个动态代理类可以代理多个类,只要实现了同一个接口即可!

11、AOP

11.1 什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务

逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

11.2 AOP在Spring中的作用

提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
  • 切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。 【Log类】
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。 【Log类中的方法】
  • 目标(Target):被通知对象。 【接口】
  • 代理(Proxy):向目标对象应用通知之后创建的对象。 【代理类】
  • 切入点(PointCut):切面通知执行的“地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

spring euraka 下载 spring5下载_java

即AOP在不改变原有代码的情况下,去增加新的功能。

11.3 使用Spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包!

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

方式一: 使用Spring的API接口

该方式主要是使用Spring API接口实现

代码实现:

1.接口和实现类:

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("增加了一个用户!");
    }
    public void delete() {
        System.out.println("删除了一个用户!");
    }
    public void update() {
        System.out.println("修改了一个用户!");
    }
    public void query() {
        System.out.println("查询了一个用户!");
    }
}

2.定义我们的增强类,一个Log前置增强和一个AfterLog后置增强类

import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
    /**
     * 这个方法会在方法执行之前自动调用
     * @param method:要执行的目标对象的方法
     * @param args:方法的参数
     * @param target:目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"类的"+method.getName()+"方法被执行了!");
    }
}
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

public class AfterLog implements AfterReturningAdvice {
    // returnValue:方法的返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法返回结果为"+returnValue);
    }
}

3.在spring的文件中注册 , 并实现aop切入实现 , 注意导入约束,配置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: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="userServiceImpl" class="com.dl.service.UserServiceImpl"/>
    <bean id="log" class="com.dl.log.Log"/>
    <bean id="afterLog" class="com.dl.log.AfterLog"/>

<!--  配置aop:需要导入aop的约束  -->
    <aop:config>
        <!-- 切入点:在什么地方执行 -->
        <!-- expression:表达式,execution(要执行的位置:返回值 类名 方法名 参数)-->
        <aop:pointcut id="pointcut" expression="execution(* com.dl.service.UserServiceImpl.*(..))"/>
        <!-- 执行环绕增加:把Log类切入到指定方法 -->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

4.测试

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userServiceImpl"); //注意:使用接口接收这个bean
        userService.add();
    }
}

方式二:使用自定义类来实现AOP

该方式主要是切面定义

代码实现:

1.接口和实现类:

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("增加了一个用户!");
    }
    public void delete() {
        System.out.println("删除了一个用户!");
    }
    public void update() {
        System.out.println("修改了一个用户!");
    }
    public void query() {
        System.out.println("查询了一个用户!");
    }
}

2.定义自己的切入类

public class DiyLog {
    public void before(){
        System.out.println("===========方法执行前========");
    }
    public void after(){
        System.out.println("===========方法执行后========");
    }
}

3.在spring中配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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="userServiceImpl" class="com.dl.service.UserServiceImpl"/>
    <bean id="log" class="com.dl.log.Log"/>
    <bean id="afterLog" class="com.dl.log.AfterLog"/>

<!--  方式二:自定义类  -->
    <bean id="diyLog" class="com.dl.diy.DiyLog"/>
    <aop:config>
        <aop:aspect ref="diyLog">
            <aop:pointcut id="p1" expression="execution(* com.dl.service.UserServiceImpl.*(..))"/>
            <aop:before method="before" pointcut-ref="p1"/>
            <aop:after method="after" pointcut-ref="p1"/>
        </aop:aspect>
    </aop:config>

</beans>

4.测试

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userServiceImpl"); //注意:使用接口接收这个bean
        userService.add();
    }
}

方式三:使用注解实现

代码实现:

1.接口和实现类:

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}
public class UserServiceImpl implements UserService{
    public void add() {
        System.out.println("增加了一个用户!");
    }
    public void delete() {
        System.out.println("删除了一个用户!");
    }
    public void update() {
        System.out.println("修改了一个用户!");
    }
    public void query() {
        System.out.println("查询了一个用户!");
    }
}

2.定义注解实现的增强类

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

//方式三:使用注解方式实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {

    @Before("execution(* com.dl.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("===========方法执行前!!!========");
    }

    @After("execution(* com.dl.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("===========方法执行后!!!========");
    }

    //在环绕增强中,我们可以指定一个参数,代表我们要处理切入的点:
    @Around("execution(* com.dl.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        System.out.println(jp.getSignature());
        Object proceed = jp.proceed(); //执行方法
        System.out.println("环绕后");
    }

}

3.在Spring配置文件中,注册bean,并增加支持注解的配置

<?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="userServiceImpl" class="com.dl.service.UserServiceImpl"/>
    <bean id="log" class="com.dl.log.Log"/>
    <bean id="afterLog" class="com.dl.log.AfterLog"/>

<!--  方式三:使用注解实现AOP  -->
    <bean id="annotationPointCut" class="com.dl.diy.AnnotationPointCut"/>
    <!-- 开启注解支持 -->
    <!-- 默认是基于接口的JDK动态代理 proxy-target-class="false" , 设置属性:proxy-target-class="true",则为cglib(基于类的动态代理)-->
    <aop:aspectj-autoproxy proxy-target-class="false"/>

</beans>

4.测试

import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) context.getBean("userServiceImpl"); //注意:使用接口接收这个bean
        userService.add();
    }
}

12、整合Mybatis

MyBatis-Spring 需要以下版本:

MyBatis-Spring

MyBatis

Spring Framework

Spring Batch

Java

2.0

3.5+

5.0+

4.0+

Java 8+

1.3

3.4+

3.2.2+

2.1+

Java 6+


相关jar包

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.27</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.15</version>
    </dependency>
    <!--  Spring操作数据库的话还需要一个spring-jdbc的包  -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.16</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.8</version>
    </dependency>
    <!--  整合mybatis和spring的包 mybatis-spring  -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.7</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
    </dependency>
</dependencies>

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

Mybatis-Spring

什么是MyBatis-Spring?

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。

文档链接:http://mybatis.org/spring/zh/index.html

整合方式一:

实体类:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private int id;
    private String name;
    private String pwd;
}

mapper接口:

import com.dl.pojo.User;
import java.util.List;

public interface UserMapper {
    public List<User> queryAll();
}

mapper.xml:

<?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.dl.mapper.UserMapper">

    <select id="queryAll" resultType="user">
        select * from user;
    </select>

</mapper>

实现类:

import com.dl.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;

public class UserMapperImpl implements UserMapper {
    private SqlSessionTemplate sqlSessionTemplate;
    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

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

mybatis-config.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>

    <typeAliases>
        <package name="com.dl.pojo"></package>
    </typeAliases>

<!--    <settings>-->
<!--        <setting name="" value=""/>-->
<!--    </settings>-->

</configuration>

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

    <!-- 数据源:使用Spring的配置替换Mybatis的配置,c3p0,dbcp,sdruid……我们这里使用Spring提供的JDBC -->
    <bean id="dataSources" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    
    <!--  sqlSessionFactory  -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSources"/>
        <!-- 绑定Mybatis配置文件  -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/dl/mapper/*.xml"/>
    </bean>
    
    <!-- sqlSessionTemplate就是之前的 sqlSession  -->
    <!--  SqlSessionTemplate 是 MyBatis-Spring 的核心。
    作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。
     SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。  -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- SqlSessionTemplate没有set方法,所以只能通过构造器注入sqlSessionFactory  -->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

</beans>

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

    <!--  导入其他的beans.xml  -->
    <import resource="spring-dao.xml"/>
    <!-- 注册实现类  -->
    <bean id="userMapperImpl" class="com.dl.mapper.UserMapperImpl">
        <property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
    </bean>

</beans>

测试:

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;

public class MyTest {
    @Test
    public void  queryAll() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapperImpl = context.getBean("userMapperImpl", UserMapper.class);
        List<User> list = userMapperImpl.queryAll();
        for (User user : list) {
            System.out.println(user);
        }
    }
}

整合方式二:

实现类:

import com.dl.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;

import java.util.List;

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    public List<User> queryAll() {
        return getSqlSession().getMapper(UserMapper.class).queryAll();
    }
}

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

    <!-- 数据源:使用Spring的配置替换Mybatis的配置,c3p0,dbcp,sdruid……我们这里使用Spring提供的JDBC -->
    <bean id="dataSources" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--  sqlSessionFactory  -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSources"/>
        <!-- 绑定Mybatis配置文件  -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/dl/mapper/*.xml"/>
    </bean>

</beans>

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

<!--  导入其他的beans.xml  -->
<import resource="spring-dao.xml"/>
    
<!-- 注册实现类  -->
<bean id="userMapperImpl2" class="com.dl.mapper.UserMapperImpl2">
    <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>

</beans>

测试:

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;

public class MyTest {
    @Test
    public void  queryAll() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapperImpl = context.getBean("userMapperImpl2", UserMapper.class);
        List<User> list = userMapperImpl.queryAll();
        for (User user : list) {
            System.out.println(user);
        }
    }
}

13、申明式事务

13.1 回顾事务

  • 把一组业务当成一个业务来做;要么都成功,要么都失败!
  • 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
  • 确保完整性和一致性。

事务ACID原则:

  • 原子性(atomicity)
    事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用。
  • 一致性(consistency)
    一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中。
  • 隔离性(isolation)
    可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
  • 持久性(durability)
    事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中。

13.2 Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理和声明式的事务管理。


编程式事务管理

  • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
  • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理

  • 一般情况下比编程式事务好用。
  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
  • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

**实现:**application-config.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: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/aop https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 数据源:使用Spring的配置替换Mybatis的配置,c3p0,dbcp,sdruid……我们这里使用Spring提供的JDBC -->
    <bean id="dataSources" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT&useSSL=true&useUnicode=true&characterEncoding=utf-8"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    <!--  sqlSessionFactory  -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSources"/>
        <!-- 绑定Mybatis配置文件  -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/dl/mapper/*.xml"/>
    </bean>
    <!-- sqlSessionTemplate就是之前的 sqlSession  -->
    <!--  SqlSessionTemplate 是 MyBatis-Spring 的核心。
    作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession。
     SqlSessionTemplate 是线程安全的,可以被多个 DAO 或映射器所共享使用。  -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- SqlSessionTemplate没有set方法,所以只能通过构造器注入sqlSessionFactory  -->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

<!-- 配置申明式事务  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSources"/>
    </bean>
<!-- 结合AOP实现事务的织入,导入约束  -->
    <!-- 配置事务通知(类) <tx:jta-transaction-manager />  -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--  name:给哪些方法配置事务;  propagation:事务的传播特性,默认值为"REQUIRED"  -->
            <tx:method name="add" propagation="REQUIRED"/>  <!--  以add开头的方法     -->
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" read-only="true"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!-- 配置事务切入   -->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.dl.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

spring事务传播特性:

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作。

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

思考:

为什么需要事务?

  • 如果不配置事务,可能存在数据提交不一致的情况;
  • 如果我们不在Spring中去配置声明式事务,我们就需要在代码中手动配置事务!
  • 事务在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎!