Spring笔记整理

Spring学习笔记,初学菜鸟整理,希望各位大佬来补充,来提学习意见

第一章Spring概述

1.1 Spring框架是什么?

Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可以在 Java SE/EE 中使用的轻量级开源框架。

Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。就是让对象和对象(模块和模块)之间关系不是使用代码关联,而是通过配置来说明。即在 Spring 中说明对象(模块)的关系。

Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“织入”。

总结归纳
spring是一个框架,核心技术是ioc,aop,实现解耦合
spring是一个容器,容器中存放的是Java对象,需要做的是把Java对象放到容器中

官网:https://spring.io/

1.2 Spring优点?

  1. 轻量
    Spring 框架使用的 jar 都比较小,一般在 1M 以下或者几百 kb。Spring 核心功能的所需
    的 jar 总共在 3M 左右。

  2. 针对接口编程,解耦合
    Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的
    对象创建方式,现在由容器完成。对象之间的依赖解耦合。

  3. AOP 编程的支持
    通过 Spring 提供的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付

  4. 方便集成各种优秀框架
    Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个插头放入插线板。不需要可以轻易的移除。

1.3 Spring体系结构

在这里插入图片描述

第二章 IOC控制反转

2.1 IOC控制反转的介绍

控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。

IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式是依赖注入。应用广泛。

依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完成功能,即 classA对 classB 有依赖。

Ioc 的实现:
➢ 依赖注入:DI(Dependency Injection),程序代码不做定位查询,这些工作由容器自行完成。

依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。

Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持对象之间依赖关系的管理。

Spring 框架使用依赖注入(DI )实现 IoC

Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。

2.2 Spring实现步骤

1.创建maven项目

2.加入maven依赖

spring依赖,版本5.2.5

 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

junit依赖

<dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

3.创建类(接口和实现类)和没有使用框架一样的普通类


//接口
public interface SomeService {
    void doSome();
}

//实现类
public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome() {
        System.out.println("doSome方法执行了——————————");
    }
}

4.创建spring配置文件声明类的信息,这些类由spring创建和管理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        告诉spring创建对象
        声明bean,告诉spring要创建哪个对象
        id:对象唯一标识,对象自定义名称,spring通过这个名称找到这个对象
        class:类的全限定名称(不能是接口,spring是通过反射机制创建对象的,必须使用类)

        spring会把创建好的对象存到map集合中,spring集合用map储存对象
        springmap.put(id值,对象)
        一个bean标签声明一个对象
    -->
    <bean id="someService" class="com.xiaojie.service.Impl.SomeServiceImpl"></bean>
</beans>

5.测试spring创建的对象

/**
     * spring默认创建对象的时间:在创建spring容器时,会创建配置文件里的Java对象
     * spring创建对象默认是通过无参构造方法
     */
    @Test
    public void test1(){
        //使用spring容器创建对象
        //1.指定配置文件
        String config = "beans.xml";
        //2.创建表示spring容器的对象,ApplicationContext
        //ApplicationContext就是表示spring容器,通过容器获取对象
        //ClassPathXmlApplactionContext:表示从类路径加载配置文件
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        SomeService ss = (SomeService) ac.getBean("someService");
        ss.doSome();

    }

2.3 使用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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       //spring 配置文件加入 java.util.Date 定义:
    <bean id="mydate" class="java.util.Date"></bean>
</beans>

测试:

//使用spring创建非自定对象
 @Test
    public  void test4(){
        String config = "beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Date date = (Date) ac.getBean("mydate");
        System.out.println(date);
    }

输出:

"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=53884:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java  idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java  idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test4
Tue Jul 13 15:24:13 CST 2021

Process finished with exit code 0

2.4 容器接口和实现类

ApplicationContext 接口

ApplicationContext 用于加载 Spring 的配置文件,在程序中充当“容器”的角色。其实现类有两个。

在这里插入图片描述
A 、 配置文件在类路径下
若 Spring 配置文件存放在项目的类路径下,则使用 ClassPathXmlApplicationContext 实现
类进行加载。

  @Test
    public void test1(){
        //1.指定配置文件,配置文件在类路径下
        String config = "beans.xml";
        //2.创建表示spring容器的对象,ApplicationContext
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        SomeService ss = (SomeService) ac.getBean("someService");
        ss.doSome();
    }

B 、 ApplicationContext 容器中对象的装配时机
ApplicationContext 容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。
以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高。但占用内存。

  //ApplicationContext容器对对象的装配时机
    @Test
    public void test5(){
        String config = "beans.xml";
        //获取容器,此时对象已装配完毕
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);

在这里插入图片描述

2.5 什么样的对象应该放在容器中?

放入容器中的对象:
dao类,service类,工具类,controller类
spring中的对象默认都是单列的,在容器中叫这个名称的对象只有一个

不放人容器中的对象:
1.实体类对象,实体类数据来自于数据库的
2.servlet,listener,filter

2.6 基于XML的DI

2.6.1 注入的分类

bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。
根据注入方式的不同,常用的有两类:set 注入、构造注入。

1.set注入(掌握)
set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。

A.简单类型注入

//学生信息类
public class Student {
    private String name;
    private Integer age;
    
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  
    <!--通过set注入给对象属性赋值-->
    <bean id="student" class="com.xiaojie.domian.Student" >
        <property name="name" value="李四"> </property>
        <property name="age" value="12"> </property>
    </bean>
</beans>

测试代码

 /**
     * spring通过set主入方法给属性赋值
     */
    @Test
    public void test5(){
        String config = "beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student st = (Student) ac.getBean("student");
        System.out.println("学生信息为:"+st);
    }

输出信息:

"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=55214:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java  idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java  idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test5
学生信息为:Student{name='李四', age=12}

Process finished with exit code 0

B.引用属性注入
当指定 bean 的某属性值为另一 bean 的实例时,通过 ref 指定它们间的引用关系。ref的值必须为某 bean 的 id 值。

public class Student {
    private String name;
    private Integer age;
    //引用属性
    private School school;

    public void setSchool(School school) {
        this.school = school;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  
    <!--通过set注入给对象属性赋值-->
    <bean id="student" class="com.xiaojie.domian.Student" >
        <property name="name" value="李四"> </property>
        <property name="age" value="12"> </property>
         <!--通过ref 给引用属性赋值,其ref的值必须为bean的id-->
        <property name="school" ref="school"/>
    </bean>
   
    <bean id="school" class="com.xiaojie.domian.School" >
        <property name="name" value="北京大学" />
        <property name="address" value="北京海淀区"/>
    </bean>
</beans>

测试:

@Test
    public void test5(){
        String config = "beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student st = (Student) ac.getBean("student");
        System.out.println("学生信息为:"+st);
    }

输出:

"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=64428:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java  idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java  idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test5
学生信息为:Student{name='李四', age=12, school=School{name='北京大学', address='北京海淀区'}}

Process finished with exit code 0

2.构造注入(理解)

public class School {
    private String name;
    private String address;

    public School(){

    }
    public School(String name,String address){
        this.address = address;
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAddress(String address) {
        this.address = address;
    }

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--通过构造注入给属性赋值-->
    <bean id="school" class="com.xiaojie.domian.School" >
        <constructor-arg name="name" value="北京大学" />
        <constructor-arg name="address" value="北京海淀区"/>
    </bean>
</beans>

测试:

 /**
     * 通过构造注入给属性赋值
     */
    @Test
    public void test6(){
        String config = "beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        School s = (School) ac.getBean("school");
        System.out.println("学校信息为:"+s);
    }

输出:

"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=60239:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java  idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java  idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test6
学校信息为:School{name='北京大学', address='北京海淀区'}

Process finished with exit code 0

2.6.2 引用类型属性自动注入

对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)。根据自动注入判断标准的不同,可以分为两种:
byName:根据名称自动注入
byType: 根据类型自动注入

1.byName 方式自动注入(按名称注入)
当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。

举例:

//学生信息类
public class Student {
    private String name;
    private Integer age;
    private School school;
    //......set/get方法
    }

//学校信息类
public class School {
    private String name;
    private String address;
    //......set/get方法
    }

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  
    <!--通过byName自动赋值-->
    <bean id="student" class="com.xiaojie.domian.Student" autowire="byName">
        <property name="name" value="李四"> </property>
        <property name="age" value="12"> </property>
    </bean>
    
    <!--声明school对象-->
    <bean id="school" class="com.xiaojie.domian.School" >
        <property name="name" value="北京大学" />
        <property name="address" value="北京海淀区"/>
    </bean>
</beans>

测试:

/**
     * spring通过byName自动赋值
     */
    @Test
    public void test7(){
        String config = "beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student s = (Student) ac.getBean("student");
        System.out.println("学生信息为:"+s);
    }

输出:

"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=60776:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java  idea代码区\SpringTest\ch01spring\target\test-classes;D:\Java  idea代码区\SpringTest\ch01spring\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.Mytest,test7
学生信息为:Student{name='李四', age=12, school=School{name='北京大学', address='北京海淀区'}}

Process finished with exit code 0

2.byType 方式自动注入(按类型注入)
Java类中引用类型的数据类型和spring容器中(配置文件)的class属性是同源关系的,这样bean能够赋值给引用类型。

同源类型就是一类的意思:

  1. Java类中引用类型的数据类型和bean的class的值是一样的。
  2. Java类中引用类型的数据类型和bean的class的值父子类关系的。
  3. Java类中引用类型的数据类型和bean的class的值接口和实现类关系的。

例如:

//学生信息类
public class Student {
    private String name;
    private Integer age;
    private School school;
    //......set/get方法
    }

//学校信息类
public class School {
    private String name;
    private String address;
    //......set/get方法
    }

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
       
	<!--此时spring它是拿到student里面的School属性名跟bean标签里的class路径一一比对,
	直到找到相匹配的对象然后赋值给School属性-->
    <bean id="student" class="com.xiaojie.domian.Student" autowire="byType">
        <property name="name" value="李四"> </property>
        <property name="age" value="12"> </property>
    </bean>

    <bean id="school" class="com.xiaojie.domian.School" >
        <property name="name" value="清华大学" />
        <property name="address" value="北京海淀区"/>
    </bean>
</beans>

测试:

 @Test
    public void test8(){
        String config = "beans.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student s = (Student) ac.getBean("student");
        System.out.println("学生信息为:"+s);
    }

--------------------------------------------------------------
输出:"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" 
学生信息为:Student{name='李四', age=12, school=School{name='清华大学', address='北京海淀区'}}

Process finished with exit code 0

2.6.3 为应用指定多个 Spring 配置文件

在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将Spring 配置文件分解成多个配置文件。包含关系的配置文件:多个配置文件中有一个总文件,总配置文件将各其它子文件通过import引入。在 Java代码中只需要使用总配置文件对容器进行初始化即可。

例如:
applictionContext.xml为父配置文件
在这里插入图片描述
spring父配置信息为:

		//关键字:classpath表示类路径(class文件所在的目录P),在spring的配置文件中要指定其他文件的位置,需要使用classpath,告诉spring到哪去加载文件
 		<import resource="classpath*:test01/spring-student.xml"/>
        <import resource="classpath*:test01/spring-school.xml"/>

测试:

 @Test
    public void test9(){
        String config = "/test01/applictionContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        Student s = (Student) ac.getBean("student");
        System.out.println("学生信息为:"+s);
    }



----------------------------------------------------------------
输出:
学生信息为:Student{name='张三', age=20, school=School{name='北京大学', address='北京海淀区'}}

Process finished with exit code 0


也可使用通配符*。但,此时要求父配置文件名不能满足所能匹配的格式,否则将出现
循环递归包含。就本例而言,父配置文件不能匹配 spring-*.xml 的格式,即不能起名为
spring-total.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">
       //使用通配符指定配置文件,注意父配置文件命名不许为spring-*.xml格式
        <import resource="classpath*:test01/spring-*.xml"/>
</beans>

2.7 基于注解的DI(掌握)

通过注解完成java对象创建,属性赋值。

2.7.1 使用注解的步骤:

  1. 加入maven的依赖,spring-context,在你加入spring-context的同时,间接加入了spring-aop的依赖,使用注解必须使用spring-aop依赖
  2. 在类中加入spring的注解(多个不同功能的注解)
  3. 在spring的配置文件中,加入一个组件扫描的标签,说明注解在你项目中的位置

2.7.2 定义 Bean 的注解@Component( 掌握)

需要在类上使用注解@Component,该注解的 value 属性用于指定该 bean 的 id 值。
例如:

/**
 * @Component 创建对象的,等同于<bean>的功能
 *             属性value,就是对象的名称,也就是bean的id
 *             value值是唯一的,创建的对象在整个spring容器中就一个
 *             位置:在类的上面
 *
 * @Compotent(value="myStudent")等同于
 * <bean id="myStudent" class="com.xiaojie.ba01.Student"></bean>
 *
 */
@Component(value = "myStudent")
public class Student {
    private String name;
    private Integer age;

    public Student(){
        System.out.println("无参构造方法被调用,对象创建成功");
    }
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

在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">

    <!--声明组件扫描器(component-scan),组件就是java对象
        base-package:指定注解在你的项目中的包名。
        comonent-scan工作方式:spring会扫描遍历base-package指定的包,
        把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。

        加入了componenet-scan标签,配置文件的变化:
        1.加入一个新的约束文件spring-context.xsd
        2.给这个新的约束文件起个命名空间的名称
    -->
    
    <context:component-scan base-package="com.xiaojie.ba01"/>
    
</beans>

另外,Spring 还提供了 3 个创建对象的注解:

➢ @ReposiStory 用于对 DAO 实现类进行注解
➢ @Service 用于对 Service 实现类进行注解
➢ @Controller 用于对 Controller 实现类进行注解

这三个注解与@Component 都可以创建对象,但这三个注解还有其他的含义
@Service 创建业务层对象,业务层对象可以加入事务功能
@Controller 注解创建的对象可以作为处理器接收用户的请求。
@Repository 放在dao的实现类上面,表示创建dao对象,dao对象是能访问数据库的。
@Service,@Controller 是对@Component 注解的细化,标注不同层的对象。即持久层对象,业务层对象,控制层对象。

指定多个包的3种形式:

 <!--第一种,使用多个扫描器,指定不同的包-->
    <context:component-scan base-package="com.xiaojie.ba01"/>
    <context:component-scan base-package="com.xiaojie.ba02"/>

    <!--第二种,多个包之间用;或者,隔开-->
    <context:component-scan base-package="com.xiaojie.ba01;com.xiaojie.ba02"/>

    <!--第三种,指定父包-->
    <context:component-scan base-package="com.xiaojie"/>

2.7.3 @Value简单属性赋值

需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。
使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加
到 setter 上


@Component("myStudent")
public class Student {
    /**
     * @Value:简单属性类型赋值
     * 属性:value是string类型的,表示简单类型的值
     * 位置:在属性的上面定义,无需set方法(推荐使用)
     */

    @Value("李四")
    private String name;
    @Value("30")
    private Integer age;

    public Student(){
   	 }
    }

2.7.4 byType 自动注入@Autowired( 掌握)

@Component("myStudent")
public class Student {
    @Value("小小")
    private String name;
    @Value("20")
    private Integer age;
    /**
     * 引用类型
     * @Autowired:spring框架提供的注解,实现引用类型的赋值
     * spring通过注解的形式给引用类型赋值,使用的是自动注解的原理,支持byName,byType
     * @Autowired默认使用byType自动注入
     * 
     */
    @Autowired
    private School school;

2.7.4 byName 自动注入@Autowired 与@Qualifier( 掌握)

需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用
于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。

  /**
     * 如果要使用byName给属性赋值,需要做的是:
     * 1.在属性上加@Autowired
     * 2.在属性上面加@Qualifier(value=“bean的id”)
     */
    @Qualifier("school")
    @Autowired
    private School school;

@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运
行。若将其值设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。

2.7.5 JDK 注解@Resource 自动注入( 掌握)

jdk11以上不支持@Resource,需要在pom文件里加入配置:

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.1</version>
</dependency>

Spring提供了对jdk中@Resource注解的支持。@Resource注解既可以按名称匹配Bean,
也可以按类型匹配 Bean。 默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。
@Resource 可在属性上,也可在 set 方法上。

  • byType 注入引用类型 注入引用类型
    @Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入。
 @Resource
    private School school;
  • byName 注入引用类型 注入引用类型
    @Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。
 @Resource(name="school")
    private School school;

2.7.6 注解与 XML 的对比

注解优点是:

  • 方便
  • 直观
  • 高效(代码少,没有配置文件的书写那么复杂)。

其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。

XML 方式优点是:

  • 配置和代码是分离的
  • 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。

xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。

第三章 AOP 面向切面编程

3.1 AOP概述

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程
序运行过程。
AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB
的动态代理

Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了,
让开发人员用一种统一的方式,使用动态代理。

3.2 动态代理(掌握)

实现方式:

  1. jdk动态代理:使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。jdk动态代理要求目标类必须实现接口
  2. cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的

动态代理的作用:
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。

3.3 面向切面编程

3.3.1 怎么理解面向切面编程(掌握)

  1. 需要在分析项目功能时,找出切面。
  2. 合理的安排切面 的执行时间(在目标方法前,还是目标方法后)
  3. 合理的安排切面的执行位置,在哪个类,哪个方法增加功能

3.3.2 面向切面编程有什么好处?

1.减少重复;
2.专注业务;
注意:面向切面编程只是面向对象编程的一种补充。

3.3 AOP编程术语(掌握)

(1 ) 切面(Aspect )
表示增强的功能,就是一堆代码,完成某个一个功能。非业务功能常见的切面功能由日志,事务,统计信息,参数检查,权限验证。

(2 ) 连接点(JoinPoint )
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。

(3 ) 切入点(Pointcut )
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

(4 ) 目标对象(Target )
目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。

(5 ) 通知(Advice )
通知表示切面的执行时间,Advice 也叫增强。切入点定义切入的位置,通知定义切入的时间。

3.4 AspectJ 对 AOP 的实现(掌握)

对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。

AspectJ 简介:

AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。

官网 地址:http://www.eclipse.org/aspectj/

3.4.1 AspectJ 的切入点表达式( 掌握)

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)

解释:
modifiers-pattern 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型

?-----表示可选的部分
以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就
是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可
以使用以下符号:
在这里插入图片描述
举例(最常用的五个,记住):

execution(public * *(…))
指定切入点为:任意公共方法。

execution(* set*(…))
指定切入点为:任何一个以“set”开始的方法。

execution(* com.xyz.service..(…))
指定切入点为:定义在 service 包里的任意类的任意方法。

execution(* com.xyz.service….(…))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“…”出现在类名中时,后
面必须跟“*”,表示包、子包下的所有类。

execution(* …service..*(…))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点

3.4.2 AspectJ 基于注解的 AOP 实现(掌握)

1.新建maven项目

2.加入依赖
1)spring依赖

 <!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

2)aspectj依赖

 <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

3)junit单元测试

    <!--单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

3.创建目标类:接口和它的实现类。
要做的是给类中的方法增加功能

public interface SomeService {
    void doSome(String name,String age);
}


public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name, String age) {
        System.out.println("==================dosome方法执行=========================");
    }
}

4.创建切面类:普通类
1)在类的上面加上 @Aspect
2)在类中定义方法,方法就是切面要执行的功能代码
在方法的上面加入aspectj中的通知注解,例如@Before
有需要指定切入点表达式execution()

/**
 *   @Aspect:是aspectj框架中的注解。
 *     作用:表示当前类是切面类。
 *     切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
 */
@Aspect
public class MyAspect {
    /**
     * 定义方法,方法是实现切面功能的。
     * 方法的定义要求:
     * 1.公共方法public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法可以有参数,也可以没有参数。
     *    如果有参数,参数不是自定义的,有几个参数类型可以使用。
     */

    /**
     * @Before:前置通知注解
     *     属性:value,是切入点表达是,表示切面的功能执行的位置。
     *     位置:在方法的上面
     * 特点:
     *      1.在目标方法之前先执行的
     *      2.不会改变目标方法的执行结果
     *      3.不会影响目标方法的执行。
     */
    @Before(value = "execution(public void com.xiaojie.ba01.SomeServiceImpl.doSome(String,String))")
    public void doAspe(){
        System.out.println("时间为:"+new Date());
    }
}

5.创建spring的配置文件:声明对象,把对象交给容器统一管理
声明对象你可以使用注解或者xml配置文件

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

    <!--把对象交给spring容器,由spring容器统一创建,管理对象-->
    <!--声明目标类对象-->
    <bean id="someService" class="com.xiaojie.ba01.SomeServiceImpl"></bean>

    <!--声明切面类对象-->
    <bean id="myAspect" class="com.xiaojie.ba01.MyAspect"></bean>

    <!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。
        创建代理对象是在内存中实现的,修改目标对象的内存中的结构。创建为代理对象
        所以目标对象就是被修改后的代理对象。
    -->
    <aop:aspectj-autoproxy />

</beans>

6.创建测试类,从spring容器中获取目标对象(实际就是代理对象)。
通过代理执行方法,实现aop的功能增强。

    @Test
    public void test1(){
        String config="applicationContext.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(config);
        //从容器中获取目标对象
        SomeService s = (SomeService) ac.getBean("someService");
        //通过代理的对象执行方法,实现目标方法执行时,增强功能
        s.doSome("zhangsan","20");
    }

7.输出:

"D:\Program Files\Java\jdk-11.0.4\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar=63618:D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\lib\idea_rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit-rt.jar;D:\Program Files\JetBrains\IntelliJ IDEA 2018.3.3\plugins\junit\lib\junit5-rt.jar;D:\Java  idea代码区\SpringTest\ch03aopaspectJ\target\test-classes;D:\Java  idea代码区\SpringTest\ch03aopaspectJ\target\classes;D:\Program Files\mave_repository\org\springframework\spring-context\5.2.5.RELEASE\spring-context-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aop\5.2.5.RELEASE\spring-aop-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-beans\5.2.5.RELEASE\spring-beans-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-core\5.2.5.RELEASE\spring-core-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-jcl\5.2.5.RELEASE\spring-jcl-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-expression\5.2.5.RELEASE\spring-expression-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\springframework\spring-aspects\5.2.5.RELEASE\spring-aspects-5.2.5.RELEASE.jar;D:\Program Files\mave_repository\org\aspectj\aspectjweaver\1.9.5\aspectjweaver-1.9.5.jar;D:\Program Files\mave_repository\junit\junit\4.11\junit-4.11.jar;D:\Program Files\mave_repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.xiaojie.AppTest,test1
时间为:Wed Jul 14 17:04:17 CST 2021
==================dosome方法执行=========================

Process finished with exit code 0
1)@Before 前置通知- 方法有 JoinPoint 参数 [ 掌握]

在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。
不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。


/**
 *   @Aspect:是aspectj框架中的注解。
 *     作用:表示当前类是切面类。
 *     切面类:是用来给业务方法增加功能的类,在这个类中有切面功能代码
 */
@Aspect
public class MyAspect {
    /**
     * 定义方法,方法是实现切面功能的。
     * 方法的定义要求:
     * 1.公共方法public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法可以有参数,也可以没有参数。
     *    如果有参数,参数不是自定义的,有几个参数类型可以使用。
     */

    /**
     * @Before:前置通知注解
     *     属性:value,是切入点表达是,表示切面的功能执行的位置。
     *     位置:在方法的上面
     * 特点:
     *      1.在目标方法之前先执行的
     *      2.不会改变目标方法的执行结果
     *      3.不会影响目标方法的执行。
     */
    @Before(value = "execution(public void com.xiaojie.ba01.SomeServiceImpl.doSome(String,String))")
    public void doAspe(){
        System.out.println("时间为:"+new Date());
    }
}
2)@AfterReturning 后置通知- 注解有 returning 属性[ 掌握]

在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

//接口实现类方法,有返回值,
public class SomeServiceImpl implements SomeService {
    public Student doOther2(String name, Integer age) {
        Student st = new Student();
        st.setAge(age);
        st.setName(name);
        return st;
    }
}
@Aspect
public class MyAspect {
    /**
     * 后置通知定义方法,方法是实现切面功能的。
     * 方法的定义要求:
     * 1.公共方法public
     * 2.方法没有返回值
     * 3.方法名称自定义
     * 4.方法有参数,参数类型为Object,参数名自定义
     *
     */

    /**
     * @AfterReturning:后置通知注解
     *     属性:1.value,是切入点表达是,表示切面的功能执行的位置。
     *          2.returning 自定义的变量,表示目标方法的返回值。
     *          自定义变量名必须和通知方法的形参名一样。
     *     位置:在方法的上面
     * 特点:
     *      1.在目标方法之后执行的
     *      2.能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能
     *      3.可以修改这个返回值
     */
    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning="res")
    public void myAfterReturning(Object res){
        //res 是目标方法执行后的返回值,根据返回值做你的切面的功能处理
        System.out.println("返回值为:"+res);
        System.out.println("事务提交");
    }
3)@Around 环绕通知- 增强方法有 ProceedingJoinPoint[ 掌握]

在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。

@Aspect
public class MyAspect {
    /**
     *环绕通知定义格式
     * 1.公共方法public
     * 2.方法有返回值,推荐使用Object
     * 3.方法名称自定义
     * 4.方法有参数,固定的参数ProceedingJoinpoint
     */

    /**
     * @Around:环绕通知
     *     属性:value,是切入点表达是,表示切面的功能执行的位置。
     *     位置:在方法的上面
     * 特点:
     *      1.它是功能最强的通知
     *      2.在目标的前后都能增强功能
     *      2.控制目标方法是否被执行调用
     *      3.修改原来目标类执行的结果,影响最后调用结果
     *
     *      环绕通知等同于jdk动态代理,InvocationHandler接口
     *
     * 参数: ProceedingJoinPoint  就等同于 Method
     *        作用:执行目标方法的
     * 返回值:就是目标方法的执行结果,可以被修改。
     */
    @Around(value = "execution(* *..SomeServiceImpl.doSome(String,String))")
    public Object doAspe(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("输出日志信息----------------------");
        Object obj=null;
        //执行目标方法,等同于Method.invoke(traget,args)
        obj = pjp.proceed();
        System.out.println("提交事务-------------------------");
        return obj;
    }
}

4)@AfterThrowing 异常通知- 注解中有 throwing 属性 [ 了解]

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。

//接口实现类,
public class SomeServiceImpl implements SomeService {
    @Override
    public void doSome(String name, String age) {

        System.out.println("==================dosome方法执行========================="+(10/0));
    }
}

//异常通知类
@Aspect
public class MyAspect {
    /**
     *异常通知定义格式
     * 1.公共方法public
     * 2.没有返回值
     * 3.方法名称自定义
     * 4.方法有一个Exception,如果还有是JoinPoint
     */

    /**
     * @AfterThrowing:异常通知
     *     属性:value,是切入点表达是,表示切面的功能执行的位置。
     *          2.throwinng 自定义的变量,表示目标方法抛出的异常对象
     *              变量名必须和方法参数名一样
     *     位置:在方法的上面
     * 特点:
     *     1.在目标方法抛出异常时执行的
     *     2.可以做异常的监控程序,监控目标方法执行时是不是有异常。
     *     如果有异常,可以发送邮件,短信进行通知
     */
    @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSome(String,String))",throwing = "ex")
    public void  myAfterThrowing(Exception ex) {
        System.out.println("异常通知:"+ex.getMessage());
    }
}


输出:


异常通知:/ by zero

java.lang.ArithmeticException: / by zero

	at com.xiaojie.ba04.SomeServiceImpl.doSome(SomeServiceImpl.java:7)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
5)@After 最终通知 [了解]

无论目标方法是否抛出异常,该增强均会被执行。

增加方法:
在这里插入图片描述
方法实现:
在这里插入图片描述
定义切面:
在这里插入图片描述

6)@Pointcut 定义切入点

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
在这里插入图片描述

第四章 Spring集成Mybatis

将 MyBatis与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring来管理。所以,该整合,只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。实现 Spring 与 MyBatis 的整合常用的方式:扫描的 Mapper 动态代理Spring 像插线板一样,mybatis 框架是插头,可以容易的组合到一起。插线板 spring 插上 mybatis,两个框架就是一个整体。

4.1 MySQL 创建数据库 user, 新建表 Student

4.2 maven 依赖 pom.xml

 <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>
    <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>
    <dependency>
        <!--mysql驱动依赖-->
      <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>
<build>

    <resources>
      <resource>
        <directory>src/main/java</directory><!--所在的目录-->
        <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>
 <plugins>
	<plugin>
		<artifactId>maven-compiler-plugin</artifactId>
			<version>3.1</version>
			<configuration>
			<source>1.8</source>
			<target>1.8</target>
		</configuration>
	</plugin>
	</plugins>
  </build>

4.3 定义实体类和接口

//实体类
public class Student {
    private String id;
    private String name;
    private String sex;
    private int age;
    private String classname;


//dao层接口
public interface StudentDao {
    int addStudent(Student student);
    List<Student>  seletStudent();
}


4.4 定义mapper文件


<?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.xiaojie.dao.StudentDao">
    <insert id="saveStudent">
        insert into student
        values(#{id},#{name},#{sex},#{age},#{classname})
    </insert>
    <select id="seletStudent" resultType="Student">
        select *from student order by id
    </select>
</mapper>

4.5 定义 Service 接口和实现类

//接口
public interface StudentService {
    int saveStudent(Student student);
    List<Student> getStudent();
}


//实现类
public class StudentServiceImpl implements StudentService {
    private StudentDao studentDao;

    //set方法,使用set注入赋值
    public void setStudentDao(StudentDao studentDao) {
        this.studentDao = studentDao;
    }

    @Override
    public int saveStudent(Student student) {
        int s = studentDao.addStudent(student);
        return s;
    }

    @Override
    public List<Student> getStudent() {
        List<Student> list = studentDao.seletStudent();
        return list;
    }
}

4.6 定义 MyBatis 主配置文件

在 src 下定义 MyBatis 的主配置文件,命名为 mybatis.xml。
这里有两点需要注意:
(1)主配置文件中不再需要数据源的配置了。因为数据源要交给 Spring 容器来管理了。
(2)这里对 mapper 映射文件的注册,使用标签,即只需给出 mapper 映射文件所在的包即可。因为 mapper 的名称与 Dao 接口名相同,可以使用这种简单注册方式。这种方式的好处是,若有多个映射文件,这里的配置也是不用改变的。当然,也可使用原来的标签方式。

<?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:控制mybatis全局行为-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--起别名标签,在com.xiaojie.domian包下的类文件,所有的别名都为类名-->
    <typeAliases>
        <package name="com.xiaojie.domain"/>
    </typeAliases>

    <mappers>
        <package name="com.xiaojie.dao"/>
    </mappers>
</configuration>

4.6 修改 Spring 配置文件

4.6.1 数据源的配置 (掌握)

使用 JDBC 模板,首先需要配置好数据源,数据源直接以 Bean 的形式配置在 Spring 配置文件中。根据数据源的不同,其配置方式不同:
Druid 数据源 DruidDataSourceDruid 是阿里的开源数据库连接池。是 Java 语言中最好的数据库连接池。Druid 能够提供强大的监控和扩展功能。Druid 与其他数据库连接池的最大区别是提供数据库的

官网:https://github.com/alibaba/druid
使用地址:https://github.com/alibaba/druid/wiki/常见问题
配置连接池:
在这里插入图片描述
Spring 配置文件:

 <!--声明数据源,作用是连接数据库-->
    <bean id="mydataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/user" />
        <property name="username" value="root" />
        <property name="password" value="111" />
        <property name="maxActive" value="20"/>
    </bean>

**也可从属性配置文件中读取配置信息,例如:
属性文件名称自定义,但一般都是放在 src 下。
在这里插入图片描述
Spring 配置文件从属性文件中读取数据时,需要在的 value 属性中使用${ },
将在属性文件中定义的 key 括起来,以引用指定属性的值。
在这里插入图片描述
该属性文件若要被 Spring 配置文件读取,其必须在配置文件中进行注册。使用
标签。
context:property-placeholder/ 方式( 掌握)
该方式要求在 Spring 配置文件头部加入 spring-context.xsd 约束文件
context:property-placeholder/标签中有一个属性 location,用于指定属性文件的位置。
在这里插入图片描述

4.6.2 注册 SqlSessionFactoryBean

 <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="mydataSource"/>
        <!--    mybatis主配置文件的位置
                configLocation属性是Resource类型,读取配置文件
                它的赋值,使用value,指定文件的路径,使用classpath:表示文件的的位置
        -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

4.6.3 定义 Mapper 扫描配置器 MapperScannerConfigurer

Mapper 扫描配置器 MapperScannerConfigurer 会自动生成指定的基本包中 mapper 的代理对象。该 Bean 无需设置 id 属性。basePackage 使用分号或逗号设置多个包。

<!--创建dao对象,使用SqlSession的getMapper(StudentDao.classMapper Scanner Configurer:在内部调用getMapper()生成每个dao接口的代理对象
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定SqlSessionFactory对象的id-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!--指定包名,包名是dao接口所在的包名
            MapperScannerConfigurer会扫面这个包中的所有接口,把每个接口都执行
            一次getMapper()方法,得到每个接口的dao对象。
            创建好的dao对象放入到spring容器中的。dao对象默认名称是接口首字母小写
        -->
        <property name="basePackage" value="com.xiaojie.dao"/>

    </bean>

4.6.4 向 Service 注入接口名

<!--声明service-->
    <bean id="studentService" class="com.xiaojie.service.Impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

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

    <!--声明数据源,作用是连接数据库-->
    <bean id="mydataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/user" />
        <property name="username" value="root" />
        <property name="password" value="111" />
        <property name="maxActive" value="20"/>
    </bean>

    <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="mydataSource"/>
        <!--    mybatis主配置文件的位置
                configLocation属性是Resource类型,读取配置文件
                它的赋值,使用value,指定文件的路径,使用classpath:表示文件的的位置
        -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!--创建dao对象,使用SqlSession的getMapper(StudentDao.classMapper Scanner Configurer:在内部调用getMapper()生成每个dao接口的代理对象
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定SqlSessionFactory对象的id-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!--指定包名,包名是dao接口所在的包名
            MapperScannerConfigurer会扫面这个包中的所有接口,把每个接口都执行
            一次getMapper()方法,得到每个接口的dao对象。
            创建好的dao对象放入到spring容器中的。dao对象默认名称是接口首字母小写
        -->
        <property name="basePackage" value="com.xiaojie.dao"/>

    </bean>

    <!--声明service-->
    <bean id="studentService" class="com.xiaojie.service.Impl.StudentServiceImpl">
        <property name="studentDao" ref="studentDao"/>
    </bean>

</beans>

第五章 Spring事务

5.1 Spring的事务管理

事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层,即 Service 层。这样做是为了能够使用事务的特性来管理具体的业务。在 Spring 中通常可以通过以下两种方式来实现对事务的管理:
(1)使用 Spring 的事务注解管理事务
(2)使用 AspectJ 的 AOP 配置管理事务

5.2 Spring 事务管理API

Spring 的事务管理,主要用到两个事务相关的接口。

(1) 事务管理器接口(重点)

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。
在这里插入图片描述

A 、 常用的两个实现类

PlatformTransactionManager 接口有两个常用的实现类:
DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

B 、 Spring 的回滚方式(理解)

Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。不过,对于受查异常,程序员也可以手工设置其回滚方式。

C 、 回顾错误与异常( 理解)

在这里插入图片描述
Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)
的实例时,才能通过 Java 虚拟机或者 Java 的 throw 语句抛出。Error 是程序在运行过程中出现的无法处理的错误,比如 OutOfMemoryError、ThreadDeath、NoSuchMethodError 等。当这些错误发生时,程序是无法处理(捕获或抛出)的,JVM 一般会终止线程。程序在编译和运行时出现的另一类错误称之为异常,它是JVM通知程序员的一种方式。通过这种方式,让程序员知道已经或可能出现错误,要求程序员对其进行处理。异常分为运行时异常与受查异常。
运行时异常,是 RuntimeException 类或其子类,即只有在运行时才出现的异常。如,NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException 等均属于运行时异常。这些异常由 JVM 抛出,在编译时不要求必须处理(捕获或抛出)。但,只要代码编写足够仔细,程序足够健壮,运行时异常是可以避免的。受查异常,也叫编译时异常,即在代码编写时要求必须捕获或抛出的异常,若不处理,则无法通过编译。如 SQLException,ClassNotFoundException,IOException 等都属于受查异常。RuntimeException 及其子类以外的异常,均属于受查异常。当然,用户自定义的 Exception
的子类,即用户自定义的异常也属受查异常。程序员在定义异常时,只要未明确声明定义的为 RuntimeException 的子类,那么定义的就是受查异常。

(2 ) 事务定义接口

事务定义接口TransactionDefinition中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作。
在这里插入图片描述

A 、 定义了五个事务隔离级别常量( 掌握)

这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。
➢ DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle
默认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题。

B 、 定义了七个事务传播行为常量( 掌握)

所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的维护情况,就称为事务传播行为。事务传播行为是加在方法上的。事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。

PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS

PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED

黄色标记为常用

(1) PROPAGATION_REQUIRED

指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。

如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。
在这里插入图片描述

(2) PROPAGATION_SUPPORTS

指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
在这里插入图片描述

(3) PROPAGATION_REQUIRES_NEW

总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
在这里插入图片描述

C 、 定义了默认事务超时时限

常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。
注意,事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该
值一般就使用默认值即可

5.3 程序举例环境搭建

(1) 创建数据库表

创建两个数据库表 sale , goods

sale 销售表
在这里插入图片描述
goods 商品表
在这里插入图片描述
goods 表数据
在这里插入图片描述

(2) maven依赖pom.xml

<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>
    <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>
    <dependency>
        <!--mysql驱动依赖-->
      <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>
    <resources>
      <resource>
        <directory>src/main/java</directory><!--所在的目录-->
        <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>
   </build>

(3)定义 dao 接口

定义两个 dao 的接口 SaleDao , GoodsDao
在这里插入图片描述
在这里插入图片描述

(4)定义dao接口对应的sql映射文件

SaleDao.xml
在这里插入图片描述
GoodsDao.xml
在这里插入图片描述

(5)定义异常类

定义 service 层可能会抛出的异常类 NotEnoughException
在这里插入图片描述

(6)定义 Service 接口

定义 Service 接口 BuyGoodsService
在这里插入图片描述

(7)定义 service 的实现类

定义 service 层接口的实现类 BuyGoodsServiceImpl

  1. 类定义
    在这里插入图片描述

  2. Dao 属性
    在这里插入图片描述

  3. Buy 方法
    在这里插入图片描述

(8)修改 Spring 配置文件内容

  1. 声明 Mybatis 对象

    <!--声明数据源,作用是连接数据库-->
    <bean id="mydataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="jdbc:mysql://localhost:3306/user" />
        <property name="username" value="root" />
        <property name="password" value="111" />
        <property name="maxActive" value="20"/>
    </bean>

    <!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--set注入,把数据库连接池付给了dataSource属性-->
        <property name="dataSource" ref="mydataSource"/>
        <!--    mybatis主配置文件的位置
                configLocation属性是Resource类型,读取配置文件
                它的赋值,使用value,指定文件的路径,使用classpath:表示文件的的位置
        -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!--创建dao对象,使用SqlSession的getMapper(StudentDao.classMapper Scanner Configurer:在内部调用getMapper()生成每个dao接口的代理对象
    -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定SqlSessionFactory对象的id-->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!--指定包名,包名是dao接口所在的包名
            MapperScannerConfigurer会扫面这个包中的所有接口,把每个接口都执行
            一次getMapper()方法,得到每个接口的dao对象。
            创建好的dao对象放入到spring容器中的。dao对象默认名称是接口首字母小写
        -->
        <property name="basePackage" value="com.xiaojie.dao"/>

    </bean>
  1. 声明业务层对象
    在这里插入图片描述

(9)定义测试类

定义测试类 MyTest。现在就可以在无事务代理的情况下运行了。
在这里插入图片描述

5.4 使用Spring的事务注解管理事务(掌握)

通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。

@Transactional 的所有可选属性如下所示:
propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为

Propagation.REQUIRED。
isolation:用于设置事务的隔离级别。该属性类型为 Isolation 枚举,默认值为
Isolation.DEFAULT。
readOnly:用于设置该方法对数据库的操作是否是只读的。该属性为 boolean,默认值
为 false。
timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为
-1,即没有时限。
rollbackFor:指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有
一个异常类时,可以不使用数组。
rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。
当然,若只有一个异常类时,可以不使用数组。
noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若
只有一个异常类时,可以不使用数组。
noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空
数组。当然,若只有一个异常类时,可以不使用数组。

需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。

实现注解的事务步骤:

  1. 声明事务管理器
<!--使用spring的事务处理-->
    <!--1.声明事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="mydataSource"/>
    </bean>

  1. 开启注解驱动
 <!--2.开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

transaction-manager:事务管理器 bean 的 id

  1. 业务层 public 方法加入事务属性
    在这里插入图片描述

5.5 使用 AspectJ 的 AOP 配置管理事务( 掌握)

使用 XML 配置事务代理的方式的不足是,每个目标类都需要配置事务代理。当目标类
较多,配置文件会变得非常臃肿。

使用 XML 配置顾问方式可以自动为每个符合切入点表达式的类生成事务代理。其用法
很简单,只需将前面代码中关于事务代理的配置删除,再替换为如下内容即可。

5.5.1 maven 依赖 pom.xml

新加入 aspectj 的依赖坐标

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>5.2.5.RELEASE</version>
</dependency>

5.5.2 在容器中添加事务管理器

 <!--声明式事务处理:和源代码完全分离的-->
    <!--1.声明事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="mydataSource"/>
    </bean>

5.5.3 配置事务通知

<!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间
            id:自定义名称,表示<tx:advice></tx:advice>之间的配置内容的
            transaction-manager:事务管理器对象的id
    -->
    <tx:advice id="myAdvice" transaction-manager="transactionManager">
        <!--tx:attributes:配置事务属性-->
        <tx:attributes>
            <!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
                name:方法名称:1)完整的方法名称,不带有包名和类
                               2)方法可以使用通配符,* 表示任意字符
                propagation:传播行为,枚举值
                isolation:隔离级别
                rollback-for:你指定的异常类名,全限定类名。发生异常一定回滚
                -->
            <tx:method name="saveStudent" propagation="REQUIRED" isolation="DEFAULT"
            rollback-for="java.lang.NullPointerException"/>

            <!--使用通配符,指定很多的方法-->
            <tx:method name="add*" propagation="REQUIRES_NEW"/>
            <!--指定修改方法-->
            <tx:method name="modify*"/>
            <!--指定删除方法-->
            <tx:method name="remove*"/>
            <!--指定查询方法,query,search,find-->
            <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>

5.5.4 配置增强器

 <!--配置aop-->
    <aop:config>
        <!--配置切入点表达式:指定哪些包中类,要使用事务
            id:切入点表达式的名称,唯一值
            expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象

            com.xiaojie.service
            com.crm.service
            com.service
          -->
        <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>

        <!--配置增强器:关联adivce和pointcut
            advice-ref:通知,上面tx:advice 哪里的配置
            pointcut-ref:切入点表达式的id
        -->
        <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
    </aop:config>

第六章 Spring 与 Web

6.1 使用 Spring 的器监听器 ContextLoaderListener( 掌握)

对于 Web 应用来说,ServletContext 对象是唯一的,一个 Web 应用,只有一个ServletContext 对象,该对象是在 Web 应用装载时初始化的。若将 Spring 容器的创建时机,放在 ServletContext 初始化时,就可以保证 Spring 容器的创建只会执行一次,也就保证了Spring 容器在整个应用中的唯一性。
当 Spring 容器创建好后,在整个应用的生命周期过程中,Spring 容器应该是随时可以被访问的。即,Spring 容器应具有全局性。而放入 ServletContext 对象的属性,就具有应用的全局性。所以,将创建好的 Spring 容器,以属性的形式放入到 ServletContext 的空间中,就保证了 Spring 容器的全局性。

Step1 :maven 依赖 pom.xml

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-web</artifactId>
	<version>5.2.5.RELEASE</version>
</dependency>

Step2 :注册监听器 ContextLoaderListener

若 要 在 ServletContext 初 始 化 时 创 建 Spring 容 器 , 就 需 要 使 用 监 听 器 接 口
ServletContextListener 对 ServletContext 进行监听。在 web.xml 中注册该监听器。

在这里插入图片描述

Step3 :指定 Spring 配置文件的位置

ContextLoaderListener 在对 Spring 容器进行创建时,需要加载 Spring 配置文件。其默认
的 Spring 配置文件位置与名称为:WEB-INF/applicationContext.xml。但,一般会将该配置文件放置于项目的 classpath 下,即 src 下,所以需要在 web.xml 中对 Spring 配置文件的位置及名称进行指定。
在这里插入图片描述

Step4 :获取 Spring 容器对象

在 Servlet 中获取容器对象的常用方式有两种:

  1. 直接从 ServletContext 中获
    从对监听器 ContextLoaderListener 的源码分析可知,容器对象在 ServletContext 的中存
    放的 key 为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。所以,可
    以直接通过 ServletContext 的 getAttribute()方法,按照指定的 key 将容器对象获取到。
    在这里插入图片描述
  2. 通过 WebApplicationContextUtils 获取
    工具类 WebApplicationContextUtils 有一个方法专门用于从 ServletContext 中获取 Spring
    容器对象:getRequiredWebApplicationContext(ServletContext sc)
    调用 Spring 提供的方法获取容器对象:
    在这里插入图片描述

附上思维导图

在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值