• SSM学习笔记之Spring, SpringIoC, 注解, SpringAOP, Spring整合MyBatis


    0x00 概述

    本文主要记录SSM框架中Spring的知识点,包括Spring,SpringIoC,注解,代理模式,SpringAOP,SpringAOP注解,Spring整合MyBatis。

    0x01 Spring概述

    1.1 Web项目开发中的耦合度问题

    • 在Servlet中需要调用service中的方法,则需要在Servlet类中通过new关键字创建Service的实例
    public interface ProductService{
            public List<Product> listProducts();
    }
    public class ProductServiceImpl1 implements ProductService{
            public List<Product> listProducts(){
                //查询热销商品
            }
    }
    public class ProductServiceImpl2 implements ProductService{
            public List<Product> listProducts(){
                //查询好评商品
            }
    }
    public class ProductListServlet extends HttpServlet{
        //在servlet中使用new关键字创建ProductServiceImpl1对象,增加了servlet和service的耦合度
        private ProductService productService = new ProductServiceImpl1();
        
        protected void doGet(HttpServletRequest request,HttpServletResponse response){
            doPost(request,response);
        }
        protected void doPost(HttpServletRequest request,HttpServletResponse response){
            productService.listProducts();
        }
    }
    •  在service实现类中需要调用DAO中的方法,也需要在service实现类通过new关键字创建DAO实现类对象
    • 如果在实现new关键字创建对象:
      • 失去了面向接口编程的灵活性
      • 代码的入侵性增强(在一个类中new另外一个,增加了耦合度),降低了代码的灵活性

    1.2 面向接口编程

    解决方法:在Servlet中定义Service接口的对象,不适用new关键字创建实现类对象,在servlet实例化的时候,通过反射动态的给Service接口的对象变量赋值。

    以上流程可以铜鼓Spring实现。

    1.3 Spring介绍

    Spring是一个“轻量级的控制反转和面向切面的容器”框架,用来解决企业项目开发的复杂问题--解耦;

    • 轻量级:体积小,对代码没有入侵性
    • 控制反转:IoC(Inverse of Control),把创建对象的工作交由Spring完成,Spring在创建对象的时候同时可以完成对象属性赋值(DI)
    • 面向切面:AOP(Aspect Oriented Programming)面向切面编程,可以在不改变原有业务逻辑的情况下实现对业务的增强
    • 容器:实例的容器,管理创建的对象

    1.4 Spring框架

    • Spring架构图 

    1.4.1 Core Container

    Spring容器组件,用于完成实例的创建和管理

    • core 核心
    • beans 实例管理
    • context 容器上下文

    1.4.2 AOP Aspects

    Spring AOP组件,实现面向切面编程

    • AOP
    • Aspects

    1.4.3 Web

    Spring web组件实际指的是SpringMVC框架,实现web项目的MVC控制

    • web (Spring对web项目的支持)
    • webmvc (SpringMVC)

    1.4.4 Data Access

    Spring数据访问组件,也是一个基于JDBC封装的持久层框架(即使没有mybatis,Spring也可以完成持久化的数据库操作)

    • tx

    1.4.5 Test

    Spring的单元测试组件,提供了Spring环境下的单元测试支持

    • test

    0x02 Spring IoC

    Spring IoC容器组件,可以完成对象的创建,对象属性赋值,对象管理

    2.1 Spring

    2.1.1 创建Maven工程

    • java
    • web

    2.1.2 添加SpringIoC依赖

    • core
    • beans
    • aop
    • expression
    • context
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.12.RELEASE</version>
    </dependency>

    2.1.3 创建Spring配置文件

    通过配置文件告诉Spring容器创建什么对象,给对象属性赋什么值

    • 在resource目录下创建名为applivationContext.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
          http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--对于一个xml文件如果作为框架的配置文件,需要遵守框架的配置规则-->
    <!--通常一个框架为了开发者能够正确的配置,都会提供xml的规范文件(dtd\xsd)-->
    <!--xsd 可以根据配置引入多个,只要引入了相应的xsd,可以使用相应标签规则属性-->
    
    
    </beans>

    2.2 SpringIoC使用

    使用SpringIoC组件创建并管理对象

    2.2.1 创建一个实体类

    public class Student {
    
        private String stuNum;
        private String stuName;
        private String stuGender;
        private int stuAge;
        private Date enterenceTime;//入学日期
    
    }

    2.2.2 在Spring配置文件中配置实体类

    • ClassPathXmlApplicationContext
    //通过Spring容器创建Student对象
    //1、初始化Spring容器,加载Spring配置文件
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    //2、通过Spring容器,获取Student对象
    Student student2 = (Student) context.getBean("stu");

    2.3 IoC和DI

    IoC(Inverse of Control)控制反转,依赖Spring对象工厂,完成对象的创建

    DI(Dependency Injection)依赖注入,在Spring完成对象创建的同时依赖Spring容器完成对象属性的赋值

    2.3.1 IoC

    通过Spring容器给创建的对象属性赋值

    <!--通过bean标签将实体类配置给Spring进行管理,id表示实体类的唯一标识(自己定义,一般是类名小写)-->
        <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="stuNum" value="10002"/>
            <property name="stuName" value="李四"/>
            <property name="stuGender" value="女"/>
            <property name="stuAge" value="18"/>
            <property name="enterenceTime" ref="date"/>
        </bean>

    2.4 DI依赖注入

    2.4.1 依赖注入的三种方式

    Spring容器加载配置文件之后,通过反射创建类的对象,并给属性赋值;

    Spring容器通过反射实现属性注入有三种方式:

    • set方法注入
    • 构造器注入
    • 接口注入

    2.4.2 set方法注入

    在bean标签中通过配置property标签给对象属性赋值,实际上就是通过反射调用set方法完成属性的注入

    • 简单类型以及字符串

        直接通过property标签的value属性赋值

    <bean id="stu" class="com.springtest.ioc.bean.Student">
        <!--简单类型-->
        <property name="stuNum" value="10001"/>
        <!--字符串类型--> 
        <property name="stuName" value="李四"/>
    </bean>
    • 日期对象

        方式1:在property标签中通过ref引用Spring容器中的一个对象

        <bean id = "date" class="java.util.Date"></bean>
       
        <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="enterenceTime" ref="date"/>
        </bean>

        方式2:在property标签中添加子标签bean来指定对象

        <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="enterenceTime">
                <bean class="java.util.Date"/>
            </property>
        </bean>
    • 自定义类对象属性

        方式1:

        <bean id="clazz" class="com.springtest.ioc.bean.Clazz">
            <property name="classId" value="2010"/>
            <property name="className" value="java2010"/>
        </bean>
        
        <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="clazz" ref="clazz"/>
        </bean>

        方式2:

        <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="clazz">
                <bean class="com.springtest.ioc.bean.Clazz">
                    <property name="classId" value="2010"/>
                    <property name="className" value="java2010"/>
                </bean>
            </property>
        </bean>

    集合类型

    • List

        1: List<String> List中的元素是字符串或者简单类型的封装类

        <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="hobbies" value="旅游,电影"/>
        </bean>
        --------------------或者下面这种引用方式也可以---------------------------------------
        <bean id="stu" class="com.springtest.ioc.bean.Student">
           <property name="hobbies">
               <list>
                   <value>旅游</value>
                   <value>电影</value>
                   <value>跑步</value>
               </list>
            </property>
        </bean>

        2: List<Object> List中的元素是对象类型

       <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="hobbies">
                <list>
                    <bean class="com.springtest.ioc.bean.Book"/>
                    <bean class="com.springtest.ioc.bean.Book"/>
                    <bean class="com.springtest.ioc.bean.Book"/>
                </list>
            </property>
        </bean>
     ------------------或者下面这种引用方式也可以-----------------------------------------------------
        <bean id="book" class="com.springtest.ioc.bean.Book"></bean>
        <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="hobbies">
                <list>
                    <ref bean="book"></ref>
                    <ref bean="book"></ref>
                    <ref bean="book"></ref>
                </list>
            </property>
        </bean>   
    • set
        Set的用法同list,只需要原来的list标签换成set标签,set注意去重
       <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="hobbies">
                <set>
                    <bean class="com.springtest.ioc.bean.Book"/>
                    <bean class="com.springtest.ioc.bean.Sport"/>
                    <bean class="com.springtest.ioc.bean.Game"/>
                </set>
            </property>
        </bean>
     ------------------或者下面这种引用方式也可以-----------------------------------------------------
        <bean id="book"  class="com.springtest.ioc.bean.Book"></bean>
        <bean id="sport" class="com.springtest.ioc.bean.Sport"></bean>
        <bean id="game"  class="com.springtest.ioc.bean.Game"></bean>
        <bean id="stu"   class="com.springtest.ioc.bean.Student">
            <property name="hobbies">
                <set>
                    <ref bean="book"></ref>
                    <ref bean="sport"></ref>
                    <ref bean="game"></ref>
                </set>
            </property>
        </bean>  
    •  Map

        键值对

        <bean id="stu" class="com.springtest.ioc.bean.Student">
            <property name="map">
                <map>
                    <entry>
                        <key>
                            <value>k1</value>
                        </key>
                        <value>123</value>
                    </entry>
                    <entry>
                        <key>
                            <value>k2</value>
                        </key>
                        <value>456</value>
                    </entry>
                </map>
            </property>
        </bean>
    • Properties
       <bean id="stu" class="com.springtest.ioc.bean.Student">
           <property name="properties">
               <props>
                   <prop key="key1">aaa</profp>
                   <prop key="key2">aaa</prop>    
               </props>
            </property>
        </bean>

    2.4.3 构造器注入

    • 简单类型,字符串,对象
    public class Student {
            private String stuNum;
            private String stuName;
            private String stuGender;
            private int stuAge;
            private Date date;
            private Clazz clazz;
        
           public Student(String stuNum, String stuName, String stuGender, int stuAge, Date date, Clazz clazz) {
            this.stuNum = stuNum;
            this.stuName = stuName;
            this.stuGender = stuGender;
            this.stuAge = stuAge;
            this.date = date;
            this.clazz = clazz;
        }
    }
        <bean id="date1" class="java.util.Date"></bean>
    
        <bean id="stu" class="com.springtest.ioc.bean.Student">
            <constructor-arg index="0" value="10001"/> <!--字符串类型-->
            <constructor-arg value="张三"/>
            <constructor-arg value="女"/>
            <constructor-arg value="21"/>  <!--简单类型-->
            <constructor-arg ref="date1"/>   <!--对象类型-->
            <constructor-arg >
                <bean class="com.spring.ioc.bean.Clazz"></bean>
            </constructor-arg>    <!--自定义类对象类型-->
    
        </bean>
    • 集合类型属性
    public class Student {
        private List<String> hobbies;
        private Set<String> sets;
        private Map<String,Object> maps;
        private Properties properties;
        
        public Student(List<String> hobbies, Set<String> sets, Map<String, Object> maps, Properties properties) {
            this.hobbies = hobbies;
            this.sets = sets;
            this.maps = maps;
            this.properties = properties;
        }
    }
    <bean id="stu2" class="com.springtest.ioc.bean.Student">
        <constructor-arg index="0">
            <list>
                <value>11</value>
                <value>13</value>
                <value>12</value>
            </list>
        </constructor-arg>
        <constructor-arg index="1">
            <set>
                <value>11</value>
                <value>13</value>
                <value>12</value>
            </set>
        </constructor-arg>
        <constructor-arg index="2">
            <map>
                <entry>
                    <key><value>1</value></key>
                    <value>value</value>
                </entry>
                <entry>
                    <key><value>2</value></key>
                    <value>value2</value>
                </entry>
            </map>
        </constructor-arg>
        <constructor-arg index="3">
            <props>
                <prop key="k1">value1</prop>
                <prop key="k2">value2</prop>
            </props>
        </constructor-arg>
    </bean>

    2.5 Bean的作用域

    /**
    
    在Bean标签中可以通过scope属性指定对象的作用域
    scope=“singleton”  表示当前bean是单例模式(默认饿汉模式;Spring容器初始化阶段就会完成此对象的创建;当在bean标签中设置lazy-init="true"  变为懒汉模式)
    scope="prototype"  表示当前bean是非单例模式,每次通过Spring容器获取此bean的对象时都会创建一个新的对象
    
    **/

    单例模式

    <bean id="book" class="com.springtest.ioc.bean.Book" scope="singleton" lazy-init="true"></bean>

    多例模式

    <bean id="book" class="com.springtest.ioc.bean.Book" scope="prototype" lazy-init="true"></bean>

    2.6 Bean的生命周期方法

    <!--在bean标签中可以通过 init-method 属性指定当前bean的初始化方法,初始化方法在构造器执行之后执行-->
        <!--在bean标签中可以通过 destroy-method 属性指定当前bean的销毁方法,销毁方法在对象销毁之前执行-->
    • Bean
    package com.springtest.ioc.bean;
    
    public class Book {
        private int bookId;
        private String bookName;
    
    
        //初始化方法:在创建当前类对象时调用的方法,进行一些资源准备工作
        public void init(){
            System.out.println("__________________init");
            this.bookId = 1;
            this.bookName = "初始值";
        }
        
        //销毁方法:在Spring容器销毁对象时调用此方法,进行一些资源回收性的操作
        public void destory(){
            System.out.println("__________destory");
        }
    
    }
    • 在Spring配置文件
        <bean id="book" class="com.springtest.ioc.bean.Book" scope="prototype"
              init-method="init" destroy-method="destory"></bean>

    2.7 自动装配

    <!--自动装配:Spring在实例化当前bean的时候从Spring容器中找到匹配的实例赋值给当前bean的属性-->
    <!--  autowire="byName" 根据当前Bean的属性名,在Spring容器中寻找匹配的对象,如果根据Name找到了bean,但是类型不匹配则会抛出异常。 -->
    <!--  autowire="byType" 根据当前Bean的属性类型,在spring容器中寻找匹配的对象,如果根据类型找到了多个类型匹配的bean,也会抛出异常-->
    <bean id="stu3" class="com.springtest.ioc.bean.Student" autowire="byName"></bean>

    2.8 SpringIoC工作原理

    0x03 SpringIoC--基于注解

    /**
    
    SpringIoC的使用,需要我们通过XML将类声明给Spring容器进行管理,从而通过Spring工厂完成对象的创建及属性值的注入;
    
    Spring除了提供基于XML的配置方式,同时提供了基于注解的配置;直接在实体类中添加注解声明Spring容器管理,以简化开发步骤。
    
    **/

    3.1 SpringIoC框架部署

    3.1.1 创建Maven项目

    3.1.2 添加SpringIoC依赖

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

    3.1.3 创建Spring配置文件

    因为Spring容器初始化时,只会加载applicationContext.xml文件,那么我们在实体类中添加的注解就不会被Spring扫描,所以我们需要在applicationContext.xml声明

    Spring的扫描范围,以达到Spring初始化时带有注解的实体类并完成初始化工作。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans
            xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd">
    
    
    <!--    声明使用注解配置-->
        <context:annotation-config/>
    <!--    声明Spring工厂注解的扫描范围-->
        <context:component-scan base-package=""/>
    
    
    </beans>

    3.2 IoC常用注解

    3.2.1 @Component("stu")

    • 类的注解,声明此类被Spring容器进行管理,相当于bean标签的作用
    • @Component(value="类的别名")value属性用于制定当前bean的id属性,或者叫别名。value属性也可以省略,如果省略,那么当前类的id默认为当前类名首字母改小写
    • @Service  @Controller  @Repository 这三个注解也可以将类声明给Spring管理,他们主要语义上的区别:
      • @Controller注解主要声明将控制器类配置给Spring管理,例如Servlet
      • @Service注解主要声明业务处理类配置给Spring管理,Service接口的实现类
      • @Repository注解主要声明持久化类配置给Spring管理,DAO接口
      • @Component除了控制器,Service,DAO之外的类一律使用此注解声明

    3.2.2 @Scope(value="prototype")

    • 类注解,用于声明当前类单例模式还是非单例模式,相当于bean标签的scope属性
    • @Scope("prototype")表示声明当前类为非单例模式(默认单例模式)

    3.2.3 @Lazy

    • 类注解,用于声明一个单例模式的bean是否为懒汉模式
    • @Lazy(true)表示声明为懒汉模式,默认为饿汉模式

    3.2.4 @PostConstruct

    • 方法注解,声明一个方法为当前类的初始化方法(在构造器之后执行),相当于bean标签的init-method属性

    3.2.5 @PreDestory

    方法注解,声明一个方法为当前类的销毁方法(在对象从容器中释放之前执行),相当于bean标签的destroy-method属性

    3.2.6 @Autowired

    • 属性注解,方法注解(set)声明当前属性自动装配,默认byType,默认必须(如果没有找到类型与属性类型匹配的bean则抛出异常)
    • @Autowired(required=false)通过required属性设置当前自动装配是否为必须(默认必须--如果没有找到类型与属性类型匹配的bean则抛出异常)
    • byType
    • ref引用
    • @Qualifier("a")通过id
    @Autowired(required = false)
    public void setClazz(@Qualifier("a") Clazz clazz) {
        this.clazz = clazz;
    }

    3.2.7 @Resource

    • 属性注解,也用于声明属性自动装配
    • 默认装配方式为byName,如果根据byName没有找到相应的bean,则继续根据byType寻找对应的bean,根据byType如果没找到bean,或者找到不止一个类型匹配的bean则抛出异常

    0x04 代理设计模式

    4.1 生活中的代理

     代理设计模式的优点:将通用性的工作交由代理对象完成,被代理对象只需专注自己的核心业务。

    4.2 静态代理

    静态代理,代理类只能够为特定的类生产代理对象,不能代理任意类

    使用代理的好处

    • 被代理类中只用关心核心业务实现,将通用的管理型逻辑(事物管理,日志管理)和业务逻辑分离
    • 将通用的代码放在代理类中实现,提供了代码的复用性
    • 通过在代理类添加业务逻辑,实现对原有业务逻辑扩展

    4.3 动态代理

    动态代理,几乎可以为所有的类产生代理对象;

    动态代理的实现方式有两种:

    • JDK动态代理
    • CGLib动态代理

    4.3.1 JDK动态代理

    package com.springtest.dao;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * JDK动态代理:是通过被代理对象实现的接口产生其代理对象
     * 1:创建一个类,实现InvocationHandler接口,重写invoke方法
     * 2:在类中定义一个Object类型的变量,用于传递被代理对象,并提供这个变量的有参构造器,用于将被代理对象传递进来
     * 3:创建getProxy方法,用于创建并返回代理对象
     * */
    public class JDKMyDynamicProxy implements InvocationHandler {
    
        //被代理对象
        private Object obj;
        public JDKMyDynamicProxy(Object obj) {
            this.obj = obj;
        }
    
        //产生代理对象,返回代理对象
        public Object getProxy(){
            //1、获取被代理对象的类加载器
            ClassLoader classLoader = obj.getClass().getClassLoader();
            //2、获取被代理对象的类所实现的接口
            Class<?>[] interfaces = obj.getClass().getInterfaces();
            //3、产生代理对象(通过被代理对象的类加载器及实现的接口)
            //第一个参数:被代理对象的类加载器
            //第二个参数:被代理对象实现的接口
            //第三个参数:使用产生的代理对象调用方法时,用于拦截方法执行的处理器
            Object proxy = Proxy.newProxyInstance(classLoader, interfaces,this);
            return proxy;
        }
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            begin();
            Object returnValue = method.invoke(obj,args);//执行method方法(入参)
            commit();
            return returnValue;
        }
        public void begin(){
            System.out.println("~~~~~~~开启事务");
        }
        public void commit(){
            System.out.println("~~~~~~~提交事务");
        }
    
    }
    
    ----------------------------------------------------------------
    测试类:
    package com.springtest.test;
    
    import com.springtest.dao.BookDAOImpl;
    import com.springtest.dao.GenaralDAO;
    import com.springtest.dao.JDKMyDynamicProxy;
    import com.springtest.dao.StudentDAOImpl;
    
    public class TestDynamicProxy {
        public static void main(String[] args) {
            //被代理对象
            BookDAOImpl bookDAO = new BookDAOImpl();
            StudentDAOImpl studentDAO = new StudentDAOImpl();
    
            //创建动态代理类对象,并将被代理对象传递到代理类中,赋值给obj
            JDKMyDynamicProxy jdkMyDynamicProxy = new JDKMyDynamicProxy(studentDAO);
    
            //proxy就是产生的代理对象,产生的代理对象可以强转成被代理对象实现的接口类型
            GenaralDAO proxy = (GenaralDAO) jdkMyDynamicProxy.getProxy();
    
            //使用代理对象调用方法,并不会执行调用的方法,而是进入到创建代理对象时指定的InvocationHandler类中的invoke方法
            //调用的方法作为一个Method参数,传递给了invoke方法
            proxy.delete();
    
        }
    }

    4.3.2 CGLib动态代理

    由于JDK动态代理时通过被代理类实现的接口来创建代理对象的,因此JDK动态代理只能代理实现了接口的类对象。如果一个类没有实现任何接口,该如何产生代理对象呢?——CGLib动态代理;
    CGLib动态代理,是通过创建被代理类的子类来创建代理对象的,因此即使没有实现任何接口的类也可以通过CGLib产生代理对象;
    CGLib动态代理不能为final修饰的类创建代理对象;
    • 添加CGLib依赖
    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
    </dependency>
    •  CGLib动态代理代码实现
    package com.springtest.dao;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * 1:添加cglib依赖
     * 2:创建一个类,实现MethodInterceptor接口,同时实现接口中的intercept方法
     * 3:在类中定义一个Object类型的变量,并提供这个变量的有参构造器,用于传递被代理对象
     * 4:定义getProxy方法创建并返回代理对象(代理对象是通过创建被代理类的子类来创建的)
     * */
    
    public class CGLibDynamicProxy implements MethodInterceptor {
    
        private Object obj;
    
        public CGLibDynamicProxy(Object obj) {
            this.obj = obj;
        }
    
        public Object getProxy(){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(obj.getClass());
            enhancer.setCallback(this);
            Object proxy = enhancer.create();
            return proxy;
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            begin();
            Object returnValue = method.invoke(obj,objects);//通过反射调用被代理类的方法
            commit();
            return returnValue;
        }
        public void begin(){
            System.out.println("~~~~~~~开启事务");
        }
        public void commit(){
            System.out.println("~~~~~~~提交事务");
        }
    }
    ------------------------------------------------------------------------------------
    测试类:
    
    
    package com.springtest.test;
    
    import com.springtest.dao.*;
    
    public class TestDynamicProxy {
        public static void main(String[] args) {
            //创建被代理对象
            BookDAOImpl bookDAO = new BookDAOImpl();
            StudentDAOImpl studentDAO = new StudentDAOImpl();
            
            //通过cglib动态代理类创建代理对象
            CGLibDynamicProxy cgLibDynamicProxy = new CGLibDynamicProxy(bookDAO);
            //代理对象实际上是被代理对象子类,因此代理对象可以直接强转为被代理类类型
            BookDAOImpl proxy = (BookDAOImpl)cgLibDynamicProxy.getProxy();
    
            //使用代理对象调用方法,实际上并没有执行这个方法,而是执行了代理类中的intercept方法,将当前调用的方法以及方法中的参数
            //传递到intercept方法
            proxy.update();
    
        }
    }

    0x05 SpringAOP

    5.1 AOP概念

    Aspect Oriented Programming面向切面编程,是一种利用“横切”的技术(底层实现就是动态代理),对原有的业务逻辑进行拦截,并切

    可以在这个拦截的横切面上添加特定的业务逻辑,对原有的业务进行增强。

    基于动态代理,实现在不改变原有业务的情况下对业务逻辑进行增强。

    5.2 SpringAOP框架部署

    5.2.1 创建Maven项目

    5.2.2 添加依赖

    • context
    • aspects
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.12.RELEASE</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.12.RELEASE</version>
    </dependency>

    5.2.3 创建Spring配置文件

    需要引入aop的命名空间

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

    5.3 AOP配置--基于XML

    在DAO的方法前后添加开启事务的提交事务的逻辑

    5.3.1 创建一个类,定义要添加的业务逻辑

    public class TxManager {
    
        public void begin(){
            System.out.println("______开启事务");
        }
    
        public void commit(){
            System.out.println("______提交事务");
        }
    
    }

    5.3.2 配置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"
            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
          http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="bookDAO" class="com.springtest.dao.BookDAOImpl"></bean>
        <bean id="studentDAO" class="com.springtest.dao.StudentDAOImpl"></bean>
    
        <bean id="txManager" class="com.springtestutils.TxManager"></bean>
        <aop:config>
            <!--声明切入点-->
            <aop:pointcut id="book_all" expression="execution(* com.liguoqing.dao.*.*(..))"/>
            <!--声明txManager为切面类-->
            <aop:aspect ref="txManager">
            <!--通知-->
                <aop:before method="begin" pointcut-ref="book_all"/>
                <aop:after method="commit" pointcut-ref="book_all"/>
            </aop:aspect>
        </aop:config>
    
    
    </beans>
    AOP开发步骤:
    • 创建切面类,在切面类定义切面方法
    • 将切面类配置给Spring容器
    • 声明切入点
    • 配置AOP通知策略

    5.4 切入点的声明

    5.4.1 各种切入点声明方式

            <!-- 使用aop:pointcut声明切入点;切入点可以是一个方法,-->
            <aop:pointcut id="book-insert" expression="execution(* com.liguoqing.dao.BookDAOImpl.insert())"/>
            <!-- BookDAOImpl类中所有无参数无返回值的方法-->
            <aop:pointcut id="book_pc1" expression="execution(void com.liguoqing.dao.BookDAOImpl.*())"/>
            <!-- BookDAOImpl类中所有无返回值的方法(对参数无要求)-->
            <aop:pointcut id="book_pc2" expression="execution(void com.liguoqing.dao.BookDAOImpl.*(..))"/>
            <!-- BookDAOImpl类中所有无参数的方法(对返回值无要求)-->
            <aop:pointcut id="book_pc3" expression="execution(* com.liguoqing.dao.BookDAOImpl.*())"/>
            <!-- BookDAOImpl类中所有的方法(对参数、返回值都无要求)-->
            <aop:pointcut id="book_pc4" expression="execution(* com.liguoqing.dao.BookDAOImpl.*(..))"/>
            <!-- dao包中所有类中的所有方法(对参数、返回值都无要求)-->
            <aop:pointcut id="pc5" expression="execution(* com.liguoqing.dao.*.*(..))"/>
            <!-- dao包中所有类中的insert方法(对参数、返回值都无要求)-->
            <aop:pointcut id="pc6" expression="execution(* com.liguoqing.dao.*.insert(..))"/>
            <!-- dao包中所有类中的insert方法(对参数、返回值都无要求)-->
            <aop:pointcut id="pc7" expression="execution(* *(..))"/>

    5.4.2 AOP使用注意事项

            // 如果要使用Spring aop面向切面编程,调用切入点方法的对象必须通过Spring容器获取
            // 如果一个类中的方法被声明为切入点并织入了切点之后,通过Spring容器获取该类对象,实则获取到的是一个代理对象,
            // 如果一个类中的方法没有被声明为切入点,通过Spring容器获取的就是这个类真是创建的对象
            // BookServiceDAOImpl bookServiceDAO = new BookServiceDAOImpl();
            BookServiceDAOImpl bookServiceDAO = (BookServiceDAOImpl) context.getBean("bookService");
            bookServiceDAO.addBook();

    5.5 AOP通知策略

    AOP通知策略:就是声明将切面类中的切点方法如何放入切入点

    • before
    • after
    • after-throwing
    • after-returning
    • around

    5.5.1 定义切面类

    package com.springtest.utils;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class MyAspect {
    
        public void method1(){
            System.out.println("~~~~~~~~~~~~~method1");
        }
        public void method2(){
            System.out.println("~~~~~~~~~~~~~method2");
        }
        public void method3(){
            System.out.println("~~~~~~~~~~~~~method3");
        }
        public void method4(){
            System.out.println("~~~~~~~~~~~~~method4");
        }
        //环绕通知的方法,必须遵守如下的定义规则
        //1:必须带有一个ProceedingJoinPoint类型的参数,
        //2:必须有Object类型的返回值
        //3:在前后增强的业务逻辑之间执行Object v = point.proceed();
        //4: 方法最后返回 return v;
        public Object method5(ProceedingJoinPoint point) throws Throwable {
            System.out.println("~~~~~~~~~~~~~method5----before");
            //此句代码的执行就表示切入点方法的执行
            Object v = point.proceed();
            System.out.println("~~~~~~~~~~~~~method5----after");
            return v;
        }
    }

     5.5.2 配置切面类

    <?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"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    
        <bean id="myAspect" class="com.springtest.utils.MyAspect"></bean>
    
        <aop:config>
        
            <!--使用aop:pointcut声明切入点;切入点可以是一个方法,-->
            <aop:pointcut id="book-insert" expression="execution(* com.liguoqing.dao.BookDAOImpl.insert())"/>
    
            <aop:aspect ref="myAspect">
            <!--aop:before 前置通知,切入到指定切入点之前-->
                <aop:before method="method1" pointcut-ref="book-insert"/>
                <!--aop:after 后置通知,切入到指定切入点之后-->
                <aop:after method="method2" pointcut-ref="book-insert"/>
                <!--aop:after-throwing 异常通知,切入点抛出异常之后-->
                <aop:after-throwing method="method3" pointcut-ref="book-insert"/>
                <!--aop:after-returning 方法返回值返回之后,对于一个java方法而言return返回值也是方法的一部分
                    因此”方法返回值返回之后“和”方法执行结束“是同一个时间点,所以after和after-returning根据配置的顺序决定执行的顺序-->
                <aop:after-returning method="method4" pointcut-ref="book-insert"/>
                <!--aop:around 环绕通知-->
                <aop:around method="method5" pointcut-ref="book-insert"/>
            </aop:aspect>
            
        </aop:config>
    
    
    </beans>

    0x06 SpringAOP注解配置

    6.1 Spring AOP注解配置框架部署

    6.1.1 创建Maven工程

    6.1.2 添加Spring依赖

    • context
    • aspects 
    <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"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <context:annotation-config/>
        <context:component-scan base-package="com.liguoqing"/>
    <!--基于注解配置的AOP代理-->
        <aop:aspectj-autoproxy/>
    
    
    
    </beans>

    6.2 AOP注解配置案例

    package com.springtest.utils;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect
    public class TransactionManager {
        @Pointcut("execution(* com.springtest.dao.*.*(..))")
        public void pc1(){
    
        }
    
        @Before("pc1()")
        public void begin(){
            System.out.println("~~~开启事务");
        }
        @After("pc1()")
        public void commit(){
            System.out.println("~~~提交事务");
        }
    
        @Around("pc1()")
        public Object printExecuteTime(ProceedingJoinPoint point) throws Throwable {
    
            long time1 = System.currentTimeMillis();
            Object v = point.proceed();
            long time2 = System.currentTimeMillis();
            System.out.println("~~~time:" + (time2 - time1));
            return v;
    
        }
    }
    注意:注解使用虽然方便,但是只能在源码上添加注解,因此我们的自定义类提倡使用注解配置,但是如果使用到第三方提供的类则需要通过XML配置形式完成配置。

    0x07 Spring整合MyBatis

    /**
    
    Spring两大核心思想:IoC和AOP
    IoC:控制反转,Spring容器可以完成对象的创建、属性注入、对象管理等工作
    AOP:面向切面,在不修改原有业务逻辑的情况下,实现原有业务的增强   
    
    **/

    7.1  Spring可以对MyBatis提供哪些支持

    • IoC支持:SpringIoC可以为MyBatis完成DataSource、SqlSessionFactory、SqlSession、DAO对象的创建
    • AOP支持:使用Spring提供的事务管理切面类完成对MyBatis数据库操作中的事务管理

    7.2 Spring整合MyBatis准备工作

    7.2.1 创建Maven工程

    7.2.2 部署MyBatis框架:

    • 添加依赖
      •  MySQL驱动 (mysql-connector-java)
      •  MyBatis(mybatis) 
    <!--MyBatis依赖-->
            <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.15</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.4.6</version>
            </dependency> 
    • 创建 MyBatis配置文件,配置文件里无需进行任何配置:mybatis-config.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
    </configuration>

    7.2.3 部署Spring框架

    • 添加依赖
    <!-- Spring依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.12.RELEASE</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.12.RELEASE</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.11.RELEASE</version>
    </dependency>
    • 创建Spring配置文件:applicationContext.xml
    <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"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd">
    
       
    
    
    </beans>

     7.2.4 添加Spring整合MyBatis的依赖

    • mybatis-spring 就是mybatis提供的兼容Spring的补丁
    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.2</version>
    </dependency>

    7.3 Spring整合MyBatis整合配置 

     7.3.1 整合Druid连接池

    • 添加druid的依赖
    !-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    •  创建druid.properties属性文件(druid.properties)
    druid.driver=com.mysql.cj.jdbc.Driver
    druid.url=jdbc:mysql://localhost:3306/db_2010_mybatis?characterEncoding = utf-8
    druid.username=root
    druid.password=123456
    
    
    #连接池参数
    #init:初始连接数  minIdle:最小连接数 maxActive:最大连接数 timeout:超时时间
    druid.pool.init = 3
    druid.pool.minIdle = 5
    druid.pool.maxActive = 20
    druid.pool.timeout = 30
    • 在applicationContext.xml中配置DruidDataSource
    <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"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <!--加载druid.properties属性文件-->
        <context:property-placeholder location="classpath:druid.properties"/>
    
    <!--依赖Spring容器完成数据源DataSource的创建-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${druid.driver}"/>
            <property name="url" value="${druid.url}"/>
            <property name="username" value="${druid.username}"/>
            <property name="password" value="${druid.password}"/>
    
            <property name="initialSize" value="${druid.pool.init}"/>
            <property name="minIdle" value="${druid.pool.minIdle}"/>
            <property name="maxActive" value="${druid.pool.maxActive}"/>
            <property name="maxWait" value="${druid.pool.timeout}"/>
    
        </bean>
    
    
    </beans>

    7.3.2 整合MyBatis--创建SqlSessionFactory

    依赖Spring容器完成MyBatis的SqlSessionFactory对象的创建
    <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"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--加载druid.properties属性文件-->
        <context:property-placeholder location="classpath:druid.properties"/>
    
        <!--依赖Spring容器完成数据源DataSource的创建-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${druid.driver}"/>
            <property name="url" value="${druid.url}"/>
            <property name="username" value="${druid.username}"/>
            <property name="password" value="${druid.password}"/>
    
            <property name="initialSize" value="${druid.pool.init}"/>
            <property name="minIdle" value="${druid.pool.minIdle}"/>
            <property name="maxActive" value="${druid.pool.maxActive}"/>
            <property name="maxWait" value="${druid.pool.timeout}"/>
    
        </bean>
    
        <!--依赖Spring容器完成MyBatis的SqlSessionFactory对象的创建-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 配置数据源-->
            <property name="dataSource" ref="druidDataSource"/>
            <!-- 配置mapper文件的路径-->
            <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
            <!-- 可选,配置需要定义别名的实体类的包-->
            <property name="typeAliasesPackage" value="com.liguoqing.pojo"/>
            <!-- 可选,配置MyBatis的主配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
        </bean>
    
    
    </beans>

     7.3.3 整合MyBatis-创建Mapper

    <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"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--加载druid.properties属性文件-->
        <context:property-placeholder location="classpath:druid.properties"/>
    
        <!--依赖Spring容器完成数据源DataSource的创建-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${druid.driver}"/>
            <property name="url" value="${druid.url}"/>
            <property name="username" value="${druid.username}"/>
            <property name="password" value="${druid.password}"/>
    
            <property name="initialSize" value="${druid.pool.init}"/>
            <property name="minIdle" value="${druid.pool.minIdle}"/>
            <property name="maxActive" value="${druid.pool.maxActive}"/>
            <property name="maxWait" value="${druid.pool.timeout}"/>
    
        </bean>
    
        <!--依赖Spring容器完成MyBatis的SqlSessionFactory对象的创建-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--        配置数据源-->
            <property name="dataSource" ref="druidDataSource"/>
    <!--        配置mapper文件的路径-->
            <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
    <!--        可选,配置需要定义别名的实体类的包-->
            <property name="typeAliasesPackage" value="com.springtest.pojo"/>
    <!--        可选,配置MyBatis的主配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
        </bean>
    --------------------------------------------------------------------------------------------------------
        <!--加载dao包中的所有DAO接口,通过sqlSessionFactory获取sqlSession,然后创建所有的DAO接口对象,存储在Spring容器-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <property name="basePackage" value="com.springtest.dao"/>
        </bean>
    
    </beans>

    7.4 Spring整合MyBatis--整合AOP配置

    使用Spring提供的事务管理切面类完成DAO中增删该操作的事务管理
    <beans
            xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:aop="http://www.springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd
          http://www.springframework.org/schema/tx
          http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--使用注解-->
        <context:annotation-config/>
    <!--注解需要扫描的包-->
        <context:component-scan base-package="com.springtest"/>
        
        <!--加载druid.properties属性文件-->
        <context:property-placeholder location="classpath:druid.properties"/>
    
        <!--依赖Spring容器完成数据源DataSource的创建-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${druid.driver}"/>
            <property name="url" value="${druid.url}"/>
            <property name="username" value="${druid.username}"/>
            <property name="password" value="${druid.password}"/>
    
            <property name="initialSize" value="${druid.pool.init}"/>
            <property name="minIdle" value="${druid.pool.minIdle}"/>
            <property name="maxActive" value="${druid.pool.maxActive}"/>
            <property name="maxWait" value="${druid.pool.timeout}"/>
    
        </bean>
    
        <!--依赖Spring容器完成MyBatis的SqlSessionFactory对象的创建-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--        配置数据源-->
            <property name="dataSource" ref="druidDataSource"/>
    <!--        配置mapper文件的路径-->
            <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
    <!--        可选,配置需要定义别名的实体类的包-->
            <property name="typeAliasesPackage" value="com.springtest.pojo"/>
    <!--        可选,配置MyBatis的主配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
        </bean>
    --------------------------------------------------------------------------------------------------------------------------------
        <!--加载dao包中的所有DAO接口,通过sqlSessionFactory获取sqlSession,然后创建所有的DAO接口对象,存储在Spring容器-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <property name="basePackage" value="com.springtest.dao"/>
        </bean>
    
        <!--1:将Spring提供的事务管理类配置给Spring容器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="druidDataSource"/>
        </bean>
        <!--2:通过Spring jdbc提供的tx标签,声明事务管理策略-->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="insert*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                <tx:method name="query*" isolation="REPEATABLE_READ" propagation="SUPPORTS"/>
            </tx:attributes>
        </tx:advice>
    
    
    </beans>

    7.4.1 事务的隔离级别

    /**
    
    isolation 设置事务隔离级别:
    READ_UNCOMMITTED(读未提交:事务1在执行的过程中,事务2可以读也可以写,事务1可以读取到事务2提交的数据(脏读)(不可重复读)(幻读)),
    READ_COMMITTED(读已提交:事务1在执行的过程中,事务2可以读也可以写,但是事务1只能读取到事务2提交后的数据(不可重复读)),
    REPEATABLE_READ(可重复读:在事务1执行过程中,事务2只能读但是不能改,事务2可以添加数据(幻读))
    SERIALIZABLE(串行化:事务1在执行过程中,事务2既不能读也不能写)
    
    **/

    7.4.2 事务的传播机制 

    /**
    
    propagation: 设置事务的传播机制:
        REQUIRED:如果上层方法没有事务,则创建一个新的事务,如果已经存在事务,则加入到事务中。
        SUPPORTS: 如果上层方法没有事务,则以非事务方式执行。如果已经存在事务,则加入到事务中。
       
        REQUIRES_NEW:如果上层方法没有事务,则创建一个新的事务,如果已经存在事务,则将当前事务挂起,自己创建一个新的事务去执行(老子一直要用新事务)。
        NOT_SUPPORTED:如果上层方法没有事务,则以非事务方式执行,如果已经存在事务,则将当前事务挂起(死都不用事务)。
        NEVER:如果上层方法没有事务,则以非事务方式执行,如果已经存在事务,则将抛出异常(死都不用事务,要是有事务我就抛异常)。
        MANDATORY:如果上层方法已经存在事务,则加入到事务中执行,如果不存在事务则抛出异常。
        NESTED:如果上层方法没有事务,则创建一个新的事务,如果已经存在事务,则嵌套到事务中。
    
    **/

    7.4.3 Spring AOP事务管理配置——xml配置

    <beans
            xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:aop="http://www.springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd
          http://www.springframework.org/schema/tx
          http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--使用注解-->
        <context:annotation-config/>
    <!--注解需要扫描的包-->
        <context:component-scan base-package="com.springtest"/>
        
        <!--加载druid.properties属性文件-->
        <context:property-placeholder location="classpath:druid.properties"/>
    
        <!--依赖Spring容器完成数据源DataSource的创建-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${druid.driver}"/>
            <property name="url" value="${druid.url}"/>
            <property name="username" value="${druid.username}"/>
            <property name="password" value="${druid.password}"/>
    
            <property name="initialSize" value="${druid.pool.init}"/>
            <property name="minIdle" value="${druid.pool.minIdle}"/>
            <property name="maxActive" value="${druid.pool.maxActive}"/>
            <property name="maxWait" value="${druid.pool.timeout}"/>
    
        </bean>
    
        <!--依赖Spring容器完成MyBatis的SqlSessionFactory对象的创建-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--        配置数据源-->
            <property name="dataSource" ref="druidDataSource"/>
    <!--        配置mapper文件的路径-->
            <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
    <!--        可选,配置需要定义别名的实体类的包-->
            <property name="typeAliasesPackage" value="com.springtest.pojo"/>
    <!--        可选,配置MyBatis的主配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
        </bean>
    ---------------------------------------------------------------------------------------------
        <!--加载dao包中的所有DAO接口,通过sqlSessionFactory获取sqlSession,然后创建所有的DAO接口对象,存储在Spring容器-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <property name="basePackage" value="com.springtest.dao"/>
        </bean>
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
        <!--1:将Spring提供的事务管理类配置给Spring容器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="druidDataSource"/>
        </bean>
        
        <!--2:通过Spring jdbc提供的tx标签,声明事务管理策略-->
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
    <!-- isolation 设置事务隔离级别:
            READ_UNCOMMITTED(读未提交:事务1在执行的过程中,事务2可以读也可以写,事务1可以读取到事务2提交的数据(脏读)(不可重复读)(幻读)),
            READ_COMMITTED(读已提交:事务1在执行的过程中,事务2可以读也可以写,但是事务1只能读取到事务2提交后的数据(不可重复读)),
            REPEATABLE_READ(可重复读:在事务1执行过程中,事务2只能读但是不能改,事务2可以添加数据(幻读))
            SERIALIZABLE(串行化:事务1在执行过程中,事务2既不能读也不能写)
    
         propagation: 设置事务的传播机制:
             REQUIRED:如果上层方法没有事务,则创建一个新的事务,如果已经存在事务,则加入到事务中。
             SUPPORTS: 如果上层方法没有事务,则以非事务方式执行。如果已经存在事务,则加入到事务中。
             REQUIRES_NEW:如果上层方法没有事务,则创建一个新的事务,如果已经存在事务,则将当前事务挂起,自己创建一个新的事务去执行(老子一直要用新事务)。
             NOT_SUPPORTED:如果上层方法没有事务,则以非事务方式执行,如果已经存在事务,则将当前事务挂起(死都不用事务)。
             NEVER:如果上层方法没有事务,则以非事务方式执行,如果已经存在事务,则将抛出异常(死都不用事务,要是有事务我就抛异常)。
             MANDATORY:如果上层方法已经存在事务,则加入到事务中执行,如果不存在事务则抛出异常。
             NESTED:如果上层方法没有事务,则创建一个新的事务,如果已经存在事务,则嵌套到事务中。
              -->
                <tx:method name="insert*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                <tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                <tx:method name="query*" isolation="REPEATABLE_READ" propagation="SUPPORTS"/>
            </tx:attributes>
        </tx:advice>
        
        <!--3、将事务管理策略以AOP配置,应用于DAO操作方法-->
        <aop:config>
            <aop:pointcut id="crud" expression="execution(* com.springtest.service.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="crud"/>
        </aop:config>
    
    </beans>

    7.4.4 Spring AOP事务管理配置——注解配置

    • applicationContext.xml 配置事务管理类,声明使用注解方式进行事务配置
    <beans
            xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:aop="http://www.springframework.org/schema/aop"
            xmlns:tx="http://www.springframework.org/schema/tx"
            xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd
          http://www.springframework.org/schema/tx
          http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!--使用注解-->
        <context:annotation-config/>
    <!--注解需要扫描的包-->
        <context:component-scan base-package="com.springtest"/>
        
        <!--加载druid.properties属性文件-->
        <context:property-placeholder location="classpath:druid.properties"/>
    
        <!--依赖Spring容器完成数据源DataSource的创建-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${druid.driver}"/>
            <property name="url" value="${druid.url}"/>
            <property name="username" value="${druid.username}"/>
            <property name="password" value="${druid.password}"/>
    
            <property name="initialSize" value="${druid.pool.init}"/>
            <property name="minIdle" value="${druid.pool.minIdle}"/>
            <property name="maxActive" value="${druid.pool.maxActive}"/>
            <property name="maxWait" value="${druid.pool.timeout}"/>
    
        </bean>
    
        <!--依赖Spring容器完成MyBatis的SqlSessionFactory对象的创建-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--        配置数据源-->
            <property name="dataSource" ref="druidDataSource"/>
    <!--        配置mapper文件的路径-->
            <property name="mapperLocations" value="classpath:mappers/*Mapper.xml"/>
    <!--        可选,配置需要定义别名的实体类的包-->
            <property name="typeAliasesPackage" value="com.springtest.pojo"/>
    <!--        可选,配置MyBatis的主配置文件-->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
        </bean>
    
        <!--加载dao包中的所有DAO接口,通过sqlSessionFactory获取sqlSession,然后创建所有的DAO接口对象,存储在Spring容器-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <property name="basePackage" value="com.springtest.dao"/>
        </bean>
    
        <!--1:将Spring提供的事务管理类配置给Spring容器-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="druidDataSource"/>
        </bean>
    -----------------------------------------------------------
    <!--    2:声明使用注解完成事务配置-->
        <tx:annotation-driven transaction-manager="transactionManager"/>
    -------------------------------------------------------------------------------------
    
        <!--2:通过Spring jdbc提供的tx标签,声明事务管理策略
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="insert*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                <tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED"/>
                <tx:method name="query*" isolation="REPEATABLE_READ" propagation="SUPPORTS"/>
            </tx:attributes>
        </tx:advice>-->
    
        <!--3、将事务管理策略以AOP配置,应用于DAO操作方法-->
        <aop:config>
            <aop:pointcut id="crud" expression="execution(* com.springtest.service.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="crud"/>
        </aop:config>
    
    </beans>
    • 在需要Spring进行事务管理的方法上添加 @Transactional 注解
    package com.springtest.service.impl;
    
    import com.springtest.service.UserService;
    import com.springtest.dao.UserDAO;
    import com.springtest.pojo.User;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Isolation;
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Resource
        private UserDAO userDAO;
    
        @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.SUPPORTS)
        public List<User> listUser() {
            return userDAO.queryUsers();
        }
    }

      

  • 相关阅读:
    1069: [SCOI2007]最大土地面积
    Kettle 解决数据锁的问题(事务(进程 ID 51)与另一个进程被死锁在 锁 资源上)
    Kettle配合Windows执行计划实现定时实行作业
    把本地项目同步到码云
    IDEA通过Maven WebApp archetype 创建Spring boot项目骨架
    从零开始完整搭建 Spring-Boot 项目开发框架的教程
    使用IDEA搭建Spring Boot入门项目
    Win10激活失败并提示错误代码0xC004C003的解决方法
    javascript (JS组成、书写位置、基本概念、作用域、内存问题、变量)
    HTML基础之标签
  • 原文地址:https://www.cnblogs.com/JetpropelledSnake/p/15974550.html
Copyright © 2020-2023  润新知