• 项目管理与SSM框架(二)| Spring


    Spring简介

    Spring是一个开源框架,为简化企业级开发而生。它以IOC(控制反转)和AOP(面向切面)为思想内核,提供了控制层

    SpringMVC、数据层SpringData、服务层事务管理等众多技术,并可以整合众多第三方框架。

    Spring将很多复杂的代码变得优雅简洁,有效的降低代码的耦合度,极大的方便项目的后期维护、升级和扩展。

    Spring官网地址:https://spring.io/

    Spring体系结构

    在这里插入图片描述
    Spring框架根据不同的功能被划分成了多个模块,这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。

    • Core Container:Spring核心模块,任何功能的使用都离不开该模块,是其他模块建立的基础。
    • Data Access/Integration:该模块提供了数据持久化的相应功能。
    • Web:该模块提供了web开发的相应功能。
    • AOP:提供了面向切面编程实现
    • Aspects:提供与AspectJ框架的集成,该框架是一个面向切面编程框架。
    • Instrumentation:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
    • Messaging:为Spring框架集成一些基础的报文传送应用
    • Test:提供与测试框架的集成

    IOC_控制反转思想

    IOC(Inversion of Control) :程序将创建对象的权利交给框架。

    之前在开发过程中,对象实例的创建是由调用者管理的

    这种写法有两个缺点:

    1. 浪费资源:StudentService调用方法时即会创建一个对象,如果不断调用方法则会创建大量StudentDao对象。
    2. 代码耦合度高:假设随着开发,我们创建了StudentDao另一个更加完善的实现类StudentDaoImpl2,如果在StudentService中想使用StudentDaoImpl2,则必须修改源码。

    而IOC思想是将创建对象的权利交给框架,框架会帮助我们创建对象,分配对象的使用,控制权由程序代码转移到了框架中,控制权
    发生了反转,这就是Spring的IOC思想。而IOC思想可以完美的解决以上两个问题。

    IOC_Spring实现IOC

    在这里插入图片描述

    1. 创建Maven工程,引入依赖
    <dependencies>
        <dependency>
    		<groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.3.13version>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
            <scope>testscope>
        dependency>
    dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 创建POJO类、Dao类和接口
    public class Student {
        private int id;
        private String name;
        private String address;
     // 省略getter/setter/构造方法/tostring
    }
    public interface StudentDao {
        // 根据id查询学生
        Student findById(int id);
    }
    public class StudentDaoImpl implements StudentDao{
        @Override
        public Student findById(int id) {
            // 模拟从数据库查找出学生
            return new Student(1,"程序员","北京");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 编写xml配置文件,配置文件中配置需要Spring帮我们创建的对象
    
    <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="studentDao" class="com.gb.dao.StudentDaoImpl">bean>    
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 测试从Spring容器中获取对象。
    public class TestContainer {
       @Test
      public void t1(){
        // 创建Spring容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // 从容器获取对象
        StudentDao studentDao1 = (StudentDao) ac.getBean("studentDao");
        StudentDao studentDao2 = (StudentDao) ac.getBean("studentDao");
        System.out.println(studentDao1.hashCode());
        System.out.println(studentDao2.hashCode());
        System.out.println(studentDao1.findById(1));
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    IOC_Spring容器类型

    在这里插入图片描述

    容器接口

    • BeanFactory:BeanFactory是Spring容器中的顶层接口,它可以对Bean对象进行管理。
    • ApplicationContext:ApplicationContext是BeanFactory的子接口。它除了继承 BeanFactory的所有功能外,还添加了对国际化、资源访问、事件传播等方面的良好支持。

    ApplicationContext有以下三个常用实现类:

    • ClassPathXmlApplicationContext:该类可以从项目中读取配置文件
    • FileSystemXmlApplicationContext:该类从磁盘中读取配置文件
    • AnnotationConfigApplicationContext:使用该类不读取配置文件,而是会读取注解
    @Test
    public void t2(){
        // 创建spring容器
        //       ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    ApplicationContext ac = new FileSystemXmlApplicationContext("C:\\Users\\a\\IdeaProjects\\spring_demo\\src\\main\\resources\\bean.xml");
        
        // 从容器中获取对象
        StudentDao userDao = (StudentDao)ac.getBean("studentDao");
        System.out.println(userDao);
        System.out.println(userDao.findById(1));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    IOC_对象的创建方式

    使用构造方法

    Spring默认使用类的空参构造方法创建bean:

    使用工厂类的方法

    Spring可以调用工厂类的方法创建bean:

    1. 创建工厂类,工厂类提供创建对象的方法:
    public class StudentDaoFactory {
        public StudentDao getStudentDao(){
            return new StudentDaoImpl(1);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 在配置文件中配置创建bean的方式为工厂方式。
    <bean id="studentDaoFactory" class="com.gb.dao.StudentDaoFactory">bean>
    
    <bean id="studentDao" factory-bean="studentDaoFactory" factory-method="getStudentDao">bean>
    
    • 1
    • 2
    • 3

    使用工厂类的静态方法

    Spring可以调用工厂类的静态方法创建bean:

    
    <bean id="studentDao" class="com.gb.dao.StudentDaoFactory2" factory-method="getStudentDao">bean>
    
    • 1
    • 2

    IOC_对象的创建策略

    Spring通过配置 中的 scope 属性设置对象的创建策略,共有五种创建策略:

    • singleton:单例,默认策略。整个项目只会创建一个对象,通过 中的 lazy-init 属性可以设置单例对象的创建时机:

    lazy-init=“false”(默认):立即创建,在容器启动时会创建配置文件中的所有Bean对象。
    lazy-init=“true”:延迟创建,第一次使用Bean对象时才会创建。

    • prototype:多例,每次从容器中获取时都会创建对象。
    • request:每次请求创建一个对象,只在web环境有效。
    • session:每次会话创建一个对象,只在web环境有效。
    • gloabal-session:一次集群环境的会话创建一个对象,只在web环境有效。

    IOC_对象的销毁时机

    对象的创建策略不同,销毁时机也不同:

    • singleton:对象随着容器的销毁而销毁。
    • prototype:使用JAVA垃圾回收机制销毁对象。
    • request:当处理请求结束,bean实例将被销毁。
    • session:当HTTP Session最终被废弃的时候,bean也会被销毁掉。
    • gloabal-session:集群环境下的session销毁,bean实例也将被销毁。

    IOC_生命周期方法

    Bean对象的生命周期包含创建——使用——销毁,Spring可以配置Bean对象在创建和销毁时自动执行的方法:

    1. 定义生命周期方法
    public class StudentDaoImpl2 implements
    StudentDao{
         // 创建时自动执行的方法
        public void init(){
            System.out.println("创建StudentDao!!!");
       }
        // 销毁时自动执行的方法
        public void destory(){
            System.out.println("销毁StudentDao!!!");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 配置生命周期方法
    
    <bean id="studentDao" class="com.gb.dao.StudentDaoImpl2" scope="singleton" init-method="init" destroy-method="destory">bean>
    
    • 1
    • 2
    • 3
    1. 测试
    @Test
    public void t3(){
        // 创建Spring容器
        ClassPathXmlApplicationContext ac =new ClassPathXmlApplicationContext("bean1.xml");
        // 销毁Spring容器,ClassPathXmlApplicationContext才有销毁容器的方法
        ac.close();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    IOC_获取Bean对象的方式

    通过id/name获取

    1. 配置文件
    <bean name="studentDao" class="com.gb.dao.StudentDaoImpl2">bean>
    <bean id="studentDao" class="com.gb.dao.StudentDaoImpl2">bean>
    
    • 1
    • 2
    1. 获取对象
    StudentDao studentDao = (StudentDao)ac.getBean("studentDao");
    
    • 1

    通过类型获取

    1. 配置文件
    <bean name="studentDao" class="com.gb.dao.StudentDaoImpl2">bean>
    
    • 1
    1. 获取对象
    StudentDao studentDao = (StudentDao)ac.getBean("StudentDao.class");
    
    • 1

    可以看到使用类型获取不需要强转。

    通过类型+id/name获取

    虽然使用类型获取不需要强转,但如果在容器中有一个接口的多个实现类对象,则获取时会报错,此时需要使用类型+id/name获取

    1. 配置文件
    <bean name="studentDao" class="com.gb.dao.StudentDaoImpl2">bean>
    <bean name="studentDao1" class="com.gb.dao.StudentDaoImpl">bean>
    
    • 1
    • 2
    1. 获取对象
    StudentDao studentDao2 = ac.getBean("studentDao",StudentDao.class);
    
    • 1

    DI_什么是依赖注入

    依赖注入(Dependency Injection,简称DI),它是Spring控制反转思想的具体实现。
    控制反转将对象的创建交给了Spring,但是对象中可能会依赖其他对象。比如service类中要有dao类的属性,我们称service依赖于dao。
    简单来说,控制反转是创建对象,依赖注入是为对象的属性赋值。

    DI_依赖注入方式

    Setter注入

    1 被注入类编写属性的setter方法

    public class StudentService {
        private StudentDao studentDao;
        public void setStudentDao(StudentDao studentDao) {
            this.studentDao = studentDao;
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 配置文件中,给需要注入属性值的 中设置
    <bean id="studentDao" class="com.gb.dao.StudentDaoImpl">bean>
    <bean id="studentService" class="com.gb.service.StudentService">
        
        
        <property name="studentDao" ref="studentDao">property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    构造方法注入

    1. 被注入类编写有参的构造方法
    public class StudentService {
        private StudentDao studentDao;
        public StudentService(StudentDao studentDao) {
            this.studentDao = studentDao;
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 给需要注入属性值的 中设置
    <bean id="studentDao" class="com.gb.dao.StudentDaoImpl">bean>
    <bean id="studentService" class="com.gb.service.StudentService">
        
        
        <constructor-arg name="studentDao" ref="studentDao">constructor-arg>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    自动注入

    自动注入不需要在 标签中添加其他标签注入属性值,而是自动从容器中找到相应的bean对象设置为属性值。

    自动注入有两种配置方式:

    • 全局配置:在 中设置 default-autowire 属性可以定义所有bean对象的自动注入策略。
    • 局部配置:在 中设置 autowire 属性可以定义当前bean对象的自动注入策略。

    autowire的取值如下:

    • no:不会进行自动注入。
    • default:全局配置default相当于no,局部配置default表示使用全局配置
    • byName:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供set方法。
    • byType:在Spring容器中查找类型与属性类型相同的bean,并进行注入。需要提供set方法。
    • constructor:在Spring容器中查找id与属性名相同的bean,并进行注入。需要提供构造方法。

    测试自动注入:

    1. 为依赖的对象提供setter和构造方法:
    public class StudentService {
        // 依赖
        private StudentDao studentDao;
        // 构造方法
        public StudentService() {}
        
        public StudentService(StudentDao studentDao) {
            this.studentDao = studentDao;
       }
        // setter方法
        public void setStudentDao(StudentDao studentDao) {
            this.studentDao = studentDao;
       }
        // 调用依赖的方法
        public Student findStudentById(int id){
            return studentDao.findById(id);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    1. 配置自动注入:
    
    <bean id="studentDao" class="com.gb.dao.StudentDaoImpl">bean>
    <bean id="studentService"class="com.gb.service.StudentService" autowire="byName">bean>
    
    • 1
    • 2
    • 3
    
    <bean id="studentDao" class="com.gb.dao.StudentDaoImpl">bean>
    <bean id="studentService" class="com.gb.service.StudentService" autowire="byType">bean>
    
    • 1
    • 2
    • 3
    
    <bean id="studentDao" class="com.gb.dao.StudentDaoImpl">bean>
    <bean id="studentService" class="com.gb.service.StudentService" autowire="constructor">bean>
    
    • 1
    • 2
    • 3
    
    <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd"
          default-autowire="constructor">
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    DI_依赖注入类型

    在这里插入图片描述
    DI支持注入bean类型、基本数据类型和字符串、List集合、Set集合、Map集合、Properties对象类型等,他们的写法如下:

    1. 准备注入属性的类
    public class StudentService {
        private StudentDao studentDao; // bean属性
        private String name; //字符串类型
        private int count; //基本数据类型
        private List<String> names; // 字符串类型List集合
        private List<Student> students1; // 对象类型List集合
        private Set<Student> students2; // 对象类型Set集合
        private Map<String,String> names2; //字符串类型Map集合
        private Map<String,Student> students3;
    	// 对象类型Map集合
        private Properties properties;
    	//Properties类型
        // 省略getter/setter/toString
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    注入bean类型

    写法一:

    <bean id="studentDao" class="com.gb.dao.StudentDaoImpl">bean>
    <bean id="studentService" class="com.gb.service.StudentService">
        <property name="studentDao" ref="studentDao">property>
    bean>
    
    • 1
    • 2
    • 3
    • 4

    写法二:

    <bean id="studentDao" class="com.itbaizhan.dao.StudentDaoImpl">bean>
    <bean id="studentService" class="com.itbaizhan.service.StudentService">
        <property name="studentDao" >
            <ref bean="studentDao">ref>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注入基本数据类型

    <bean id="studentService"
    class="com.itbaizhan.service.StudentService">
        
        <property name="name" value="程序员">property>
        
        <property name="count">
            <value>10value>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    注入List集合

    <bean id="studentService" class="com.gb.service.StudentService">
        
        <property name="names">
            <list>
                <value>程序员value>
                <value>悍匪value>
            list>
        property>
        
        <property name="students1">
            <list>
                <bean class="com.gb.domain.Student">
                    <property name="id" value="1"/>
                    <property name="name" value="Java"/>
                    <property name="address" value="北京"/>
    			bean>
                <bean class="com.gb.domain.Student">
                    <property name="id" value="2"/>
                    <property name="name" value="大数据"/>
                    <property name="address" value="北京"/>
                bean>
            list>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    注入Set集合

    <bean id="studentService" class="com.itbaizhan.service.StudentService">
        
        <property name="students2">
            <set>
                <bean class="com.itbaizhan.domain.Student">
                    <property name="id" value="1"/>
                    <property name="name" value="悍匪"/>
                    <property name="address" value="北京"/>
                bean>
                <bean class="com.itbaizhan.domain.Student">
                    <property name="id" value="2"/>
                    <property name="name" value="程序员"/>
                    <property name="address"value="北京"/>
                bean>
            set>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注入Map集合

    简单数据类型Map集合:

    <bean id="studentService" class="com.gb.service.StudentService">
        <property name="names2">
            <map>
                <entry key="student1" value="bz"/>
                <entry key="student2" value="sxt"/>
            map>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    对象类型Map集合:

    <bean id="studentService" class="com.gb.service.StudentService">
        <property name="students3">
            <map>
                <entry key="student1" value-ref="s1"/>
                <entry key="student2" valueref="s2"/>
            map>
        property>
    bean>
    <bean id="s1"class="com.gb.domain.Student">
        <property name="id" value="1"/>
        <property name="name" value="尚学堂"/>
        <property name="address" value="北京"/>
    bean>
    <bean id="s2" class="com.gb.domain.Student">
        <property name="id" value="2"/>
        <property name="name" value="百战"/>
        <property name="address" value="北京"/>
    bean>            
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    注入Properties对象

    <bean id="studentService" class="com.gb.service.StudentService">
        <property name="properties">
            <props>
                <prop key="配置1">值1prop>
                <prop key="配置2">值2prop>
            props>
        property>
    bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注解实现IOC

    准备工作

    1. 创建一个新的Spring项目。
    2. 编写pojo,dao,service类。
    3. 编写空的配置文件,如果想让该文件支持注解,需要添加新的约束:
    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/context
                  http://www.springframework.org/schema/context/spring-context.xsd">
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    @Component注解

    作用:用于创建对象,放入Spring容器,相当于
    位置:类上方
    注意:

    • 要在配置文件中配置扫描的包,扫描到该注解才能生效。
     
     <context:component-scan base-package="com.gb">context:component-scan>
    
    • 1
    • 2

    @Component 注解配置bean的默认id是首字母小写的类名。也可以手动设置bean的id值。

    // 此时bean的id为studentDaoImpl
    @Component
    public class StudentDaoImpl implements StudentDao{
        public Student findById(int id) {
            // 模拟根据id查询学生
            return new Student(1,"程序员","北京");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    // 此时bean的id为studentDao
    @Component("studentDao")
    public class StudentDaoImpl implements StudentDao{
        public Student findById(int id) {
            // 模拟根据id查询学生
            return new Student(1,"程序员","北京");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    @Repository、@Service、@Controller

    作用:这三个注解和@Component的作用一样,使用它们是为了区分该类属于什么层。

    位置:

    • @Repository用于Dao层
    • @Service用于Service层
    • @Controller用于Controller层

    @Scope

    作用:指定bean的创建策略
    位置:类上方
    取值:singleton prototype request session globalsession

    @Service
    @Scope("singleton")
    public class StudentService {}
    
    • 1
    • 2
    • 3

    @Autowired

    作用:从容器中查找符合属性类型的对象自动注入属性中。用于代替 中的依赖注入配置。
    位置:属性上方、setter方法上方、构造方法上方。
    注意:

    1. @Autowired 写在属性上方进行依赖注入时,可以省略setter方法。
    2. 容器中没有对应类型的对象会报错
    3. 容器中有多个对象匹配类型时,会找beanId等于属性名的对象,找不到会报错。

    @Qualifier

    作用:在按照类型注入对象的基础上,再按照bean的id注入。
    位置:属性上方
    注意:@Qualifier必须和@Autowired一起使用。

    @Value

    作用:注入String类型和基本数据类型的属性值。
    位置:属性上方

    @Configuration

    纯注解实现IOC需要一个Java类代替xml文件。这个Java类上方需要添加@Configuration,表示该类是一个配置类,作用是代替配置文件。

    @Configuration
    public class SpringConfig {  
    }
    
    • 1
    • 2
    • 3

    @ComponentScan

    作用:指定spring在初始化容器时扫描的包。
    位置:配置类上方

    @Configuration
    @ComponentScan("com.gb")
    public class SpringConfig {
    }
    
    • 1
    • 2
    • 3
    • 4

    @PropertySource

    作用:代替配置文件中的 扫描配置文件
    位置:配置类上方
    注意:配置文件位置前要加关键字 classpath

    @Configuration
    @PropertySource("classpath:db.properties")
    public class JdbcConfig {
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    @Bean

    作用:将方法的返回值对象放入Spring容器中。如果想将第三方类的对象放入容器,可以使用@Bean
    位置:配置类的方法上方。
    属性:name:给bean对象设置id
    注意:@Bean修饰的方法如果有参数,spring会根据参数类型从容器中查找可用对象。

    @Import

    作用:如果配置过多,会有多个配置类,该注解可以为主配置类导入其他配置类
    位置:主配置类上方

    SpringAOP_AOP简介

    在这里插入图片描述
    AOP的全称是Aspect Oriented Programming,即面向切面编程。是实现功能统一维护的一种技术,它将业务逻辑的各个部分进行隔
    离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率。

    • 作用:在不修改源码的基础上,对已有方法进行增强。
    • 实现原理:动态代理技术。
    • 优势:减少重复代码、提高开发效率、维护方便
    • 应用场景:事务处理、日志管理、权限控制、异常处理等方面。

    SpringAOP_AOP相关术语

    名称说明
    Joinpoint(连接点) 指能被拦截到的点,在Spring中只有方法能被拦截。
    Pointcut(切点) 指要对哪些连接点进行拦截,即被增强的方法。
    Advice(通知) 指拦截后要做的事情,即切点被拦截后执行的方法。
    Aspect(切面) 切点+通知称为切面
    Target(目标) 被代理的对象
    Proxy(代理) 代理对象
    Weaving(织入) 生成代理对象的过程

    SpringAOP_AOP入门

    AspectJ是一个基于Java语言的AOP框架,在Spring框架中建议使用AspectJ实现AOP。
    接下来我们写一个AOP入门案例:dao层的每个方法结束后都可以打印一条日志:

    1. 引入依赖
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>springcontextartifactId>
        <version>5.3.13version>
    dependency>
    
    <dependency>
        <groupId>org.aspectjgroupId>
        <artifactId>aspectjweaverartifactId>
        <version>1.8.7version>
    dependency>
    
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
        <scope>testscope>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 编写连接点
    @Repository
    public class UserDao {
        public void add(){
            System.out.println("用户新增");
       }
        public void delete(){
            System.out.println("用户删除");
       }
        public void update(){
            System.out.println("用户修改");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 编写通知类
    public class MyAspectJAdvice {
        // 后置通知
        public void myAfterReturning() {
            System.out.println("打印日志...");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 配置切面
    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           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
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx 		http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop 	https://www.springframework.org/schema/aop/spring-aop.xsd">
    
    	<context:component-scan base-package="com.gb">context:component-scan>
        
        
        <bean id="myAspectJAdvice" class="com.gb.advice.MyAspectAdvice">bean>
        
        
        <aop:config>
        
            <aop:aspect ref="myAspectJAdvice">
                
                <aop:pointcut id="myPointcut" expression="execution(* com.gb.dao.UserDao.*(..))"/>
                
                <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"/>
            aop:aspect>
        aop:config>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    SpringAOP_通知类型

    AOP有以下几种常用的通知类型:

    通知类型描述
    前置通知在方法执行前添加功能
    后置通知在方法正常执行后添加功能
    异常通知在方法抛出异常后添加功能
    最终通知无论方法是否抛出异常,都会执行该通知
    环绕通知在方法执行前后添加功能
    1. 编写通知方法
    // 通知类
    public class MyAspectAdvice {
        // 后置通知
        public void myAfterReturning(JoinPoint joinPoint) {
            System.out.println("切点方法名:" + joinPoint.getSignature().getName());
            System.out.println("目标对象:" + joinPoint.getTarget());
            System.out.println("打印日志" + joinPoint.getSignature().getName() + "方法被执行了!");
       }
        // 前置通知
        public void myBefore() {
            System.out.println("前置通知...");
       }
        // 异常通知
        public void myAfterThrowing(Exception ex) {
            System.out.println("异常通知...");
            System.err.println(ex.getMessage());
       }
        // 最终通知
        public void myAfter() {
            System.out.println("最终通知");
       }
        // 环绕通知
        public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕前");
            Object obj = proceedingJoinPoint.proceed(); // 执行方法
            System.out.println("环绕后");
            return obj;
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    1. 配置切面
    
    <aop:config>
        
        <aop:aspect ref="myAspectJAdvice">
             
            <aop:pointcut id="myPointcut" expression="execution(* com.gb.dao.UserDao.*(..))"/>
            
    		<aop:before method="myBefore" pointcut-ref="myPointcut">aop:before>
            
            <aop:after-returning method="myAfterReturning" pointcutref="myPointcut"/>
            
            <aop:after-throwing method="myAfterThrowing" pointcutref="myPointcut" throwing="ex"/>
            
            <aop:after method="myAfter" pointcut-ref="myPointcut">aop:after>
            
            <aop:around method="myAround" pointcut-ref="myPointcut">aop:around>
        aop:aspect>
    aop:config>    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    SpringAOP_切点表达式

    使用AspectJ需要使用切点表达式配置切点位置,写法如下:

    • 标准写法:访问修饰符 返回值 包名.类名.方法名(参数列表)

    • 访问修饰符可以省略。

    • 返回值使用 * 代表任意类型。

    • 包名使用 * 表示任意包,多级包结构要写多个 * ,使用 *… 表示任意包结构

    • 类名和方法名都可以用 * 实现通配。

    • 参数列表

      • 基本数据类型直接写类型
      • 引用类型写 包名.类名
      • * 表示匹配一个任意类型参数
      • … 表示匹配任意类型任意个数的参数
    • 全通配: * *…*.*(…)

    SpringAOP_多切面配置

    在这里插入图片描述

    我们可以为切点配置多个通知,形成多切面,比如希望dao层的每个方法结束后都可以打印日志并发送邮件:

    1. 编写发送邮件的通知:
    public class MyAspectJAdvice2 {
      // 后置通知
      public void myAfterReturning(JoinPoint joinPoint) {
        System.out.println("发送邮件");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 配置切面:
    
    <bean id="myAspectJAdvice" class="com.itbaizhan.advice.MyAspectAdvice">bean>
    <bean id="myAspectJAdvice2" class="com.itbaizhan.advice.MyAspectAdvice2">bean>
    
    <aop:config>
        
        <aop:aspect ref="myAspectJAdvice">
            
            <aop:pointcut id="myPointcut" expression="execution(* *..*.*(..))"/>
            
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut"/>
        aop:aspect>
        
        <aop:aspect ref="myAspectJAdvice2">
            <aop:pointcut id="myPointcut2" expression="execution(* com.gb.dao.UserDao.*(..))"/>
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut2"/>
        aop:aspect>
    aop:config>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    SpringAOP_注解配置AOP

    Spring可以使用注解代替配置文件配置切面:

    1. 在xml中开启AOP注解支持
       
        <aop:aspectj-autoproxy>aop:aspectj-autoproxy>
    
    • 1
    • 2
    1. 在通知类上方加入注解 @Aspect
    2. 在通知方法上方加入注解
      @Before/@AfterReturning/@AfterThrowing/@After/@Around
    @Aspect
    @Component
    public class MyAspectJAdvice {
        @Pointcut("execution(* com.gb.dao.UserDao.*(..))")
        public void point(){}
        // 后置通知
        @AfterReturning("point()")
        public void myAfterReturning(JoinPoint joinPoint) {
            System.out.println("切点方法名:" + joinPoint.getSignature().getName());
            System.out.println("目标对象:" + joinPoint.getTarget());
            System.out.println("打印日志" + joinPoint.getSignature().getName() + "方法被执行了!");
        }
    
        @Before("point()")
        // 前置通知
        public void myBefore() {
            System.out.println("前置通知...");
        }
    
        @AfterThrowing(value = "point()",throwing = "ex")
        // 异常通知
        public void myAfterThrowing(Exception ex) {
            System.out.println("异常通知...");
            System.err.println(ex.getMessage());
        }
    
        @After("point()")
        // 最终通知
        public void myAfter() {
            System.out.println("最终通知");
        }
    
        @Around("point()")
        // 环绕通知
        public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕前");
            Object obj = proceedingJoinPoint.proceed(); // 执行方法
            System.out.println("环绕后");
            return obj;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    SpringAOP_原生Spring实现AOP

    除了AspectJ,Spring支持原生方式实现AOP。

    1. 引入依赖
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-aopartifactId>
        <version>5.3.13version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 编写通知类
    package com.gb.advice;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.AfterReturningAdvice;
    import org.springframework.aop.MethodBeforeAdvice;
    import org.springframework.aop.ThrowsAdvice;
    
    import java.lang.reflect.Method;
    
    public class SpringAop implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice, MethodInterceptor {
      /**
       * 前置通知
       * @param method 目标方法
       * @param args 目标方法的参数列表
       * @param target 目标对象
       * @throws Throwable
       */
      @Override
      public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置通知");
       }
    
    
      /**
       * 后置通知
       * @param returnValue 目标方法的返回值
       * @param method 目标方法
       * @param args 目标方法的参数列表
       * @param target 目标对象
       * @throws Throwable
       */
      @Override
      public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置通知");
       }
    
    
      /**
       * 环绕通知
       * @param invocation 目标方法
       * @return
       * @throws Throwable
       */
      @Override
      public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前");
        Object proceed = invocation.proceed();
        System.out.println("环绕后");
        return proceed;
       }
    
      /**
       * 异常通知
       * @param ex 异常对象
       */
      public void afterThrowing(Exception ex){
        System.out.println("发生异常了!");
       }
    }
    Spring原生方式实现AOP时,只支持四种通知类型:
    | 通知类型 | 实现接口 |
    |:--|:--|  
    前置通知 | MethodBeforeAdvice
    后置通知 |AfterReturningAdvice
    异常通知 |ThrowsAdvice
    环绕通知 |MethodInterceptor
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    1. 编写配置类
    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           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
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        
        <context:component-scan base-package="com.gb">context:component-scan>
    
        <bean id="springAop" class="com.gb.advice.SpringAop">bean>
    
        
        <aop:aspectj-autoproxy proxy-target-class="true">aop:aspectj-autoproxy>
    
        
        <bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
            
            <property name="target" ref="userDao">property>
            
            <property name="interceptorNames">
                <list>
                    <value>springAopvalue>
                list>
            property>
            
            <property name="proxyTargetClass" value="true">property>
        bean>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    1. 编写测试类
    public class UserDaoTest2 {
        @Test
        public void testAdd(){
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
            UserDao userDao = (UserDao)ac.getBean("userDaoProxy"); // 获取的是代理对象
            userDao.update();
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    SpringAOP_SchemaBased实现AOP

    SchemaBased(基础模式)配置方式是指使用Spring原生方式定义通知,而使用AspectJ框架配置切面。

    1. 编写通知类
    // Spring原生Aop的通知类
    public class SpringAop2 implements MethodBeforeAdvice, AfterReturningAdvice, ThrowsAdvice, MethodInterceptor {
      /**
       * 前置通知
       * @param method 目标方法
       * @param args 目标方法的参数列表
       * @param target 目标对象
       * @throws Throwable
       */
      @Override
      public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("前置通知");
       }
    
      /**
       * 后置通知
       * @param returnValue 目标方法的返回值
       * @param method 目标方法
       * @param args 目标方法的参数列表
       * @param target 目标对象
       * @throws Throwable
       */
      @Override
      public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置通知");
       }
    
    
      /**
       * 环绕通知
       * @param invocation 目标方法
       * @return
       * @throws Throwable
       */
      @Override
      public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前");
        Object proceed = invocation.proceed();
        System.out.println("环绕后");
        return proceed;
       }
    
      /**
       * 异常通知
       * @param ex 异常对象
       */
      public void afterThrowing(Exception ex){
        System.out.println("发生异常了!");
       }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    1. 配置切面
    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           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
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    
        
        <context:component-scan base-package="com.gb">context:component-scan>
    
        <bean id="springAop2" class="com.gb.advice.SpringAop2">bean>
    
        
        <aop:config>
            
            <aop:pointcut id="myPointcut" expression="execution(* com.gb.dao.UserDao.*(..))"/>
            
            <aop:advisor advice-ref="springAop2" pointcut-ref="myPointcut"/>
        aop:config>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    1. 测试
    public class UserDaoTest3 {
        @Test
        public void add() {
            ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext4.xml");
            UserDao userDao = (UserDao) ac.getBean("userDao");
            userDao.add();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Spring整合MyBatis

    我们知道使用MyBatis时需要写大量创建SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession等对象的代码,而Spring的作用是帮我们创建和管理对象,所以我们可以使用Spring整合MyBatis,简化MyBatis开发。

    搭配环境

    <dependencies>
            
            <dependency>
                <groupId>org.mybatisgroupId>
                <artifactId>mybatisartifactId>
                <version>3.5.7version>
            dependency>
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <version>8.0.26version>
            dependency>
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>druidartifactId>
                <version>1.1.21version>
            dependency>
            
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-contextartifactId>
                <version>5.3.13version>
            dependency>
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-txartifactId>
                <version>5.3.13version>
            dependency>
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-jdbcartifactId>
                <version>5.3.13version>
            dependency>
            
            <dependency>
                <groupId>org.mybatisgroupId>
                <artifactId>mybatis-springartifactId>
                <version>2.0.6version>
            dependency>
    
            
            <dependency>
                <groupId>junitgroupId>
                <artifactId>junitartifactId>
                <version>4.12version>
                <scope>testscope>
            dependency>
            
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-testartifactId>
                <version>5.3.13version>
            dependency>
        dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    编写配置文件

    编写数据库配置文件db.properties

    jdbc.driverClassName=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql:///student
    jdbc.username=root
    jdbc.password=521314
    
    • 1
    • 2
    • 3
    • 4

    创建MyBatis配置文件SqlMapConfig.xml,数据源、扫描接口都交由Spring管理,不需要在MyBatis配置文件中设置。

    
    DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    configuration>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    创建Spring配置文件applicationContext.xml

    
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd">
    
        
        <context:component-scan base-package="com.gb">context:component-scan>
    
    
        
        <context:property-placeholder location="classpath:db.properties">context:property-placeholder>
    
        
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}">property>
            <property name="url" value="${jdbc.url}">property>
            <property name="username" value="${jdbc.username}">property>
            <property name="password" value="${jdbc.password}">property>
        bean>
    
        
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource">property>
        bean>
    
        
    
    
    
    
        <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.gb.dao">property>
        bean>
    beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    编写持久层接口和service类

    @Repository
    public interface StudentDao {
        // 查询所有学生
        @Select("select * from student")
        List<Student> findAll();
        // 添加学生
        @Insert("insert into student values(null,#{name},#{sex},#{address})")
        void add(Student student);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    @Service
    public class StudentService {
        // SqlSession对象
        @Autowired
        private SqlSessionTemplate sqlSession;
        // 使用SqlSession获取代理对象
        public List<Student> findAllStudent(){
            StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
            return studentDao.findAll();
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    整合Junit进行单元测试

    引入Junit和Spring整合Junit依赖

    
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
        <scope>testscope>
    dependency>
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-testartifactId>
        <version>5.3.13version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    编写测试类

    // JUnit使用Spring方式运行代码,即自动创建spring容器。
    @RunWith(SpringJUnit4ClassRunner.class)
    // 告知创建spring容器时读取哪个配置类或配置文件
    // 配置类写法:@ContextConfiguration(classes=配置类.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    public class StudentServiceTest {
    
        @Autowired
        private StudentService studentService;
    
        @Test
        public void t1(){
            List<Student> studentList = studentService.findStudenAll();
            studentList.forEach(System.out::println);
        }
        @Test
        public void t2(){
            Student student = new Student("龙傲天","男","荒芜大陆");
            studentService.addStudent(student);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    自动创建代理对象

    Spring提供了MapperScannerConfigurer对象,该对象可以自动扫描包创建代理对象,并将代理对象放入容器中,此时不需要使用SqlSession手动创建代理对象。

    1. 创建MapperScannerConfigurer对象
     	 <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.gb.dao">property>
        bean>
    
    • 1
    • 2
    • 3
    1. Service类直接使用代理对象即可
    @Service
    public class StudentService {
        // 直接注入代理对象
        @Autowired
        private StudentDao studentDao;
      
        // 直接使用代理对象
        public void addStudent(Student student){
            studentDao.add(student);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Spring事务_事务简介

    事务:不可分割的原子操作。即一系列的操作要么同时成功,要么同时失败。

    开发过程中,事务管理一般在service层,service层中可能会操作多次数据库,这些操作是不可分割的。否则当程序报错时,可能会造成数据异常。

    Spring事务_Spring事务管理方案

    在Spring框架中提供了两种事务管理方案:

    1. 编程式事务:通过编写代码实现事务管理。
    2. 声明式事务:基于AOP技术实现事务管理。

    在Spring框架中,编程式事务管理很少使用,Spring的声明式事务管理在底层采用了AOP技术,其最大的优点在于无需通过编程的方式管理事务,只需要在配置文件中进行相关的规则声明,就可以将事务规则应用到业务逻辑中。

    使用AOP技术为service方法添加如下通知:
    在这里插入图片描述

    Spring事务_Spring事务管理器

    Spring依赖事务管理器进行事务管理,事务管理器即一个通知类,我们为该通知类设置切点为service层方法即可完成事务自动管理。
    由于不同技术操作数据库,进行事务操作的方法不同。如:JDBC提交事务是 connection.commit() ,MyBatis提交事务是 sqlSession.commit() ,所以Spring提供了多个事务管理器。

    事务管理器名称作用
    org.springframework.jdbc.datasource.DataSourceTransactionManager针对JDBC技术提供的事务管理器。适用于JDBC和MyBatis。
    org.springframework.orm.hibernate3.HibernateTransactionManager针对于Hibernate框架提供的事务管理器。适用于Hibernate框架。
    org.springframework.orm.jpa.JpaTransactionManager针对于JPA技术提供的事务管理器。适用于JPA技术。
    org.springframework.transaction.jta.JtaTransactionManager跨越了多个事务管理源。适用在两个或者是多个不同的数据源中实现事务控制。

    我们使用MyBatis操作数据库,接下来使用 DataSourceTransactionManager 进行事务管理。

    1. 引入依赖
    
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-txartifactId>
        <version>5.3.13version>
    dependency>
    
    <dependency>
        <groupId>org.aspectjgroupId>
        <artifactId>aspectjweaverartifactId>
        <version>1.8.7version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 引入约束
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 进行事务配置
        
        <tx:advice id="txAdvice">
            <tx:attributes>
                
                <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT"/>
                
                <tx:method name="find*" read-only="true" isolation="READ_UNCOMMITTED">tx:method>
            tx:attributes>
        tx:advice>
    
        
        <aop:config>
            <aop:pointcut id="myPoint" expression="execution(* com.gb.service.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint">aop:advisor>
        aop:config>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Spring事务_事务控制的API

    Spring进行事务控制的功能是由三个接口提供的,这三个接口是Spring实现的,在开发中我们很少使用到,只需要了解他们的作用即可:

    PlatformTransactionManager接口

    PlatformTransactionManager是Spring提供的事务管理器接口,所有事务管理器都实现了该接口。该接口中提供了三个事务操作方法:

    • TransactionStatus getTransaction(TransactionDefinition definition):获取事务状态信息。
    • void commit(TransactionStatus status):事务提交
    • void rollback(TransactionStatus status):事务回滚

    TransactionDefinition接口

    TransactionDefinition是事务的定义信息对象,它有如下方法:

    • String getName():获取事务对象名称。
    • int getIsolationLevel():获取事务的隔离级别。
    • int getPropagationBehavior():获取事务的传播行为。
    • int getTimeout():获取事务的超时时间。
    • boolean isReadOnly():获取事务是否只读。

    TransactionStatus接口

    TransactionStatus是事务的状态接口,它描述了某一时间点上事务的状态信息。它有如下方法:

    • void flush() 刷新事务
    • boolean hasSavepoint() 获取是否存在保存点
    • boolean isCompleted() 获取事务是否完成
    • boolean isNewTransaction() 获取是否是新事务
    • boolean isRollbackOnly() 获取是否回滚
    • void setRollbackOnly() 设置事务回滚

    Spring事务_事务的相关配置

    中可以进行事务的相关配置:

    <tx:advice id="txAdvice">
        <tx:attributes>
            <tx:method name="*"/>
            <tx:method name="find*" read-only="true"/>
        tx:attributes>
    tx:advice>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    中的属性:

    • name:指定配置的方法。 * 表示所有方法, find* 表示所有以find开头的方法。
    • read-only:是否是只读事务,只读事务不存在数据的修改,数据库将会为只读事务提供一些优化手段,会对性能有一定提升,建议在查询中开启只读事务。
    • timeout:指定超时时间,在限定的时间内不能完成所有操作就会抛异常。默认永不超时
    • rollback-for:指定某个异常事务回滚,其他异常不回滚。默认所有异常回滚。
    • no-rollback-for:指定某个异常不回滚,其他异常回滚。默认所有异常回滚。
    • propagation:事务的传播行为
    • isolation:事务的隔离级别

    Spring事务_事务的传播行为

    事务传播行为是指多个含有事务的方法相互调用时,事务如何在这些方法间传播。

    如果在service层的方法中调用了其他的service方法,假设每次执行service方法都要开启事务,此时就无法保证外层方法和内层方法处于同一个事务当中。

    // method1的所有方法在同一个事务中
    public void method1(){
        // 此时会开启一个新事务,这就无法保证method1()中所有的代码是在同一个事务中
        method2();
        System.out.println("method1");
    }
    public void method2(){
        System.out.println("method2");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    事务的传播特性就是解决这个问题的,Spring帮助我们将外层方法和内层方法放入同一事务中。

    传播行为介绍
    REQUIRED默认。支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
    SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
    MANDATORY支持当前事务,如果当前没有事务,就抛出异常。
    REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
    NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
    NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
    NESTED必须在事务状态下执行,如果没有事务则新建事务,如果当前有事务则创建一个嵌套事务

    Spring事务_事务的隔离级别

    事务隔离级别反映事务提交并发访问时的处理态度,隔离级别越高,数据出问题的可能性越低,但效率也会越低。

    隔离级别脏读不可重复读幻读
    READ_UNCOMMITED(读取未提交内容)YesYes
    READ_COMMITED(读取提交内容)NoYes
    REPEATABLE_READ(重复读)NoNo
    SERIALIZABLE(可串行化)NoNo

    如果设置为DEFAULT会使用数据库的隔离级别。

    • SqlServer , Oracle默认的事务隔离级别是READ_COMMITED
    • Mysql的默认隔离级别是REPEATABLE_READ

    Spring事务_注解配置声明式事务

    1. 注册事务注解驱动
    
    <tx:annotation-driven transactionmanager="transactionManager">tx:annotation-driven>
    
    • 1
    • 2
    1. 在需要事务支持的方法或类上加@Transactional
    2. 3 配置类代替xml中的注解事务支持:在配置类上方写@EnableTranscationManagement
  • 相关阅读:
    BM23 二叉树的前序遍历
    Istio1.12:安装和快速入门
    基于5G边缘网关的储能在线监测方案
    【看表情包学Linux】软硬链接 | 软连接数 | 创建软硬链接 | 动静态库 | 生成静态库 | 生成动态库
    【RT-Thread】nxp rt10xx 设备驱动框架之--adc搭建和使用
    【漏洞复现】金和OA FileUploadMessage 文件读取
    LeetCode-878-第N个神奇数字
    Consumer位移管理-Kafka从入门到精通(十一)
    Qt编写物联网管理平台50-超强跨平台
    mysql里的主从复制
  • 原文地址:https://blog.csdn.net/weixin_65180200/article/details/133763334