Spring学习笔记

需要获取此学习笔记的小伙伴可以+q2760598291获取,记得点赞收藏哦!感谢你的关注!

第一章 初识Spring

1.1 Spring简介
  • Spring是一个为简化企业级开发而生的开源框架

  • Spring是一个IOC(DI)AOP容器框架。

  • IOC全称:Inversion of Control【控制反转】

    • 将对象【万物皆对象】控制权交给Spring
    • 注意:new的对象,不能被JVM垃圾回收机制回收
      在这里插入图片描述
  • DI全称:(Dependency Injection):依赖注入,可以理解为不仅可以管理对象本身以外,还可以管理其依赖关系,比如管理其属性。

  • AOP全称:Aspect-Oriented Programming,面向切面编程

  • 官网:https://spring.io/

1.2 Spring模块

在这里插入图片描述

1.3 搭建Spring框架步骤
  • 导入jar包,该包中含有spring-jcl包(日志包),也就是说不用向mybatis一样依赖第三方日志 log4j

    <!--导入spring-context,加上其依赖,共6个jar包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.1</version>
    </dependency>
    <!--导入junit4.12-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    
  • 编写核心配置文件

    • 配置文件名称:applicationContext.xml【beans.xml或spring.xml】

    • 配置文件路径:src/main/resources

    • 示例代码

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
      
          <!-- 将对象装配到IOC容器中-->
      <!--    id指明在测试类创建对象中传入的类取名字-->
      <!--    例如:Student student = (Student)context.getBean("studentLiu");-->
      <!--    class说明是指明id所对应的bean类-->
          <bean id="studentLiu" class="springdemo.Student">
      <!--        通过property设置对象的初始化属性值-->
              <property name="studentId" value="2"></property>
              <property name="studentName" value="刘翰林"></property>
          </bean>
      </beans>
      
  • 使用核心类库

    public class TestMySpring {
        @Test
        public void test1(){
            //使用Spring之前创建对象
    //        Student student = new Student();
    
    
            //使用Spring之后创建对象
            //1、先创建容器对象
            //2、再通过容器对象获得对象
            //			("此处路径默认为编译后的根目录,并非源码目录,
    // 	IDEA将放在res资源目录中的文件,编译后自动放在项目的根目录,这样就可以直接使用文件名访问了")
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//获取容器对象
            Student student = (Student) context.getBean("studentLiu");//通过容器对象的getbean方法获取对象
            System.out.println(student);
        }
    }
    
1.4 Spring特性
  • 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API。
    • Servlet是侵入式的,自定义的类要实现Servlet接口才能用Servlet的作用
  • 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期。
  • 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
  • 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的JDBCTemplate)。
  • 控制反转:IOC——Inversion of Control,翻转资源获取方向。把自己创建资源、向环境索取资源 变成环境将资源准备好,我们享受资源注入。
  • 面向切面编程:AOP——Aspect Oriented Programming,在不修改源代码的基础上增强代码功 能。
1.5 Spring中getBean()三种方式
  • getBean(String beanId):通过beanId获取对象

    • 不足:需要强制类型转换,不灵活
  • getBean(Class clazz):通过Class方式获取对象

    • 不足:容器中有多个相同类的bean对象的时候,会报如下错误:

      expected single matching bean but found 2: stuZhenzhong,stuZhouxu

  • 【推荐使用】getBean(String beanId,Clazz clazz):通过beanId和Class获取对象

    • 第一个参数决定获取容器中的哪个对象
    • 第二个参数决定将容器中获取的对象赋值给什么类型的对象
  • 代码:

  • @Test
        public void test1(){
            //使用Spring之前创建对象
    //        Student student = new Student();
    
    
            //使用Spring之后创建对象
            //1、先创建容器对象
            //2、再通过容器对象获得对象
            //       ("此处路径默认为编译后的根目录,并非源码目录,
    //     IDEA将放在res资源目录中的文件,编译后自动放在项目的根目录,这样就可以直接使用文件名访问了")
    
            //getBean使用三种方式
            //方式一:使用getBean(String beanId),此处的beanId就是配置文件中指明的id,弊端:每次都需要对其返回值类型进行强转
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");//获取容器对象
            Student student = (Student) context.getBean("studentLiu");//通过容器对象的getbean方法获取对象
            System.out.println(student);
    
    
            //方式二:使用getBean(class clazz),这种方法的弊端:,当配置文件中的bean类对应多个beanId的时候就会报错
            Student student1 = context.getBean(Student.class);
            System.out.println(student1);
    
            //方式三:方法一和方法二的结合getBean(String beanId,class clazz)
            Student student3 = context.getBean("studentLiu", Student.class);
            System.out.println(student3);
        }
    

注意:框架默认都是通过无参构造器,帮助我们创建对象。

​ 所以:如提供对象的构造器时,一定添加无参构造器

1.6 bean标签详解
  • 属性
    • id:bean对象的唯一标识
    • class:定义bean的类型【class全类名】
    • name:bean对象的不唯一标识(所以现在一般不用),getBean也可以通过name的方式获取bean对象
    • scope:bean的作用域,详情看 第七章
    • init-method:绑定类的初始化方法,详情看 8.1小节
    • destroy-method:绑定类的销毁方法,详情看 8.1小节
    • Autowire:基于XML的自动装配,详情看 第九章
  • 子标签
    • property:为对象中属性赋值【set注入(4.1小节)】
      • name属性:设置属性名称
      • value属性:设置属性数值
      • ref属性:引用外部bean,看 3.3 小节

day06

第二章 SpringIOC底层实现

IOC:将对象的控制反转给Spring

2.1 BeanFactory与ApplicationContexet

ioc的底层原理实现

在这里插入图片描述

  • BeanFactory:IOC容器的基本实现,是Spring内部的使用接口,是面向Spring本身的,不是提供给开发人员使用的。****
  • ApplicationContext:BeanFactory的子接口的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
2.2 图解IOC类的结构

在这里插入图片描述

  • BeanFactory:Spring底层IOC实现【面向Spring框架,内部接口】
      • ApplicationContext:面向程序员
        • ConfigurableApplicationContext:提供关闭或刷新容器对象方法
            • ClassPathXmlApplicationContext:基于类路径检索xml文件
            • AnnotationConfigApplicationContext:基于注解创建容器对象
            • FileSystemXmlApplicationContext:基于文件系统检索xml文件

第三章 Spring依赖注入数值问题【重点】

3.1 字面量数值
  • 数据类型角度:基本数据类型及包装类、String
  • 语法角度:value属性或value标签
3.2 CDATA区
  • 语法:<![CDATA[value值、SQL语句等等]]>,在sql中遇到也可以使用,所有的xml中都可以使用

  • 作用:在xml中定义**特殊字符(比如 <>>,>代表标签结束)**时,使用CDATA区

  • 代码示例:

    <bean id="studentLiu" class="springdemo.Student">
    <!--        通过property设置对象的初始化属性值-->
            <property name="studentId" value="2"></property>
    
    <!--        当xml中有特殊的符号或者值时候(比如<<刘翰林>>),会报错比如如下所示-->
    <!--        <property name="studentName" value="<<刘翰林>>"></property>-->
    <!--        这时候可以使用<![CDATA[此处放特殊值]]>来处理,如下-->
            <property name="studentName">
                <value><![CDATA[<<刘翰林>>]]]></value>
            </property>
    <!--        注意:不能利用标签的属性来设置cdata不然也会报错,如下所示:-->
    <!--        <property name="studentName" value="<![CDATA[<<刘翰林>>]]]>"></property>-->
        </bean>
    
3.3 外部已声明bean及级联属性赋值
  • 语法:ref

  • 注意:级联属性更改数值会影响外部声明bean【ref赋值的是引用】

  • 示例代码

    	<bean id="dept1" class="demo.pojo.Dept">
            <property name="deptId" value="1"></property>
            <property name="deptName" value="技术部"></property>
    
        </bean>
    
        <bean id="studentJieJie" class="demo.pojo.Employee">
            <property name="email" value="JieJie@qq.com"></property>
            <property name="id" value="1"></property>
            <property name="lastName" value="JieJie"></property>
            <property name="salary" value="500.0"></property>
    
    <!--        因为这里的属性不是字面量,所以不能使用value的方式去赋值-->
            <property name="dept" ref="dept1"></property>
    
    <!--        级联属性更改数值会影响外部声明bean【ref赋值的是引用】,如下图-->
    <!--        这里会更改dept1的dept.Name的值,因为上面赋值赋的是引用,这里使用级联方式赋值-->
            <property name="dept.deptName" value="财务部"></property>
    
        </bean>
    

在这里插入图片描述

3.4 内部bean
  • 概述

    • 内部类:在一个类中完整定义另一个类,当前类称之为内部类
    • 内部bean:在一个bean中完整定义另一个bean,当前bean称之为内部bean
  • 注意:内部bean不会直接装配到IOC容器中,即不可以通过getBean()获取该内部对象

  • 示例代码

    <!--    测试内部bean-->
        <bean id="liu" class="demo.pojo.Employee">
            <property name="id" value="2"></property>
            <property name="email" value="liu@qq.com"></property>
            <property name="lastName" value="liu"></property>
            <property name="salary" value="9999.0"></property>
            <property name="dept">
    <!--            定义内部bean-->
                <bean class="demo.pojo.Dept">
                    <property name="deptId" value="1"></property>
                    <property name="deptName" value="技术部"></property>
    
                </bean>
            </property>
        </bean>
    
3.5 集合
  • List

    <!--    测试集合-->
        <bean id="dept6" class="demo.pojo.Dept">
            <property name="deptId" value="6"></property>
            <property name="deptName" value="创新部"></property>
    <!--        可以直接在属性中定义集合,也可以把集合提取出来,如下所示-->
            <property name="empList">
                <!-- 该属性的对象是什么类型的集合,就用什么标签,如Map集合,用<map> -->
                <list>
                    <ref bean="liu"></ref>
                    <ref bean="studentJieJie"></ref>
                </list>
            </property>
        </bean>
    
    <!--    将集合提取出来的示例-->
        <!--    测试提取List, util:list需要引入命名空间,将这个标签敲出来,alt+enter即可-->
        <util:list id="myList">
            <ref bean="liu"></ref>
            <ref bean="studentJieJie"></ref>
        </util:list>
        <bean id="dept7" class="demo.pojo.Dept">
            <property name="deptId" value="7"></property>
            <property name="deptName" value="经理部"></property>
            <property name="empList" ref="myList"></property>
        </bean>
    
  • Map

    • 方式一
    <!--    测试map集合方式一-->
        <bean id="dept8" class="demo.pojo.Dept">
            <property name="deptId" value="8"></property>
            <property name="deptName" value="后勤部"></property>
            <property name="empMap">
    <!--            这里也可以使用两种方式,直接写在里面或者外部引用,这里写在里面,外部引用见下边-->
                <map>
    <!--                这里的<entry>又有两种方式可以用:-->
    <!--                第一种:利用entry标签的属性-->
                    <entry key="1" value-ref="studentJieJie"></entry>
    <!--                第二种:使用entry标签里面再使用标签-->
                    <entry>
    <!--                    key指的是注入属性集合的键-->
                        <key><value>2</value></key>
                        <ref bean="liu"></ref>
                    </entry>
                </map>
            </property>
    
        </bean>
    

方式二:

<!--    测试map集合方式二-->
<!--            使用util:map标签-->
<util:map id="myMap">
    <entry key="1" value-ref="studentJieJie"></entry>
    <entry>
        <key><value>2</value></key>
        <ref bean="liu"></ref>

    </entry>

</util:map>
<bean id="dept9" class="demo.pojo.Dept">
    <property name="deptId" value="9"></property>
    <property name="deptName" value="九号部门"></property>
    <property name="empMap" ref="myMap"></property>
</bean>

第四章 Spring依赖注入方式【基于XML】

在ApplicationContext对象创建的时候,就会执行注入,

为属性赋值方式

  • 通过setXxx()方法
  • 通过构造器
  • 反射
4.1 set方法注入(常用)
  • 语法:<property name=“” value=“”>
  • 要求:有相应的setXxx()方法
4.2 构造器注入
  • 语法:<constructor-arg name=“” value=“”>
  • 要求:有相应参数个数的构造器
4.3 p名称空间注入

导入名称空间:xmlns:p=“http://www.springframework.org/schema/p”

  • 语法:<bean p:xxx>

  • 底层:也是set方法注入

  • 示例代码

    <bean id="studentJie" class="springdemo.Student">
    <!--        这种是set注入,也就是设置属性的值是通过对象的set方法-->
            <property name="studentId" value="3"></property>
            <property name="studentName" value="洁洁"></property>
        </bean>
    
        <bean id="studentLe" class="springdemo.Student">
    <!--        这种是构造器注入,也就是设置属性是通过构造器-->
            <constructor-arg name="studentId" value="4"></constructor-arg>
            <constructor-arg name="studentName" value="乐乐"></constructor-arg>
        </bean>
    
    
    <!--    这种使用的是p名称空间注入,其底层也是调用的set方法,需要导入名称空间:xmlns:p="http://www.springframework.org/schema/p"-->
        <bean id="studentLi"
              class="springdemo.Student"
              p:studentId="5"
              p:studentName=""></bean>
    
4.4 //TO DO 基于注解注入

第五章 Spring管理第三方bean

5.1 加载外部属性文件标签

<context:property-placeholder location=“classpath: xxxx” />,用法看下面

5.2 Spring管理druid步骤
  • 导入jar包

    <!--导入druid的jar包-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    <!--导入mysql的jar包-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <!--          <version>5.1.37</version> -->
        <version>8.0.26</version>
    </dependency>
    
  • 编写db.properties配置文件

    db.driver=com.mysql.cj.jdbc.Driver
    db.url=jdbc:mysql://localhost:3306/test_ssm
    db.username=root
    db.password=lhl20020704
    
  • 编写applicationContext_druid.xml相关代码

    <!--    加载外部属性文件db.properties,mybatis用的<properties>-->
        <!-- 加入context的命名空间 -->
        <context:property-placeholder location="db.properties"></context:property-placeholder>
    
    
        <!--    装配数据源
    		注意:druid数据源,既可以mybatis管理(mybatis-config.xml的<environments>的子标签<datasource>中的<property>),
    		也可以Spring管理(推荐,如下)
    -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${db.driver}"></property>
            <property name="url" value="${db.url}"></property>
            <property name="username" value="${db.username}"></property>
            <property name="password" value="${db.password}"></property>
    
        </bean>
    
  • 测试

    @Test
        public void test5() throws SQLException {
            ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext_druid.xml");
            DruidDataSource dataSource = ioc.getBean("dataSource", DruidDataSource.class);
            DruidPooledConnection connection = dataSource.getConnection();
    
            System.out.println(dataSource);
            System.out.println("==========================");
            System.out.println(connection);
    
    
        }
    

第六章 Spring中FactoryBean(BeanFactory是spring内部创建对象使用的接口)

6.1 Spring中两种bean
  • 一种是普通bean:设置的类型就是返回的类型
  • FactoryBean是Spring提供的一种整合第三方框架的常用机制。和普通的bean不同,配置一个 FactoryBean类型的bean,在获取bean的时候得到的并不是class属性中配置的这个类的对象,而是 getObject()方法的返回值。通过这种机制,Spring可以帮我们把复杂组件创建的详细过程和繁琐细节都 屏蔽起来,只把最简洁的使用界面展示给我们。
  • 另一种是工厂bean【FactoryBean】:返回类型通过该工厂bean的getObject方法指定
    • 作用:如需我们程序员参与到bean的创建时,使用FactoryBean。即bean的控制权不完全交给Spring
6.2 FactoryBean使用步骤
  • 实现FactoryBean接口

  • 重写方法【三个】

    package com.atguigu.factory;
    
    import com.atguigu.pojo.Dept;
    import org.springframework.beans.factory.FactoryBean;
    
    public class MyFactoryBean implements FactoryBean<Dept> {
    
        /**
         * getObject():参数对象创建的方法
         * @return
         * @throws Exception
         */
        @Override
        public Dept getObject() throws Exception {
            Dept dept = new Dept(101,"研发部门");
            //.....
            return dept;
        }
    
        /**
         * 设置参数对象Class
         * @return
         */
        @Override
        public Class<?> getObjectType() {
            return Dept.class;
        }
    
        /**
         * 设置当前对象是否为单例,默认为false
         * @return
         */
        @Override
        public boolean isSingleton() {
            return true;
        }
    
    }
    
  • 装配工厂bean,配置applicationContext_factorybean.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        
    	<bean id="myFactoryBean" class="com.atguigu.factory.MyFactoryBean"></bean>
    
    </beans>
    
  • 测试

    @Test
    public void testFactoryBean(){
        ApplicationContext context = 
            	new ClassPathXmlApplicationContext("applicationContext_factorybean.xml");
        Dept dept = context.getBean("myFactoryBean", Dept.class);// 注意是Dept.class -->
    	System.out.println("dept = " + dept);
    }
    

第七章 Spring中bean的作用域

7.1 语法
  • 在bean标签中添加属性:scope属性即可,scope的值为下面四种
7.2 四个作用域
  • singleton【默认值】:单例【在容器中只有一个对象】,<bean>不加scope属性时,默认单例
    • 对象创建时机:创建容器对象时,创建对象执行
  • prototype:多例【在容器中有多个对象】
    • 对象创建时机:getBean()方法被调用时,创建对象执行
  • request:请求域
    • 当前请求有效,离开请求域失效
    • 当前请求:URL不变即为当前请求
  • session:会话域
    • 当前会话有效,离开当前会话失效
    • 当前会话:当前浏览不关闭不更换即为当前会话(情况默认),默认存活30分钟
7.3 测试prototype多例
  • applicationContext-actionScope.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--    设置成单例模式singleton,容器中只有一个对象,在容器创建时调用-->
        <!--    设置成多例模式prototype,容器中会创建多个对象,在容器调用getBean方法时创建-->
        <bean id="dept" class="demo.pojo.Dept" scope="prototype">
            <property name="deptId" value="1"></property>
            <property name="deptName" value="技术部"></property>
        </bean>
    </beans>
    
  • testActionScope方法

    @Test
        public void test7(){
            ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext-beanScope.xml");
            Dept dept = ioc.getBean("dept", Dept.class);
            Dept dept1 = ioc.getBean("dept", Dept.class);
            
            System.out.println("是否相等:" + (dept == dept1));
    //        运行结果:
    //        调用了构造器...
    //        调用了构造器...
    //        是否相等:false
            }
    

第八章 Spring中bean的生命周期

8.1 bean的生命周期

① 通过构造器或工厂方法创建bean实例

② 为bean的属性设置值和对其他bean的引用(调用set方法

③ 调用bean的初始化方法

④ bean可以使用了

当容器关闭时,调用bean的销毁方法

  • Student类

    public class Student {
        private Integer studentId;
        private String studentName;
    
        /**
         * 初始化方法
         * @date
         * @author
         * @return
         * @throws
         */
        public void initStudent(){
            System.out.println("初始化对象...");
        }
    
        /**
         * 销毁方法
         * @date
         * @author
         * @return
         * @throws
         */
        public void destroyStudent(){
            System.out.println("对象被销毁了...");
        }
    
        public Integer getStudentId() {
            return studentId;
        }
    
        public void setStudentId(Integer studentId) {
            this.studentId = studentId;
        }
    
        public String getStudentName() {
            return studentName;
        }
    
        public void setStudentName(String studentName) {
            this.studentName = studentName;
        }
    
        @Override
        public String toString() {
            System.out.println("对象被使用了...");
            return "Student{" +
                    "studentId=" + studentId +
                    ", studentName='" + studentName + '\'' +
                    '}';
        }
    
        public Student(Integer studentId, String studentName) {
            this.studentId = studentId;
            this.studentName = studentName;
        }
    
        public Student() {
            System.out.println("对象被创建了...");
        }
    }
    
  • applicationContext-lifeCycle.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        
    <!--    分别设置对象的初始化方法(init-method)和销毁方法(destroy-method)-->
        <bean id="student"
              class="demo.pojo.Student"
              init-method="initStudent"
              destroy-method="destroyStudent"
              scope="prototype">
            <property name="studentId" value="1"></property>
            <property name="studentName" value="刘翰林"></property>
        </bean>
    </beans>
    
  • 测试示例

    /**
         *bean声明周期的测试
         * @date
         * @author
         * @return
         * @throws
         */
        @Test
        public void test8(){
            //如果是单例,则对象的初始化方法和创建都随着ioc的创建而调用
            //如果是多例,则对象的初始化方法和创建都随着ioc.getBean方法的调用而调用
            ConfigurableApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext_lifeCycle.xml");
            Student student = ioc.getBean("student", Student.class);
            System.out.println(student);//对象的使用
            ioc.close();//对象的销毁
    //        值得注意的是此处ioc对象的销毁得使用ConfigurableApplicationContext类
        }
    
8.2 bean的后置处理器
  • 作用:在调用初始化方法前后对bean进行额外的处理。

  • 实现:

    • 自定义一个类实现BeanPostProcessor接口该类一般放processor包下

    • 重写方法

      • postProcessBeforeInitialization(Object, String):在bean的初始化之前执行
    • postProcessAfterInitialization(Object, String):在bean的初始化之后执行

    • 在容器中装配后置处理器

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
      <!--    装配后置处理器,id属性可以省略,class属性为后置处理器的全类名-->
      <!--    装配之后,对这个容器(也就是这个配置文件中)所有的bean都有效-->
          <bean id="processor" class="demo.processor.MyProcessor"></bean>
      </beans>
      
  • 注意:装配后置处理器会为当前容器(xml)中每个bean均装配(即每个bean都有这个后置处理器了),不能为局部bean装配后置处理器

8.3 添加后置处理器后bean的生命周期

① 通过构造器或工厂方法创建bean实例

② 为bean的属性设置值和对其他bean的引用

3.1 postProcessBeforeInitialization(Object, String):在bean的初始化之前执行

③ 调用bean的初始化方法

3.2 postProcessAfterInitialization(Object, String):在bean的初始化之后执行

④ bean可以使用了

当容器关闭时,调用bean的销毁方法

第九章 Spring中自动装配【基于XML】

代码看day06的P17-20,该部分建议看视频复习,总时长20分钟左右,代码示例部分看明白了也行

9.1 Spring中提供两种装配方式,所谓的装配可以理解成就是spring帮助我们new对象
  • 手动装配:本章节以上的的,手动的给属性赋值了
  • 自动装配:本章节讨论基于XML的自动装配,还有一种基于注解的自动装配,看10.2小节
9.2 Spring自动装配语法及规则(注意要重写set方法,因为自动装配是基于set注入的)
  • 在bean标签中添加属性:Autowire即可

    • byName:类中要装配对象的属性名称与容器中的bean标签的Id进行匹配,如果属性名与beanId数值一致,则自动装配成功

      代码示例

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
      
          <bean id="daoImpl" class="demo.dao.daoImpl"></bean>
      
      <!--    将bean标签中的autowire属性设置成byName,就是byName的自动装配方式-->
      <!--    byName的自动装配方式,意思匹配该类的属性与此容器中是否有其他bean标签的id名相等的,有则自动装配-->
      <!--    比如这里就是自动装配了daoImpl,因为在serviceImpl类中有个属性叫daoImpl-->
          <bean id="serviceImpl" 
                class="demo.service.serviceImpl" 
                autowire="byName"></bean>
      </beans>
      

      serviceImpl类:

      public class serviceImpl implements service {
          daoImpl daoImpl = new daoImpl();//注意这里可以不用new对象,不然会报空指针异常
          								//此处记得要重写set方法,因为自动装配是基于set方法注入的!!!
          @Override
          public void saveObject() {
              daoImpl.insertObject();
          }
      }
      
    • byType:类中要装配对象的属性类型与容器中class进行匹配,属性的类型是一个接口,class对应的类的这个接口的实现类,如果唯一匹配则自动装配成功,装配成class对应的类【如第二张图中DeptServiceImpl类,第十七行配置了自动装配,其类中成员 deptDao 会被第二张图中第七行的<bean>装配】

      • 匹配0个:未装配

      • 匹配多个,会报错

        expected single matching bean but found 2: deptDao,deptDao2

  • 注意:基于XML方式的自动装配,只能装配非字面量数值

  • 代码示例:注意第二张图的第七行的<bean>必须有,deptService这个<bean>配置为自动给

9.3 总结
  • 基于xml自动装配,底层使用set注入,也就意味着应提供属性相应的set方法

  • 最终:不建议使用byName、byType,建议使用注解方式自动装配

day07

第十章 Spring中注解【非常重要】

10.1 使用注解将对象装配到IOC容器中

约定:约束>配置【注解>XML】>代码

位置:在类上面标识

注意:

  • Spring本身不区分四个注解【四个注解本质是一样的@Component】,提供四个注解的目的只有一个:提高代码的可读性
  • 如:在类上加上注解:@Repository ,就不用配置XML的自动装配的bean了(也就是第九章代码示例第二张图中的第七行代码)
  • 使用注解装配对象,默认将类名首字母小写作为bean的Id。
  • 也可以使用value属性,设置beanId;当注解中只使用一个value属性时,value关键字可省略。如:使用@Repository(String value() default “” )来指定beanId。@Repository(value = “deptDapImpl**”)**

使用注解其实就是代替了标签将对象装配到容器中

  • 注意1:使用注解自动装配后,注解的形参就是bean的Id,具体看上面详解

  • 注意2:@Scope(“prototype”)此注解是设置装配对象是单例还是多例,getBean时,若beanId相同,则获取的对象为同一个(相同地址),也就是相当于;若beanId不同,对获取的是不同的对象(地址不同),也就是类似。scope属性的值看 7.2小节

  • 注意3:若注解只有一个属性value,则编写参数时可以省略value=,多个属性则必须加上

  • 装配对象四个注解

    • @Component:装配普通组件到IOC容器
    • @Repository:装配持久化层组件到IOC容器
    • @Service:装配业务逻辑层组件到IOC容器
    • @Controller:装配表述层|控制层组件到IOC容器。JavaWeb叫Servlet

    值得注意的是,当使用这四个注解不指明value的值时,他们会默认使用该类首字母小写的类名

  • 使用注解步骤

    • 导入相关jar包【已导入,即导入spring-context依赖即可】

    • 开启组件扫描,就是扫描器扫描所有的注解

      <!--    开启组件扫描
              base-package:设置能扫描注解的包名【当前包及其子包】,
      					  若有多个包,中间逗号隔开【不推荐,若包太多,太繁琐】,应采用第十一章的方法
            未开启或者context的命名空间会报错:NoSuchBeanDefinitionException 异常
          -->
          <context:component-scan base-package="demo"></context:component-scan>
      
    • 使用注解标识组件

  • 代码示例,为简化步骤,Controller的注解自动装配与Dao和Service方法类似,用@Controller即可

    • DeptDaoImpl类,持久化层组件使用@Repository注解

      @Repository(value = "deptDao")
      public class DeptDaoImpl implements DeptDao {
          @Override
          public void insertData() {
              System.out.println("插入数据成功!!");
          }
      }
      
    • DeptServiceImpl类,业务逻辑层组件使用@Service注解

      @Service(value = "deptService")
      public class DeptServiceImpl implements DeptService {
          DeptDaoImpl daoImpl;
          @Override
          public void addData() {
              daoImpl.insertData();
          }
      }
      
    • DeptController类,控制层组件使用@Controller注解

    • @Controller("deptController")
      @Scope("prototype")//此注解是设置装配对象是单例还是多例
      public class DeptController {
          DeptService deptService;
          /**
           * 模拟处理数据
           * @date
           * @author
           * @return
           * @throws
           */
          public void saveDept(){
              deptService.addData();
          }
      }
      
    • applicationContext.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns: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
             https://www.springframework.org/schema/context/spring-context.xsd">
      
          <!-- 注意:命名空间是第4、7、9这三行的,别弄错了 -->
      
          <!--    开启组件扫描
              base-package:设置能扫描注解的包名【当前包及其子包】,
      					  若有多个包,中间逗号隔开【不推荐,若包太多,太繁琐】,应采用第十一章的方法
            未开启或者context的命名空间会报错:NoSuchBeanDefinitionException 异常
          -->
          <context:component-scan base-package="com.atguigu"></context:component-scan>
      
      </beans>
      
  • 测试代码

    /**
    * 测试四个注解【将对象装配到容器中】
    */
    public class MyTest {
        @Test
        public void test1(){
            ApplicationContext ioc =
                    new ClassPathXmlApplicationContext("applicationContext.xml");
            DeptController deptController = ioc.getBean("deptController", DeptController.class);
            DeptServiceImpl deptService = ioc.getBean("deptService", DeptServiceImpl.class);
            DeptDaoImpl deptDao = ioc.getBean("deptDao", DeptDaoImpl.class);
            Dept dept = ioc.getBean("dept", Dept.class);
    
            System.out.println("deptController" + deptController);
            System.out.println("deptService" + deptService);
            System.out.println("deptDao" + deptDao);
            System.out.println("dept" + dept);
    //        运行结果:
    //        deptControllerdemo.controller.DeptController@7a52f2a2
    //        deptServicedemo.service.impl.DeptServiceImpl@78047b92
    //        deptDaodemo.dao.impl.DeptDaoImpl@8909f18
    //        deptDept{deptId=null, deptName='null', empList=null, empMap=null}
        }
    }
    
10.2 使用注解装配对象中属性【自动装配】

之前是xml自动装配,对比9.2小节

  • 属性装配时,deptDao**.insertDept**(dept); 会报空指针异常

  • @Autowired注解

    • 作用:自动装配对象中属性

    • 装配原理:反射机制

    • 装配方式

      • 先按照byType进行匹配

        • 匹配1个:匹配成功,正常使用

        • 匹配0个:

          • 默认【@Autowired(required=true)】报错

            /*expected at least 1 bean which qualifies as autowire candidate. 	Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
            */
            
          • @Autowired(required=false),不会报错

        • 匹配多个

          • 再按照byName进行唯一筛选

            • 筛选成功【对象中属性名称==beanId】,正常使用

            • 筛选失败,报如下错误:

              【对象中属性名称!=beanId】
              //expected single matching bean but found 2: deptDao,deptDao2
              【又有两个 beanId 相同】
              //最上面:BeanDefinitionStoreException
              //Caused by ... : Annotation-specified bean name 'deptDao' for bean class [com.atguigu.dao.impl.DeptDaoImpl2] conflicts with existing, non-compatible bean definition of same name and class [com.atguigu.dao.impl.DeptDaoImpl]
              
    • @Autowired中required属性

      • true:表示被标识的属性必须装配数值,如未装配,会报错
      • false:表示被标识的属性不必须装配数值,如未装配,不会报错
  • @Qualifier注解

    • 作用:配合@Autowired一起使用,通过装配对象注解的参数,设置的beanId名称装配到@Qualifier

      注解的属性中,此时也就不需要beanId与属性名称一致了

    • 注意:不能单独使用,需要与@Autowired一起使用。一般用于有多个同类型的bean时,一个的时候直

      ​ 接通过byType的装配好

    • 用法:@Qualifier(“beanId”)

      要装配的属性

      @Service(value = "deptService")
      public class DeptServiceImpl implements DeptService {
          @Autowired  //该注解是自动装配属性,按照先使用byType后byName的方式
          @Qualifier("deptDaoImpl1")  //配合Autowired使用,当容器中有多个该类对象时指定id使用哪个
          DeptDao deptDaoImpl;
          @Override
          public void addData() {
              deptDaoImpl.insertData();
          }
      }
      
  • @Value注解

    • @Autowired和@Qualifier都是用来装配非字面量值的
    • 作用:装配对象中的属性【字面量数值】,也就是初始值。如:@Value(“le”) String name;
    • @Component("student")
      public class Student {
          @Value("1")
          private Integer studentId;
          @Value("刘")
          private String studentName;
      }
      //在创建该类对象时就会z
      

第十一章 Spring中组件扫描

11.1 默认使用情况
<!--    开启组件扫描
        base-package:设置能扫描注解的包名【当前包及其子包】,
					  若有多个包,中间逗号隔开【不推荐,若包太多,太繁琐】,应采用第十一章的方法
      未开启或者context的命名空间会报错:NoSuchBeanDefinitionException 异常
    -->
    <context:component-scan base-package="com.atguigu"></context:component-scan>
11.2 包含扫描
  • 注意:
    • 使用包含扫描之前,必须设置use-default-filters=“false”关闭当前及其子包的扫描】,关闭默认的组件扫描
    • type
      • annotation:设置被扫描注解的全类名
      • assignable:设置被扫描**[实现]类**的全类名
<!--    包含扫描,指定需要扫描的组件-->
    <context:component-scan base-package="demo" use-default-filters="false"><!--关闭当前包及其子包的扫描-->
<!--        这种是扫描指定的注解-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        
<!--        这种是扫描指定的类-->
        <context:include-filter type="assignable" expression="demo.service.impl.DeptServiceImpl"/>
        <context:include-filter type="assignable" expression="demo.pojo.Student"/>
    </context:component-scan>
11.3 排除扫描
<!--    排除扫描-->
<!--就是不扫描指定包下的指定类或者声明的注解-->
    <context:component-scan base-package="demo">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="assignable" expression="demo.controller.DeptController"/>
    </context:component-scan>

第十三章 Spring完全注解开发【0配置】

13.1 完全注解开发步骤
  1. 创建配置类
  2. 在class上面添加注解
    • @Configuration:标识当前类是一个配置类,作用:代替XML配置文件
    • @ComponentScan:设置组件扫描当前包及其子包
  3. 使用AnnotationConfigApplicationContext容器对象
13.2 示例代码
@Configuration  //该注解表明此类是作为配置的类
@ComponentScan(basePackages = "demo")       //此注解表明组件扫描的包名
public class SpringConfig {
}
@Test
    public void test1(){
        //创建容器对象,基于XML配置文件,该处未使用xml配置了,所以不能使用该构造器
//        ApplicationContext context =
//                new ClassPathXmlApplicationContext("applicationContext.xml");
        //使用AnnotationConfigApplicationContext容器对象
        
        ApplicationContext ioc =
                new AnnotationConfigApplicationContext(SpringConfig.class);
//        此处记得注意,getBean的名字是该类上标明注解的名
//        比如在DeptServiceImpl中就声明了注解:@Service(value = "deptService"),
//        所以下面传入的bean名应该是deptService
        DeptServiceImpl deptServiceImpl = ioc.getBean("deptService", DeptServiceImpl.class);
        deptServiceImpl.addData();

    }

第十四章 Spring集成Junit4

了解,后面会讲Junit5【看20.3 小节】,现在都用Junit5了

14.1 为什么要集成?
  • 现象:每写一个测试方法都得重新获取一个容器对象,且获取的步骤都大致相同,只有参数不同。

  • 好处:一旦注解写好之后,将不再需要创建容器对象。只需在测试类定义一个属性,通过注解的方式给属性装

    即可。

14.2 集成步骤
  1. 导入jar包

    • spring-test-5.3.1.jar 【版本需与spring-context版本一致】

      <!-- junit4 的jar包-->
      
      <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>5.3.1</version>
          <scope>test</scope>
      </dependency>
      
  2. 指定Spring的配置文件的路径

    • 【@ContextConfiguration】
  3. 指定Spring环境下运行Junit4的运行器

    • @RunWith(Junit的版本)。如:@RunWith(SpringJUnit4ClassRunner.class)
14.3 示例代码
@ContextConfiguration(locations = "classpath:applicationContext.xml")//指明配置文件的路径
@RunWith(SpringJUnit4ClassRunner.class)//指定Spring环境下运行Junit4的运行器
public class JunitTest {
    @Autowired  //此处记得自动装配属性
    private DeptServiceImpl deptService;
    @Test
    public void test1(){
//        ApplicationContext ioc =
//                new AnnotationConfigApplicationContext(SpringConfig.class);
//        deptService = ioc.getBean("deptService", DeptServiceImpl.class);
//        这种做法需要每次此时都要new一个容器对象


//        这种做法由于属性自动装配了可以直接使用
        deptService.addData();
    }
}

第十五章 AOP前奏

本章主要讲解代理模式
可回顾javase的静态代理和动态代理
控制台输出对象,类名以$开头的就代表是代理对象

15.1 代理模式
  • 代理模式:我们需要做一件事情,又不期望自己亲力亲为,此时,可以找一个代理【中介】

  • 我们【目标对象】与中介【代理对象】不能相互转换,因为是“兄弟”关系

在这里插入图片描述

15.2 为什么需要代理【程序中】
  • 需求:实现【加减乘除】计算器类

    • 在加减乘除方法中,添加日志功能【在计算之前,记录日志。在计算之后,显示结果。】
  • 实现后发现问题如下

    • 日志代码比较分散,可以提取日志类
    • 日志代码比较混乱,日志代码【非核心业务代码】与加减乘除方法【核心业务代码】书写一处
  • 总结:在核心业务代码中,需要添加日志功能,但不期望在核心业务代码中书写日志代码

    • 此时:使用代理模式解决问题【先将日志代码横向提取到日志类中,再动态织入回到业务代码中
15.3 手动实现动态代理环境搭建
  • 实现方式

    • 基于接口实现动态代理: JDK动态代理
    • 基于继承实现动态代理: Cglib、Javassist动态代理
  • 实现动态代理关键步骤

    • 一个类:Proxy
      • 概述:Proxy代理类的基类【类似Object】
      • 作用:newProxyInstance():创建代理对象
    • 一个接口:InvocationHandler
      • 概述:实现【动态织入效果】关键接口
      • 作用:invoke(),执行invoke()实现动态织入效果
15.4 手动实现动态代理关键步骤

注意:代理对象与实现类【目标对象】是“兄弟”关系,不能相互转换

  • 创建类【为了实现创建代理对象工具类】

  • 提供属性【目标对象:实现类】

  • 提供方法【创建代理对象】

  • 提供有参构造器【避免目标对为空】

  • 代码示例:

    • Calc接口

      public interface Calc {
          public int add(int a, int b);
          public int sub(int a, int b);
          public int mul(int a, int b);
          public int div(int a, int b);
      }
      
    • CalcImpl类

      
      public class CalcImpl implements Calc {
          @Override
          public int add(int a, int b) {
              return a + b;
          }
      
          @Override
          public int sub(int a, int b) {
              return a - b;
          }
      
          @Override
          public int mul(int a, int b) {
              return a * b;
          }
      
          @Override
          public int div(int a, int b) {
              return a / b;
          }
      }
      
    • MyLogging类

      
      public class MyLogging {
          /**
           * 方法之前
           * @param methodName
           * @param args
           */
          public static void beforeMethod(String methodName, Object[] args){
              System.out.println("==>Calc中"+ methodName +"方法(),参数:" + Arrays.toString(args));
          }
      
          /**
           * 方法之后
           * @param methodName
           * @param rs
           */
          public static void afterMethod(String methodName, Object rs){
              System.out.println("==>Calc中"+ methodName +"方法(),结果:" + rs);
          }
      }
      
    • MyProxy类(代理类)

      package com.atguigu.beforeaop;
      
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      
      public class MyProxy {
      
          /**
           * 目标对象【目标客户】
           */
          private Object target;
      
          public MyProxy(Object target){
              this.target = target;
          }
      
          /**
           * 获取目标对象的,代理对象
           * @return
           */
          public Object getProxyObject(){
              Object proxyObj = null;
      
              /**
                  类加载器【ClassLoader loader】,目标对象类加载器
                  目标对象实现接口:Class<?>[] interfaces,目标对象实现所有接口
                  InvocationHandler h
               */
              ClassLoader classLoader = target.getClass().getClassLoader();
              Class<?>[] interfaces = target.getClass().getInterfaces();
              //创建代理对象
              proxyObj = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
                  //执行invoke()实现动态织入效果
                  @Override
                  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                      //获取方法名【目标对象】
                      String methodName = method.getName();
                      //执行目标方法之前,添加日志
                      MyLogging.beforeMethod(methodName,args);
                      //触发目标对象目标方法
                      Object rs = method.invoke(target, args);
                      //执行目标方法之后,添加日志
                      MyLogging.afterMethod(methodName,rs);
                      return rs;
                  }
              });
              return proxyObj;
          }
      
      //    class invocationImpl implements InvocationHandler{
      //    }
      
      }
      
    • 测试方法

      @Test
      public void testBeforeAop(){
      
          //        int add = calc.add(1, 2);
          //        System.out.println("add = " + add);
      
          //目标对象
          Calc calc = new CalcImpl();
          //代理工具类
          MyProxy myProxy = new MyProxy(calc);
          //获取代理对象
          Calc calcProxy = (Calc)myProxy.getProxyObject();
          //测试
          //        int add = calcProxy.add(1, 2);
          int div = calcProxy.div(2, 1);
      
      }
      

day08

第十六章 Spring中AOP【重点】

16.1 AspectJ框架【AOP框架】
  • AspectJ是Java社区里最完整最流行的AOP框架。不是Spring家族的,Spring只是提供支持。
  • 在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。
  • 使用了这个框架后,动态代理将不需要我们手动编写
16.2 使用AspectJ步骤
  1. 添加jar包支持

    <!--        添加AspectJ-->
    <!--spirng-aspects的jar包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.1</version>
    </dependency>
    
  2. 配置文件

    • 开启组件扫描
    • 开启AspectJ注解支持
    <!--    开启组件扫描-->
        <context:component-scan base-package="com.atguigu"></context:component-scan>
    <!--    开启AspectJ注解支持,需引入名称空间-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  3. CalcImpl类上面添加注解:@Component(“calc”)

  4. 将MyLogging类上面添加注解

    • @Component:将当前类标识为一个组件,使该类装配到容器中
    • @Aspect:将当前类标识为切面类【非核心业务提取类】
  5. 将MyLogging中的方法中添加通知注解(一共五个)

    • @Before。参数为value,值为切入点表达式(看17.3小节
    
    @Component      //将当前类标识为一个组件
    @Aspect         //将当前类标识为【切面类】【非核心业务提取类】
    public class MyLogging {
        /**
         * 方法之前,方法里的好多东西暂时未讲
         */
        //此注解可以理解为就是在value里面这个方法执行之前,先执行下面这个方法beforeMethod
        @Before(value = "execution(public int demo.CalcImpl.add(int, int))") 
        public  void beforeMethod(JoinPoint joinPoint){
            //通过joinPoint获取参数和方法名
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
    
            System.out.println("==>Calc中"+ methodName +"方法(),参数:" + Arrays.toString(args));
        }
    }
    
  6. 测试

    @Test
    public void testAop(){
        //创建容器对象
        ApplicationContext context =
                new ClassPathXmlApplicationContext("applicationContext_aop.xml");
    
        Calc calc = context.getBean("calc", Calc.class);
    
         //错误的,代理对象不能转换为目标对象【代理对象与目标对象是兄弟关系】
         //getBean方法,第一个参数决定获取容器中的哪个对象,
         //            第二个参数决定将容器中获取的对象赋值给什么类型的对象
         //CalcImpl calc = context.getBean("calc", CalcImpl.class);
    
        int add = calc.add(1, 2);
    
    }
    
//TO DO 通知注解
16.3 Spring中AOP概述
  • AOP:Aspect-Oriented Programming,面向切面编程【面向对象一种补充】,对对象的横向补充
    • 优势:
      • 解决代码分散问题
      • 解决代码混乱问题
  • OOP:Object-Oriented Programming,面向对象编程,对对象的纵向补充
16.4 Spring中AOP相关术语
  1. 横切关注点:非核心业务代码【日志】,称之为横切关注点

  2. 切面(Aspect):将横切关注点提取到类中,这个类称之为切面类

  3. 通知(Advice):将横切关注点提取到类中之后,横切关注点更名为:通知也叫通知方法

  4. 目标(Target):目标对象,指的是需要被代理的对象【实现类(CalcImpl)】

  5. 代理(Proxy):代理对象可以理解为:中介

  6. 连接点(Joinpoint):通知方法需要指定通知位置(也就是@before的value的值),

    ​ 这个位置称之为:连接点【通知之前,没执行通知注解之前

    ​ 可以理解为就是非核心业务代码【日志】执行的位置(个人理解

  7. 切入点(pointcut):通知方法需要指定通知位置,

    ​ 这个位置称之为:切入点【通知之后,执行了通知注解之后,更名为切入点

    如何区分连接点和切入点,简单理解以下:可以理解为,可以插入通知的地方为连接点,在连接点插入通知之后,该连接点就被称为切入点。(个人理解)连接点是在应用执行过程中能够插入切面(Aspect)的一个点

第十七章 AspectJ详解【重点】

17.1 AspectJ中切入点表达式
  • 语法:@Before(value=“execution(权限修饰符 返回值类型 包名.类名.方法名(参数类型))”)

  • 通配符

    【*】:

    ​ 【*】:可以代表任意权限修饰符&返回值类型

    ​ 【*】:可以代表任意包名、任意类名、任意方法名

    【…】:

    ​ 【…】:代表任意参数类型及参数个数

  • 重用切入点表达式

    1. 使用**@PointCut注解**,提取可重用的切入点表达式

      @Pointcut("execution(* com.atguigu.aop.CalcImpl.*(..) )")
      public void myPointCut(){}
      
    2. 使用**方法名()**引入切入点表达式,支持引用其他类的重用切入点表达式,在方法名加上其他类的全类名即可(两个类属于同包可直接写类名.方法名),如引用MyLogging的重用切入表达式:@Before(value = “MyLogging.myPointCut()”)

      //之前:
      @Before(value = "execution(public int com.atguigu.aop.CalcImpl.add(int, int) )")
      public void beforeMethod(JoinPoint joinPoint){}
      //使用重用切入点表达式之后
      @Before(value = "myPointCut()")
      public void beforeMethod(JoinPoint joinPoint){}
      
      @After("myPointCut()")
      public void afterMethod(){...}
      
17.2 AspectJ中JoinPoint对象
  • JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象(通俗点来说就是JoinPoint对象能够获取到核心业务代码方法的参数和方法名等)

  • JoinPoint【切入点对象】,通知方法的参数不能随便写,要是JoinPoint类的对象

  • 作用:

    • 获取方法名称

      //获取方法签名【方法签名=方法名+参数列表】
      joinPoint.getSignature();
      //获取方法名称
      String methodName = joinPoint.getSignature().getName();
      
    • 获取参数

      Object[] args = joinPoint.getArgs();
      
17.3 AspectJ中通知
  • 前置通知

    • 语法:@Before

    • 执行时机:指定方法执行之前执行【如目标方法中有异常,会执行

      • 指定方法:切入点表达式设置位置
    • 示例代码

      //重用的切入表达式
          @Pointcut("execution(public int demo.CalcImpl.add(int, int))")
          public void myPointCut(){}
      
          /**
           * 前置通知,在方法执行之前通知
           * @date
           * @author
           * @return
           * @throws
           */
          //此注解可以理解为就是在value里面这个方法执行之前,先执行下面这个方法beforeMethod
          @Before(value = "myPointCut()")
          public  void beforeMethod(JoinPoint joinPoint){
              //通过joinPoint获取参数和方法名
              String methodName = joinPoint.getSignature().getName();
              Object[] args = joinPoint.getArgs();
      
              System.out.println("【前置通知】==>Calc中"+ methodName +"方法(),参数:" + Arrays.toString(args));
          }
      
  • 后置通知

    • 语法:@After

    • 执行时机:指定方法所有通知执行之后执行【如目标方法中有异常,会执行

    • 示例代码

      /**
           * 后置通知,在方法执行之后通知
           * @date
           * @author
           * @return
           * @throws
           */
          @After("execution(* *.*.add(..))")
          public void afterMethod(JoinPoint joinPoint){
              String methodName = joinPoint.getSignature().getName();
      
              System.out.println("【后置通知】==>Calc中"+ methodName +"方法(),执行之后:");
          }
      
      
  • 返回通知

    • 语法:@AfterReturnning

    • 执行时机:指定方法返回结果时执行,比后置通知早【如目标方法中有异常,不执行

    • 注意事项:@AfterReturnning中returning属性与入参中参数名一致,即将返回值封装到哪个形参中

    • 拓展:属性value可以用pointcut代替,底层同时提供了value和pointcut,建议用value,风格更统一

    • 示例代码

      /**
           * 返回通知,在方法返回结果之后执行
           * @date
           * @author
           * @return
           * @throws
           */
      //    获得的返回结果应该作为参数声明在方法中如:Object result,且在注解中指明returning的值和参数名一致,如:returning = "result"
          @AfterReturning(value = "execution(public int demo.CalcImpl.add(int,int))",returning = "result")
          public void afterReturn(JoinPoint joinPoint,Object result){
              String methodName = joinPoint.getSignature().getName();
      
              System.out.println("【返回通知】==>Calc中"+ methodName +"方法(),返回结果之后,结果为:" + result);
          }
      
  • 异常通知

    • 语法:@AfterThrowing

    • 执行时机:指定方法出现异常时执行,比后置通知早,与返回通知两者只会执行其一

      ​ 【如目标方法中无异常,不执行

    • 注意事项:@AfterThrowing中的throwing属性值与入参参数名一致

    • 示例代码:

      /**
           * 异常通知
           * @date
           * @author
           * @return
           * @throws
           */
          @AfterThrowing(value = "myPointCut()",throwing = "ex")
          public void afterException(JoinPoint joinPoint,Exception ex){
              //第二个形参写 Throwable ex 也可,Throwable是Exception的父类
              String methodName = joinPoint.getSignature().getName();
      
              System.out.println("【异常通知】==>Calc中"+ methodName +"方法(),出现异常执行,异常为:" + ex);
          }
      
    • 总结

      • 有异常:前置通知=》异常通知=》后置通知
      • 无异常:前置通知=》返回通知=》后置通知
  • 环绕通知【前四个通知整合】

    • 语法:@Around

    • 作用:整合前四个通知

    • 注意:

      • 参数中必须使用ProceedingJoinPoint(是JoinPoint的一个子类),而不能使用JoinPoint,因为JoinPoint没有proceed方法
      • proceed方法是用来调用(执行)目标方法的,这样就可以在执行proceed方法前后等等位置,使用try-catch-finally,加上另外四个通知,起到整合四个通知的作用
    • 环绕通知必须将返回结果,作为环绕通知方法的返回值

  • 示例代码

    /**
         * 环绕通知
         * @date
         * @author
         * @return
         * @throws
         */
        @Around(value = "myPointCut()")
        public Object around(ProceedingJoinPoint joinPoint){
    
            String methodName = joinPoint.getSignature().getName();
            Object[] args = joinPoint.getArgs();
    
            Object result = null;
            try {
                //这里写前置通知
                System.out.println("【前置通知】==>Calc中"+ methodName +"方法(),参数:" + Arrays.toString(args));
    
                result = joinPoint.proceed();
    
                //这里写返回通知
                System.out.println("【返回通知】==>Calc中"+ methodName +"方法(),返回结果之后,结果为:" + result);
    
            } catch (Throwable ex) {
                ex.printStackTrace();
                //这里写异常通知
                System.out.println("【异常通知】==>Calc中"+ methodName +"方法(),出现异常执行,异常为:" + ex);
    
            }finally {
                //这里写后置通知
                System.out.println("【后置通知】==>Calc中"+ methodName +"方法(),执行之后:");
    
            }
            return result;
        }
    
17.4 定义切面优先级
  • 现象:可能在被代理类方法之前,添加日志功能、验证功能等多个切面类,而这些类又有相同类型的通知,但此时却不确定他们的执行顺序(假设都有前置通知,此时将不确定谁的前置通知先执行)。

  • 语法:@Order(value=index)

    • index是int类型,默认值是int可存储的最大值
    • 数值越小,优先级越高【一般建议使用正整数】
  • 示例代码

    @Component  //将该类表示为一个组件,可以装入到ioc容器中
    @Aspect     //将该类标识为一个切面类
    @Order(value = 5)//指明该切面类的优先级,value越小,优先级越高,通知在其他切面类通知之前执行
    public class MyValidate {}
    
    @Component  //将此类标识为一个组件,可以装配到ioc容器中
    @Aspect     //将此类表示为切面列【非核心业务提取类】
    @Order(value = 2)   //指明该切面类的优先级,value越小,优先级越高,通知在其他切面类通知之前执行
    public class MyLogging {}
    
17.5 基于XML方式配置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: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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置计算器实现类-->
    <bean id="calculator" class="com.atguigu.spring.aop.xml.CalculatorImpl"></bean>

    <!--配置切面类-->
    <bean id="loggingAspect" class="com.atguigu.spring.aop.xml.LoggingAspect"></bean>

    <!--AOP配置-->
    <aop:config>
        <!--配置切入点表达式-->
        <aop:pointcut id="pointCut"
                      expression="execution(* com.atguigu.spring.aop.xml.Calculator.*(..))"/>
        <!--配置切面-->
        <aop:aspect ref="loggingAspect">
            <!--前置通知-->
            <aop:before method="beforeAdvice" pointcut-ref="pointCut"></aop:before>
            <!--返回通知-->
            <aop:after-returning method="returningAdvice" pointcut-ref="pointCut" returning="result"></aop:after-returning>
            <!--异常通知-->
            <aop:after-throwing method="throwingAdvice" pointcut-ref="pointCut" throwing="e"></aop:after-throwing>
            <!--后置通知-->
            <aop:after method="afterAdvice" pointcut-ref="pointCut"></aop:after>
            <!--环绕通知-->
            <aop:around method="aroundAdvice" pointcut-ref="pointCut"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

第十八章 Spring中JdbcTemplate

18.1 JdbcTemplate简介
  • Spring提供的JdbcTemplate是一个小型持久化层框架,简化Jdbc代码。
    • Mybatis是一个半自动化的ORM持久化层框架
18.2 JdbcTemplate基本使用
  • 导入jar包

    <!--spring-context-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.1</version>
    </dependency>
    
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    
    <!--导入mysql的jar包-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.37</version>
    </dependency>
    <!--导入druid的jar包-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    
    <!-- spring-orm:jdbcTemplate的jar包【前提有:spring的jar,MySQL的jar包,druid的jar包】-->
    <!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
    <!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.3.1</version>
    </dependency>
    
  • 编写配置文件

    • db.properties:设置连接数据库属性

    • applicationContext.xml【spring配置文件】

      • 加载外部属性文件
      • 装配数据源【DataSources】
      • 装配JdbcTemplate
    • 示例代码

      <!--    加载外部属性文件-->
          <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
      
      <!--    将datasource装配进容器中-->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
              <property name="password" value="${db_password}"></property>
              <property name="username" value="${db_username}"></property>
              <property name="url" value="${db_url}"></property>
              <property name="driverClassName" value="${db_driver}"></property>
      
          </bean>
      
      <!--    装配JdbcTemplate-->
          <!-- 自定义类可用注解来装配bean,但第三方得用xml方式装配bean(毕竟咱也不可能修改对方的源代码吧) -->
          <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
              <property name="dataSource" ref="dataSource"></property>
          </bean>
      
  • 使用核心类库【JdbcTemplate】

    • 通过getBean获取JdbcTemplate的对象
18.3 JdbcTemplate的常用API

JdbcTemplate默认:自动提交事务

  • jdbcTemplate.update(String sql,Object… args):通用的增删改方法

    //        ***********通用的增删改方法****************
    //        jdbcTemplate.update(String sql,Object... args):通用的增删改方法
    //        增
    //        String sql = "insert into tbl_dept (dept_name) values (?)";
    //        jdbcTemplate.update(sql,"财贸部");
    //        改
    //        String sql = "update tbl_dept set dept_name = ? where dept_id = ?";
    //        jdbcTemplate.update(sql,"销售部",2);
    //        删
    //        String sql = "delete from tbl_dept where dept_id = ?";
    //        jdbcTemplate.update(sql,2);
    
  • jdbcTemplate.batchUpdate(String sql,List<Object[]> args):通用批处理增删改方法

    //        ***********通用批处理增删改方法****************
    //        其实批处理操作所传的参数可以理解为一个object[]对象为处理一条数据所需要的参数
    //        jdbcTemplate.batchUpdate(String sql,List<Object[]> args):通用批处理增删改方法
    //        删
    //        String sql = "delete from tbl_dept where dept_id = ?";
    //        List<Object[]> list = new ArrayList();
    //        Object[] o1 = {"3"};
    //        Object[] o2 = {"4"};
    //        list.add(o1);
    //        list.add(o2);
    //        jdbcTemplate.batchUpdate(sql,list);
    //        增
    //        String sql = "insert into tbl_employee (last_name,email,salary,dept_id) values(?,?,?,?)";
    //        List<Object[]> list = new ArrayList<>();
    //        Object[] objects1 = new Object[]{"洁洁","jiejie@qq.com",500.0,1};
    //        Object[] objects2 = new Object[]{"洁洁","jiejie@qq.com",500.0,1};
    //        Object[] objects3 = new Object[]{"洁洁","jiejie@qq.com",500.0,1};
    //        list.add(objects1);
    //        list.add(objects2);
    //        list.add(objects3);
    //        jdbcTemplate.batchUpdate(sql,list);
    //        改
            String sql = "update tbl_employee set last_name = ?,email = ? where id = ?";
            List<Object[]> list = new ArrayList<>();
            Object[] o1 = {"测试","ceshi@qq.com",6};
            Object[] o2 = {"测试","ceshi@qq.com",7};
            Object[] o3 = {"测试","ceshi@qq.com",8};
            Object[] o4 = {"测试","ceshi@qq.com",9};
            list.add(o1);
            list.add(o2);
            list.add(o3);
            list.add(o4);
            jdbcTemplate.batchUpdate(sql,list);
    
  • jdbcTemplate.queryForObject(String sql,Class clazz,Object… args):查询单个数值

    • 第二个参数为返回值类型

    • String sql = “select count(1) from tbl_xxx”;

      //        *******************查询单个数值********************
      //        jdbcTemplate.queryForObject(String sql,Class clazz,Object... args):查询单个数值
      //        String sql = "select count(*) from tbl_employee";
      //        Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
      //        System.out.println(integer);
      
  • jdbcTemplate.queryForObject(String sql,RowMapper rm,Object… args):查询单个对象

    • RowMapper跟DBUtils的XxxHandler用法类似

    • String sql = “select col1,col2… from tbl_xxx”;

    • sping中的RowMapper可以将数据中的每一行数据封装成用户定义的类。我们在数据库查询中,如果返回的类型是用户自定义的类型(其实我们在数据库查询中大部分返回的都是自定义的类)则需要包装(通俗点来说其实就是指定查询出来的多条数据封装成哪个类的对象。)

      //        jdbcTemplate.queryForObject(String sql,RowMapper<T> rm,Object... args):查询单个对象
              String sql = "select * from tbl_employee where id = ?";
      
              RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
      
      
      //        Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
      //        这里的参数不能直接写employee.class
              Employee employee = jdbcTemplate.queryForObject(sql, rowMapper, 1);
              System.out.println(employee);
      
  • jdbcTemplate.query(String sql,RowMapper rm,Obejct… args):查询多个对象

    //        jdbcTemplate.query(String sql,RowMapper<T> rm,Obejct... args):查询多个对象
            String sql = "select * from tbl_employee where id = ? or id = ?";
            RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
            List<Employee> employeeList = jdbcTemplate.query(sql, rowMapper, 1, 3);
            System.out.println(employeeList);
    
18.4 使用JdbcTemplate搭建Service&Dao层
  • Service层依赖Dao层

  • Dao层依赖JdbcTemplate

  • 示例代码

    @Repository
    public class DeptDaoImpl implements DeptDao {
    
        @Autowired
        @Qualifier("jdbcTemplate")
        private JdbcTemplate jdbcTemplate;
    
        @Override
        public List<Dept> selectAllDepts() {
    
            String sql = "select dept_id,dept_name from tbl_dept";
            RowMapper<Dept> rowMapper = new BeanPropertyRowMapper<>(Dept.class);
            List<Dept> list = jdbcTemplate.query(sql, rowMapper);
    
            return list;
        }
    }
    
    /**
     * @author Chunsheng Zhang 尚硅谷
     * @create 2022/3/29 16:30
     */
    @Service("deptService")
    public class DeptServiceImpl implements DeptService {
    
        @Autowired
        @Qualifier("deptDaoImpl")
        private DeptDao deptDao;
    
        @Override
        public List<Dept> getAllDepts() {
            return deptDao.selectAllDepts();
        }
    }
    

第十九章 Spring声明式事务管理

回顾事务

  1. 事务四大特征【ACID】
    • 原子性:一气呵成的,不可分割的
    • 一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,而B账户没有加钱
    • 隔离性:事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
    • 持久性:持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
  2. 事务三种行为
    • 开启事务:connection.setAutoCommit(false)
    • 提交事务:connection.commit()
    • 回滚事务:connection.rollback()
19.1 Spring中支持事务管理
  • 编程式事务管理【传统事务管理】

    1. 获取数据库连接Connection对象

    2. 取消事务的自动提交【开启事务】

    3. 执行操作

    4. 正常完成操作时手动提交事务

    5. 执行失败时回滚事务

    6. 关闭相关资源

    • 不足:
      • 事务管理代码【非核心业务】与核心业务代码相耦合
        • 事务管理代码分散
        • 事务管理代码混乱
  • 声明式事务管理【使用AOP管理事务】

    • 先横向提取【事务管理代码】,再动态织入
19.2 使用声明式事务管理

不用事务管理代码,发现:同一个业务中,会出现局部成功及局部失败的现象【不正常】

  • 添加支持【AspectJ的jar包】

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.1</version>
    </dependency>
    
  • 创建数据库表

    CREATE TABLE `t_book` (
    `book_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
    `book_name` VARCHAR(20) DEFAULT NULL COMMENT '图书名称',
    `price` INT(11) DEFAULT NULL COMMENT '价格',
    `stock` INT(10) UNSIGNED DEFAULT NULL COMMENT '库存(无符号)',
    PRIMARY KEY (`book_id`)
    ) ENGINE=INNODB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
    INSERT INTO `t_book`(`book_id`,`book_name`,`price`,`stock`) VALUES (1,'斗破苍
    穹',80,100),(2,'斗罗大陆',50,100);
    CREATE TABLE `t_user` (
    `user_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
    `username` VARCHAR(20) DEFAULT NULL COMMENT '用户名',
    `balance` INT(10) UNSIGNED DEFAULT NULL COMMENT '余额(无符号)',
    PRIMARY KEY (`user_id`)
    ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
    INSERT INTO `t_user`(`user_id`,`username`,`balance`) VALUES (1,'admin',50);
    
  • 编写配置文件

    • 配置事务管理器 <bean>
    • 开启事务注解支持
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           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
           https://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx.xsd">
        
        <!-- 需引入外部配置文件,不然ref="dataSource"无效 -->
        
    	<!--  配置事务管理器-->
        <bean id="transactionManager" 			     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
    	<!-- 开启事务注解支持
           transaction-manager默认值:transactionManager。
    		该属性的值为事务管理器的beanId,若事务管理器的beanId是该默认值,则该属性可以不配置-->
        <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    </beans>
    
  • 在需要事务管理的业务方法上,添加注解**@Transactional**,该注解可以声明在方法上也可以声明在类上

    //添加事务后,如果是通过getbean方式获取,则第二个参数不能写接口的实现类.class,
    //因为获取的是代理对象,代理对象不能转换为实现类,他们是兄弟关系
    @Transactional
        @Override
        public void buyBook(Integer bookId, Integer userId) {
            //查询图书的价格
            Integer price = bookDao.getPriceByBookId(bookId);
            //更新图书的库存
            bookDao.updateStock(bookId);
            //更新用户的余额
            bookDao.updateBalance(userId, price);
        }
    
  • 关于@Transactional注解的属性:

    @Transactional(
    //            readOnly = true   //该属性(默认为false)设置成true说明此事务只可读,如果出现增删改会报错
    //            timeout = 3     //该属性(默认为-1)设置成3表示如果该事务三分钟没有执行完成,就会回滚
    //            noRollbackFor = {ArithmeticException.class}//该属性表示当出现以下异常时不回滚,注意这里是数组,指明异常的类对象
    //            noRollbackForClassName = {"java.lang.ArithmeticException"}//该属性表示当出现以下异常时不回滚,指明异常的全类名
        )
    
  • 总结:

    • 添加声明式事务管理之后,获取是代理对象,代理对象不能转换为目标对象【实现类】
    • 原因看16.3小节上面的代码块
19.3 Spring声明式事务管理属性

@Transactional注解的属性propagation、isolation、timeout、readOnly、rollbackFor[noRollbackFor]

  • 事务传播行为【Propagation】

    • 通俗理解就是当一个事务调用中调用的方法也是一个事务,这时候我们就可以设置事务的传播行为属性,有如下几种

    • 当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。

      • 如:执行事务方法method()1【事务x】之后,调用事务方法method2()【事务y】,此时需要设置method()2方法的事务传播行为,即设置按照事务x执行还是按照事务y执行
    • Spring的7种传播行为,均是Propagation枚举类的属性:

      传播属性描述
      REQUIRED**默认属性。**如果有事务在运行,当前的方法就在这个事务内运行;否则就启动一个新的事务,并在自己的事务内运行。即执行事务x
      REQUIRES_NEW当前的方法必须启动新事务,并在自己的事务内运行;如果有事务正在运行,应该将它挂起。即执行事务y
      SUPPORTS如果有事务在运行,当前的方法就在这个事务内运行,否则可以不运行在事务中。
      NOT_SUPPORTED当前的方法不应该运行在事务中,如果有运行的事务将它挂起
      MANDATORY当前的方法必须运行在事务中,如果没有正在运行的事务就抛出异常。
      NEVER当前的方法不应该运行在事务中,如果有正在运行的事务就抛出异常。
      NESTED如果有事务正在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行。
    • 代码示例

      • 结账方法 checkout()

        @Transactional
            @Override
            public void checkout(Integer[] bookIds, Integer userId) {
                for(Integer bookId : bookIds){
                    try {
                        bookService.buyBook(bookId,userId);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        
      • 购书方法 buyBook() 注意:该方法规定每次只能买一本

        @Transactional(													  
        //            propagation = Propagation.REQUIRED //这样使用的是调用该事务的事务,也就是当调用他的事务失败时,												   此事务也会回滚
                  propagation = Propagation.REQUIRES_NEW  //这样使用的是该事务本身的事务,就算调用他的父事务执行失													   败,该事务也不会回滚
          )
          @Override
          public void buyBook(Integer bookId, Integer userId) {
              //查询图书的价格
              Integer price = bookDao.getPriceByBookId(bookId);
              //更新图书的库存
              bookDao.updateStock(bookId);
              //更新用户的余额
              bookDao.updateBalance(userId, price);
        //        int x = 1/0;
          }
        
  • 事务隔离级别【Isolation】详情请看jdbc关于事务的隔离级别的md笔记,数据库高级会讲

    • 隔离级别概述:一个事务与其他事务之间的隔离等级【1,2,4,8】

    • 问题:脏读、不可重复读、幻读

    • 隔离等级,Isolation枚举类的属性

      • 读未提交【1】:READ_UNCOMMITTED
      • 读已提交【2】:READ_COMMITTED
      • 可重复读【4】:REPEATABLE_READ
      • 串行化【8】:SERIALIZABLE
    • 示例

    @Transactional(isolation = Isolation.DEFAULT)//使用数据库默认的隔离级别
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)//读未提交
    @Transactional(isolation = Isolation.READ_COMMITTED)//读已提交
    @Transactional(isolation = Isolation.REPEATABLE_READ)//可重复读
    @Transactional(isolation = Isolation.SERIALIZABLE)//串行化
    
  • 事务超时【timeout】

    • 设置事务的超时时间,达到指定时间后会强制事务回滚
    • 类型:int;单位:秒
    • 默认值:-1秒【未设置强制回滚时间】,也就是说,此时,Mybatis是不会去检测Spring事务是否超时的
  • 事务只读【readOnly】

    • 事务只有查询操作时,才能设置成只读
    • 默认值:false
  • 事务回滚【不回滚】:

    • 默认所有异常都会回滚
    • rollbackFor:设置需要回滚的异常;值为:异常类类名.class
    • noRollbackFor:设置不需要回滚的异常;值为:异常类类名.class
  • 示例

    @Transactional(
    //            readOnly = true,   //该属性(默认为false)设置成true说明此事务只可读,如果出现增删改会报错
    //            timeout = 3 ,    //该属性(默认为-1)设置成3表示如果该事务三分钟没有执行完成,就会回滚
    //            noRollbackFor = {ArithmeticException.class},//该属性表示当出现以下异常时不回滚,注意这里是数组,
        													//指明异常的类对象
    //            noRollbackForClassName = {"java.lang.ArithmeticException"}//该属性表示当出现以下异常时不回滚,指																		  明异常的全类名
        )
    
    @Override
        public void buyBook(Integer bookId, Integer userId) {
    //		让该事务休眠,TimeUnit.SECONDS说明失眠的时间单位是分钟
    //        try {
    //            TimeUnit.SECONDS.sleep(5);
    //        } catch (InterruptedException e) {
    //            e.printStackTrace();
    //        }
            //查询图书的价格
            Integer price = bookDao.getPriceByBookId(bookId);
            //更新图书的库存
            bookDao.updateStock(bookId);
            //更新用户的余额
            bookDao.updateBalance(userId, price);
    //        int x = 1/0;
        }
    
19.4 基于XML方式,配置声明式事务管理(了解)

该部分可看b站新版课件

第二十章 Spring5新特性

Spring5框架基于java8,运行时兼容JDK9

20.1 新注解

在这里插入图片描述

20.2 Spring5整合Log4j2
  • 导入jar包【并将Log4j的jar包删除】

    <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <version>2.11.2</version>
        <scope>test</scope>
    </dependency>
    
  • 编写配置文件【log4j2.xml】

不需要自己配置,复制即可(网上找类似的)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P8FrMF1G-1690105233709)(03_Spring.assets\14.jpg)]

20.3 Spring5整合Junit5
  • 导入jar包【并将Junit4的jar包删除】

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.7.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.1</version>
        <scope>test</scope>
    </dependency>
    
  • @ContextConfiguration(locations = “classpath:applicationContext.xml”) 还是用来指定 Spring 配置文件位置,和整合 junit4 一样。

  • @ExtendWith(SpringExtension.class) 表示使用 Spring 提供的扩展功能。

  • 复合注解(将上面两个注解整合) :@SpringJUnitConfig(locations = “classpath:applicationContext.xml”)

  • 测试示例

    /**
     * @author Chunsheng Zhang 尚硅谷
     * @create 2022/3/28 14:12
     */
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    @ExtendWith(SpringExtension.class)
    //方式二:@SpringJUnitConfig(locations = "classpath:applicationContext.xml")
    public class TestSpringJunit4 {
    
        @Autowired
        private DeptService deptService;
        
        @Test
        public void testService(){
            //创建容器对象
    //        ApplicationContext context =
    //                new ClassPathXmlApplicationContext("applicationContext.xml");
    //        DeptService deptService = context.getBean("deptService", DeptServiceImpl.class);
            deptService.saveDept(new Dept());
        }
    }
    

第二十一章 Jar包总结

21.1 Spring

导入jar包,该包中含有spring-jcl包(日志包),也就是说不用向mybatis一样依赖第三方日志 log4j

<!--导入spring-context,加上其依赖,共6个jar包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.1</version>
</dependency>
21.2 Junit4
  • maven工程下test目录就用到了到jar包

  • 如何根据方法快速生成单元测试方法?用法观看 idea快捷键.docx 文档

    <!--导入junit4.12 :一般情况要进行单元测试,只需加该包即可,下面那个包有其需求再加-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    
  • spring-test的使用:观看第十四章

    <!-- spring-test :
    					加入该依赖后,可实现
    					1> 不需要new容器对象,而通过注解来获取容器对象
    					2> 不需要getBean,而直接使用
    -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.1</version>
        <scope>test</scope>
    </dependency>
    
21.3 MySQL8.0.26
<!--导入mysql的jar包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <!--          <version>5.1.37</version> -->
    <version>8.0.26</version>
</dependency>
21.4 Druid

详细讲解:第五章

<!-- 前提:需加入MySQL的jar包-->
<!--导入mysql的jar包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <!--          <version>5.1.37</version> -->
    <version>8.0.26</version>
</dependency>

<!--导入druid的jar包-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>
21.5 ApectJ

详细讲解:第十六、十七章

<!--spirng-aspects的jar包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.1</version>
</dependency>
21.6 JdbcTemplate

详细讲解:第十八章

<!--spring-context-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.1</version>
</dependency>

<!--导入mysql的jar包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.37</version>
</dependency>
<!--导入druid的jar包-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

<!-- spring-orm:jdbcTemplate的jar包【前提有:spring的jar,MySQL的jar包,druid的jar包】-->
<!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
<!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>5.3.1</version>
</dependency>
21.7 Log4j2

详细讲解:20.2小节

<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.11.2</version>
    <scope>test</scope>
</dependency>
21.8 Junit5

详细讲解:20.3小节

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.7.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.1</version>
    <scope>test</scope>
</dependency>
 <artifactId>spring-test</artifactId>
  <version>5.3.1</version>
  <scope>test</scope>
```
21.3 MySQL8.0.26
<!--导入mysql的jar包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <!--          <version>5.1.37</version> -->
    <version>8.0.26</version>
</dependency>
21.4 Druid

详细讲解:第五章

<!-- 前提:需加入MySQL的jar包-->
<!--导入mysql的jar包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <!--          <version>5.1.37</version> -->
    <version>8.0.26</version>
</dependency>

<!--导入druid的jar包-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>
21.5 ApectJ

详细讲解:第十六、十七章

<!--spirng-aspects的jar包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.1</version>
</dependency>
21.6 JdbcTemplate

详细讲解:第十八章

<!--spring-context-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.1</version>
</dependency>

<!--导入mysql的jar包-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.37</version>
</dependency>
<!--导入druid的jar包-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

<!-- spring-orm:jdbcTemplate的jar包【前提有:spring的jar,MySQL的jar包,druid的jar包】-->
<!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
<!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>5.3.1</version>
</dependency>
21.7 Log4j2

详细讲解:20.2小节

<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.11.2</version>
    <scope>test</scope>
</dependency>
21.8 Junit5

详细讲解:20.3小节

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.7.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.1</version>
    <scope>test</scope>
</dependency>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值