Spring

Spring

第一章 Spring概述

1.1 什么是spring

  • spring就是一个java框架,使用java语言开发的,轻量级、开源的框架。可以在j2se、j2ee项目中都可以使用
  • spring核心技术:ioc、aop
  • spring又叫做容器,spring作为容器,装的是java对象。可以让spring创建java对象,给属性赋值
  • spring作用:实现解耦合,解决java对象之间的耦合,解决模块之间的耦合
  • tomcat也是容器:管理的是servlet、listener、filter等
    • 创建HelloServlet类,写web.xml文件
    • spring:创建SomeServiceImpl类,写spring的配置文件

1.2 spring的官网

  • 地址:https://spring.io

1.3 Spring优点

  • Spring是一个框架,是一个半成品的软件,有20个模块组成。它是一个容器管理对象,容器是装东西的,Spring容器不装文本、数字,装的是对象。Spring是存储对象的容器
    1. 轻量
  • Spring框架使用的jar都比较小,一般在1M以下或者几百kb。Spring核心功能的所需的jar总共在3M左右。Spring框架运行占用的资源少,运行效率高,不依赖其他jar
    2. 针对接口编程,解耦合
  • Spring提供了ioc控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合
    3. AOP编程的支持
  • 通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松付在Spring中,开发人员可以从繁杂的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量
    4. 方便集成各种优秀框架
  • Spring不排斥各种优秀的开源框架,相反Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts、Hibernate、MyBatis)等的直接支持,简化框架的使用。Spring像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个插头放入插线板,不需要可以轻易的移除

第二章 IoC控制反转

2.1 概念

  • IoC(Inversion of Control):控制反转,是一个理论、一个指导思想,指导开发人员如何使用对象,管理对象的。把对象的创建,属性赋值,对象的生命周期都交给代码之外的容器管理。
  1. IoC分为控制和反转:

    • 控制:对象创建,属性赋值,对象生命周期管理
    • 反转:把开发人员管理对象的权限转移给了代码之外的容器实现,由容器完成对象的管理
    • 正转:开发人员在代码中,使用new构造方法创建对象。开发人员掌握了对象的创建,属性赋值,对象从开始到销毁的全部过程。开发人员有对对象全部控制
    • 通过容器,可以使用容器中的对象(容器已经创建了对象,对象属性赋值了,对象也组装好了)。
    • Spring就是一个容器,可以管理对象,创建对象,给属性赋值。
  2. IoC的技术实现

    • DI(依赖注入):Dependency Injection,缩写是DI,是IoC的一种技术实现。程序只需要提供要使用的对象名称就可以了,对象如何创建,如何从容器中查找,获取都由容器内部自己实现。
    • 依赖名词:比如说A类使用了B的属性或者方法,叫做A依赖B
public class B{
    //方法
    public void Order(){}
}

public class A{
    //属性
    private B b = new B();
    //方法
    public void buy(){
        b.Order();
    }
}
//执行了A的buy方法
A a = new A();
a.buy();
  1. Spring框架使用的DI实现IoC
    • 通过Spring框架,只需要提供要使用的对象名称就可以了 ,从容器中获取名称对应的对象
    • spring底层使用的反射机制,通过反射创建对象,给属性赋值

2.2 Spring的配置文件

  • spring标准的配置文件:
    1. 跟标签是beans
    2. beans后面的是约束文件说明
    3. beans里面是bean的声明
    4. 声明是bean:bean就是java对象,spring容器管理的java对象,叫做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">

</beans>

2.3 spring容器创建对象的特点

  1. 容器对象ApplicationContext:接口
    • 通过ApplicationContext对象,获取要使用的其他java对象,执行getBean(“bean标签的id”)
  2. spring默认是调用类的无参数构造方法,创建对象
  3. spring读取配置文件,一次创建好所有的java对象,都放在map中

2.4 DI:给属性赋值

  • spring调用类的无参数构造方法,创建对象,对象创建后给属性赋值
  • 给属性赋值可以使用:
    1. xml配置文件中的标签和属性
    2. 使用注解
  • DI分类:
    1. set注入,也叫做设值注入
    2. 构造注入
    3. p命名空间注入
2.4.1 基于xml的DI
  • 在xml配置文件中使用标签和属性,完成对象创建,属性赋值
  1. set注入
    • 概念:spring调用类中的set方法,在set方法中可以完成属性赋值。推荐使用
<!--
    DI:给属性赋值
    简单类型:java中的基本数据类型和String
    1.set注入:spring调用类的set方法,通过set方法完成属性赋值
        简单类型的set注入:
        语法:<bean id="xxx" class="yyy">
                <property name="属性名" value="简单类型的数据值"/>
                ...
             </bean>
    2.引用类型set注入:
         语法:<bean id="xxx" class="yyy">
                <property name="属性名" ref="bean的id"/>
                ...
              </bean>
 -->
<!--简单类型set注入-->
<bean id="myStudent" class="com.kgc.ba01.Student">
    <property name="name" value="张三"/><!--setName("张三")-->
    <property name="age" value="20"/><!--setAge(20)-->
</bean>
<!--声明日期类-->
<bean id="myDate" class="java.util.Date">
    <property name="time" value="29595295295925"/><!--setTime()-->
</bean>
<!--声明school-->
<bean id="mySchool" class="com.kgc.ba01.School">
    <property name="name" value="北京大学"/>
    <property name="address" value="北京海淀区"/>
</bean>
<!--引用类型set注入-->
<bean id="myStudent" class="com.kgc.ba01.Student">
    <property name="name" value="张三"/><!--setName("张三")-->
    <property name="age" value="20"/><!--setAge(20)-->
    <!--引用类型的赋值-->
    <property name="school" ref="mySchool"/>
</bean>
  1. 构造注入
    • 构造注入:spring调用类中的有参构造方法,在创建对象的同时,给属性赋值
<!--
    构造注入:spring调用类的有参构造方法,创建对象同时给属性赋值
    语法:
        <bean id="xxx" class="yyy">
           <constructor-arg>:表示一个构造方法的形参
           标签里属性:   name:构造方法形参名
                       index:构造方法的参数位置
                       value:简单类型的形参值
                       ref  :引用类型的形参值
        </bean>
 -->
<!--声明school-->
<bean id="mySchool" class="com.kgc.ba01.School">
    <property name="name" value="北京大学"/>
    <property name="address" value="北京海淀区"/>
</bean>
<!--构造注入,使用name属性-->
<bean id="myStudent" class="com.kgc.ba01.Student">
    <constructor-arg name="name" value="李四"/>
    <constructor-arg name="age" value="22"/>
    <constructor-arg name="school" ref="mySchool"/>
</bean>
<!--构造注入,使用index,参数的位置,构造方法参数从左往右,位置是0、1、2-->
<bean id="myStudent2" class="com.kgc.ba01.Student">
    <constructor-arg index="1" value="22"/>
    <constructor-arg index="0" value="李四"/>
    <constructor-arg index="2" ref="mySchool"/>
</bean>
<!--构造注入,省略index属性-->
<bean id="myStudent3" class="com.kgc.ba01.Student">
    <constructor-arg  value="李四"/>
    <constructor-arg  value="22"/>
    <constructor-arg  ref="mySchool"/>
</bean>
<!--声明file对象-->
<bean id="myFile" class="java.io.File">
    <constructor-arg name="parent" value="D:\\course"/>
    <constructor-arg name="child" value="xxx.txt"/>
</bean>
  1. p命名空间注入
    • p命名的一种注入方式,属性直接写在bean标签里面,以p:属性=“属性值”命名
<!--
    p标签命名方式:p命名的一种注入方式,属性直接写在bean标签里面,以p:属性=“属性值”命名
    步骤:
        1.先在beans标签声明p命名:
        xmlns:p="http://www.springframework.org/schema/p"
        2.
        语法:
            简单类型
            <bean id="xxx" class="yyy" p:属性="属性值".../>
            引用类型
            <bean id="xxx" class="yyy" p:属性-ref="bean标签id".../>
 -->
<!--声明school-->
<bean id="mySchool" class="com.kgc.ba01.School">
    <property name="name" value="北京大学"/>
    <property name="address" value="北京海淀区"/>
</bean>
<!--p标签命名方式-->
<bean id="myStudent" class="com.kgc.ba01.Student" p:name="张三" p:age="20" p:school-ref="mySchool"/>
  1. 引用类型的自动注入
    • 概念:spring可以根据某些规则给引用类型完成赋值,只对引用类型有效,规则byName、byType
      1. byName(按名称注入):java类中引用类型属性名称和spring容器中bean的id名称一样的,且数据类型也是一样的,这些bean能够赋值给引用类型
      2. byType(按类型注入):java类中引用类型的数据类型和spring容器中bean的class值是同源关系的,这样的bean能赋值给引用类型
<!--
    引用类型自动注入:spring根据byName,byType规则给引用类型赋值
    1.byName语法:
            <bean id="xxx" class="yyy" autowire="byName">
                简单类型属性赋值
            </bean>
    2.byType:java类中引用类型的数据类型和spring容器中bean的class值是同源关系的,
              这样的bean能赋值给引用类型
      同源关系:
         1.java中引用类型的数据类型 和bean的class值是一样的
         2.java中引用类型的数据类型 和bean的class值是父子类关系的
         3.java中引用类型的数据类型 和bean的class值是接口和实现类关系的
         语法:
             <bean id="xxx" class="yyy" autowire="byType">
                 简单类型属性赋值
             </bean>
         注意:在xml配置文件中,符合条件的对象,只能有一个
              多余一个是报错的
 -->
<!--声明school-->
<bean id="school" class="com.kgc.ba01.School">
    <property name="name" value="北京大学"/>
    <property name="address" value="北京海淀区"/>
</bean>
<!--byName自动注入-->
<bean id="myStudent" class="com.kgc.ba01.Student" autowire="byName">
    <property name="name" value="张三"/>
    <property name="age" value="20"/>
</bean>
<!--byType自动注入-->
<bean id="myStudent2" class="com.kgc.ba01.Student" autowire="byType">
    <property name="name" value="张三"/>
    <property name="age" value="20"/>
</bean>
  1. 项目中使用多个spring配置文件

    • 分多个配置文件的方式:
      1. 按功能模块分,一个模块一个配置文件
      2. 按类的功能分,数据库操作相关的类在一个文件,service类在一个配置文件,配置redis,事务等等的一个配置文件
    • spring管理多个配置文件:常用的是包含关系的配置文件,项目中有一个总的文件,里面是有import标签包含其他的多个配置文件
    • 语法:
    总的文件(xml)
    <import resource="其他文件的路径1"/>
    <import resource="其他文件的路径2"/>
    关键字"classpath":表示类路径,也就是类文件(class文件)所在的目录。spring到类路径中加载文件
    				 什么时候使用classpath:在一个文件中要使用其他的文件,需要使用classpath
    
    <!--当前是总的文件,目的是包含其他的多个配置文件,一般不声明bean
        语法:
        <import resource="classpath:其他文件的路径"/>
        classpath:表示类路径,类文件所在的目录,spring通过类路径加载配置文件
     -->
    <import resource="classpath:ba01/spring-student.xml"/>
    <import resource="classpath:ba01/spring-school.xml"/>
    <!--包含关系的配置文件,可使用通配符(*:表示任意字符)
        注意:总的文件名称,不能包含在通配符范围内(applicationContext.xml不能叫做
        spring-applicationContext.xml)
     -->
    <import resource="classpath:ba01/spring-*.xml"/>
    
2.4.2 基于注解的DI
  • 基于注解的DI:使用spring提供的注解,完成java对象创建,属性赋值

  • 注解使用的核心步骤:

    1. 在源代码加入注解,例如@Component
    2. 在spring的配置文件中,加入组件扫描器的标签
    <!--声明组件扫描器:使用注解必须加入这个语句    component-scan:翻译过来是组件扫描器,组件是java对象         属性:base-package注解在你的项目中的包名              框架会扫描这个包和子包中的所有类,找类中的所有注解              遇到注解后,按照注解表示的功能,去创建对象,给属性赋值 -->    <context:component-scan base-package="注解所在的包名"/>
    
扫描多个包的三种方式:
<!--扫描多个包的三种方式--><!--第一种:使用多次组件扫描器--><context:component-scan base-package="com.kgc.ba01"/><context:component-scan base-package="com.kgc.ba02"/><!--第二种:使用分隔符(;或,),指定多个包--><context:component-scan base-package="com.kgc.ba01;com.kgc.ba02"/><!--第三种:指定父包--><context:component-scan base-package="com.kgc"/>
创建对象的四个注解:
/**
 * @Component: 表示创建对象,对象放到容器中,作用是<bean>
 *     属性:value,表示对象名称,也就是bean的id属性值
 *     位置:在类的上面,表示创建此类的对象
 *     @Component(value = "myStudent") 等同于
 *     <bean id="myStudent" class="com.kgc.ba01.Student"/>
 *     和@Component功能相同的创建对象的注解
 *     1.@Repository:放在dao接口的实现类上面,表示创建dao对象,持久层对象,能访问数据库
 *     2.@Service:放在业务层接口的实现类上面,表示创建业务层对象,业务层对象有事务的功能
 *     3.@Controller:放在控制器类的上面,表示创建控制器对象,属于表示层对象
 *                    控制器对象能接受请求,把请求的处理结果显示给用户
*      以上四个注解都能创建对象,但是@Repository、@Service、@Controller它们有角色说明,
 *      表示对象是分层的,对象是属于不同层,具有额外的功能
 *
 */
//使用value指定对象的名称
//@Component(value = "myStudent")
//省略value
//@Component("myStudent2")
//没有提供自定义的对象名称,所有框架的默认对象名称:类名首字母小写
@Component
public class Student {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
简单类型@Value
@Component("myStudent")
public class Student {
    /**
     * 简单类型属性赋值:@Value
     *  属性:value 简单类型属性值
     *  位置:1.在属性定义的上面,无需set方法,推荐使用
     *       2.在set方法的上面
     */
    @Value(value = "李四")
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }
    @Value("40")
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
@Value使用外部属性配置文件
  • 步骤:

    1. 创建配置文件:myconf.properties
    myname=李四myage=20
    
    1. 读取外部属性配置文件
    <!--读取外部的属性配置文件    property-placeholder:读取properties这样的文件 --><context:property-placeholder location="classpath:myconf.properties"/>
    
    1. 使用配置文件
    @Component("myStudent")public class Student {    //使用外部属性配置文件中的数据,语法@Value(${key})    @Value("${myname}")    private String name;    private int age;    public void setName(String name) {        this.name = name;    }    @Value("${myage}")    public void setAge(int age) {        this.age = age;    }    @Override    public String toString() {        return "Student{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}
    
@Autowired:自动注入byType
@Component("myStudent")
public class Student {
    @Value("${myname}")
    private String name;
    private int age;

    /**
     * 引用类型
     *@Autowired: spring框架提供的,给引用类型赋值的,使用自动注入原理
     *              支持byName、byType。默认是byType
     *			属性:required:boolean类型的属性,默认true
     * 				 true:spring在启动的时候,创建容器对象时候,会检查引用类型是否赋值成功。
     *				 	   如果赋值失败,终止程序执行,并报错
     *			 	 flase:引用类型赋值失败,程序正常执行,不报错,引用类型的值是null
     *          位置:1.在属性定义的上面,无需set方法,推荐使用
     *               2.在set方法的上面
     */
    //默认使用byType
    @Autowired
    private School school;
    public void setName(String name) {
        this.name = name;
    }
    @Value("${myage}")
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
@Autowired:自动注入byName
@Component("myStudent")
public class Student {
    @Value("${myname}")
    private String name;
    private int age;

    /**
     * byName自动注入:
     * 1.@Autowired:给引用类型赋值
     * 2.@Qualifier(value=“bean的id”):从容器中找到指定名称的对象,
     *                                 把这个对象赋值给引用类型
     */
    //byName
    @Autowired
    @Qualifier("mySchool")
    private School school;
    public void setName(String name) {
        this.name = name;
    }
    @Value("${myage}")
    public void setAge(int age) {
        this.age = age;
    }

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

@Resource默认是byName
@Component("myStudent")public class Student {    @Value("${myname}")    private String name;    private int age;    /**     * @Resource: 来自jdk中,给引用类型赋值的,支持byName、byType,默认是byName     *            spring支持这个注解的使用     *        位置:1.在属性定义的上面,无需set方法,推荐使用     *             2.在set方法的上面     *        说明:使用jdk1.8带有@Resource注解,高于1.8没有这个@Resource     *             需要加入一个依赖     *             <dependency>     *                  <groupId>javax.annotation</groupId>     *                  <artifactId>javax.annotation-api</artifactId>     *                  <version>1.3.2</version>     *              </dependency>     */    //默认使用byName自动注入    //先使用byName赋值,如果赋值失败,再使用byType    //本例是byType赋值成功    @Resource    private School school;}
@Resource只使用byName注入
@Component("myStudent")public class Student {    @Value("${myname}")    private String name;    private int age;    /**     * @Resource 只使用byName赋值     *  使用注解属性name="bean的id"     */    //只使用byName自动注入    @Resource(name = "mySchool")    private School school;  }

2.5 IoC总结

  • IoC:管理对象的,把对象放在容器中,创建,赋值,管理依赖关系
  • IoC通过管理对象,实现解耦合。IoC解决处理业务逻辑对象之间的耦合关系,也就是service和dao之间的解耦合
  • spring作为容器适合管理什么对象?
    1. service对象,dao对象
    2. 工具类对象
  • 不适合交给spring的对象?
    1. 实体类
    2. servlet、listener、filter等web中的对象。它们是tomcat创建和管理的

第三章 AOP 面向切面编程

3.1 增加功能,导致的问题

  • 在源代码中,业务方法中增加的功能
    1. 源代码可能改动的比较多
    2. 重复的代码比较多
    3. 代码难于维护

3.2 AOP概念

3.2.1 什么是AOP
  • AOP(Aspect Orient Programming):面向切面编程

  • Aspect :表示切面,给业务方法增加的功能,叫做切面。切面一般都是非业务功能,而且切面功能一般都是可以复用的。例如:日志功能、事务功能、权限检查、参数检查、统计信息等等

  • Orient :面向,对着

  • Programming:编程

  • 怎么理解面向切面编程?(以切面为核心设计开发你的应用)

    1. 设计项目时,找出切面的功能
    2. 安排切面的执行时间,执行的位置
3.2.2 AOP的作用
  1. 让切面功能复用
  2. 让开发人员专注业务逻辑,通过开发效率
  3. 实现业务功能和其他非业务功能解耦合
  4. 给存在的业务方法,增加功能,不用修改原来的代码
3.2.3 AOP中术语
  1. Aspect :切面,给业务方法增加的功能
  2. JoinPoint:连接点,连接切面的业务方法。在这个业务方法执行时,会同时执行切面的功能
  3. Pointcut:切入点,是一个或多个连接点集合,表示这些方法执行时,都能增加切面的功能。表示切面执行的位置
  4. target:目标对象,给哪个对象增加切面的功能,这个对象就是目标对象
  5. Advice:通知(增强),表示切面的执行时间,在目标方法之前执行切面,函数目标方法之后执行切面
  • AOP中重要的三个要素:Aspect 、Pointcut、Advice。三个概念的理解是:我们在Advice的时间,在Pointcut的位置,执行Aspect
  • AOP是这个动态的思想。在程序运行期间,创建代理(ServiceProxy),使用代理执行方法时,增加切面的功能。这个代理对象是存在内存中的
3.2.4 什么时候用AOP
  • 你要给某些方法增加相同的一些功能,源代码不能改,给业务方法增加非业务功能,也可以使用AOP
3.2.5 AOP技术思想的实现
  • 使用框架实现AOP。实现AOP的框架有很多,有名的有两个:
    1. Spring:Spring框架实现AOP思想中的部分功能。Spring框架实现AOP的操作比较繁琐,笨重
    2. Aspectj:独立的框架,专门做AOP。属于Eclipse

3.3 使用Aspectj框架实现AOP

  • Aspectj框架可以使用注解和xml配置文件两种方式实现AOP
3.3.1 通知
  • Aspectj表示切面执行时间,用到通知(Advice)。这个通知可以使用注解表示
  • 主要五个注解,表示切面的5个执行时间,这些注解叫做通知注解
  1. Before:前置通知
  2. AfterRetunring:后置通知
  3. Around:环绕通知
  4. AfterThrowing:异常通知
  5. After:最终通知
3.3.2 Pointcut(位置)
  • Pointcut用来表示切面执行的位置,使用Aspectj中切入点表达式
  • 切入点表达式语法:execution(访问权限 方法返回值 方法声明(参数) 异常类型)
    • 注:方法返回值和方法声明(参数)必须有
3.3.3 代理生成器
<!--声明自动代理生成器:目的是创建目标对象的代理    调用aspectj框架中的功能,寻找spring容器中的所有目标对象,    把每个目标对象加入切面类中的功能,生成代理    这个代理对象是修改的内存中的目标对象,这个目标对象就是代理对象(ServiceProxy)--><aop:aspectj-autoproxy />
3.3.4 @Before前置通知
  • @Before:在目标方法之前执行的
/** *@Aspect: 切面类的注解 *      位置:放在某个类的上面 *      作用:表示当前类是切面类 *      切面类:表示切面功能的类 */@Aspectpublic class MyAspect {    /**     * 前置通知方法的定义     * 1.方法是public     * 2.方法是void     * 3.方法名称自定义     * 4.方法可以有参数,如果有是JoinPoint,也可以没有     */    /**     * @Before: 前置通知     *      属性:value切入点表达式,表示切面的执行位置,     *      在这个方法执行时,会同时执行切面的功能     *      位置:方法的上面     *      特点:     *          1.执行时间:在目标方法之前先执行的     *          2.不会影响目标方法的执行     *          3.不会修改目标方法的执行结果     */    //定义方法,表示切面的具体功能    @Before("execution(* com.kgc.service..*.*(..))")    public void myBefore(){        //切面的代码        System.out.println("前置通知,切面的功能,在目标方法之前先执行:"+new Date());    }}
3.3.5 Joinpoint
  • JoinPoint:表示正在执行的业务方法
@Aspectpublic class MyAspect {    /**     * 切面类中的通知方法,可以有参数     * JoinPoint必须是他     * JoinPoint:表示正在执行的业务方法。相当于反射中Method     * 使用要求:必须是参数列表的第一个     * 作用:获取方法执行时的信息,例如方法名称,方法的参数集合     */    @Before("execution(* com.kgc.service..*.*(..))")    public void myBefore(JoinPoint js){        //获取方法的定义        System.out.println("前置通知中,获取目标方法的定义:"+js.getSignature());        System.out.println("前置通知中,获取方法名称;"+js.getSignature().getName());        //获取方法执行时的参数        Object[] args = js.getArgs();//数组中存放的是方法的所有参数        for (Object arg : args) {            System.out.println("前置通知,获取方法的参数:"+arg);        }    }}
3.3.6 @AfterReturning后置通知
  • @AtferReturning:在目标方法之后执行的
@Aspectpublic class MyAspect {    /**     * 后置通知方法的定义     * 1.方法是public     * 2.方法是void     * 3.方法名称自定义     * 4.方法有参数,推荐使用Object类型     */    /**     * @AfterReturning: 后置通知     * 属性:value切入点表达式     *      returning:自定义表达式,表示方法的返回值的     *                 自定义变量名称必须和通知方法的形参名一样     * 位置:在方法的上面     * 特点:     * 1.在目标方法之后,执行的     * 2.能获取到目标方法的执行结果     * 3.不会影响目标方法的执行     * 方法的参数:     *  Object res :表示目标方法的返回值,使用res接收方法的调用结果     *  Object res = 方法名();     *  后置通知的执行顺序     *  Object res = 方法名(..);     *  myAfterReturning(res);     */    @AfterReturning(value = "execution(* com.kgc.service..*.*(..))",returning = "res")    public void myAfterReturning(JoinPoint js,Object res){        System.out.println("后置通知,在目标方法之后,执行的。能拿到执行结果:"+res);    }}
3.3.7 @Around环绕通知
  • @Around(value=“切入点表达式”)
  • 使用环绕通知:就是调用切面类中的通知方法
@Aspectpublic class MyAspect {    /**     * 环绕通知方法的定义     * 1.方法是public     * 2.方法必须有返回值,推荐使用Object类型     * 3.方法名称自定义     * 4.方法必须有ProceedingJoinPoint参数     */    /**     * @Around: 环绕通知     *      属性:value切入点表达式     *      位置:在方法定义的上面     *      返回值:Object,表示调用目标方法希望得到执行结果(不一定是目标方法自己的返回值)     *      参数:ProceedingJoinPoint,相当于反射中Method     *      作用:执行目标方法,等于Method.invoke()     *      特点:     *      1.在目标方法的前和后都能增强功能     *      2.控制目标方法是否执行     *      3.修改目标方法的执行结果     */    @Around(value = "execution(* com.kgc.service..*.*(..))")    public Object myAround(ProceedingJoinPoint pjp) throws Throwable {        //获取方法执行时的参数值        String name ="";        Object[] args = pjp.getArgs();        if (args!=null && args.length>0){            Object arg = args[0];            if (arg!=null){                name = (String) arg;            }        }        Object methodReturn = null;        System.out.println("执行了环绕通知,在目标方法之前,输出日志时间="+new Date());        //执行目标方法        if ("lisi".equals(name)){            methodReturn = pjp.proceed();//method.invoke();表示执行业务方法本身        }        System.out.println("环绕通知,在目标方法之后,增加了事务提交功能");        //return "HelloAround";        //返回目标方法执行结果        return methodReturn;    }}
3.3.8 @AfterThrowing异常通知
  • 语法:@AfterThrowing(value =“切入点表达式”,throwing=“自定义变量”)
@Aspectpublic class MyAspect {    /**     * 异常通知方法的定义     * 1.方法是public     * 2.方法没有返回值,是void     * 3.方法名称自定义     * 4.方法有参数是Exception     */    /**     * @AfterThrowing: 异常通知     *    属性:value切入点表达式     *          throwing 自定义变量,表示目标方法抛出的异常     *                   变量名必须和通知方法的形参名一样     *    位置:在方法的上面     *    特点:     *    1.在目标方法抛出异常后执行的,没有异常不执行     *    2.能够获取到目标方法的异常信息     *    3.不是异常处理程序,可以得到发生异常的通知,可以发送邮件、短信通知开发人员     *      看做是目标方法的监控程序     *  异常通知的执行     *  try{     *      someServiceImpl.doSome(..)     *  }catch(Exception ex){     *      myAfterThrowing(ex);     *  }     */    @AfterThrowing(value = "execution(* com.kgc.service..*.*(..))",throwing = "ex")    public void myAfterThrowing(Exception ex){        System.out.println("异常通知,在目标方法抛出异常时执行的,异常原因是:"+ex.getMessage());        /**         * 异常发生可以做:         * 1.记录异常的时间、位置等信息         * 2.发生邮件,短信通知开发人员         */    }}
3.3.9@After最终通知
  • 语法:@After(value=“切入点表达式”)
@Aspectpublic class MyAspect {    /**     * 最终通知方法的定义     * 1.方法是public     * 2.方法没有返回值,是void     * 3.方法名称自定义     * 4.方法没有参数     */    /**     * @After: 最终通知     *  属性:value 切入点表达式     *  位置:在方法的上面     *  特点:     *  1.在目标方法之后执行的     *  2.总是会被执行     *  3.可以用来做程序最后的收尾工作,例如:清除临时数据,变量,清理内存     *  最终通知:     *  try{     *      someServiceImpl.doSome(..)     *  }finally{     *      myAfter()     *  }     */    @After("execution(* com.kgc.service..*.*(..))")    public void myAfter(){        System.out.println("最终通知,总是会被执行的");    }}
3.3.10 @Pointcut定义和管理切入点注解
  • 语法:@Pointcut(value=“切入点表达式”)
@Aspectpublic class MyAspect {    @Before("mypt()")    public void myBefore(){        System.out.println("前置通知,在目标方法之前先执行的");    }    @After("mypt()")    public void myAfter(){        System.out.println("最终通知,总是会被执行的");    }    /**     * @Pointcut: 定义和管理切入点,表示通知注解     *      属性:value 切入点表达式     *      位置:在一个自定义表达式方法的上面,这个方法看做是切入点表达式的别名     *           在其他的通知注解中,可以使用方法名称,就表示使用这个切入点表达式了     */    @Pointcut("execution(* com.kgc.service..*.*(..))")    private void mypt(){        //无需代码    }}
3.3.11 配置文件使用通知
<!--自动代理生成器--><aop:aspectj-autoproxy/><!--声明切面类对象--><bean id="loggerAop" class="com.kgc.aop.LoggerAop"/><!--aop--><aop:config>    <!--切入点表达式-->    <aop:pointcut id="pointcut" expression="execution(* com.kgc.service..*.*(..))"/>    <aop:aspect ref="loggerAop">        <!--环绕通知-->        <aop:around method="around" pointcut-ref="pointcut"/>    </aop:aspect></aop:config>

3.4 AOP总结

  • AOP是一种动态的技术思想,目的是实现业务功能和非业务功能的解耦合。业务功能是独立的模块,其他功能也是独立的模块,例如:事务功能、日志等等。让这些事务,日志功能是可以被复用的。
  • 当目标方法需要一些功能时,可以在不修改,不能修改源代码的情况下,使用AOP技术在程序执行期间,生成代理对象,通过代理执行业务方法,同时增加功能

第四章 Spring集成MyBatis

4.1 集成思路

  • Spring能集成很多的框架,是Spring一个优势功能。通过集成功能,让开发人员使用其他框架更方便。
  • 集成使用的是Spring IoC核心技术

4.2 如何使用MyBatis

  • 使用MyBatis,需要创建MyBatis框架中的某些对象,使用这些对象,就能使用MyBatis提供的功能了
  • 分析:MyBatis执行sql语句,需要使用那些对象
    1. 需要有dao接口的代理对象,例如StudentDao接口,需要一个它的代理对象。使用SqlSession.getMapper(StudentDao.class),得到dao代理对象
    2. 需要有SqlSessionFactory,创建SqlSessionFactory对象,才能使用openSession()得到SqlSession对象
    3. 数据源DataSource对象,使用一个更强大,功能更多的连接池对象代替MyBatis自己的PooledDataSource

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q4P0WH5K-1627891130751)(C:\Users\naizhi\AppData\Roaming\Typora\typora-user-images\image-20210731162426306.png)]

4.3 集成MyBatis使用

1. 使用mysql库,创建学生表
CREATE TABLE student(	id INT PRIMARY KEY AUTO_INCREMENT,#编号	`name` VARCHAR(80) NOT NULL,#姓名	age INT NOT NULL#年龄);#学生表
2. 创建Maven项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bw3IvVsu-1627891130755)(C:\Users\naizhi\AppData\Roaming\Typora\typora-user-images\image-20210728204208945.png)]

3. 加入依赖
  • spring依赖、mybatis依赖、mysql驱动、junit依赖、mybatis-spring依赖(mybatis网站上提供的,用来在spring项目中,创建mybatis对象)、spring有关事务的依赖。
    • mybatis和spring整合的时候,事务是自动提交的
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.kgc</groupId>
  <artifactId>ch03-mybatis</artifactId>
  <version>1.0.0</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <!--单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--spring事务依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.1</version>
    </dependency>
    <!--mybatis和spring集成-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>
    <!--阿里的连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>
  </dependencies>
  
  <build>
    <!--资源插件:处理src/main/java目录下的xml-->
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>
  </build>
</project>

4. 创建实体类Student
/** * 学生类 */public class Student {    private Integer id;//编号    private String name;//姓名    private Integer age;//年龄    //get|set    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAge() {        return age;    }    public void setAge(Integer age) {        this.age = age;    }    @Override    public String toString() {        return "Student{" +                "id=" + id +                ", name='" + name + '\'' +                ", age=" + age +                '}';    }}
5. 创建Dao接口和mapper文件写sql语句
/** * 学生数据访问层接口 */public interface StudentDao {    //添加学生    int insertStudent(Student student);    //查询学生    List<Student> selectStudent();}
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.kgc.dao.StudentDao">    <!--添加学生-->    <insert id="insertStudent">        insert into student values(null,#{name},#{age})    </insert>    <!--查询学生-->    <select id="selectStudent" resultType="com.kgc.pojo.Student">        select * from student     </select></mapper>
6. mybatis主配置文件
<?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>    <!--设置日志-->    <settings>        <setting name="autoMappingBehavior" value="FULL"/>        <setting name="logImpl" value="STDOUT_LOGGING"/>    </settings>    <mappers>        <package name="com/kgc/dao"/>    </mappers></configuration>
7. 创建service接口和它的实现类
/** * 学生业务逻辑层接口 */public interface StudentService {    //添加学生    int addStudent(Student student);    //查询学生    List<Student> queryStudent();}
/** * 学生业务逻辑层实现类 */@Servicepublic class StudentServiceImpl implements StudentService {    @Autowired     private StudentDao studentDao;    //实现添加学生    @Override    public int addStudent(Student student) {        return studentDao.insertStudent(student);    }    //实现查询学生    @Override    public List<Student> queryStudent() {        return studentDao.selectStudent();    }}
8. 创建spring的配置文件
  1. 声明数据源DataSource,使用阿里的Druid连接池
  2. 声明SqlSessionFactoryBean类,在这个类内部创建的是SqlSessionFactory对象
  3. 声明MapperScannerConfigurer类,在内部创建dao代理对象,创建的对象都放在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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!--组件扫描器-->
    <context:component-scan base-package="com.kgc"/>
    <!--声明数据源DataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/springDb"/>
        <property name="username" value="root"/>
        <property name="password" value="ok"/>
    </bean>
    <!--声明SqlSessionFactoryBean:在这个类的内部,创建SqlSessionFactory-->
    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--指定数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--指定mybatis主配置文件
            Resource可以直接使用 value属性赋值
        -->
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>
    <!--声明MapperScannerConfigurer
        作用:循环basePackage所表示的包,把包中的每个接口都找到,调用SqlSession.getMapper
        把每个dao接口都创建出dao代理对象,dao代理对象放在容器中
        相当于:
        ApplicationContext ctx =...
        SqlSessionFactory factory = ctx.getBean("factory");
        SqlSession session = factory.openSession();
        for(接口:com.kgc.dao){
            接口 对象 = session.getMapper(接口)
            springMap.put(接口名(首字母小写),对象)
        }
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定SqlSessionFactory对象的名称-->
        <property name="sqlSessionFactoryBeanName" value="factory"/>
        <!--指定基本包,dao接口所在的包名-->
        <property name="basePackage" value="com.kgc.dao"/>
    </bean>
</beans>

第五章 Spring事务

5.1 事务的概念

  • 什么是事务?事务是一些sql序列的集合,是多条sql,作为一个整体执行
mysql执行事务
beginTransaction 开启事务
insert into ...
update ...
delete from ...
endTransaction 事务结束
  • 声明情况下需要使用事务?
    • 一个操作需要多条(2条或2条以上)的sql语句一起完成,操作才能成功

5.2 程序中的事务使用

  • 事务:加在业务类的方法上面(public方法上面),表示业务方法执行时,需要事务的支持
public interface AccountDao{    public void updateA(){}    public void updateB(){}}public class AccountService{    private AccoutDao dao;    //在service(业务类)的public方法上面,需要说明事务    public void trans(String a,String b,Integer money){        dao.updateA();        dao.updateB();    }}

5.3 事务管理器

不同的数据库访问技术,处理事务是不同的
  1. 使用jdbc访问数据库,事务处理
public void updateAccount(){    Connection con = ...    con.setAutoCommit(false);    stat.insert();    stat.update();    con.commit();    con.setAutoCommit(true)}
  1. mybatis执行数据库,处理事务
public void updateAccount(){    SqlSession session = SqlSession.openSession(true);    try{        session.insert("insert into ...");        session.insert("update ...");        session.commit();     }catch(Exception ex){        session.rollback();    }}
Spring统一管理事务,把不同的数据库访问技术事务处理统一起来
  • 使用spring的事务管理器,管理不同数据库服务技术的事务处理。开发人员只需要掌握spring的事务处理一个方案,就可以实现使用不同数据库访问技术的事务管理。
  • 管理事务面向的是spring,由spring管理事务,做事务提交,事务回顾。
5.3.1 Spring事务管理器
  • spring框架使用事务管理器对象,管理所有的事务。
  • 事务管理器接口:PlatformTransactionManager
    • 作用:定义了事务的操作方法,主要是commit(),rollback()
  • 事务管理器有很多实现类:一种数据库的访问技术有一个实现类。由实现类具体完成事务的提交、回顾。
    • 意味着:jdbc或者mybatis访问数据库有自己的事务管理器实现类:DataSourceTransactionManager
    • hibernate框架,它的事务管理器实现类:HibernateTransactionManager
  • 事务管理器工作方式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fX3pW71M-1627891130759)(C:\Users\naizhi\AppData\Roaming\Typora\typora-user-images\image-20210801140542903.png)]

5.3.2 事务的提交和回顾的时机
  • 声明时候提交事务,回顾事务?
    • 当你的业务方法正常执行时,没有异常,事务是正常提交的。如果你的业务方法抛出运行时异常,事务是回顾的。
  • 异常的分类:
    • Error:严重错误,回顾事务
    • Exception:异常类,可以处理的异常情况
      1. 运行时异常:RuntimeException和他的子类都是运行时异常,在程序执行过程中抛出的异常。常见的运行时异常:空指针异常NullPoinerException、数字格式异常NumberFormatException、算术异常ArithmeticException等
      2. 受查异常:编写java代码的时候,必须处理的异常。例如:IOException、SQLException、FileNotFoundException
  • 区分方法:
    • 方法中抛出运行时异常,事务回顾,其他情况(正常执行方法、受查异常)介绍提交事务
5.3.3 事务使用的是AOP的环绕通知
  • 环绕通知:可以在目标方法的前和后都能增强功能,不需要修改原代码
spring给业务方法在执行时,增加上事务的切面功能@Around("execution(* 所有业务类中的方法)")    public Object myAround(ProceedingJoinPoint pjp){    try{        PlatformTransactionManager.beginTransaction();//使用spring的事务管理器,开启事务        pjp.proceed();//执行目标方法,doSome()        PlatformTransactionManager.commit();//业务方法正常执行,提交事务    }catch(Exception e){        PlatformTransactionManager.rollback();//业务方法异常,回顾事务    }}    

5.4 事务定义接口TransactionDefinition

  • TransactionDefinition接口,定义了三类常量,常量定义了有关事务控制的属性。
  • 事务的属性:
    1. 隔离级别
    2. 传播行为
    3. 事务的超时
  • 给业务方法说明事务属性,和ACID不一样。
5.4.1 隔离级别
  • 隔离级别:控制事务之间影响的程度。有5个值,只有4个隔离级别
    1. DEFAULT:采用DB默认的事务隔离级别。mysql默认为REPEATABLE_READ;oracle默认为READ_COMMITTED
    2. READ_UNCOMMITTED:读未提交。未解决任何并发问题
    3. READ_COMMITTED:读已提交。解决脏读,存在不可重复读和幻读
    4. REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
    5. SERIALIZABLE:串行化。不存在任何并发问题
5.4.2 超时时间
  • 超时时间:表示一个业务方法最长的执行时间,如果到达时间没有执行完毕,spring会回顾事务。
  • 超时时间,以秒为单位,整数值,默认是-1。
5.4.3 传播行为
  • 传播行为:业务方法在调用时,事务在方法之间的传递和使用

  • 使用传播行为,标识方法有无事务

  • 传播行为有7个值:

    1. PROPAGATION_REQUIRED
    2. PROPAGATION_SUPPORTS
    3. PROPAGATION_REQUIRES_NEW
    4. PROPAGATION_MANDATORY
    5. PROPAGATION_NOT_SUPPORTED
    6. PROPAGATION_NEVER
    7. PROPAGATION_NESTED
  • 前三个需要掌握:

  1. REQUIRED:spring默认传播方式,方法在调用的时候,如果存在事务就使用当前的事务,如果没有事务则新建事务,方法在新事务中执行
  2. SUPPORTS:支持,方法有事务可以正常执行,没有事务也可以正常执行
  3. REQUIRES_NEW:方法需要一个新事务。如果调用方法时,存在一个事务,则原来的方法暂停,直到新事务执行完毕。如果方法调用时,没有事务,则新建一个事务,在新事务执行代码

5.5 举例环境搭建

  • 模拟用户下订单,向订单表添加销售记录,从商品表减少库存
1. 创建数据库表
CREATE TABLE goods(	id INT PRIMARY KEY,#商品编号	`name` VARCHAR(100) NOT NULL,#商品名称	amount INT NOT NULL,#库存	price FLOAT NOT NULL#价格);#商品表CREATE TABLE sale(	id INT PRIMARY KEY AUTO_INCREMENT,#销售编号	gid INT NOT NULL,#商品编号	nums INT NOT NULL,#数量	FOREIGN KEY(gid) REFERENCES goods(id));#销售表#添加商品数据INSERT INTO goodsVALUES(1001,'笔记本电脑',100,6000),(1002,'手机',50,3000);
2. 创建Maven项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfkkPUR4-1627891130763)(C:\Users\naizhi\AppData\Roaming\Typora\typora-user-images\image-20210728204208945.png)]

3. 加入依赖
  • spring依赖、mybatis依赖、mysql驱动、junit依赖、mybatis-spring依赖(mybatis网站上提供的,用来在spring项目中,创建mybatis对象)、spring有关事务的依赖。
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>  <groupId>com.kgc</groupId>  <artifactId>ch03-mybatis</artifactId>  <version>1.0.0</version>  <properties>    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>    <maven.compiler.source>1.8</maven.compiler.source>    <maven.compiler.target>1.8</maven.compiler.target>  </properties>  <dependencies>    <!--单元测试-->    <dependency>      <groupId>junit</groupId>      <artifactId>junit</artifactId>      <version>4.11</version>      <scope>test</scope>    </dependency>    <!--spring依赖-->    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-context</artifactId>      <version>5.2.5.RELEASE</version>    </dependency>    <!--spring事务依赖-->    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-tx</artifactId>      <version>5.2.5.RELEASE</version>    </dependency>    <dependency>      <groupId>org.springframework</groupId>      <artifactId>spring-jdbc</artifactId>      <version>5.2.5.RELEASE</version>    </dependency>    <!--mybatis依赖-->    <dependency>      <groupId>org.mybatis</groupId>      <artifactId>mybatis</artifactId>      <version>3.5.1</version>    </dependency>    <!--mybatis和spring集成-->    <dependency>      <groupId>org.mybatis</groupId>      <artifactId>mybatis-spring</artifactId>      <version>1.3.1</version>    </dependency>    <!--mysql驱动-->    <dependency>      <groupId>mysql</groupId>      <artifactId>mysql-connector-java</artifactId>      <version>5.1.9</version>    </dependency>    <!--阿里的连接池-->    <dependency>      <groupId>com.alibaba</groupId>      <artifactId>druid</artifactId>      <version>1.1.12</version>    </dependency>  </dependencies>    <build>    <!--资源插件:处理src/main/java目录下的xml-->    <resources>      <resource>        <directory>src/main/java</directory>        <includes>          <include>**/*.properties</include>          <include>**/*.xml</include>        </includes>        <filtering>false</filtering>      </resource>    </resources>  </build></project>
4. 创建实体类
/** * 商品类 */public class Goods {    private Integer id;//商品编号    private String name;//商品名称    private Integer amount;//库存    private double price;//价格    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public Integer getAmount() {        return amount;    }    public void setAmount(Integer amount) {        this.amount = amount;    }    public double getPrice() {        return price;    }    public void setPrice(double price) {        this.price = price;    }    @Override    public String toString() {        return "Goods{" +                "id=" + id +                ", name='" + name + '\'' +                ", amount=" + amount +                ", price=" + price +                '}';    }}
/**
 * 销售类
 */
public class Sale {
    private Integer id;//销售编号
    private Integer gid;//商品编号
    private Integer nums;//数量

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getGid() {
        return gid;
    }

    public void setGid(Integer gid) {
        this.gid = gid;
    }

    public Integer getNums() {
        return nums;
    }

    public void setNums(Integer nums) {
        this.nums = nums;
    }

    @Override
    public String toString() {
        return "Sale{" +
                "id=" + id +
                ", gid=" + gid +
                ", nums=" + nums +
                '}';
    }
}

5. 创建dao接口和mapper文件
/**
 * 销售数据访问层接口
 */
public interface SaleDao {
    //添加销售记录
    int insertSale(Sale sale);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kgc.dao.SaleDao">
    <!--添加销售记录-->
    <insert id="insertSale">
        insert into sale values(null,#{gid},#{nums})
    </insert>
</mapper>
/**
 * 商品数据访问层接口
 */
public interface GoodsDao {
    //查询指定商品
    Goods selectGoods(Integer id);
    //修改库存
    int updateGoods(Goods goods);
}
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.kgc.dao.GoodsDao"><!--查询指定商品--><select id="selectGoods" resultType="com.kgc.pojo.Goods">        select * from goods where id = #{id}    </select><!--修改库存--><update id="updateGoods">        update goods set amount = amount - #{amount} where id = #{id}    </update></mapper>
6. 创建mybatis主配置文件
<?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>    <!--设置日志-->    <settings>        <setting name="autoMappingBehavior" value="FULL"/>        <setting name="logImpl" value="STDOUT_LOGGING"/>    </settings>    <mappers>        <package name="com/kgc/mapper"/>    </mappers></configuration>
7. 创建service接口和实现类
  • 实现buy方法
/** * 商品业务逻辑层接口 */public interface BuyGoodsService {    //购买商品    void buy(Integer gid,Integer num);}
/**
 * 商品业务逻辑层实现类
 */
@Service
public class BuyGoodsServiceImpl implements BuyGoodsService {
    @Autowired
    private SaleDao saleDao;
    @Autowired
    private GoodsDao goodsDao;
    //实现购买商品
    @Override
    public void buy(Integer gid, Integer num) {
        System.out.println("buy方法的开始");
        //生成销售记录
        Sale sale = new Sale();
        sale.setGid(gid);
        sale.setNums(num);
        saleDao.insertSale(sale);
        //查询商品
        Goods goods = goodsDao.selectGoods(gid);
        if (goods==null){
            throw  new NullPointerException(gid+"商品不存在");
        }else if (goods.getAmount()<num){
            throw  new NotEnougthException(gid+"库存不足");
        }
        //更新库存
        Goods goods1 = new Goods();
        goods1.setAmount(num);
        goods1.setId(gid);
        goodsDao.updateGoods(goods1);
        System.out.println("buy方法的结束");
    }
}
/**
 * 运行时异常
 */
public class NotEnougthException extends RuntimeException{
    public NotEnougthException() {
        super();
    }

    public NotEnougthException(String message) {
        super(message);
    }
}
8. 创建spring的配置文件
  • 声明数据源、SqlSessionFactoryBean、dao代理对象、组件扫描器
<?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">    <!--组件扫描器-->    <context:component-scan base-package="com.kgc"/>    <!--声明数据源DataSource-->    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">        <property name="url" value="jdbc:mysql://localhost:3306/springDb"/>        <property name="username" value="root"/>        <property name="password" value="ok"/>    </bean>    <!--声明SqlSessionFactoryBean-->    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">        <!--指定数据源-->        <property name="dataSource" ref="dataSource"/>        <!--指定mybatis主配置文件-->        <property name="configLocation" value="classpath:mybatis.xml"/>    </bean>    <!--声明MapperScannerConfigurer-->    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <!--指定SqlSessionFactory对象的名称-->        <property name="sqlSessionFactoryBeanName" value="factory"/>        <!--指定基本包,dao接口所在的包名-->        <property name="basePackage" value="com.kgc.dao"/>    </bean></beans>

5.6 Spring框架使用自己的注解@Transactional控制事务

  • @Transactional注解,使用注解的属性控制事务(隔离级别、传播行为、超时)
  • 属性:
    1. propagation:事务的传播行为,他使用的Propagation类的枚举值。例如:Propagation.REQUIRED
    2. isolation:使用isolation类的枚举值,表示隔离级别。默认isolation.DEFAULT
    3. readOnly:boolean类型的值,表示数据库操作是不是只读的。默认是false
    4. timeout:事务超时,默认为1,整数值,单位是秒。例如:timeout=20
    5. rollbackFor:表示回顾的异常类列表,他的值是一个数组,每个值是异常类型的class
    6. rollbackForClassName:表示回顾的异常类列表,他的值是异常类的名称,是String类型的值
    7. noRollbackFor:表示不需要回顾的异常类列表,是class类型的
    8. noRollbackForClassName:表示不需要回顾的异常类列表,是String类型的值
  • 位置:
    1. 在业务方法的上面,是在public方法的上面
    2. 在类的上面
  • 注解的使用步骤:
    1. 在spring的配置文件,声明事务的内容
      • 声明事务管理器,说明使用哪个事务管理器对象
      • 声明使用注解管理事务,开启是注解驱动
    2. 在类的源代码中,加入@Transactional
  • 事务的控制模式:
    1. 编码式,在代码中编程控制事务
    2. 声明式事务,不用编码
声明事务管理器
<!--声明事务的控制-->
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--指定数据源-->
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解驱动:告诉框架使用注解管理事务
    transaction-manager:指定事务管理器的id
 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
使用事务修改5.5中的代码
/**
 * 商品业务逻辑层实现类
 */
@Service
public class BuyGoodsServiceImpl implements BuyGoodsService {
    @Autowired
    private SaleDao saleDao;
    @Autowired
    private GoodsDao goodsDao;
    /**
     *rollbackFor的使用:
     * 1.框架首先检查方法抛出的异常是不是在rollbackFor的数组中,如果在一定回顾
     * 2.如果方法抛出的异常不在rollbackFor数组,框架会继续检查抛出的异常是不是RuntimeException
     * 如果是RuntimeException,一定回顾。
     */
    //@Transactional 放在public方法的上面,表示方法有事务功能
    @Transactional(propagation = Propagation.REQUIRED,
                   isolation = Isolation.DEFAULT,
                   readOnly = false,timeout = 20,
                   rollbackFor = {NullPointerException.class,NotEnougthException.class})
    @Override
    public void buy(Integer gid, Integer num) {
        System.out.println("buy方法的开始");
        //生成销售记录
        Sale sale = new Sale();
        sale.setGid(gid);
        sale.setNums(num);
        saleDao.insertSale(sale);
        //查询商品
        Goods goods = goodsDao.selectGoods(gid);
        if (goods==null){
            throw  new NullPointerException(gid+"商品不存在");
        }else if (goods.getAmount()<num){
            throw  new NotEnougthException(gid+"库存不足");
        }
        //更新库存
        Goods goods1 = new Goods();
        goods1.setAmount(num);
        goods1.setId(gid);
        goodsDao.updateGoods(goods1);
        System.out.println("buy方法的结束");
    }
}
  • @Transactional使用的特点:
    1. spring框架自己提供的事务控制
    2. 适合中小型项目
    3. 使用方便,效率高

5.7 使用Aspectj框架在spring的配置文件中,声明事务控制

  • 使用Aspectj的aop声明事务控制,叫做声明式事务

  • 使用步骤:

    1. pom.xml加入spring-aspects的依赖
    2. 在spring的配置文件中声明事务的内容
      1. 声明事务管理器
      2. 事务业务方法需要的事务属性
      3. 声明切入点表达式
  • 声明式事务:

<!--声明式事务:不用写代码-->
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--指定数据源-->
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--声明业务方法的事务属性(隔离级别、传播行为、超时)
        id:给业务方法配置事务段代码起个名称,唯一值
        transaction-manager:事务管理器的id
    -->
<tx:advice id="serviceAdvice" transaction-manager="transactionManager">
    <!--给具体的业务方法增加事务的说明-->
    <tx:attributes>
        <!--
                给具体的业务方法,说明他需要的事务属性
                name:业务方法的名称,配置name的值:1.业务方法的名称 2.带有部分通配符的方法名称 3. 使用*
                propagation:传播行为
                isolation:管理级别
                timeout:超时时间
                read-only:是否只读,默认是false
                rollback-for:指定回顾的异常类列表,使用的异常全限定名称
            -->
        <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" timeout="20" read-only="false" rollback-for=""/>
        <!--在业务方法有命名规则后,可以对一些方法使用事务-->
        <tx:method name="add*" propagation="REQUIRES_NEW"/>
        <tx:method name="modify*" propagation="REQUIRED"/>
        <tx:method name="remove*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>
<!--声明切入点表达式:表示哪些包中的类,类中的方法参与事务-->
<aop:config>
    <!--声明切入点表达式
            expression:切入点表达式,表示哪些类和类中的方法要参与事务
            id:切入点表达式的名称,唯一值
        -->
    <aop:pointcut id="pointcut" expression="execution(* com.kgc.service..*.*(..))"/>
    <!--关联切入点表达式和事务通知-->
    <aop:advisor advice-ref="serviceAdvice" pointcut-ref="pointcut"/>
</aop:config>
  • 声明式事务优缺点:
    1. 缺点:理解难,配置复杂
    2. 优点:代码和事务配置是分开的,控制事务源代码不用修改;能快速的了解和掌握项目的全部事务;适合大型项目

第六章 Spring与Web

  • 现在使用容器对象的问题

    1. 在web应用中创建容器对象次数多
    2. 在多个servlet中,分别创建容器对象
  • 需要一个什么样的容器对象

    1. 容器对象只有一个,创建一个就可以了
    2. 容器对象应该在整个项目中共享使用,多个servlet都能使用同一个容器对象
  • 解决问题使用监听器:ServletContextListener(两个方法:初始时执行的,销毁时执行的)

  • 在监听器中,创建好的容器对象,应该放在web应用中的ServletContext作用域中

6.1 ContextLoaderListener

  • ContextLoaderListener是一个监听器对象,是spring框架提供的。
  • 使用这个监听器作用:
    1. 创建容器对象,只有一次
    2. 把容器对象放入到ServletContext作用域
  • 使用步骤:
    1. 在pom.xml加入依赖spring-web
    2. web.xml声明监听器
添加依赖
<!--监听器依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>
声明监听器
<!--声明监听器
    默认监听器:创建容器对象时,读取的配置文件:/WEB-INF/applicationContext.xml
  -->
<!--自定义容器使用的配置文件路径
    context-param:叫做上下文参数,给监听器提供参数的
    contextConfigLocation:名称是固定的,表示自定义spring配置文件的路径
  -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <!--自定义配置文件的路径-->
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

6.2 ContextLoaderListener源代码

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    //监听器的初始方法
    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }
}

private WebApplicationContext context;

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
                long startTime = System.currentTimeMillis();

            try {
                if (this.context == null) {
                    //创建spring容器对象
                    this.context = this.createWebApplicationContext(servletContext);
                }
				//把容器对象放到servletContext作用域
                //key=WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
                //value=容器对象
                servletContext.setAttribute
                    (WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
              
            } catch (Error | RuntimeException var8) {
                
            }
        }
    }
//WebApplicationContext是web项目中使用的容器对象
public interface WebApplicationContext extends ApplicationContext {}
  • 所有监听器获取容器对象
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        String  age = request.getParameter("age");
        //创建容器对象
        //ApplicationContext ctx = new ClassPathXmlApplicationContext("WEB-INF/applicationContext.xml");
        //使用监听器已经创建好了容器对象,从ServletContext获取容器对象
        WebApplicationContext ctx = null;
        String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
        ServletContext sc = getServletContext();//getServletContext,servlet中的方法
        //ServletContext sc = request.getServletContext();//HttpServletRequest对象的方法
        Object attr = sc.getAttribute(key);
        if (attr!=null){
            ctx = (WebApplicationContext) attr;
        }
        StudentService studentService = ctx.getBean(StudentService.class);
        Student student = new Student();
        student.setName(name);
        student.setAge(Integer.valueOf(age));
        studentService.addStudent(student);
        //给用户显示请求的处理结果
        request.getRequestDispatcher("/show.jsp").forward(request,response);
    }
  • 使用spring工具类获取容器对象
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String name = request.getParameter("name");
        String  age = request.getParameter("age");
        //使用spring提供的工具方法,获取容器对象
        WebApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
        StudentService studentService = ctx.getBean(StudentService.class);
        Student student = new Student();
        student.setName(name);
        student.setAge(Integer.valueOf(age));
        studentService.addStudent(student);
        //给用户显示请求的处理结果
        request.getRequestDispatcher("/show.jsp").forward(request,response);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我走后的夜与昼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值