狂神Spring-5完整版 基础知识全面详解 Spring-Mybatis框架整合

1、spring

1.1、简介

  • Spring:---->给软件行业带来春天

  • 2002年,首次推出了spring框架的雏形:interfaace21框架

  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版

  • Rod Johnson,Spring 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

maven依赖

spring

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

jdbc

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

1.2、优点

  • Spring是一个开源的免费的框架(容器)
  • spring是一个轻量级的、非入侵的框架!(导入spring不会导致项目不能运行)
  • 控制反转(IOC)和面向切面编程(AOP)
  • 支持事务处理,对框架整合的支持!

spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架

1.3、Spring的组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIZu4Y7A-1677035935112)(https://cdn.mianshigee.com/upload/note/210313/1246590.jpg?2017102711233)]

Spring 七大模块:https://blog.csdn.net/weixin_39885962/article/details/125743470

1.4、拓展

现代化的Java开发,就是基于spring的开发

在这里插入图片描述

  • spring boot
    • 一个快速开发的脚手架!
    • 基于springboot可以快速的开发单个微服务
    • 约定大于配置
  • spring cloud
    • springcloud是基于springboot实现的

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

弊端:发展了太久之后,违背了原来的理念,配置十分繁琐

2、IOC理论推导

  1. UserDao 接口

    public interface UserDao {
          
          
        public void getUser();
    }
    
  2. UserDaoImpl 实现类

    public class UserDaoImpl implements UserDao{
          
          
        @Override
        public void getUser() {
          
          
            System.out.println("获取数据");
        }
    }
    
    public class UserDaoMysqlImpl implements UserDao{
          
          
        @Override
        public void getUser() {
          
          
            System.out.println("mysql获取用户数据");
        }
    }
    
    public class UserDaoOracleImpl implements UserDao{
        @Override
        public void getUser() {
            System.out.println("Oracle获取数据");
        }
    }
    
  3. UserService 业务接口

    public interface UserService{
          
          
        public void getUser();
    }
    
  4. UserServiceImpl 业务实现

    public class UserServiceImpl implements UserService{
          
          
        private UserDao userDao;
        //利用set进行动态值的注入
        public  void setUserDao(UserDao userDao){
          
          
            this.userDao = userDao;
        }
        @Override
        public void getUser() {
          
          
            userDao.getUser();
        }
    }
    
  5. 测试

    public class MyTest {
          
          
        public static void main(String[] args) {
          
          
    
            UserServiceImpl userService = new UserServiceImpl();
    
            userService.setUserDao(new UserDaoOracleImpl());
            userService.getUser();
        }
    }
    

之前的业务中,用户的需求可能会影响我们原来的代码,需要根据用户的需求去修改源代码,如果程序代码量十分大,修改一次的成本也就十分昂贵。

我们使用一个set接口实现

public class UserServiceImpl implements UserService{
    
    
    private UserDao userDao;
    //利用set进行动态值的注入
    public  void setUserDao(UserDao userDao){
    
    
        this.userDao = userDao;
    }
    @Override
    public void getUser() {
    
    
        userDao.getUser();
    }
}
  • 之前,程序是主动创建的,控制权在程序员手中。
  • 使用了set注入后,程序不在具有主动性,而是变成了被动的接收对象。

在这里插入图片描述

这种思想从本质上解决了问题,程序员不用再去管理对象的创建了。系统耦合性大大降低,更加专注的在业务的是线上!这也就是IOC的原型

IOC的本质:

  • 控制反转IOC,是一种设计思想,DI依赖注入是实现IOC的一种方法。
  • 也有人人为DI只是IOC的另一种说法,没有IOC的程序中,我们使用面向对象编程,对象的创建于对象之间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转以后将对象的创建转移给第三方,也就是spring容器。个人认为所谓的控制反转就是:获取依赖对象的方式反转了
  • 采用xml配置bean的时候,bean的定义信息是和实现分离的,而采用注解的方式可以把两者合并为一体,bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
  • 控制反转是一种通过xml或者注解,并且通过第三方去创建或者获取特定对象的方式。在spring中实现控制反转的是IOC容器,其实现方法是DI依赖注入。

3、HelloSpring

  1. 新建maven项目,编写实体类

    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 + '\'' +
                    '}';
        }
    }
    
  2. 编写Spring的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 = 对象
    -->
        <bean id="hello" class="com.hwt.pojo.Hello">
            <property name="str" value="Spring"/>
        </bean>
    
    </beans>
    
  3. 测试

    public class MyTest {
          
          
        public static void main(String[] args) {
          
          
            //获取Spring的上下文对象
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            Hello hello = (Hello) context.getBean("hello");
            System.out.println(hello.toString());
        }
    }
    

ClassPathXmlApplicationContext继承关系
在这里插入图片描述

思考问题:

  • Hello对象是谁创建的?
    • Hello对象是由spring创建的
  • Hello对象的属性是怎么设置的?
    • Hello对象的属性是由spring容器设置的

这个过程就叫做IOC控制反转:

  • **控制:**谁来控制对象的创建,传统应用程序中的对象是由程序本身创建的,使用spring后,对象是由spring来创建的。
  • **反转:**程序本身不创建对象,而变成被动的接收对象。
  • **依赖注入:**就是利用set方法来进行注入的

OC是一种编程思想,由主动的编程变成被动的接收。

可以通过new ClassPathXMLApplicationContext去浏览一下源码。

OK,到了现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改

所谓的IOC,就是:对象由Spring来创建,管理,装配!

4、IOC创建bean(对象)的方式

4.1、使用无参构造创建对象,默认的。

  • 实体类

    public class User {
          
          
        private String name;
    
        public User() {
          
          
            System.out.println("User的无参构造");
        }
    
        public String getName() {
          
          
            return name;
        }
    
        public void setName(String name) {
          
          
            this.name = name;
        }
        public void show(){
          
          
            System.out.println("name="+name);
        }
    }
    
  • 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="user" class="com.hwt.pojo.User">
            <property name="name" value="高启强"/>
        </bean>
    
    </beans>
    
  • 测试

    public class MyTest {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
            User user = (User) context.getBean("user");
            user.show();
        }
    }
    

4.2、假设我们要使用有参构造创建对象,有三种方式

  1. 下标赋值

    <!--第一种、 下标赋值-->
    <bean id="user" class="com.hwt.pojo.User">
        <constructor-arg index="0" value="高启盛"/>
    </bean>
    
  2. 参数类型赋值

    <!--第二种, 参数类型赋值-->
    <bean id="user" class="com.hwt.pojo.User">
        <constructor-arg type="java.lang.String" value="泰叔"/>
    </bean>
    
  3. 参数名赋值

    <bean id="user" class="com.hwt.pojo.User">
        <constructor-arg name="name" value="徐江"/>
    </bean>
    

在配置文件加载的时候,容器中管理的对象就已经初始化了!

5、Spring配置

5.1、别名:

如果添加了别名,我们也可以使用别名获取到这个对象,原来的名字也可以使用

<alias name="user" alias="user21"/>

5.2、Bean的配置

<!--
    id:bean唯一的标识符,也就相当于对象名
    class:bean 对象对应的限定名: 包名 + 类名
    name:也就是别名,可以取多个别名
-->
<bean id="userT" class="com.hwt.pojo.UserT" name="t"></bean>

5.3、import

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

假设,现在项目中有很多个人开发,这些人负责不同的类的开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的bean.xml合并为一个总的ApplicationContext.xml

  • 张三

  • 李四

  • 王五

  • ApplicationContext.xml(合并到该文件中)

    <import resource="beans.xml"/>
    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>
    

使用的时候直接使用总的配置就行了

6、依赖注入

6.1、构造器注入

就是 4、IOC创建对象的方式

6.2、Set方式注入

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

环境搭建

  1. 复杂类型

    public class Address {
        private String address;
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    }
    
  2. 真实测试对象

    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 String wife;
        private Properties info;
            
            get  set
    }
    
  3. 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">
    <!--普通值注入,vlue-->
        <bean id="student" class="com.hwt.pojo.Student">
            <property name="name" value="高启强"/>
        </bean>
    
    </beans>
    
  4. 测试

    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.getName());
        }
    }
    
  5. 其他注入信息

    <bean id="address" class="com.kuang.pojo.Address">
        <property name="address" value="西安"></property>
    </bean>
    
    <bean id="name" class="com.kuang.pojo.Student">
        <property name="name" value="秦疆"></property>
        <property name="address" ref="address"/>
        <property name="books" >
            <array>
            <value>西游记</value>
            <value>三国演义</value>
            <value>水浒传</value>
            </array>
        </property>
    
        <property name="hobbies">
            <list>
                <value>看书</value>
                <value>听歌</value>
                <value>看电影</value>
                <value>玩手机</value>
            </list>
        </property>
    
        <property name="card">
            <map>
                <entry key="身份证号" value="111111222222333333"></entry>
                <entry key="银行卡" value="512345156663322"></entry>
            </map>
        </property>
    
        <property name="games">
            <set>
                <value>LOL</value>
                <value>BOB</value>
                <value>COC</value>
            </set>
        </property>
    
        <property name="wife">
            <null/>
        </property>
    
        <property name="info">
            <props>
                <prop key="学号">123456</prop>
                <prop key="url">123456</prop>
                <prop key="root">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>
    </bean>
    

6.3、其他方式

使用p命名空间和c命名空间进行注入

带有 P-namespace 的 XML 快捷方式

P-Namespace 允许你使用bean元素的属性(而不是嵌套的<property/>元素)来描述你的属性值协作 bean,或者两者兼而有之。

Spring 支持可扩展的配置格式带有名称空间,其基于 XML 模式定义。本章讨论的beans配置格式是在 XML 模式文档中定义的。然而,P-命名空间不是在 XSD 文件中定义的,并且仅存在于 Spring 的核心中。

带有 C-Namespace 的 XML 快捷方式

带有 P-namespace 的 XML 快捷方式类似, Spring 3.1 中引入的 C-namespace 允许内联属性用于配置构造函数参数,而不是嵌套constructor-arg元素。

(1.4 依赖关系)

  • 使用:

    <?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="user" class="com.hwt.pojo.User" p:name="高启强" p:age="20"/>
    
        <!--c命名空间注入,通过构造器注入:construct-args -->
        <bean id="user2" class="com.hwt.pojo.User" c:age="40" c:name="龚开疆"/>
    
    </beans>
    
  • 测试:

    public void test2(){
          
          
        ApplicationContext context = new ClassPathXmlApplicationContext("userbean.xml");
        User user = context.getBean("user2",User.class);
        System.out.println(user);
    }
    
  • 注意:使用p命名和p命名不能直接导入,需要导入xml约束

xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

p命名空间的注入其实就是set的注入方式,只不过我们通过一个约束的引入,使我们用起来更加的简单方便。

c命名空间还是使用上面的实体类,只不过加上一个无参构造和一个有参构造,因为我们要使用有参构造注入属性值

6.4、Bean的作用域

下表描述了受支持的作用域:

Scope 说明
singleton (缺省)将单个 Bean 定义作用于每个 Spring IOC 容器的单个对象实例。
prototype Bean 将单个定义作用于任意数量的对象实例。
request Bean 将单个定义作用于单个 HTTP 请求的生命周期。即, 每个 HTTP 请求都有其自己的实例,一个 Bean 创建于单个 Bean 定义的后面。 Spring ApplicationContext仅在可感知 Web 的上下文中有效。
session 将一个 Bean 定义的范围应用于 HTTPSession的生命周期。 Spring ApplicationContext仅在网络感知的上下文中有效。
application 将一个 Bean 定义的范围应用于ServletContext的生命周期。 Spring ApplicationContext仅在网络感知的上下文中有效。
websocket 将单个 Bean 定义作用于WebSocket的生命周期。 Spring ApplicationContext仅在网络感知的上下文中有效。
  1. 单例模式(Spring默认机制)并发会影响

    <bean id="user2" class="com.hwt.pojo.User" c:age="40" c:name="龚开疆" scope="singleton"/>
    
  2. 原型模式:每次从容器中get的时候,都会产生一个对象

    <bean id="user2" class="com.hwt.pojo.User" c:age="40" c:name="龚开疆" scope="prototype"/>
    
  3. 其余的request、session、application这些只能在web中使用到。

7、Bean的自动装配

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

在spring中的三种装配方式:

  • 在xml中显示的配置
  • 在Java中显示配置;
  • 隐式的自动装配bean【重点】

7.1、测试

**环境搭配:**创建一个项目,一个人具有两个宠物。

<?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="cat" class="com.hwt.pojo.Cat"/>
    <bean id="dog" class="com.hwt.pojo.Dog"/>

    <bean id="people" class="com.hwt.pojo.People">
        <property name="name" value="高启强"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>

</beans>

7.2、ByName自动装配

<!--
    byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的bean id!
-->
<bean id="people" class="com.hwt.pojo.People" autowire="byName">
    <property name="name" value="高启强"/>

</bean>

7.3、ByType自动装配

<!--
   byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean!
-->
<bean id="people" class="com.hwt.pojo.People" autowire="byType">
    <property name="name" value="高启强"/>
</bean>

小结:

  • ByName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致。
  • ByType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致。

7.4、使用注解实现自动装配

jdk1.5支持的注解,spring2.5就支持注解了

在配置 Spring 方面,注释是否比 XML 更好?
基于注释的配置的引入提出了这样一个问题:这种方法是否比 XML“更好”。简短的回答是“视情况而定。”长期的答案是,每种方法都有其优点和缺点,通常,由开发人员来决定哪种策略更适合他们。由于它们的定义方式,注释在其声明中提供了大量的上下文,从而导致更短和更简洁的配置。然而,XML 擅长将组件连接起来,而不会涉及它们的源代码或重新编译它们。一些开发人员更喜欢让连接靠近源代码,而另一些开发人员则认为,带注释的类不再是 POJO,此外,配置变得分散且更难控制。

要使用注解须知:

  • 导入约束: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>
    
  • @Autowired

    • 直接在属性上使用即可,也可以在set方法上使用。
    • 使用Autowired我们就可以不用编写set方法了,前提是你这个自动装配的属性在IOC容器中存在,且符合类型ByType!

科普:

字段标记了了这个注解,说明这个字段可以为null;

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

测试代码:

@Autowired注解:

public class People {
    
    
    //如果显式定义Autowired的required属性为false,说明这个对象可以为null,否则不能为空
    @Autowired(required = false)
    private Cat cat;
    @Autowired
    private Dog dog;

    private String name;
}

如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解完成时候,我们可以使用@Qualifier(value = “xxx”)去配置@Autowired的使用**,指定一个唯一的bean对象注入!**

@Autowired:
@Autowired是Spring框架提供的注解,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。
@Qualifier:
@Qualifier是Spring框架提供的注解,它可以和@Autowired一起使用,用来指定需要装配的组件的id。当不同的bean实例具有相同的类型时,通过@Qualifier来指定装配哪一个bean实例。

public class People {
    
    
    //如果显式定义Autowired的required属性为false,说明这个对象可以为null,否则不能为空
    @Autowired
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog1")
    private Dog dog;

    private String name;
}

@Resource注解:

public class People {
    
    
    //如果显式定义Autowired的required属性为false,说明这个对象可以为null,否则不能为空
    @Resource(name = "cat1")
    private Cat cat;
    @Resource
    private Dog dog;

    private String name;
}

小结:

@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以用在属性字段上。
  • @Autowired通过ByType的方式实现,而且必须要求这个对象存在。
  • @Resource默认是通过ByName方式实现,如果找不到名字,在通过ByType方式实现,如果两个都找不见,就报错。
  • 执行顺序不同:@Autowired通过byType的方式实现。

8、使用注解开发

在Spring4之后,要使用注解开发,必须要保证aop的包导入了

在这里插入图片描述

使用注解需要导入约束,配置注解的支持!

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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>
  1. bean

  2. 属性如何注入

    //等价于 <bean id="user" class="com.hwt.pojo.User"/>
    //Component:组件
    @Component
    public class User {
          
          
        @Value("高启强")
        public String name;
    }
    
  3. 衍生的注解

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

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

    这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean

  4. 自动装配

    • @Autowired:自动装配通过类型,名字。如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value = “xxx”)去配置。

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

    • @Resource:自动装配通过名字,类型。

  5. 作用域

    @Component
    @Scope("singleton")//prototype原型模式  singleton单例模式
    public class User {
          
          
        @Value("高启强")
        public String name;
    }
    
  6. 小结

    xml与注解:

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

    xml与注解最佳实践:

    • xml用来管理bean;

    • 注解只负责完成属性的注入;

      • 在使用的过程中,只需要注意:让注解生效,就需要开启注解的支持和扫描包

    9、使用Java的方式配置spring

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

在这里插入图片描述

  • 实体类

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    //@Component注解的意思就是这个类被spring接管了,注册到容器中
    @Component
    public class User {
          
          
        private String name;
    
        @Override
        public String toString() {
          
          
            return "User{" +
                    "name='" + name + '\'' +
                    '}';
        }
        public String getName() {
          
          
            return name;
        }
        @Value("高启强")
        public void setName(String name) {
          
          
            this.name = name;
        }
    }
    
  • 配置文件

    import com.hwt.pojo.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Import;
    import org.springframework.stereotype.Component;
    
    //@Configuration也会被spring容器接管,注册到容器中,他本来就是一个@Component
    //它代表的是一个配置类,和之前的beans.xml一样
    @Configuration
    @ComponentScan("com.hwt.pojo")
    @Import(Config2.class)//使用注解引入其他配置
    public class Config {
          
          
        /*
            注册一个bean,就相当于就相当于我们之前写的一个bean标签
            这个的方法的名字,相当于bean标签中的id属性
            这个方法的返回值相当于bean标签中的class属性
        */
        @Bean
        public User getUser(){
          
          
            return new User();//即使要返回注入到bean的对象
        }
    }
    
  • 测试类

    public class MyTest {
          
          
        @Test
        public void test1(){
          
          
            //如果完全使用了配置类方式去做,就只能通过AnnotationConfigApplicationContext上下文来获取容器,
            //通过配置类的class对象加载
            ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
            User getUser = context.getBean("getUser", User.class);
            System.out.println(getUser.getName());
        }
    }
    

10、代理模式

代理模式就是AOP的底层!【spring aop和spring MVC】

代理模式的分类:

  • 静态代理
  • 动态代理

加粗样式

10.1、静态代理

角色分析:

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

代码实现步骤:

  1. 接口

    public interface Rent {
          
          
        public void rent();
    }
    
  2. 真实角色

    //房东
    public class Host implements Rent {
          
          
        @Override
        public void rent() {
          
          
            System.out.println("房东要出租房子");
        }
    }
    
  3. 代理角色

    //中介
    public class Proxy implements Rent{
          
          
        private Host host;
    
        public Proxy() {
          
          
        }
    
        public Proxy(Host host) {
          
          
            this.host = host;
        }
        @Override
        public void rent() {
          
          
            host.rent();
            seeHost();
            hetong();
            fare();
        }
        //看房
        public void seeHost(){
          
          
            System.out.println("中介带的看房");
        }
        //签合同
        public void hetong(){
          
          
            System.out.println("签合同");
        }
        //收中介费
        public void fare(){
          
          
            System.out.println("收中介费");
        }
    }
    
  4. 客户端访问代理角色

    public class Cilent {
          
          
        public static void main(String[] args) {
          
          
            Host host  = new Host();
    //        host.rent();
            //代理中介帮房东租房子,代理一般会有其他操作
            Proxy proxy = new Proxy(host);
            proxy.rent();
        }
    }
    

代理模式的好处:

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

缺点:

  • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率会变低。

10.2、加深理解

代码操作步骤:

  1. 接口:

    public interface UserService {
          
          
        public void add();
        public void delete();
        public void update();
        public void query();
    }
    
  2. 真实角色:

    public class UserServiceImpl implements UserService{
          
          
        @Override
        public void add() {
          
          
            System.out.println("增加了一个用户");
        }
    
        @Override
        public void delete() {
          
          
            System.out.println("删除了一个用户");
        }
    
        @Override
        public void update() {
          
          
            System.out.println("修改了一个用户");
        }
    
        @Override
        public void query() {
          
          
            System.out.println("查询了一个用户");
        }
    }
    
  3. 代理角色:

    public class UserServiceProxy implements UserService{
          
          
        private UserServiceImpl userService;
    
        public UserServiceProxy() {
          
          
        }
        public UserServiceProxy(UserServiceImpl userService) {
          
          
            this.userService = userService;
        }
        @Override
        public void add() {
          
          
            log("add");
            userService.add();
        }
        @Override
        public void delete() {
          
          
            log("delete");
            userService.delete();
        }
        @Override
        public void update() {
          
          
            log("update");
            userService.update();
        }
        @Override
        public void query() {
          
          
            log("query");
            userService.query();
        }
        //日志方法
        public void log(String msg){
          
          
            System.out.println("使用了"+msg+"方法");
        }
    }
    
  4. 客户访问代理角色:

    public class Cilent {
          
          
        public static void main(String[] args) {
          
          
            UserServiceImpl userService = new UserServiceImpl();
    //        userService.add();
            UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
            userServiceProxy.add();;
        }
    }
    

AOP的原理:
在这里插入图片描述

10.3、动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的。
  • 动态代理分为两大类:基于接口的动态代理【jdk】和基于类的动态代理【cglib】
    • 基于接口:JDK动态代理【我们这里使用】
    • 基于类的:cglib
    • java字节码实现:javassist

Javassist是一个开源)的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。

需要了解两个类:

  • Proxy:代理;

  • InvocationHandler:调用处理程序;

代码实现

  • 接口

    //租房,抽象角色
    public interface Rent {
          
          
        public void rent();
    }
    
  • 真实角色

    //房东,要出租房子
    public class Host implements Rent {
          
          
        public void rent() {
          
          
            System.out.println("我要出租房子了");
        }
    }
    
  • ProxyInvocationHandler类

    //自动生产代理类
    public class ProxyInvocationHandler implements InvocationHandler {
          
          
        //被代理的接口
        private Rent rent;
    
        public void setRent(Rent rent) {
          
          
            this.rent = rent;
        }
    
        //生成得到代理类
        public Object getProxy() {
          
          
                                                        //类装载器
            //Proxy.newProxyInstance(this.getClass().getClassLoader(), 被代理的接口,InvocationHandler)
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    
        }
    
        //处理代理实例并返回结果  ,被代理的人
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
          
          
            seeHouse();;
            //动态代理的本质,就是使用反射机制
            Object invoke = method.invoke(rent, objects);
            fare();
            return invoke;
        }
    
        public void seeHouse(){
          
          
            System.out.println("带中介看房子");
        }
        public void fare(){
          
          
            System.out.println("收中介费");
        }
    }
    
  • 测试

    //真实角色
    public class Client {
          
          
        public static void main(String[] args) {
          
          
    
            //真实角色
            Host host = new Host();
            //代理角色,现在不存在,要自动生成
            ProxyInvocationHandler pih = new ProxyInvocationHandler();
    
            //通过调用程序处理角色来处理我们要调用的接口对象!
            pih.setRent(host);
            Rent proxy= (Rent) pih.getProxy();//proxy就是自动生成的代理类,不是我们写的
            proxy.rent();
        }
    }
    
  • 提炼出的ProxyInvocationHandler作为工具类

    //自动生产代理类
    public class ProxyInvocationHandler implements InvocationHandler {
          
          
        //被代理的接口
        private Object target;
    
        public void setRent(Object target) {
          
          
            this.target = target;
        }
    
        //生成得到代理类
        public Object getProxy() {
          
          
                                                        //类装载器
            //Proxy.newProxyInstance(this.getClass().getClassLoader(), 被代理的接口,InvocationHandler)
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    
        }
    
        //处理代理实例并返回结果  ,被代理的人
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
          
          
            log(method.getName());
            //动态代理的本质,就是使用反射机制
            Object invoke = method.invoke(target, objects);
            return invoke;
        }
    
        public void log(String msg) {
          
          
            System.out.println("执行了" + msg + "方法");
        }
    }
    

动态代理的好处:

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

11、AOP

11.2、什么是AOP

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

在这里插入图片描述

11.2、AOP在spring中的作用

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

横切关注点:跨越应用程序多个模块的方法或功能;也就是,与我们的业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志、安全、缓存、事务等等…

切面(Aspect):横切关注点被模块化的特殊对象。即,他是一个类

通知(Advice):切面必须要完成的工作。即,他是类中的一个方法

目标(Target):被通知的对象

代理(Proxy):向目标对象应用通知之后创建的对象

切入点(PointCut):切面通知执行的“地点”的定义

连接点(JointPoint):与切入点匹配的执行点

在这里插入图片描述

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

通知类型 连接点 实现接口
前置通知 方法方法前 org.springframework.aop.MethodBeforeAdvice
后置通知 方法后 org.springframework.aop.AfterReturningAdvice
环绕通知 方法前后 org.aopalliance.intercept.MethodInterceptor
异常抛出通知 方法抛出异常 org.springframework.aop.ThrowsAdvice
引介通知 类中增加新的方法属性 org.springframework.aop.IntroductionInterceptor

11.3、使用spring实现AOP

使用AOP织入,需要导入一个依赖包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
 </dependency>
11.3.1、方式一:

使用spring的API接口【springAPI接口实现】

  1. 在service包下,定义UserService业务接口和UserServiceImpl实现类

    public interface UserService {
          
          
        public void add();
        public void delete();
        public void update();
        public void query();
    }
    
    public class UserServiceImpl implements UserService{
          
          
        @Override
        public void add() {
          
          
            System.out.println("增加了一个用户");
        }
    
        @Override
        public void delete() {
          
          
            System.out.println("删除了一个用户");
        }
    
        @Override
        public void update() {
          
          
            System.out.println("修改了一个用户");
        }
    
        @Override
        public void query() {
          
          
            System.out.println("查询了一个用户");
        }
    }
    
  2. 在log包下,定义我们的增强类,一个log前置增强和一个afterlog后置增强类

    public class Log implements MethodBeforeAdvice {
          
          
        //method:要执行目标表对象的方法
        //arg:参数
        //o:目标对象
        @Override
        public void before(Method method, Object[] objects, Object o) throws Throwable {
          
          
    
            System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
        }
        
    }
    
    public class AfterLog implements AfterReturningAdvice {
          
          
        //returnValue;返回值
        @Override
        public void afterReturning(Object returnValue, Method method, Object[] objects, Object o1) 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="userService" class="com.hwt.service.UserServiceImpl"/>
        <bean id="log" class="com.hwt.log.Log"/>
        <bean id="afterLog" class="com.hwt.log.AfterLog"/>
    <!--    方式一:使用原生的Spring API接口-->
    <!--    配置aop的约束-->
        <aop:config>
            <!--切入点  expression:表达式 execution(要执行的位置)-->
            <aop:pointcut id="pointcut" expression="execution(* com.hwt.service.UserServiceImpl.*(..))"/>
            <!--执行环绕增加 -->
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
        </aop:config>
    
    </beans>
    
  4. 测试

    public class MyTest {
          
          
        public static void main(String[] args) {
          
          
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserService userService = (UserService) context.getBean("userService");
            userService.add();
        }
    }
    

execution()表达式可以分为五个部分

  1. execution():表达式主体。
  2. 第一个*****号:表示返回类型,*****号表示所有的类型。
  3. 包名:表示需要拦截的包名,后面的两个句点分别表示当前包和当前包的所有子包com.sample.service.impl包、子孙包下所有类的方法。
  4. 第二个*****号:表示类名,*号表示所有的类。
  5. *****(…) :第三个星号表示方法名,*****号表示所有的方法,后面括弧里面表示方法的参数,两个句点表示任何参数。
11.3.2、方式二:

自定义类来实现AOP【切面定义】

  1. 在diy包下定义自己的DiyPointCut切入类

    public class DiyPointCut {
          
          
        public void before(){
          
          
            System.out.println("=======方法执行前========");
        }
        public void after(){
          
          
            System.out.println("======方法执行后=====");
        }
    }
    
  2. 去spring中配置文件

    <!--方式二:自定义类    -->
    <bean id="diy" class="com.hwt.diy.DiyPointCut"/>
    <aop:config>
        <!--自定义切面, ref要引用的类-->
        <aop:aspect ref="diy">
            <!--切入点 -->
            <aop:pointcut id="point" expression="execution(* com.hwt.service.UserServiceImpl.*(..))"/>
            <!--通知-->
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
    
  3. 测试

    public class DiyPointCut {
          
          
        public void before(){
          
          
            System.out.println("=======方法执行前========");
        }
        public void after(){
          
          
            System.out.println("======方法执行后=====");
        }
    }
    
11.3.3、方式三:

注解实现

  1. 定义直接实现的AnnotationPointCut增强类

    @Aspect//标注这个类是一个切面
    public class AnnotationPointCut {
          
          
        @Before("execution(* com.hwt.service.UserServiceImpl.*(..))")
        public void before(){
          
          
            System.out.println("=======方法执行前========");
        }
        @After("execution(* com.hwt.service.UserServiceImpl.*(..))")
        public void after(){
          
          
            System.out.println("======方法执行后=====11");
        }
        //在环绕增强中可以给定一个参数,代表我们要获取处理切入的点
        @Around("execution(* com.hwt.service.UserServiceImpl.*(..))")
        public void around(ProceedingJoinPoint jp) throws Throwable {
          
          
            System.out.println("环绕前");
            Signature signature = jp.getSignature();//获得签名
            System.out.println(signature);
            Object o = jp.proceed();
            System.out.println("环绕后");
        }
    }
    
  2. 在spring配置文件中,注册bean,并增加支持注解的配置

    <!--方式三: 注解-->
    <bean id="annotationPointCut" class="com.hwt.diy.AnnotationPointCut"/>
    <!--开启注解支持  jdk(默认proxy-target-class="false") 
     cglib(proxy-targetclass="true") -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    

12、MyBatis

12.1、Mybatis测试

步骤:

  1. 导入相关jar包

    • Junit

    • mybatis

    • MySQL

    • spring相关

    • aop织入器

    • mybatis-spring整合包【重点】再次还导入lombok包

    • 配置maven静态资源过滤问题!

使用到的jar包:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.13</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>
</dependencies>
  1. 编写实体类

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Alias("user")
    public class User {
          
          
        private int id;
        private String name;
        private String pwd;
    }
    
  2. 编写核心配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "https://mybatis.org/dtd/mybatis-3-config.dtd">
    <!--configuration核心配置文件-->
    <configuration>
        <typeAliases>
            <typeAlias type="com.hwt.pojo.User"/>
        </typeAliases>
        <environments default="development">
            <environment id="development">
                <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
            <mapper class="com.hwt.dao.UserMapper"/>
        </mappers>
    </configuration>
    
  3. 编写接口

    public interface UserMapper {
          
          
        public List<User> slelctUser();
    }
    
  4. 编写Mapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.hwt.dao.UserMapper">
    
        <select id="slelctUser" resultType="com.hwt.pojo.User">
            select * from user;
        </select>
    
    </mapper>
    
  5. 测试

    public class Mytext {
          
          
        @Test
        public void test() throws IOException {
          
          
            String resourcse = "mybatis-config.xml";
            InputStream in = Resources.getResourceAsStream(resourcse);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
            SqlSession sqlSession = sqlSessionFactory.openSession(true);
    
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> userList = mapper.slelctUser();
            for (User user : userList) {
          
          
                System.out.println(user);
            }
        }
    }
    

12.2、MyBatis-spring

什么是mybatis-spring

mybatis-spring会帮助你将mybatis代码无缝的整合到spring中。

文档链接:http://mybatis.org/spring/zh/getting-started.html

实现方式一:

如果使用 Maven 作为构建工具,仅需要在 pom.xml 中加入以下代码即可:

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.2</version>
    </dependency>
  1. 引入spring配置文件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">
        
    </beans>
    
  2. 配置数据源替换配置文件mybatis的数据源

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "https://mybatis.org/dtd/mybatis-3-config.dtd">
    <!--configuration核心配置文件-->
    <configuration>
    <!--    起别名-->
        <typeAliases>
            <typeAlias type="com.hwt.pojo.User"/>
        </typeAliases>
    
    <!--设置-->
    <!--    <settings>-->
    <!--        <setting name="" value=""/>-->
    <!--    </settings>-->
    
    </configuration>
    
  3. 配置SQLSessionFactory,关联mybatis

     <!--DataSource:使用Spring的数据源替换Mybatis的配置 c3p0 dbcp druid
    我们这里使用Spring提供的JDBC:-->
     <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
         <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
         <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
         <property name="username" value="root"/>
         <property name="password" value="root"/>
     </bean>
    
     <!--配置SQLSessionFactory,关联mybatis-->
    
     <!--配置SQLSessionFactory-->
     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
         <property name="dataSource" ref="datasource"/>
    
         <!--关联mybatis-->
         <property name="configLocation" value="classpath:mybatis-config.xml"/>
         <property name="mapperLocations" value="classpath:com/hwt/dao/*.xml"/>
     </bean>
    
  4. 注册sqlsessionTemplate,关联SQLSessionFactory

    <!--注册sqlsessionTemplate,关联SQLSessionFactory-->
    <!--sqlsessionTemplate:也就是之前我们用的SQLSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <!--注册sqlsessionTemplate,关联SQLSessionFactory 没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>
    
  5. 需要UserMapper接口的UserMapperImpl实现类,私有化sqlsessionTemplate

    //给接口添加实现类
    public class UserMapperImpl implements UserMapper{
          
          
        //原来所有操作,都是使用sqlSession来执行,现在使用sqlSessionTemplate
        private SqlSessionTemplate sqlSession;
    
        public void setSqlSession(SqlSessionTemplate sqlSession) {
          
          
            this.sqlSession= sqlSession;
        }
    
        @Override
        public List<User> slelctUser() {
          
          
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            return mapper.slelctUser();
        }
    }
    
  6. 将自己写的实现类,注入的spring配置中

    <bean id="userMapper" class="com.hwt.dao.UserMapperImpl">
        <property name="sqlSession" ref="sqlSession"/>
    </bean>
    
  7. 测试

    @Test
    public void test2(){
          
          
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        List<User> userList = userMapper.slelctUser();
        for (User user : userList) {
          
          
            System.out.println(user);
        }
    }
    
实现方式二:
  1. 将上面写的UserMapperImpl修改一下

    //给接口添加实现类
    public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
          
          
    
    
        @Override
        public List<User> slelctUser() {
          
          
            return getSqlSession().getMapper(UserMapper.class).slelctUser();
        }
    
    }
    
  2. 实体类注入到Spring配置文件中。

    <bean id="userMapper2" class="com.hwt.dao.UserMapperImpl2">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    
  3. 测试:记得修改spring配置文件名

    @Test
    public void test3() throws IOException {
          
          
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper mapper = (UserMapper) context.getBean("userMapper2");
        for (User user : mapper.slelctUser()) {
          
          
            System.out.println(user);
        }
    }
    

13、声明式事务

13.1、回顾事务

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

事务的ACID原则:

  • 原子性:事务是原子性操作,要么同时全部完成,要么完全不起作用。
  • **一致性:**一旦所有事务动作完成,事务就要被提交,数据和资源处于一种满足业务规格的一致性状态事务提交前和提交后,数据的完整性不变
  • **隔离性:**多个事务同时处理相同的数据,每个事务都应该与其他事务隔离开来,防止数据损坏
  • **持久性:**事务一旦完成,无论系统发生什么错误,结果都不会收到影响,通常情况下,事务一旦被提交,就会被持久化到数据库中。

测试不使用事务:

  1. 在之前的案例中,我们给userMapper接口新增两个方法,删除和增加用户;

    //添加一个用户
    int addUser(User user);
    
    //根据id删除用户
    int deleteUser(int id);
    
  2. UserMapper文件,我们故意把 deletes 写错,测试!

    <insert id="addUser" parameterType="user">
        insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
    </insert>
    
    <delete id="deleteUser" parameterType="int">
        deletes from user where id = #{id}
    </delete>
    
  3. 编写接口实践类,在查询中分别调用添加和sql有问题的删除

    //给接口添加实现类
    public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
          
          
    
    
        //增加一些操作
        public List<User> slelctUser() {
          
          
            User user = new User(6, "小王", "185161");
            UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
            mapper.addUser(user);
            mapper.deleteUser(5);
    
            return mapper.slelctUser();
        }
    
        //新增
        public int addUser(User user) {
          
          
            return getSqlSession().getMapper(UserMapper.class).addUser(user);
        }
    
        //删除
        public int deleteUser(int id) {
          
          
            return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
        }
    }
    
  4. 测试

    public class MyTest {
          
          
        public static void main(String[] args) {
          
          
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
            List<User> userList = userMapper.slelctUser();
            for (User user : userList) {
          
          
                System.out.println(user);
            }
        }
    }
    

在这里插入图片描述

通过测试发现sql报错:提示delete写错了

但是刷新数据库发现 数据库插入成功!

没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!

以前我们都需要自己手动管理事务,十分麻烦!

但是Spring给我们提供了事务管理,我们只需要配置即可;

13.2、Spring中的事务管理

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

编程式事务管理

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

声明式事务管理(AOP)

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

Spring事务传播属性(Propagation):

  1. REQUIRED(默认属性)

    如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。 被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务。

  2. MANDATORY

​ 支持当前事务,如果当前没有事务,就抛出异常。

  1. NEVER
    以非事务方式执行,如果当前存在事务,则抛出异常。

  2. NOT_SUPPORTED
    以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

  3. REQUIRES_NEW
    新建事务,如果当前存在事务,把当前事务挂起。

  4. SUPPORTS
    支持当前事务,如果当前没有事务,就以非事务方式执行。

  5. NESTED
    支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。

  6. 使用spring管理实务,注意头文件的约束导入:tx

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/tx
            https://www.springframework.org/schema/tx/spring-tx.xsd">
    
  7. JDBC事务

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

    <!--结合AOP实现事务的织入-->
    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--给那些方法配置事务-->
        <!--配置事务的传播特性: new -->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <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>
    
  9. 配置事务切入

    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.hwt.dao.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>
    

为什么需要事务?

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

猜你喜欢

转载自blog.csdn.net/qq_44624801/article/details/129158995